Skip to content

Commit bdf275c

Browse files
Merge pull request #85 from 404GamerNotFound/codex/erweiterung-von-sensor.py-fur-container-sensoren
Document dynamic container sensors
2 parents 529e2a8 + ec1d818 commit bdf275c

File tree

14 files changed

+266
-112
lines changed

14 files changed

+266
-112
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v1.2.10
4+
- Dynamically create container CPU and RAM sensors when new Docker containers appear on a monitored server.
5+
- Mark removed containers as unavailable so they disappear automatically after reloading the integration configuration.
6+
- Resolve SSH key paths relative to the Home Assistant config directory, validate that the file exists during setup, and accept
7+
those relative paths in service calls.
8+
- Document where to store SSH private keys and which path to provide when using the configuration wizard.
9+
310
## v1.2.9
411
- Provide service translation strings across all supported languages so hassfest validation passes.
512
- Document the 1.2.9 release in each README to keep metadata consistent with the manifest.

README.de.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Die Integration stellt außerdem Home-Assistant-Dienste bereit, um ad-hoc Befehl
3434
- Betriebssystem-Version
3535
- Installierte Pakete (Anzahl und Liste)
3636
- Docker-Installation, laufende Container und Auslastung einzelner Container (CPU und Speicher)
37+
- Automatische Erstellung neuer CPU- und Speichersensoren, sobald zusätzliche Container starten
3738
- VNC-Unterstützung
3839
- HTTP/HTTPS-Webserver-Status
3940
- SSH aktiviert
@@ -104,6 +105,16 @@ cards:
104105
- sensor.vps1_temp
105106
```
106107
108+
## Ablage des SSH-Schlüssels
109+
110+
- Unter **Home Assistant OS** die private SSH-Schlüsseldatei in das Verzeichnis `/config/ssh/` kopieren (z. B. über das File-
111+
Editor-Add-on oder die Samba-Freigabe). Ein Schlüssel `id_vserver` landet so unter `/config/ssh/id_vserver`.
112+
- Im Konfigurationsassistenten entweder den absoluten Pfad `/config/ssh/id_vserver` oder den relativen Pfad `ssh/id_vserver`
113+
(ausgehend vom Home-Assistant-Konfigurationsordner) eintragen. Beide Varianten werden unterstützt.
114+
- Es muss immer die **private** Schlüsseldatei angegeben werden, nicht die `.pub`-Datei.
115+
- Bei Home Assistant Container/Core kann auch ein beliebiger absoluter Pfad verwendet werden, auf den Home Assistant zugreifen
116+
darf.
117+
107118
## Sicherheitshinweise
108119
- Es wird empfohlen, einen dedizierten, eingeschränkten Benutzer für das SSH-Monitoring zu erstellen (mit nur Lesezugriff auf `/proc` und `df`).
109120
- SSH-Passwortauthentifizierung wird unterstützt, aber **SSH-Schlüssel-Authentifizierung** wird für den produktiven Einsatz dringend empfohlen.
@@ -112,8 +123,8 @@ cards:
112123
---
113124

114125
## Release-Management
115-
- Aktuelle stabile Version: **v1.2.9** (siehe `manifest.json`).
116-
- Erstellen Sie für jede veröffentlichte Version ein Git-Tag (z. B. `git tag v1.2.9`) sowie ein zugehöriges GitHub-Release, damit HACS Updates sauber nachvollziehen kann.
126+
- Aktuelle stabile Version: **v1.2.10** (siehe `manifest.json`).
127+
- Erstellen Sie für jede veröffentlichte Version ein Git-Tag (z. B. `git tag v1.2.10`) sowie ein zugehöriges GitHub-Release, damit HACS Updates sauber nachvollziehen kann.
117128
- Nutzen Sie das vorhandene Skript `scripts/bump_version.py`, um die Versionsnummer der Integration vor einer neuen Veröffentlichung zu erhöhen.
118129
- Pflegen Sie wichtige Änderungen zusätzlich in der [`CHANGELOG.md`](CHANGELOG.md).
119130

README.es.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ La integración también proporciona servicios de Home Assistant para ejecutar c
3434
- Versión del sistema operativo
3535
- Paquetes instalados (cantidad y lista)
3636
- Detección de Docker, contenedores en ejecución y uso por contenedor (CPU y memoria)
37+
- Creación automática de sensores de CPU y memoria por contenedor cuando se detectan nuevos contenedores
3738
- Estado de soporte VNC
3839
- Estado de servidor web HTTP/HTTPS
3940
- Estado de servicio SSH
@@ -104,6 +105,16 @@ cards:
104105
- sensor.vps1_temp
105106
```
106107
108+
## Ubicación de la clave SSH
109+
110+
- En **Home Assistant OS**, copia tu clave privada SSH en el directorio `/config/ssh/` (por ejemplo mediante el complemento File
111+
Editor o el recurso compartido Samba). Una clave llamada `id_vserver` quedará en `/config/ssh/id_vserver`.
112+
- En el asistente de configuración introduce la ruta absoluta `/config/ssh/id_vserver` o la ruta relativa al directorio de conf
113+
iguración de Home Assistant, por ejemplo `ssh/id_vserver`. Ambas formas son válidas.
114+
- Indica siempre el archivo de clave **privada**. No uses el archivo público `.pub`.
115+
- En instalaciones Home Assistant Container/Core también puedes proporcionar cualquier ruta absoluta a la que Home Assistant ten
116+
ga acceso.
117+
107118
## Notas de seguridad
108119
- Se recomienda crear un usuario dedicado y restringido para la supervisión por SSH (con acceso de solo lectura a `/proc` y `df`).
109120
- Se admite autenticación por contraseña, pero se recomienda encarecidamente la **autenticación por clave SSH** para uso en producción.
@@ -112,8 +123,8 @@ cards:
112123
---
113124

