1 """Support for sensor data from RainMachine."""
3 from __future__
import annotations
5 from dataclasses
import dataclass
6 from datetime
import datetime, timedelta
7 from typing
import Any, cast
10 DOMAIN
as SENSOR_DOMAIN,
14 SensorEntityDescription,
23 from .
import RainMachineConfigEntry, RainMachineData
24 from .const
import DATA_PROGRAMS, DATA_PROVISION_SETTINGS, DATA_ZONES
25 from .entity
import RainMachineEntity, RainMachineEntityDescription
28 EntityDomainReplacementStrategy,
30 async_finish_entity_domain_replacements,
34 DEFAULT_ZONE_COMPLETION_TIME_WOBBLE_TOLERANCE =
timedelta(seconds=5)
36 TYPE_FLOW_SENSOR_CLICK_M3 =
"flow_sensor_clicks_cubic_meter"
37 TYPE_FLOW_SENSOR_CONSUMED_LITERS =
"flow_sensor_consumed_liters"
38 TYPE_FLOW_SENSOR_LEAK_CLICKS =
"flow_sensor_leak_clicks"
39 TYPE_FLOW_SENSOR_LEAK_VOLUME =
"flow_sensor_leak_volume"
40 TYPE_FLOW_SENSOR_START_INDEX =
"flow_sensor_start_index"
41 TYPE_FLOW_SENSOR_WATERING_CLICKS =
"flow_sensor_watering_clicks"
42 TYPE_LAST_LEAK_DETECTED =
"last_leak_detected"
43 TYPE_PROGRAM_RUN_COMPLETION_TIME =
"program_run_completion_time"
44 TYPE_RAIN_SENSOR_RAIN_START =
"rain_sensor_rain_start"
45 TYPE_ZONE_RUN_COMPLETION_TIME =
"zone_run_completion_time"
48 @dataclass(frozen=True, kw_only=True)
50 SensorEntityDescription, RainMachineEntityDescription
52 """Describe a RainMachine sensor."""
57 @dataclass(frozen=True, kw_only=True)
59 SensorEntityDescription, RainMachineEntityDescription
61 """Describe a RainMachine completion timer sensor."""
66 SENSOR_DESCRIPTIONS = (
68 key=TYPE_FLOW_SENSOR_CLICK_M3,
69 translation_key=TYPE_FLOW_SENSOR_CLICK_M3,
70 native_unit_of_measurement=f
"clicks/{UnitOfVolume.CUBIC_METERS}",
71 entity_category=EntityCategory.DIAGNOSTIC,
72 entity_registry_enabled_default=
False,
73 state_class=SensorStateClass.MEASUREMENT,
74 api_category=DATA_PROVISION_SETTINGS,
75 data_key=
"flowSensorClicksPerCubicMeter",
78 key=TYPE_FLOW_SENSOR_CONSUMED_LITERS,
79 translation_key=TYPE_FLOW_SENSOR_CONSUMED_LITERS,
80 device_class=SensorDeviceClass.WATER,
81 entity_category=EntityCategory.DIAGNOSTIC,
82 native_unit_of_measurement=UnitOfVolume.LITERS,
83 entity_registry_enabled_default=
False,
84 state_class=SensorStateClass.TOTAL_INCREASING,
85 api_category=DATA_PROVISION_SETTINGS,
86 data_key=
"flowSensorWateringClicks",
89 key=TYPE_FLOW_SENSOR_LEAK_CLICKS,
90 translation_key=TYPE_FLOW_SENSOR_LEAK_CLICKS,
91 entity_category=EntityCategory.DIAGNOSTIC,
92 native_unit_of_measurement=
"clicks",
93 entity_registry_enabled_default=
False,
94 state_class=SensorStateClass.TOTAL_INCREASING,
95 api_category=DATA_PROVISION_SETTINGS,
96 data_key=
"flowSensorLeakClicks",
99 key=TYPE_FLOW_SENSOR_LEAK_VOLUME,
100 translation_key=TYPE_FLOW_SENSOR_LEAK_VOLUME,
101 device_class=SensorDeviceClass.WATER,
102 entity_category=EntityCategory.DIAGNOSTIC,
103 native_unit_of_measurement=UnitOfVolume.LITERS,
104 entity_registry_enabled_default=
False,
105 state_class=SensorStateClass.TOTAL_INCREASING,
106 api_category=DATA_PROVISION_SETTINGS,
107 data_key=
"flowSensorLeakClicks",
110 key=TYPE_FLOW_SENSOR_START_INDEX,
111 translation_key=TYPE_FLOW_SENSOR_START_INDEX,
112 icon=
"mdi:water-pump",
113 entity_category=EntityCategory.DIAGNOSTIC,
114 native_unit_of_measurement=
"index",
115 entity_registry_enabled_default=
False,
116 api_category=DATA_PROVISION_SETTINGS,
117 data_key=
"flowSensorStartIndex",
120 key=TYPE_FLOW_SENSOR_WATERING_CLICKS,
121 translation_key=TYPE_FLOW_SENSOR_WATERING_CLICKS,
122 icon=
"mdi:water-pump",
123 entity_category=EntityCategory.DIAGNOSTIC,
124 native_unit_of_measurement=
"clicks",
125 entity_registry_enabled_default=
False,
126 state_class=SensorStateClass.TOTAL_INCREASING,
127 api_category=DATA_PROVISION_SETTINGS,
128 data_key=
"flowSensorWateringClicks",
131 key=TYPE_LAST_LEAK_DETECTED,
132 translation_key=TYPE_LAST_LEAK_DETECTED,
133 icon=
"mdi:pipe-leak",
134 entity_category=EntityCategory.DIAGNOSTIC,
135 entity_registry_enabled_default=
False,
136 device_class=SensorDeviceClass.TIMESTAMP,
137 api_category=DATA_PROVISION_SETTINGS,
138 data_key=
"lastLeakDetected",
141 key=TYPE_RAIN_SENSOR_RAIN_START,
142 translation_key=TYPE_RAIN_SENSOR_RAIN_START,
143 icon=
"mdi:weather-pouring",
144 entity_category=EntityCategory.DIAGNOSTIC,
145 entity_registry_enabled_default=
False,
146 device_class=SensorDeviceClass.TIMESTAMP,
147 api_category=DATA_PROVISION_SETTINGS,
148 data_key=
"rainSensorRainStart",
155 entry: RainMachineConfigEntry,
156 async_add_entities: AddEntitiesCallback,
158 """Set up RainMachine sensors based on a config entry."""
159 data = entry.runtime_data
167 f
"{data.controller.mac}_freeze_protect_temp",
168 f
"select.{data.controller.name.lower()}_freeze_protect_temperature",
169 breaks_in_ha_version=
"2022.12.0",
170 remove_old_entity=
True,
175 api_category_sensor_map = {
176 DATA_PROVISION_SETTINGS: ProvisionSettingsSensor,
179 sensors: list[ProvisionSettingsSensor | TimeRemainingSensor] = [
180 api_category_sensor_map[description.api_category](entry, data, description)
181 for description
in SENSOR_DESCRIPTIONS
183 (coordinator := data.coordinators[description.api_category])
is not None
185 and key_exists(coordinator.data, description.data_key)
189 program_coordinator = data.coordinators[DATA_PROGRAMS]
190 zone_coordinator = data.coordinators[DATA_ZONES]
192 for uid, program
in program_coordinator.data.items():
198 key=f
"{TYPE_PROGRAM_RUN_COMPLETION_TIME}_{uid}",
199 name=f
"{program['name']} Run Completion Time",
200 device_class=SensorDeviceClass.TIMESTAMP,
201 entity_category=EntityCategory.DIAGNOSTIC,
202 api_category=DATA_PROGRAMS,
208 for uid, zone
in zone_coordinator.data.items():
214 key=f
"{TYPE_ZONE_RUN_COMPLETION_TIME}_{uid}",
215 name=f
"{zone['name']} Run Completion Time",
216 device_class=SensorDeviceClass.TIMESTAMP,
217 entity_category=EntityCategory.DIAGNOSTIC,
218 api_category=DATA_ZONES,
228 """Define a sensor that shows the amount of time remaining for an activity."""
230 entity_description: RainMachineSensorCompletionTimerDescription
235 data: RainMachineData,
236 description: RainMachineSensorCompletionTimerDescription,
239 super().
__init__(entry, data, description)
246 """Return the core data for this entity."""
247 return cast(dict[str, Any], self.coordinator.data[self.
entity_descriptionentity_description.uid])
251 """Return the data key that contains the activity status."""
255 """Handle entity which will be added."""
261 """Calculate the number of seconds remaining."""
262 raise NotImplementedError
266 """Update the state."""
281 new_timestamp = now +
timedelta(seconds=seconds_remaining)
286 < DEFAULT_ZONE_COMPLETION_TIME_WOBBLE_TOLERANCE
296 """Define a sensor that shows the amount of time remaining for a program."""
300 """Return the data key that contains the activity status."""
304 """Calculate the number of seconds remaining."""
306 self.
_data_data.coordinators[DATA_ZONES].data[zone[
"id"]][
"remaining"]
307 for zone
in [z
for z
in self.
activity_dataactivity_data[
"wateringTimes"]
if z[
"active"]]
312 """Define a sensor that handles provisioning data."""
314 entity_description: RainMachineSensorDataDescription
318 """Update the state."""
319 system = self.coordinator.data.get(
"system", {})
326 TYPE_FLOW_SENSOR_CONSUMED_LITERS,
327 TYPE_FLOW_SENSOR_LEAK_VOLUME,
331 if clicks_per_m3 := system.get(
"flowSensorClicksPerCubicMeter"):
337 TYPE_LAST_LEAK_DETECTED,
338 TYPE_RAIN_SENSOR_RAIN_START,
352 """Define a sensor that shows the amount of time remaining for a zone."""
355 """Calculate the number of seconds remaining."""
357 int, self.coordinator.data[self.
entity_descriptionentity_description.uid][
"remaining"]
int calculate_seconds_remaining(self)
None update_from_latest_data(self)
None __init__(self, ConfigEntry entry, RainMachineData data, RainMachineSensorCompletionTimerDescription description)
None update_from_latest_data(self)
dict[str, Any] activity_data(self)
None async_added_to_hass(self)
int calculate_seconds_remaining(self)
int calculate_seconds_remaining(self)
SensorExtraStoredData|None async_get_last_sensor_data(self)
None async_finish_entity_domain_replacements(HomeAssistant hass, ConfigEntry entry, Iterable[EntityDomainReplacementStrategy] entity_replacement_strategies)
None async_setup_entry(HomeAssistant hass, RainMachineConfigEntry entry, AddEntitiesCallback async_add_entities)
bool key_exists(dict[str, Any] data, str search_key)