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 dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Final
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.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow
from . import HomeWizardConfigEntry
from .const import DOMAIN
@ -48,7 +50,7 @@ class HomeWizardSensorEntityDescription(SensorEntityDescription):
enabled_fn: Callable[[CombinedModels], bool] = lambda x: True
has_fn: Callable[[CombinedModels], bool]
value_fn: Callable[[CombinedModels], StateType]
value_fn: Callable[[CombinedModels], StateType | datetime]
@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
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, ...]] = (
HomeWizardSensorEntityDescription(
key="smr_version",
@ -611,6 +622,19 @@ SENSORS: Final[tuple[HomeWizardSensorEntityDescription, ...]] = (
has_fn=lambda data: data.measurement.cycles is not None,
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
@property
def native_value(self) -> StateType:
def native_value(self) -> StateType | datetime | None:
"""Return the sensor value."""
return self.entity_description.value_fn(self.coordinator.data)

View File

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

View File

@ -432,6 +432,89 @@
'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]
DeviceRegistryEntrySnapshot({
'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.parametrize(
("device_fixture", "entity_ids"),
@ -301,6 +302,7 @@ pytestmark = [
"sensor.device_frequency",
"sensor.device_power",
"sensor.device_state_of_charge",
"sensor.device_last_restart",
"sensor.device_voltage",
],
),
@ -449,6 +451,7 @@ async def test_sensors(
[
"sensor.device_current",
"sensor.device_frequency",
"sensor.device_last_restart",
"sensor.device_voltage",
],
),
@ -546,6 +549,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -595,6 +599,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -651,6 +656,7 @@ async def test_external_sensors_unreachable(
"sensor.device_smart_meter_model",
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -701,6 +707,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -739,6 +746,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -790,6 +798,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",
@ -828,6 +837,7 @@ async def test_external_sensors_unreachable(
"sensor.device_state_of_charge",
"sensor.device_tariff",
"sensor.device_total_water_usage",
"sensor.device_last_restart",
"sensor.device_voltage_phase_1",
"sensor.device_voltage_phase_2",
"sensor.device_voltage_phase_3",