114125
## Gestión de lanzamientos
115-
- Versión estable actual: **v1.2.9** (coincide con `manifest.json`).
116-
- Crea una etiqueta Git (por ejemplo, `git tag v1.2.9`) y una versión en GitHub para cada lanzamiento a fin de que HACS pueda seguir las actualizaciones correctamente.
126+
- Versión estable actual: **v1.2.10** (coincide con `manifest.json`).
127+
- Crea una etiqueta Git (por ejemplo, `git tag v1.2.10`) y una versión en GitHub para cada lanzamiento a fin de que HACS pueda seguir las actualizaciones correctamente.
117128
- Utiliza el script existente `scripts/bump_version.py` para incrementar la versión de la integración al preparar una nueva publicación.
118129
- Registra los cambios relevantes en [`CHANGELOG.md`](CHANGELOG.md) junto con cada versión.
119130

README.fr.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ L'intégration fournit également des services Home Assistant pour exécuter des
3434
- Version du système d'exploitation
3535
- Paquets installés (nombre et liste)
3636
- Détection de Docker, conteneurs en cours d'exécution et utilisation par conteneur (CPU et mémoire)
37+
- Création automatique de capteurs CPU et mémoire par conteneur lorsque de nouveaux conteneurs sont détectés
3738
- Statut du support VNC
3839
- Statut du serveur web HTTP/HTTPS
3940
- Statut du service SSH
@@ -104,6 +105,16 @@ cards:
104105
- sensor.vps1_temp
105106
```
106107
108+
## Emplacement de la clé SSH
109+
110+
- Sous **Home Assistant OS**, copiez votre clé privée SSH dans le répertoire `/config/ssh/` (par exemple via l'add-on File Editor
111+
ou le partage Samba). Une clé nommée `id_vserver` se retrouvera dans `/config/ssh/id_vserver`.
112+
- Dans l'assistant de configuration, saisissez soit le chemin absolu `/config/ssh/id_vserver`, soit le chemin relatif au réperto
113+
ire de configuration de Home Assistant, par exemple `ssh/id_vserver`. Les deux formats sont acceptés.
114+
- Indiquez toujours le fichier de clé **privée**. Ne pointez pas vers le fichier public `.pub`.
115+
- Pour les installations Home Assistant Container/Core, vous pouvez également fournir tout chemin absolu accessible par Home Ass
116+
istant.
117+
107118
## Notes de sécurité
108119
- Il est recommandé de créer un utilisateur dédié et restreint pour la surveillance SSH (avec un accès en lecture seule à `/proc` et `df`).
109120
- L'authentification par mot de passe est prise en charge, mais l'**authentification par clé SSH** est fortement recommandée pour un usage en production.
@@ -112,8 +123,8 @@ cards:
112123
---
113124

114125
## Gestion des versions
115-
- Version stable actuelle : **v1.2.9** (conforme à `manifest.json`).
116-
- Créez une étiquette Git (par exemple `git tag v1.2.9`) et une publication GitHub pour chaque version afin que HACS puisse suivre correctement les mises à jour.
126+
- Version stable actuelle : **v1.2.10** (conforme à `manifest.json`).
127+
- Créez une étiquette Git (par exemple `git tag v1.2.10`) et une publication GitHub pour chaque version afin que HACS puisse suivre correctement les mises à jour.
117128
- Utilisez le script `scripts/bump_version.py` existant pour incrémenter la version de l'intégration lors de la préparation d'une nouvelle publication.
118129
- Consignez les changements importants dans [`CHANGELOG.md`](CHANGELOG.md) pour chaque version.
119130

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ The integration also provides Home Assistant services to run ad-hoc commands on
3636
- Operating system version
3737
- Installed packages (count and list)
3838
- Docker installation, running containers, and per-container CPU/memory usage
39+
- Automatic creation of per-container CPU and memory sensors whenever new containers start
3940
- VNC support status
4041
- HTTP/HTTPS web server status
4142
- SSH enabled status
@@ -108,6 +109,15 @@ cards:
108109
- sensor.vps1_temp
109110
```
110111
112+
## SSH Key Storage
113+
114+
- When running **Home Assistant OS**, copy your SSH private key into the `/config/ssh/` directory (for example via the File
115+
Editor add-on or Samba share). A key named `id_vserver` should end up at `/config/ssh/id_vserver`.
116+
- In the configuration wizard enter either the absolute path `/config/ssh/id_vserver` or the path relative to your Home
117+
Assistant configuration directory, e.g. `ssh/id_vserver`. Both forms are now accepted.
118+
- Always reference the **private** key file. Do not point Home Assistant to the `.pub` public key.
119+
- For Home Assistant Container/Core installations you may also supply any absolute path that Home Assistant can read.
120+
111121
## Security Notes
112122
- It is recommended to create a dedicated, restricted user for SSH monitoring (with read-only access to `/proc` and `df`).
113123
- SSH password authentication is supported, but **SSH key authentication** is strongly recommended for production use.
@@ -116,8 +126,8 @@ cards:
116126
---
117127

