diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index 97772c6033a..191ace64625 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -14,11 +14,11 @@ from homeassistant.components.sensor import ( SensorStateClass, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import TEMP_CELSIUS, VOLUME_CUBIC_METERS +from homeassistant.const import TEMP_CELSIUS, VOLUME_CUBIC_METERS, VOLUME_LITERS from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.util.dt import utcnow +from homeassistant.util.dt import utc_from_timestamp, utcnow from . import RainMachineData, RainMachineEntity from .const import ( @@ -45,10 +45,14 @@ DEFAULT_ZONE_COMPLETION_TIME_WOBBLE_TOLERANCE = timedelta(seconds=5) TYPE_FLOW_SENSOR_CLICK_M3 = "flow_sensor_clicks_cubic_meter" TYPE_FLOW_SENSOR_CONSUMED_LITERS = "flow_sensor_consumed_liters" +TYPE_FLOW_SENSOR_LEAK_CLICKS = "flow_sensor_leak_clicks" +TYPE_FLOW_SENSOR_LEAK_VOLUME = "flow_sensor_leak_volume" TYPE_FLOW_SENSOR_START_INDEX = "flow_sensor_start_index" TYPE_FLOW_SENSOR_WATERING_CLICKS = "flow_sensor_watering_clicks" TYPE_FREEZE_TEMP = "freeze_protect_temp" +TYPE_LAST_LEAK_DETECTED = "last_leak_detected" TYPE_PROGRAM_RUN_COMPLETION_TIME = "program_run_completion_time" +TYPE_RAIN_SENSOR_RAIN_START = "rain_sensor_rain_start" TYPE_ZONE_RUN_COMPLETION_TIME = "zone_run_completion_time" @@ -67,7 +71,7 @@ class RainMachineSensorCompletionTimerDescription( RainMachineEntityDescription, RainMachineEntityDescriptionMixinUid, ): - """Describe a RainMachine sensor.""" + """Describe a RainMachine completion timer sensor.""" SENSOR_DESCRIPTIONS = ( @@ -87,12 +91,34 @@ SENSOR_DESCRIPTIONS = ( name="Flow sensor consumed liters", icon="mdi:water-pump", entity_category=EntityCategory.DIAGNOSTIC, - native_unit_of_measurement="liter", + native_unit_of_measurement=VOLUME_LITERS, entity_registry_enabled_default=False, state_class=SensorStateClass.TOTAL_INCREASING, api_category=DATA_PROVISION_SETTINGS, data_key="flowSensorWateringClicks", ), + RainMachineSensorDataDescription( + key=TYPE_FLOW_SENSOR_LEAK_CLICKS, + name="Flow sensor leak clicks", + icon="mdi:pipe-leak", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement="clicks", + entity_registry_enabled_default=False, + state_class=SensorStateClass.TOTAL_INCREASING, + api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorLeakClicks", + ), + RainMachineSensorDataDescription( + key=TYPE_FLOW_SENSOR_LEAK_VOLUME, + name="Flow sensor leak volume", + icon="mdi:pipe-leak", + entity_category=EntityCategory.DIAGNOSTIC, + native_unit_of_measurement=VOLUME_LITERS, + entity_registry_enabled_default=False, + state_class=SensorStateClass.TOTAL_INCREASING, + api_category=DATA_PROVISION_SETTINGS, + data_key="flowSensorLeakClicks", + ), RainMachineSensorDataDescription( key=TYPE_FLOW_SENSOR_START_INDEX, name="Flow sensor start index", @@ -110,7 +136,7 @@ SENSOR_DESCRIPTIONS = ( entity_category=EntityCategory.DIAGNOSTIC, native_unit_of_measurement="clicks", entity_registry_enabled_default=False, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL_INCREASING, api_category=DATA_PROVISION_SETTINGS, data_key="flowSensorWateringClicks", ), @@ -125,6 +151,28 @@ SENSOR_DESCRIPTIONS = ( api_category=DATA_RESTRICTIONS_UNIVERSAL, data_key="freezeProtectTemp", ), + RainMachineSensorDataDescription( + key=TYPE_LAST_LEAK_DETECTED, + name="Last leak detected", + icon="mdi:pipe-leak", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.TIMESTAMP, + state_class=SensorStateClass.MEASUREMENT, + api_category=DATA_PROVISION_SETTINGS, + data_key="lastLeakDetected", + ), + RainMachineSensorDataDescription( + key=TYPE_RAIN_SENSOR_RAIN_START, + name="Rain sensor rain start", + icon="mdi:weather-pouring", + entity_category=EntityCategory.DIAGNOSTIC, + entity_registry_enabled_default=False, + device_class=SensorDeviceClass.TIMESTAMP, + state_class=SensorStateClass.MEASUREMENT, + api_category=DATA_PROVISION_SETTINGS, + data_key="rainSensorRainStart", + ), ) @@ -293,30 +341,36 @@ class ProvisionSettingsSensor(RainMachineEntity, SensorEntity): @callback def update_from_latest_data(self) -> None: """Update the state.""" - if self.entity_description.key == TYPE_FLOW_SENSOR_CLICK_M3: - self._attr_native_value = self.coordinator.data.get("system", {}).get( - "flowSensorClicksPerCubicMeter" - ) - elif self.entity_description.key == TYPE_FLOW_SENSOR_CONSUMED_LITERS: - clicks = self.coordinator.data.get("system", {}).get( - "flowSensorWateringClicks" - ) - clicks_per_m3 = self.coordinator.data.get("system", {}).get( - "flowSensorClicksPerCubicMeter" - ) + system = self.coordinator.data.get("system", {}) + new_value = system.get(self.entity_description.data_key) - if clicks and clicks_per_m3: - self._attr_native_value = (clicks * 1000) / clicks_per_m3 + # Calculate volumetric sensors + if ( + self.entity_description.key + in { + TYPE_FLOW_SENSOR_CONSUMED_LITERS, + TYPE_FLOW_SENSOR_LEAK_VOLUME, + } + and new_value + ): + if clicks_per_m3 := system.get("flowSensorClicksPerCubicMeter"): + self._attr_native_value = round((new_value * 1000) / clicks_per_m3, 1) + return + + # Convert timestamp sensors to datetime + if self.entity_description.key in { + TYPE_LAST_LEAK_DETECTED, + TYPE_RAIN_SENSOR_RAIN_START, + }: + # Timestamp may return 0 instead of null, explicitly set to None + if new_value: + self._attr_native_value = utc_from_timestamp(new_value) else: self._attr_native_value = None - elif self.entity_description.key == TYPE_FLOW_SENSOR_START_INDEX: - self._attr_native_value = self.coordinator.data.get("system", {}).get( - "flowSensorStartIndex" - ) - elif self.entity_description.key == TYPE_FLOW_SENSOR_WATERING_CLICKS: - self._attr_native_value = self.coordinator.data.get("system", {}).get( - "flowSensorWateringClicks" - ) + return + + # Return all other sensor values or None + self._attr_native_value = new_value class UniversalRestrictionsSensor(RainMachineEntity, SensorEntity):