feat: server telemetry, update entity, sync-clock controls
- Server device exposing CPU/RAM/GPU/temperature/battery sensors via /api/v1/system/performance, plus last-restart timestamp (cached with jitter threshold so the recorder doesn't see poll wobble) and version. - Update entity backed by /api/v1/system/update — installs via /apply, hides the install button when the server reports can_auto_update=false. - Sync-clock entities: reset button, speed number, running switch, and the event listener now refreshes on entity_changed events too. - Bump manifest to 0.4.0.
This commit is contained in:
@@ -35,6 +35,7 @@ PLATFORMS: list[Platform] = [
|
||||
Platform.SENSOR,
|
||||
Platform.NUMBER,
|
||||
Platform.SELECT,
|
||||
Platform.UPDATE,
|
||||
]
|
||||
|
||||
|
||||
@@ -61,6 +62,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
# Create device entries for each target and remove stale ones
|
||||
device_registry = dr.async_get(hass)
|
||||
current_identifiers: set[tuple[str, str]] = set()
|
||||
|
||||
# Server device — owns the system telemetry sensors (CPU, RAM, GPU, etc.)
|
||||
server_identifier = (DOMAIN, f"{entry.entry_id}_server")
|
||||
sw_version = (
|
||||
coordinator.server_version
|
||||
if coordinator.server_version and coordinator.server_version != "unknown"
|
||||
else None
|
||||
)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={server_identifier},
|
||||
name=server_name,
|
||||
manufacturer="LedGrab",
|
||||
model="Server",
|
||||
sw_version=sw_version,
|
||||
configuration_url=server_url,
|
||||
)
|
||||
current_identifiers.add(server_identifier)
|
||||
|
||||
if coordinator.data and "targets" in coordinator.data:
|
||||
for target_id, target_data in coordinator.data["targets"].items():
|
||||
info = target_data["info"]
|
||||
@@ -93,6 +113,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
)
|
||||
current_identifiers.add(scenes_identifier)
|
||||
|
||||
# One device per sync clock — groups its switch/number/button/sensor.
|
||||
sync_clocks = coordinator.data.get("sync_clocks", []) if coordinator.data else []
|
||||
for clock in sync_clocks:
|
||||
clock_identifier = (DOMAIN, clock["id"])
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={clock_identifier},
|
||||
name=clock.get("name", clock["id"]),
|
||||
manufacturer=server_name,
|
||||
model="Sync Clock",
|
||||
configuration_url=server_url,
|
||||
)
|
||||
current_identifiers.add(clock_identifier)
|
||||
|
||||
# Remove devices for targets that no longer exist
|
||||
for device_entry in dr.async_entries_for_config_entry(device_registry, entry.entry_id):
|
||||
if not device_entry.identifiers & current_identifiers:
|
||||
@@ -106,28 +140,37 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
DATA_EVENT_LISTENER: event_listener,
|
||||
}
|
||||
|
||||
# Track target and scene IDs to detect changes
|
||||
# Track target, scene, and sync-clock IDs to detect changes
|
||||
known_target_ids = set(coordinator.data.get("targets", {}).keys() if coordinator.data else [])
|
||||
known_scene_ids = set(
|
||||
p["id"] for p in (coordinator.data.get("scene_presets", []) if coordinator.data else [])
|
||||
)
|
||||
known_clock_ids = set(
|
||||
c["id"] for c in (coordinator.data.get("sync_clocks", []) if coordinator.data else [])
|
||||
)
|
||||
|
||||
def _on_coordinator_update() -> None:
|
||||
"""Detect target/scene list changes and trigger reload."""
|
||||
nonlocal known_target_ids, known_scene_ids
|
||||
"""Detect target/scene/clock list changes and trigger reload."""
|
||||
nonlocal known_target_ids, known_scene_ids, known_clock_ids
|
||||
|
||||
if not coordinator.data:
|
||||
return
|
||||
|
||||
targets = coordinator.data.get("targets", {})
|
||||
|
||||
# Reload if target or scene list changed
|
||||
# Reload if target, scene, or sync-clock list changed
|
||||
current_ids = set(targets.keys())
|
||||
current_scene_ids = set(p["id"] for p in coordinator.data.get("scene_presets", []))
|
||||
if current_ids != known_target_ids or current_scene_ids != known_scene_ids:
|
||||
current_clock_ids = set(c["id"] for c in coordinator.data.get("sync_clocks", []))
|
||||
if (
|
||||
current_ids != known_target_ids
|
||||
or current_scene_ids != known_scene_ids
|
||||
or current_clock_ids != known_clock_ids
|
||||
):
|
||||
known_target_ids = current_ids
|
||||
known_scene_ids = current_scene_ids
|
||||
_LOGGER.info("Target or scene list changed, reloading integration")
|
||||
known_clock_ids = current_clock_ids
|
||||
_LOGGER.info("Target, scene, or sync-clock list changed, reloading integration")
|
||||
hass.async_create_task(hass.config_entries.async_reload(entry.entry_id))
|
||||
|
||||
coordinator.async_add_listener(_on_coordinator_update)
|
||||
|
||||
Reference in New Issue
Block a user