Poll the myStrom motion sensor readings (#180416)

This commit is contained in:
Franck Nijhof
2026-08-29 08:20:28 +00:00
parent 60d579f0ca
commit 45f9ea3a50
2 changed files with 63 additions and 1 deletions
+24 -1
View File
@@ -1,10 +1,12 @@
"""Support for myStrom sensors of switches/plugs."""
from collections.abc import Callable
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from datetime import datetime, timedelta
import logging
from typing import Any, override
from pymystrom.exceptions import MyStromConnectionError
from pymystrom.pir import MyStromPir
from pymystrom.switch import MyStromSwitch
@@ -29,12 +31,17 @@ from homeassistant.util.dt import utcnow
from .const import DOMAIN, MANUFACTURER
from .models import MyStromConfigEntry
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True, kw_only=True)
class MyStromSensorEntityDescription[_DeviceT](SensorEntityDescription):
"""Class describing mystrom sensor entities."""
value_fn: Callable[[_DeviceT], float | None]
# Only needed where nothing else on the device polls; a switch is kept
# fresh by its own entity refreshing the shared device.
update_fn: Callable[[_DeviceT], Coroutine[Any, Any, None]] | None = None
SENSOR_TYPES_PIR: tuple[MyStromSensorEntityDescription[MyStromPir], ...] = (
@@ -50,6 +57,7 @@ SENSOR_TYPES_PIR: tuple[MyStromSensorEntityDescription[MyStromPir], ...] = (
else None
)
),
update_fn=lambda device: device.get_temperatures(),
),
MyStromSensorEntityDescription(
key="illuminance",
@@ -61,6 +69,7 @@ SENSOR_TYPES_PIR: tuple[MyStromSensorEntityDescription[MyStromPir], ...] = (
float(device.intensity) if device.intensity is not None else None
)
),
update_fn=lambda device: device.get_light(),
),
)
@@ -180,6 +189,20 @@ class MyStromSensor[_DeviceT](MyStromSensorBase):
"""Return the value of the sensor."""
return self.entity_description.value_fn(self.device)
async def async_update(self) -> None:
"""Get the latest reading from the device."""
if (update_fn := self.entity_description.update_fn) is None:
return
try:
await update_fn(self.device)
except MyStromConnectionError:
if self.available:
self._attr_available = False
_LOGGER.error("No route to myStrom device")
else:
self._attr_available = True
class MyStromSwitchUptimeSensor(MyStromSensorBase):
"""Representation of a MyStrom Switch uptime sensor."""
+39
View File
@@ -0,0 +1,39 @@
"""Test the myStrom sensors."""
from datetime import timedelta
from freezegun.api import FrozenDateTimeFactory
from homeassistant.core import HomeAssistant
from .test_init import init_integration
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_pir_sensors_are_polled(
hass: HomeAssistant,
config_entry: MockConfigEntry,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the motion sensor readings are refreshed while polling."""
await init_integration(hass, config_entry, 110)
device = config_entry.runtime_data.device
assert hass.states.get("sensor.mystrom_device_temperature").state == "24.87"
assert hass.states.get("sensor.mystrom_device_illuminance").state == "16.0"
# The mock only reports readings once they have been fetched, and nothing
# else talks to a motion sensor, so this stays cleared unless the sensors
# fetch them themselves.
device._requested_state = False
device._state["temperature_compensated"] = 21.5
device._state["intensity"] = 42
freezer.tick(timedelta(minutes=5))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert device._requested_state is True
assert hass.states.get("sensor.mystrom_device_temperature").state == "21.5"
assert hass.states.get("sensor.mystrom_device_illuminance").state == "42.0"