diff --git a/homeassistant/components/rachio/binary_sensor.py b/homeassistant/components/rachio/binary_sensor.py index 49f46578f76..495001e39b9 100644 --- a/homeassistant/components/rachio/binary_sensor.py +++ b/homeassistant/components/rachio/binary_sensor.py @@ -4,6 +4,7 @@ import logging from homeassistant.components.binary_sensor import ( DEVICE_CLASS_CONNECTIVITY, + DEVICE_CLASS_MOISTURE, BinarySensorEntity, ) from homeassistant.core import callback @@ -12,13 +13,21 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import ( DOMAIN as DOMAIN_RACHIO, KEY_DEVICE_ID, + KEY_RAIN_SENSOR_TRIPPED, KEY_STATUS, KEY_SUBTYPE, SIGNAL_RACHIO_CONTROLLER_UPDATE, + SIGNAL_RACHIO_RAIN_SENSOR_UPDATE, STATUS_ONLINE, ) from .entity import RachioDevice -from .webhooks import SUBTYPE_COLD_REBOOT, SUBTYPE_OFFLINE, SUBTYPE_ONLINE +from .webhooks import ( + SUBTYPE_COLD_REBOOT, + SUBTYPE_OFFLINE, + SUBTYPE_ONLINE, + SUBTYPE_RAIN_SENSOR_DETECTION_OFF, + SUBTYPE_RAIN_SENSOR_DETECTION_ON, +) _LOGGER = logging.getLogger(__name__) @@ -34,6 +43,7 @@ def _create_entities(hass, config_entry): entities = [] for controller in hass.data[DOMAIN_RACHIO][config_entry.entry_id].controllers: entities.append(RachioControllerOnlineBinarySensor(controller)) + entities.append(RachioRainSensor(controller)) return entities @@ -64,16 +74,6 @@ class RachioControllerBinarySensor(RachioDevice, BinarySensorEntity): def _async_handle_update(self, *args, **kwargs) -> None: """Handle an update to the state of this sensor.""" - async def async_added_to_hass(self): - """Subscribe to updates.""" - self.async_on_remove( - async_dispatcher_connect( - self.hass, - SIGNAL_RACHIO_CONTROLLER_UPDATE, - self._async_handle_any_update, - ) - ) - class RachioControllerOnlineBinarySensor(RachioControllerBinarySensor): """Represent a binary sensor that reflects if the controller is online.""" @@ -98,11 +98,6 @@ class RachioControllerOnlineBinarySensor(RachioControllerBinarySensor): """Return the name of an icon for this sensor.""" return "mdi:wifi-strength-4" if self.is_on else "mdi:wifi-strength-off-outline" - async def async_added_to_hass(self): - """Get initial state.""" - self._state = self._controller.init_data[KEY_STATUS] == STATUS_ONLINE - await super().async_added_to_hass() - @callback def _async_handle_update(self, *args, **kwargs) -> None: """Handle an update to the state of this sensor.""" @@ -115,3 +110,61 @@ class RachioControllerOnlineBinarySensor(RachioControllerBinarySensor): self._state = False self.async_write_ha_state() + + async def async_added_to_hass(self): + """Subscribe to updates.""" + self._state = self._controller.init_data[KEY_STATUS] == STATUS_ONLINE + + self.async_on_remove( + async_dispatcher_connect( + self.hass, + SIGNAL_RACHIO_CONTROLLER_UPDATE, + self._async_handle_any_update, + ) + ) + + +class RachioRainSensor(RachioControllerBinarySensor): + """Represent a binary sensor that reflects the status of the rain sensor.""" + + @property + def name(self) -> str: + """Return the name of this sensor including the controller name.""" + return f"{self._controller.name} rain sensor" + + @property + def unique_id(self) -> str: + """Return a unique id for this entity.""" + return f"{self._controller.controller_id}-rain_sensor" + + @property + def device_class(self) -> str: + """Return the class of this device.""" + return DEVICE_CLASS_MOISTURE + + @property + def icon(self) -> str: + """Return the icon for this sensor.""" + return "mdi:water" if self.is_on else "mdi:water-off" + + @callback + def _async_handle_update(self, *args, **kwargs) -> None: + """Handle an update to the state of this sensor.""" + if args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_SENSOR_DETECTION_ON: + self._state = True + elif args[0][0][KEY_SUBTYPE] == SUBTYPE_RAIN_SENSOR_DETECTION_OFF: + self._state = False + + self.async_write_ha_state() + + async def async_added_to_hass(self): + """Subscribe to updates.""" + self._state = self._controller.init_data[KEY_RAIN_SENSOR_TRIPPED] + + self.async_on_remove( + async_dispatcher_connect( + self.hass, + SIGNAL_RACHIO_RAIN_SENSOR_UPDATE, + self._async_handle_any_update, + ) + ) diff --git a/homeassistant/components/rachio/const.py b/homeassistant/components/rachio/const.py index 016218906f4..7f8111bd5e5 100644 --- a/homeassistant/components/rachio/const.py +++ b/homeassistant/components/rachio/const.py @@ -12,6 +12,12 @@ CONF_CUSTOM_URL = "hass_url_override" CONF_MANUAL_RUN_MINS = "manual_run_mins" DEFAULT_MANUAL_RUN_MINS = 10 +# Slope constants +SLOPE_FLAT = "ZERO_THREE" +SLOPE_SLIGHT = "FOUR_SIX" +SLOPE_MODERATE = "SEVEN_TWELVE" +SLOPE_STEEP = "OVER_TWELVE" + # Keys used in the API JSON KEY_DEVICE_ID = "deviceId" KEY_IMAGE_URL = "imageUrl" @@ -24,6 +30,7 @@ KEY_MODEL = "model" KEY_ON = "on" KEY_DURATION = "totalDuration" KEY_RAIN_DELAY = "rainDelayExpirationDate" +KEY_RAIN_SENSOR_TRIPPED = "rainSensorTripped" KEY_STATUS = "status" KEY_SUBTYPE = "subType" KEY_SUMMARY = "summary" @@ -40,9 +47,7 @@ KEY_FLEX_SCHEDULES = "flexScheduleRules" KEY_SCHEDULE_ID = "scheduleId" KEY_CUSTOM_SHADE = "customShade" KEY_CUSTOM_CROP = "customCrop" - -ATTR_ZONE_TYPE = "type" -ATTR_ZONE_SHADE = "shade" +KEY_CUSTOM_SLOPE = "customSlope" # Yes we really do get all these exceptions (hopefully rachiopy switches to requests) RACHIO_API_EXCEPTIONS = ( @@ -57,6 +62,7 @@ STATUS_ONLINE = "ONLINE" SIGNAL_RACHIO_UPDATE = f"{DOMAIN}_update" SIGNAL_RACHIO_CONTROLLER_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_controller" SIGNAL_RACHIO_RAIN_DELAY_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_rain_delay" +SIGNAL_RACHIO_RAIN_SENSOR_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_rain_sensor" SIGNAL_RACHIO_ZONE_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_zone" SIGNAL_RACHIO_SCHEDULE_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_schedule" diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 95f01d31518..b16e3ce529e 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -9,13 +9,12 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util.dt import as_timestamp, now from .const import ( - ATTR_ZONE_SHADE, - ATTR_ZONE_TYPE, CONF_MANUAL_RUN_MINS, DEFAULT_MANUAL_RUN_MINS, DOMAIN as DOMAIN_RACHIO, KEY_CUSTOM_CROP, KEY_CUSTOM_SHADE, + KEY_CUSTOM_SLOPE, KEY_DEVICE_ID, KEY_DURATION, KEY_ENABLED, @@ -33,6 +32,10 @@ from .const import ( SIGNAL_RACHIO_RAIN_DELAY_UPDATE, SIGNAL_RACHIO_SCHEDULE_UPDATE, SIGNAL_RACHIO_ZONE_UPDATE, + SLOPE_FLAT, + SLOPE_MODERATE, + SLOPE_SLIGHT, + SLOPE_STEEP, ) from .entity import RachioDevice from .webhooks import ( @@ -50,11 +53,14 @@ from .webhooks import ( _LOGGER = logging.getLogger(__name__) -ATTR_ZONE_SUMMARY = "Summary" -ATTR_ZONE_NUMBER = "Zone number" ATTR_SCHEDULE_SUMMARY = "Summary" ATTR_SCHEDULE_ENABLED = "Enabled" ATTR_SCHEDULE_DURATION = "Duration" +ATTR_ZONE_NUMBER = "Zone number" +ATTR_ZONE_SHADE = "Shade" +ATTR_ZONE_SLOPE = "Slope" +ATTR_ZONE_SUMMARY = "Summary" +ATTR_ZONE_TYPE = "Type" async def async_setup_entry(hass, config_entry, async_add_entities): @@ -235,6 +241,7 @@ class RachioZone(RachioSwitch): self._person = person self._shade_type = data.get(KEY_CUSTOM_SHADE, {}).get(KEY_NAME) self._zone_type = data.get(KEY_CUSTOM_CROP, {}).get(KEY_NAME) + self._slope_type = data.get(KEY_CUSTOM_SLOPE, {}).get(KEY_NAME) self._summary = "" self._current_schedule = current_schedule super().__init__(controller) @@ -281,6 +288,15 @@ class RachioZone(RachioSwitch): props[ATTR_ZONE_SHADE] = self._shade_type if self._zone_type: props[ATTR_ZONE_TYPE] = self._zone_type + if self._slope_type: + if self._slope_type == SLOPE_FLAT: + props[ATTR_ZONE_SLOPE] = "Flat" + elif self._slope_type == SLOPE_SLIGHT: + props[ATTR_ZONE_SLOPE] = "Slight" + elif self._slope_type == SLOPE_MODERATE: + props[ATTR_ZONE_SLOPE] = "Moderate" + elif self._slope_type == SLOPE_STEEP: + props[ATTR_ZONE_SLOPE] = "Steep" return props def turn_on(self, **kwargs) -> None: @@ -312,7 +328,7 @@ class RachioZone(RachioSwitch): if args[0][KEY_ZONE_ID] != self.zone_id: return - self._summary = kwargs.get(KEY_SUMMARY, "") + self._summary = args[0][KEY_SUMMARY] if args[0][KEY_SUBTYPE] == SUBTYPE_ZONE_STARTED: self._state = True diff --git a/homeassistant/components/rachio/webhooks.py b/homeassistant/components/rachio/webhooks.py index a3f95d5a5f3..5daf7852725 100644 --- a/homeassistant/components/rachio/webhooks.py +++ b/homeassistant/components/rachio/webhooks.py @@ -16,6 +16,7 @@ from .const import ( KEY_TYPE, SIGNAL_RACHIO_CONTROLLER_UPDATE, SIGNAL_RACHIO_RAIN_DELAY_UPDATE, + SIGNAL_RACHIO_RAIN_SENSOR_UPDATE, SIGNAL_RACHIO_SCHEDULE_UPDATE, SIGNAL_RACHIO_ZONE_UPDATE, ) @@ -29,14 +30,17 @@ SUBTYPE_COLD_REBOOT = "COLD_REBOOT" SUBTYPE_SLEEP_MODE_ON = "SLEEP_MODE_ON" SUBTYPE_SLEEP_MODE_OFF = "SLEEP_MODE_OFF" SUBTYPE_BROWNOUT_VALVE = "BROWNOUT_VALVE" -SUBTYPE_RAIN_SENSOR_DETECTION_ON = "RAIN_SENSOR_DETECTION_ON" -SUBTYPE_RAIN_SENSOR_DETECTION_OFF = "RAIN_SENSOR_DETECTION_OFF" # Rain delay values TYPE_RAIN_DELAY_STATUS = "RAIN_DELAY" SUBTYPE_RAIN_DELAY_ON = "RAIN_DELAY_ON" SUBTYPE_RAIN_DELAY_OFF = "RAIN_DELAY_OFF" +# Rain sensor values +TYPE_RAIN_SENSOR_STATUS = "RAIN_SENSOR_DETECTION" +SUBTYPE_RAIN_SENSOR_DETECTION_ON = "RAIN_SENSOR_DETECTION_ON" +SUBTYPE_RAIN_SENSOR_DETECTION_OFF = "RAIN_SENSOR_DETECTION_OFF" + # Schedule webhook values TYPE_SCHEDULE_STATUS = "SCHEDULE_STATUS" SUBTYPE_SCHEDULE_STARTED = "SCHEDULE_STARTED" @@ -60,6 +64,7 @@ LISTEN_EVENT_TYPES = [ "DEVICE_STATUS_EVENT", "ZONE_STATUS_EVENT", "RAIN_DELAY_EVENT", + "RAIN_SENSOR_DETECTION_EVENT", "SCHEDULE_STATUS_EVENT", ] WEBHOOK_CONST_ID = "homeassistant.rachio:" @@ -68,6 +73,7 @@ WEBHOOK_PATH = URL_API + DOMAIN SIGNAL_MAP = { TYPE_CONTROLLER_STATUS: SIGNAL_RACHIO_CONTROLLER_UPDATE, TYPE_RAIN_DELAY_STATUS: SIGNAL_RACHIO_RAIN_DELAY_UPDATE, + TYPE_RAIN_SENSOR_STATUS: SIGNAL_RACHIO_RAIN_SENSOR_UPDATE, TYPE_SCHEDULE_STATUS: SIGNAL_RACHIO_SCHEDULE_UPDATE, TYPE_ZONE_STATUS: SIGNAL_RACHIO_ZONE_UPDATE, }