118128
## Release Management
119-
- Current stable release: **v1.2.9** (matching `manifest.json`).
120-
- Create a Git tag (e.g. `git tag v1.2.9`) and a corresponding GitHub release for every published version so HACS can track updates reliably.
129+
- Current stable release: **v1.2.10** (matching `manifest.json`).
130+
- Create a Git tag (e.g. `git tag v1.2.10`) and a corresponding GitHub release for every published version so HACS can track updates reliably.
121131
- Use the existing `scripts/bump_version.py` helper to increment the integration version when preparing a new release.
122132
- Document notable changes in [`CHANGELOG.md`](CHANGELOG.md) alongside each release.
123133

custom_components/vserver_ssh_stats/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import voluptuous as vol
1414
import paramiko
15+
16+
from .util import resolve_private_key_path
1517
_LOGGER = logging.getLogger(__name__)
1618

1719
DOMAIN = "vserver_ssh_stats"
@@ -109,7 +111,7 @@ def _exec_cmd() -> str:
109111
"port": data.get("port", 22),
110112
"password": data.get("password"),
111113
}
112-
key = data.get("key")
114+
key = resolve_private_key_path(hass, data.get("key"))
113115
if key:
114116
connect_args["key_filename"] = key
115117
client.connect(**{k: v for k, v in connect_args.items() if v})
@@ -138,7 +140,7 @@ def _exec_update() -> str:
138140
"port": data.get("port", 22),
139141
"password": data.get("password"),
140142
}
141-
key = data.get("key")
143+
key = resolve_private_key_path(hass, data.get("key"))
142144
if key:
143145
connect_args["key_filename"] = key
144146
client.connect(**{k: v for k, v in connect_args.items() if v})
@@ -176,7 +178,7 @@ def _exec_reboot() -> str:
176178
"port": data.get("port", 22),
177179
"password": data.get("password"),
178180
}
179-
key = data.get("key")
181+
key = resolve_private_key_path(hass, data.get("key"))
180182
if key:
181183
connect_args["key_filename"] = key
182184
client.connect(**{k: v for k, v in connect_args.items() if v})
@@ -251,6 +253,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
251253
servers = json.loads(data.get("servers_json", "[]"))
252254
except ValueError: # pragma: no cover - validation handled in flow
253255
servers = []
256+
for server in servers:
257+
key = resolve_private_key_path(hass, server.get("key"))
258+
if key:
259+
server["key"] = key
254260
hass.data[DOMAIN][entry.entry_id] = {
255261
"interval": data.get("interval"),
256262
"servers": servers,

custom_components/vserver_ssh_stats/config_flow.py

Lines changed: 70 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import hashlib
55
import json
6+
from pathlib import Path
67
from typing import Any
78

89
import voluptuous as vol
@@ -12,6 +13,7 @@
1213

1314
from . import DOMAIN
1415
from .ssh_discovery import discover_ssh_hosts, guess_local_network
16+
from .util import resolve_private_key_path
1517

1618
DEFAULT_INTERVAL = 30
1719

@@ -95,35 +97,44 @@ async def async_step_user(self, user_input: dict[str, Any] | None = None):
9597
}
9698
if user_input.get("password"):
9799
server["password"] = user_input["password"]
98-
if user_input.get("key"):
99-
server["key"] = user_input["key"]
100-
self._servers.append(server)
101-
if user_input.get("add_another"):
102-
hosts = await self._get_discovered_hosts()
103-
return self.async_show_form(
104-
step_id="user",
105-
data_schema=_build_server_schema(
106-
hosts,
107-
include_interval=False,
108-
interval_default=self._interval,
109-
default_name=vol.UNDEFINED,
110-
),
100+
key_input = user_input.get("key")
101+
if key_input:
102+
resolved = resolve_private_key_path(self.hass, key_input)
103+
if not Path(resolved).exists():
104+
errors["key"] = "key_missing"
105+
defaults = user_input
106+
else:
107+
server["key"] = resolved
108+
if errors:
109+
defaults = user_input
110+
else:
111+
self._servers.append(server)
112+
if user_input.get("add_another"):
113+
hosts = await self._get_discovered_hosts()
114+
return self.async_show_form(
115+
step_id="user",
116+
data_schema=_build_server_schema(
117+
hosts,
118+
include_interval=False,
119+
interval_default=self._interval,
120+
default_name=vol.UNDEFINED,
121+
),
122+
)
123+
124+
hosts_for_id = ",".join(sorted(server["host"] for server in self._servers))
125+
unique_id = hashlib.sha256(hosts_for_id.encode()).hexdigest()
126+
await self.async_set_unique_id(unique_id)
127+
self._abort_if_unique_id_configured()
128+
data = {
129+
"interval": self._interval,
130+
"servers_json": json.dumps(self._servers),
131+
}
132+
title = (
133+
self._servers[0]["name"]
134+
if len(self._servers) == 1
135+
else "VServer SSH Stats"
111136
)
112-
113-
hosts_for_id = ",".join(sorted(server["host"] for server in self._servers))
114-
unique_id = hashlib.sha256(hosts_for_id.encode()).hexdigest()
115-
await self.async_set_unique_id(unique_id)
116-
self._abort_if_unique_id_configured()
117-
data = {
118-
"interval": self._interval,
119-
"servers_json": json.dumps(self._servers),
120-
}
121-
title = (
122-
self._servers[0]["name"]
123-
if len(self._servers) == 1
124-
else "VServer SSH Stats"
125-
)
126-
return self.async_create_entry(title=title, data=data)
137+
return self.async_create_entry(title=title, data=data)
127138

128139
hosts = await self._get_discovered_hosts()
129140
default_name = self._discovered_name if first_server else vol.UNDEFINED
@@ -213,6 +224,11 @@ def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
213224
)
214225
except ValueError:
215226
self._existing_servers = []
227+
hass = config_entry.hass
228+
for server in self._existing_servers:
229+
key = resolve_private_key_path(hass, server.get("key")) if hass else server.get("key")
230+
if key:
231+
server["key"] = key
216232
self._pending_servers: list[dict[str, Any]] = []
217233

