44
55from collections .abc import Callable
66from dataclasses import dataclass
7+ from datetime import datetime , timedelta
8+ from typing import Any
79
810from pymystrom .switch import MyStromSwitch
911
1315 SensorEntityDescription ,
1416 SensorStateClass ,
1517)
16- from homeassistant .const import UnitOfPower , UnitOfTemperature
18+ from homeassistant .const import (
19+ EntityCategory ,
20+ UnitOfEnergy ,
21+ UnitOfPower ,
22+ UnitOfTemperature ,
23+ )
1724from homeassistant .core import HomeAssistant
1825from homeassistant .helpers .device_registry import DeviceInfo
1926from homeassistant .helpers .entity_platform import AddConfigEntryEntitiesCallback
27+ from homeassistant .util .dt import utcnow
2028
2129from .const import DOMAIN , MANUFACTURER
2230from .models import MyStromConfigEntry
@@ -44,6 +52,15 @@ class MyStromSwitchSensorEntityDescription(SensorEntityDescription):
4452 native_unit_of_measurement = UnitOfPower .WATT ,
4553 value_fn = lambda device : device .consumption ,
4654 ),
55+ MyStromSwitchSensorEntityDescription (
56+ key = "energy_since_boot" ,
57+ translation_key = "energy_since_boot" ,
58+ device_class = SensorDeviceClass .ENERGY ,
59+ state_class = SensorStateClass .TOTAL_INCREASING ,
60+ native_unit_of_measurement = UnitOfEnergy .JOULE ,
61+ suggested_unit_of_measurement = UnitOfEnergy .KILO_WATT_HOUR ,
62+ value_fn = lambda device : device .energy_since_boot ,
63+ ),
4764 MyStromSwitchSensorEntityDescription (
4865 key = "temperature" ,
4966 device_class = SensorDeviceClass .TEMPERATURE ,
@@ -62,39 +79,106 @@ async def async_setup_entry(
6279 """Set up the myStrom entities."""
6380 device : MyStromSwitch = entry .runtime_data .device
6481
65- async_add_entities (
82+ entities : list [ MyStromSensorBase ] = [
6683 MyStromSwitchSensor (device , entry .title , description )
6784 for description in SENSOR_TYPES
6885 if description .value_fn (device ) is not None
69- )
86+ ]
7087
88+ if device .time_since_boot is not None :
89+ entities .append (MyStromSwitchUptimeSensor (device , entry .title ))
7190
72- class MyStromSwitchSensor (SensorEntity ):
73- """Representation of the consumption or temperature of a myStrom switch/plug."""
91+ async_add_entities (entities )
7492
75- entity_description : MyStromSwitchSensorEntityDescription
93+
94+ class MyStromSensorBase (SensorEntity ):
95+ """Base class for myStrom sensors."""
7696
7797 _attr_has_entity_name = True
7898
7999 def __init__ (
80100 self ,
81101 device : MyStromSwitch ,
82102 name : str ,
83- description : MyStromSwitchSensorEntityDescription ,
103+ key : str ,
84104 ) -> None :
85105 """Initialize the sensor."""
86- self .device = device
87- self .entity_description = description
88-
89- self ._attr_unique_id = f"{ device .mac } -{ description .key } "
106+ self ._attr_unique_id = f"{ device .mac } -{ key } "
90107 self ._attr_device_info = DeviceInfo (
91108 identifiers = {(DOMAIN , device .mac )},
92109 name = name ,
93110 manufacturer = MANUFACTURER ,
94111 sw_version = device .firmware ,
95112 )
96113
114+
115+ class MyStromSwitchSensor (MyStromSensorBase ):
116+ """Representation of the consumption or temperature of a myStrom switch/plug."""
117+
118+ entity_description : MyStromSwitchSensorEntityDescription
119+
120+ def __init__ (
121+ self ,
122+ device : MyStromSwitch ,
123+ name : str ,
124+ description : MyStromSwitchSensorEntityDescription ,
125+ ) -> None :
126+ """Initialize the sensor."""
127+ super ().__init__ (device , name , description .key )
128+ self .device = device
129+ self .entity_description = description
130+
97131 @property
98132 def native_value (self ) -> float | None :
99133 """Return the value of the sensor."""
100134 return self .entity_description .value_fn (self .device )
135+
136+
137+ class MyStromSwitchUptimeSensor (MyStromSensorBase ):
138+ """Representation of a MyStrom Switch uptime sensor."""
139+
140+ entity_description = SensorEntityDescription (
141+ key = "time_since_boot" ,
142+ device_class = SensorDeviceClass .TIMESTAMP ,
143+ entity_category = EntityCategory .DIAGNOSTIC ,
144+ translation_key = "time_since_boot" ,
145+ )
146+
147+ def __init__ (
148+ self ,
149+ device : MyStromSwitch ,
150+ name : str ,
151+ ) -> None :
152+ """Initialize the uptime sensor."""
153+ super ().__init__ (device , name , self .entity_description .key )
154+ self .device = device
155+ self ._last_value : datetime | None = None
156+ self ._last_attributes : dict [str , Any ] = {}
157+
158+ @property
159+ def native_value (self ) -> datetime | None :
160+ """Return the uptime of the device as a datetime."""
161+
162+ if self .device .time_since_boot is None or self .device .boot_id is None :
163+ return None
164+
165+ # Return cached value if boot_id hasn't changed
166+ if (
167+ self ._last_value is not None
168+ and self ._last_attributes .get ("boot_id" ) == self .device .boot_id
169+ ):
170+ return self ._last_value
171+
172+ self ._last_value = utcnow () - timedelta (seconds = self .device .time_since_boot )
173+
174+ return self ._last_value
175+
176+ @property
177+ def extra_state_attributes (self ) -> dict [str , Any ]:
178+ """Return the optional state attributes."""
179+
180+ self ._last_attributes = {
181+ "boot_id" : self .device .boot_id ,
182+ }
183+
184+ return self ._last_attributes
0 commit comments