Ich habe in den letzten Tagen an einem Script gebaut, dass ich aus dem Home Assistant ausführen möchte.
Aufgabestellung
Im Dashboard möchte ich Buttons einbauen über die ich ein Python Script ausführen möchte. Das Script soll eine SSH Verbindung zu einem remote Rechner aufbauen und diesen dann herunterfahren. Es sollen unterschiedliche Rechner heruntergefahren werden können. Dazu wird das Script beim Starten mit Argumenten gefüttert, die dann das Ziel festlegen, und welches Kommando ausgeführt werden soll.
Python
Möchte man Python als Scriptsprache verwenden, sollen in der Regel auch Bibliotheken nutzbar sein. Die Einbindung eines Moduls erfolgt dann üblicherweise über das Schlüsselwort import <modul-name>
.
Für das Anlegen eines SSH-Clients möchte ich gerne das Modul paramiko verwenden. Über das Terminal lässt sich die Bibliothek per
pip install paramiko
installieren
Nachdem ich mein Python Skript erstellt habe, lief es im Terminal problemlos, so dass mit der Integration in HA begonnen werden konnte.
Hier gibt es dann schon den ersten Haken.Die Standard Python Installation unterstützt nicht die Verwendung des Schlüsselworts import. Aber es gibt ja noch andere Python Integrationen in HA.
Letztendlich bin ich bei PythonScriptsPro gelandet, die per HACS installierbar ist. Sie ersetzt dann die standard python_script Komponente.
In der Configuration.yaml kann die Python Unterstützung über folgenden Eintrag aktiviert werden:
#support python scripts
python_script:
requirements:
- instagrapi
- datetime
- paramiko>=3.4.0
Die zusätzlichen Module die importierbar sein sollen, sind dann unter requirements konfiguriert.
Hier das Script:
import paramiko
logger.debug(data)
ha_target = data.get("dns")
ha_command = data.get("command")
def paramiko_if(host, port, command):
# Create object of SSHClient and
# connecting to SSH
ssh = paramiko.SSHClient()
# Adding new host key to the local
# HostKeys object(in case of missing)
# AutoAddPolicy for missing host key to be set before connection setup.
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host[0], port, host[1], host[2])
# Execute command on SSH terminal
# using exec_command
stdin, stdout, stderr = ssh.exec_command(command)
resp = stdout.read()
stderr.read()
ssh.close()
logger.info(f"SSH response:\n{ resp }")
def switch_device(device, command):
if device == 'NAS':
host = ['<IP-Adresse>', '<USER>', '<PASSWORT>']
port = '28'
paramiko_if(host, port, command)
if device == 'weiterer Rechner':
.........
logger.info("shutdown.py executed")
switch_device(ha_target, ha_command)
Die Zugangsdaten der Rechner habe ich in dem Python Script in der Struktur host angelegt. An das Script werden die Daten “dns” und “command” übergeben. Dns enthält dabei den Rechnernamen (also z.B. NAS), command das Kommando, dass auf dem Remote Rechner auszuführen ist.
Die logger.info Ausgabe wird übrigens im home-assistant.log eingetragen, dass sich im standard CONFIG Verzeichnis befindet. Ein Logeintrag könnte dann so aussehen:
2024-02-11 23:33:32.603 INFO (SyncWorker_40) [custom_components.python_script] shutdown.py executed
2024-02-11 23:33:32.623 INFO (Thread-11) [paramiko.transport] Connected (version 2.0, client OpenSSH_8.9p1)
2024-02-11 23:33:32.698 INFO (Thread-11) [paramiko.transport] Authentication (password) successful!
2024-02-11 23:33:32.921 INFO (SyncWorker_40) [custom_components.python_script] SSH response:
b''
Ansteuern des Scripts
Im Dashboard habe ich mir folgende Elemente angelegt.
Dadurch kann ich jeden einzelnen Rechner per Wake on Lan hochfahren und auch wieder ausschalten. Bei den Buttons handelt es sich um custom button cards.
Die Erkennung, wann ein Gerät online ist, erfolgt über die Ping integration. Diese stellt den Gerätezustand (on- oder offline) als binary_sensor zur Verfügung. Der yaml Code für das NAS Laufwerk sieht dann wiefolgt aus:
type: vertical-stack
cards:
- type: horizontal-stack
cards:
- type: entity
entity: binary_sensor.192_168_110_43
state_color: true
name: Nas
card_mod:
style: |
ha-card {
height: 80px !important;
}
ha-card .value{
font-size: 17px;
}
- type: horizontal-stack
cards:
- type: custom:button-card
entity: switch.virtual_switch_1
size: 30px
entity_picture: /local/wol.png
show_entity_picture: true
card_mod:
style: |
ha-card {
height: 80px !important;
}
tap_action:
action: call-service
service: wake_on_lan.send_magic_packet
metadata: {}
data:
broadcast_port: 9
mac: 00:11:32:7B:xx:xx
confirmation:
text: '[[[ return `Wake On Lan durchführen` ]]]'
- type: custom:button-card
entity: switch.virtual_switch_1
size: 30px
entity_picture: /local/shutdwn.png
show_entity_picture: true
card_mod:
style: |
ha-card {
height: 80px !important;
}
tap_action:
action: call-service
service: python_script.exec
data:
file: python_scripts/shutdown.py
dns: NAS
command: sudo shutdown -h now
confirmation:
text: '[[[ return `shutdown durchführen ?` ]]]'
Die weiteren Elemente sind nach dem Gleichen Schema aufgebaut. Es geht also mit einem horizontalen Stack weiter.
Das Aufwecken per WOL geschieht über die Core funktionalität und den dazugehörigen Service wake_on_lan.send_magic_packet kombiniert mit der MAC Adresse meines Geräts.
Das Herunterfahren läuft über das Python script (Shutdown.py heißt das auf meinem System) ab. Dem Python Service werden beim Aufruf wie eingangs erwähnt, die Daten dsn und das Kommando command übergeben.
Dabei unterscheiden sich die Befehle zum Herunterfahren von Unix- und Windows-Systemen
- Unix: sudo shutdown -h now
- Windows: shutdown /s /t 5
Damit die Rechner nicht versehentlich gestartet oder heruntergefahren werden, wenn man den Button drückt, habe ich noch eine “Confirmation” eingebaut. Somit erfolgt die Ausführung des Befehls erst nach Bestätigung durch den Anwender.
Auf Wunsch kann ich auch noch kurz beschreiben, wie der SSH-Server auf einem Windows Rechner aufzusetzen ist, damit der Shutdown funktioniert.