218234
async def async_step_init(self, user_input: dict[str, Any] | None = None):
@@ -271,23 +287,32 @@ async def async_step_servers(self, user_input: dict[str, Any] | None = None):
271287
}
272288
if user_input.get("password"):
273289
server["password"] = user_input["password"]
274-
if user_input.get("key"):
275-
server["key"] = user_input["key"]
276-
self._pending_servers.append(server)
277-
if user_input.get("add_another"):
278-
hosts = await self._get_discovered_hosts()
279-
return self.async_show_form(
280-
step_id="servers",
281-
data_schema=_build_server_schema(
282-
hosts,
283-
include_interval=False,
284-
interval_default=self._interval,
285-
default_name=vol.UNDEFINED,
286-
),
287-
)
288-
289-
self._update_entry(self._pending_servers)
290-
return self.async_create_entry(title="", data={})
290+
key_input = user_input.get("key")
291+
if key_input:
292+
resolved = resolve_private_key_path(self.hass, key_input)
293+
if not Path(resolved).exists():
294+
errors["key"] = "key_missing"
295+
defaults = user_input
296+
else:
297+
server["key"] = resolved
298+
if errors:
299+
defaults = user_input
300+
else:
301+
self._pending_servers.append(server)
302+
if user_input.get("add_another"):
303+
hosts = await self._get_discovered_hosts()
304+
return self.async_show_form(
305+
step_id="servers",
306+
data_schema=_build_server_schema(
307+
hosts,
308+
include_interval=False,
309+
interval_default=self._interval,
310+
default_name=vol.UNDEFINED,
311+
),
312+
)
313+
314+
self._update_entry(self._pending_servers)
315+
return self.async_create_entry(title="", data={})
291316

292317
hosts = await self._get_discovered_hosts()
293318
return self.async_show_form(

0 commit comments

Comments
 (0)