Add last restart sensor to HomeWizard (#136763)

This commit is contained in:
Duco Sebel 2025-01-29 10:51:58 +01:00 committed by GitHub
parent fe31dc936c
commit 60b6a11d4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 122 additions and 2 deletions

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Final from typing import Final
from homewizard_energy.models import CombinedModels, ExternalDevice from homewizard_energy.models import CombinedModels, ExternalDevice
@ -33,6 +34,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow
from . import HomeWizardConfigEntry from . import HomeWizardConfigEntry
from .const import DOMAIN from .const import DOMAIN
@ -48,7 +50,7 @@ class HomeWizardSensorEntityDescription(SensorEntityDescription):
enabled_fn: Callable[[CombinedModels], bool] = lambda x: True enabled_fn: Callable[[CombinedModels], bool] = lambda x: True
has_fn: Callable[[CombinedModels], bool] has_fn: Callable[[CombinedModels], bool]
value_fn: Callable[[CombinedModels], StateType] value_fn: Callable[[CombinedModels], StateType | datetime]
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -64,6 +66,15 @@ def to_percentage(value: float | None) -> float | None:
return value * 100 if value is not None else None return value * 100 if value is not None else None
def time_to_datetime(value: int | None) -> datetime | None:
"""Convert seconds to datetime when value is not None."""
return (
utcnow().replace(microsecond=0) - timedelta(seconds=value)
if value is not None
else None
)
SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = ( SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
HomeWizardSensorEntityDescription( HomeWizardSensorEntityDescription(
key="smr_version", key="smr_version",
@ -611,6 +622,19 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
has_fn=lambda data: data.measurement.cycles is not None, has_fn=lambda data: data.measurement.cycles is not None,
value_fn=lambda data: data.measurement.cycles, value_fn=lambda data: data.measurement.cycles,
), ),
HomeWizardSensorEntityDescription(
key="last_restart",
translation_key="last_restart",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
has_fn=(
lambda data: data.system is not None and data.system.uptime_s is not None
),
value_fn=(
lambda data: time_to_datetime(data.system.uptime_s) if data.system else None
),
),
) )
@ -697,7 +721,7 @@ class HomeWizardSensorEntity(HomeWizardEntity, SensorEntity):
self._attr_entity_registry_enabled_default = False self._attr_entity_registry_enabled_default = False
@property @property
def native_value(self) -> StateType: def native_value(self) -> StateType | datetime | None:
"""Return the sensor value.""" """Return the sensor value."""
return self.entity_description.value_fn(self.coordinator.data) return self.entity_description.value_fn(self.coordinator.data)

View File

@ -137,6 +137,9 @@
}, },
"state_of_charge_pct": { "state_of_charge_pct": {
"name": "State of charge" "name": "State of charge"
},
"last_restart": {
"name": "Last restart"
} }
}, },
"switch": { "switch": {

View File

@ -432,6 +432,89 @@
'state': '50.0', 'state': '50.0',
}) })
# --- # ---
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_last_restart:device-registry]
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': None,
'connections': set({
tuple(
'mac',
'5c:2f:af:ab:cd:ef',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'homewizard',
'5c2fafabcdef',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'HomeWizard',
'model': 'Plug-In Battery',
'model_id': 'HWE-BAT',
'name': 'Device',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '1.00',
'via_device_id': None,
})
# ---
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_last_restart:entity-registry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
'entity_id': 'sensor.device_last_restart',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
'original_icon': None,
'original_name': 'Last restart',
'platform': 'homewizard',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'last_restart',
'unique_id': 'HWE-P1_5c2fafabcdef_last_restart',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_last_restart:state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'timestamp',
'friendly_name': 'Device Last restart',
}),
'context': <ANY>,
'entity_id': 'sensor.device_last_restart',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '2025-01-28T21:39:04+00:00',
})
# ---
# name: test_sensors[HWE-BAT-entity_ids10][sensor.device_power:device-registry] # name: test_sensors[HWE-BAT-entity_ids10][sensor.device_power:device-registry]
DeviceRegistryEntrySnapshot({ DeviceRegistryEntrySnapshot({
'area_id': None, 'area_id': None,

View File

@ -19,6 +19,7 @@ pytestmark = [
] ]
@pytest.mark.freeze_time("2025-01-28 21:45:00")
@pytest.mark.usefixtures("entity_registry_enabled_by_default") @pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize( @pytest.mark.parametrize(
("device_fixture", "entity_ids"), ("device_fixture", "entity_ids"),
@ -301,6 +302,7 @@ pytestmark = [
"sensor.device_frequency", "sensor.device_frequency",
"sensor.device_power", "sensor.device_power",
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_last_restart",
"sensor.device_voltage", "sensor.device_voltage",
], ],
), ),
@ -449,6 +451,7 @@ async def test_sensors(
[ [
"sensor.device_current", "sensor.device_current",
"sensor.device_frequency", "sensor.device_frequency",
"sensor.device_last_restart",
"sensor.device_voltage", "sensor.device_voltage",
], ],
), ),
@ -546,6 +549,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -595,6 +599,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -651,6 +656,7 @@ async def test_external_sensors_unreachable(
"sensor.device_smart_meter_model", "sensor.device_smart_meter_model",
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -701,6 +707,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -739,6 +746,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -790,6 +798,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",
@ -828,6 +837,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge", "sensor.device_state_of_charge",
"sensor.device_tariff", "sensor.device_tariff",
"sensor.device_total_water_usage", "sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1", "sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2", "sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3", "sensor.device_voltage_phase_3",