diff --git a/.vscode/settings.json b/.vscode/settings.json index eac6e21..06a4062 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "jsons", "levelname", "markdownlint", + "miaucl", "mkdocs", "Mosquitto", "mountpoint", diff --git a/README.md b/README.md index 8ebd1b9..5c335d7 100755 --- a/README.md +++ b/README.md @@ -81,6 +81,12 @@ The sensor state equals average throughput of the interface during the collectio This will publish network throughput information about Server1's `eth0` interface to the MQTT broker once every 60 seconds. The sensor state will equal the average network throughput over the previous 15 seconds. +### Thermal zones + +`linux2mqtt` can publish temperature metrics for thermal zones using the `temp` option. Each thermal zone will present as a separate sensor in Home Assistant. The sensor state reports the temperature in `°C`. Additional data is accessible as state attributes on each sensor. + +`linux2mqtt --name Server1 -vvvvv --cpu=60 --vm --temp` + ## Compatibility `linux2mqtt` has been tested to work on CentOS, Ubuntu, and Debian (Raspberry Pi), even tough some features are not available everywhere. **Python 3.10 (or above) is recommended.** diff --git a/linux2mqtt/__init__.py b/linux2mqtt/__init__.py index bc6c36e..38de28d 100644 --- a/linux2mqtt/__init__.py +++ b/linux2mqtt/__init__.py @@ -38,6 +38,7 @@ DiskUsageMetrics, NetworkMetrics, NetworkMetricThread, + TempMetrics, VirtualMemoryMetrics, ) from .type_definitions import ( @@ -79,6 +80,7 @@ "NetworkMetrics", "NetworkMetricThread", "DiskUsageMetrics", + "TempMetrics", "HOMEASSISTANT_PREFIX_DEFAULT", "MQTT_CLIENT_ID_DEFAULT", "MQTT_PORT_DEFAULT", diff --git a/linux2mqtt/linux2mqtt.py b/linux2mqtt/linux2mqtt.py index a1b2036..ef2c493 100755 --- a/linux2mqtt/linux2mqtt.py +++ b/linux2mqtt/linux2mqtt.py @@ -13,6 +13,7 @@ from typing import Any, List import paho.mqtt.client +import psutil from . import __VERSION__ from .const import ( @@ -38,6 +39,7 @@ CPUMetrics, DiskUsageMetrics, NetworkMetrics, + TempMetrics, VirtualMemoryMetrics, ) from .type_definitions import Linux2MqttConfig, LinuxDeviceEntry @@ -548,6 +550,9 @@ def main() -> None: default=None, metavar="NIC", ) + parser.add_argument( + "--temp", help="Publish temperature of thermal zones", action="store_true" + ) try: args = parser.parse_args() @@ -616,6 +621,13 @@ def main() -> None: net = NetworkMetrics(n, i) stats.add_metric(net) + if args.temp: + st = psutil.sensors_temperatures() # type: ignore + for device in st: + for thermal_zone in st[device]: + tm = TempMetrics(device=device, thermal_zone=thermal_zone.label) + stats.add_metric(tm) + if not (args.vm or args.cpu or args.du or args.net): main_logger.warning("No metrics specified. Nothing will be published.") diff --git a/linux2mqtt/metrics.py b/linux2mqtt/metrics.py index d2aa31c..af1bf42 100755 --- a/linux2mqtt/metrics.py +++ b/linux2mqtt/metrics.py @@ -58,7 +58,7 @@ class BaseMetric: ha_sensor_type: SensorType = "sensor" - polled_result: Dict[str, str | int | float] | None + polled_result: Dict[str, str | int | float | None] | None def __init__(self, *args: Any, **kwargs: Any) -> None: """Initialize base class.""" @@ -579,3 +579,79 @@ def poll(self, result_queue: Queue[BaseMetric]) -> bool: th.daemon = True th.start() return True # Expect a deferred result + + +class TempMetrics(BaseMetric): + """Thermal zones metric.""" + + icon = "mdi:thermometer" + device_class = "temperature" + unit_of_measurement = "°C" + state_field = "current" + + _name_template = "Thermal Zone ({}/{})" + _device: str + _thermal_zone: str + + def __init__(self, device: str, thermal_zone: str): + """Initialize the thermal zone metric. + + Parameters + ---------- + device + The device + thermal_zone + The thermal zone + + Raises + ------ + Linux2MqttConfigException + Bad config + + """ + super().__init__() + self._device = device + self._thermal_zone = thermal_zone + self._name = self._name_template.format(device, thermal_zone) + + def poll(self, result_queue: Queue[Self]) -> bool: + """Poll new data for the thermal zone metric. + + Parameters + ---------- + result_queue + (Unused) + + Returns + ------- + bool + True as the data is readily available + + Raises + ------ + Linux2MqttMetricsException + thermal zone information could not be gathered or prepared for publishing + + """ + try: + st = psutil.sensors_temperatures() # type: ignore + thermal_zone = next( + ( + item + for item in st.get(self._device, []) + if item.label == self._thermal_zone + ), + None, + ) + assert thermal_zone + self.polled_result = { + "label": thermal_zone.label, + "current": thermal_zone.current, + "high": thermal_zone.high, + "critical": thermal_zone.critical, + } + return False + except Exception as ex: + raise Linux2MqttMetricsException( + "Could not gather and publish thermal zone data" + ) from ex diff --git a/requirements.txt b/requirements.txt index a20a37a..64d8b0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ paho-mqtt jsons -psutil +psutil>=5.0.0,<6.0.0 numpy typing-extensions \ No newline at end of file diff --git a/requirements_dev.txt b/requirements_dev.txt index 229fa6f..6d4c1f4 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,8 +1,8 @@ paho-mqtt types-paho-mqtt jsons -psutil -types-psutil +psutil>=5.0.0,<6.0.0 +types-psutil>=5.0.0,<6.0.0 numpy typing-extensions mypy>=1.8.0