From 2c35c39a1cc07ac50ff9ddaeeadab21a17679cc2 Mon Sep 17 00:00:00 2001 From: "Edgar P. Burkhart" <git@edgarpierre.fr> Date: Sun, 9 Mar 2025 23:10:45 +0100 Subject: [PATCH] Add uptime sensor and state topics for system and user clients --- hasspy/__init__.py | 1 + hasspy/mqtt.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/hasspy/__init__.py b/hasspy/__init__.py index 7807b61..565cd4e 100644 --- a/hasspy/__init__.py +++ b/hasspy/__init__.py @@ -66,5 +66,6 @@ def main() -> int: log.info("Shutting down") + ha.timer.cancel() ha.loop_stop() return 0 diff --git a/hasspy/mqtt.py b/hasspy/mqtt.py index 5f60c67..43e5e02 100644 --- a/hasspy/mqtt.py +++ b/hasspy/mqtt.py @@ -2,6 +2,7 @@ import io import json import logging import re +from datetime import datetime, timezone from pathlib import Path from subprocess import run from threading import Thread, Timer @@ -150,6 +151,10 @@ class HassSystemClient(HassClient): if code != 0: log.error(f"Failed to execute command: {cmd}") + @property + def state_topic(self) -> str: + return f"{self.node_id}/system/state" + @property def components(self) -> dict[str, dict[str, Any]]: return { @@ -176,6 +181,14 @@ class HassSystemClient(HassClient): "icon": "mdi:sleep", "payload_press": "SUSPEND", }, + "uptime": { + "unique_id": f"{self.node_id}_uptime", + "p": "sensor", + "name": "Uptime", + "icon": "mdi:clock", + "device_class": "timestamp", + "value_template": "{{ value_json.uptime|timestamp_utc }}", + }, } @property @@ -184,8 +197,18 @@ class HassSystemClient(HassClient): "power": "POWER_OFF" if Path("/run/systemd/shutdown/scheduled").exists() else "POWER_ON", + "uptime": self.uptime_value, } + @property + def uptime_value(self) -> float | None: + code, out = run_command(["uptime", "--since"]) + if code != 0: + log.error("Failed to get uptime") + return None + + return datetime.fromisoformat(out.strip()).astimezone(timezone.utc).timestamp() + class HassUserClient(HassClient): commands = { @@ -224,6 +247,10 @@ class HassUserClient(HassClient): def availability_topic(self) -> str: return f"{self.node_id}/user/availability" + @property + def state_topic(self) -> str: + return f"{self.node_id}/user/state" + @property def components(self) -> dict[str, dict[str, Any]]: return {