* fix binary sensor offline/online fixed self._handle_update on line 66 to produce args, kwargs. Updated the binary sensor to check the correct index in the tuple. * Fixed Standby switch Set standby switch to poll in order to get status when homeassistant starts up. Updated the index for the switch to get the status from the tuple.
233 lines
7.2 KiB
Python
233 lines
7.2 KiB
Python
"""Integration with the Rachio Iro sprinkler system controller."""
|
|
from abc import abstractmethod
|
|
from datetime import timedelta
|
|
import logging
|
|
|
|
from homeassistant.components.switch import SwitchDevice
|
|
from homeassistant.helpers.dispatcher import dispatcher_connect
|
|
|
|
from . import (
|
|
CONF_MANUAL_RUN_MINS,
|
|
DOMAIN as DOMAIN_RACHIO,
|
|
KEY_DEVICE_ID,
|
|
KEY_ENABLED,
|
|
KEY_ID,
|
|
KEY_NAME,
|
|
KEY_ON,
|
|
KEY_SUBTYPE,
|
|
KEY_SUMMARY,
|
|
KEY_ZONE_ID,
|
|
KEY_ZONE_NUMBER,
|
|
SIGNAL_RACHIO_CONTROLLER_UPDATE,
|
|
SIGNAL_RACHIO_ZONE_UPDATE,
|
|
SUBTYPE_SLEEP_MODE_OFF,
|
|
SUBTYPE_SLEEP_MODE_ON,
|
|
SUBTYPE_ZONE_COMPLETED,
|
|
SUBTYPE_ZONE_STARTED,
|
|
SUBTYPE_ZONE_STOPPED,
|
|
)
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
ATTR_ZONE_SUMMARY = "Summary"
|
|
ATTR_ZONE_NUMBER = "Zone number"
|
|
|
|
|
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
|
"""Set up the Rachio switches."""
|
|
manual_run_time = timedelta(
|
|
minutes=hass.data[DOMAIN_RACHIO].config.get(CONF_MANUAL_RUN_MINS)
|
|
)
|
|
_LOGGER.info("Rachio run time is %s", str(manual_run_time))
|
|
|
|
# Add all zones from all controllers as switches
|
|
devices = []
|
|
for controller in hass.data[DOMAIN_RACHIO].controllers:
|
|
devices.append(RachioStandbySwitch(hass, controller))
|
|
|
|
for zone in controller.list_zones():
|
|
devices.append(RachioZone(hass, controller, zone, manual_run_time))
|
|
|
|
add_entities(devices)
|
|
_LOGGER.info("%d Rachio switch(es) added", len(devices))
|
|
|
|
|
|
class RachioSwitch(SwitchDevice):
|
|
"""Represent a Rachio state that can be toggled."""
|
|
|
|
def __init__(self, controller, poll=True):
|
|
"""Initialize a new Rachio switch."""
|
|
self._controller = controller
|
|
|
|
if poll:
|
|
self._state = self._poll_update()
|
|
else:
|
|
self._state = None
|
|
|
|
@property
|
|
def should_poll(self) -> bool:
|
|
"""Declare that this entity pushes its state to HA."""
|
|
return False
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
"""Get a name for this switch."""
|
|
return f"Switch on {self._controller.name}"
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return whether the switch is currently on."""
|
|
return self._state
|
|
|
|
@abstractmethod
|
|
def _poll_update(self, data=None) -> bool:
|
|
"""Poll the API."""
|
|
pass
|
|
|
|
def _handle_any_update(self, *args, **kwargs) -> None:
|
|
"""Determine whether an update event applies to this device."""
|
|
if args[0][KEY_DEVICE_ID] != self._controller.controller_id:
|
|
# For another device
|
|
return
|
|
|
|
# For this device
|
|
self._handle_update(args, kwargs)
|
|
|
|
@abstractmethod
|
|
def _handle_update(self, *args, **kwargs) -> None:
|
|
"""Handle incoming webhook data."""
|
|
pass
|
|
|
|
|
|
class RachioStandbySwitch(RachioSwitch):
|
|
"""Representation of a standby status/button."""
|
|
|
|
def __init__(self, hass, controller):
|
|
"""Instantiate a new Rachio standby mode switch."""
|
|
dispatcher_connect(
|
|
hass, SIGNAL_RACHIO_CONTROLLER_UPDATE, self._handle_any_update
|
|
)
|
|
super().__init__(controller, poll=True)
|
|
self._poll_update(controller.init_data)
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
"""Return the name of the standby switch."""
|
|
return f"{self._controller.name} in standby mode"
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return a unique id by combining controller id and purpose."""
|
|
return f"{self._controller.controller_id}-standby"
|
|
|
|
@property
|
|
def icon(self) -> str:
|
|
"""Return an icon for the standby switch."""
|
|
return "mdi:power"
|
|
|
|
def _poll_update(self, data=None) -> bool:
|
|
"""Request the state from the API."""
|
|
if data is None:
|
|
data = self._controller.rachio.device.get(self._controller.controller_id)[1]
|
|
|
|
return not data[KEY_ON]
|
|
|
|
def _handle_update(self, *args, **kwargs) -> None:
|
|
"""Update the state using webhook data."""
|
|
if args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_ON:
|
|
self._state = True
|
|
elif args[0][0][KEY_SUBTYPE] == SUBTYPE_SLEEP_MODE_OFF:
|
|
self._state = False
|
|
|
|
self.schedule_update_ha_state()
|
|
|
|
def turn_on(self, **kwargs) -> None:
|
|
"""Put the controller in standby mode."""
|
|
self._controller.rachio.device.off(self._controller.controller_id)
|
|
|
|
def turn_off(self, **kwargs) -> None:
|
|
"""Resume controller functionality."""
|
|
self._controller.rachio.device.on(self._controller.controller_id)
|
|
|
|
|
|
class RachioZone(RachioSwitch):
|
|
"""Representation of one zone of sprinklers connected to the Rachio Iro."""
|
|
|
|
def __init__(self, hass, controller, data, manual_run_time):
|
|
"""Initialize a new Rachio Zone."""
|
|
self._id = data[KEY_ID]
|
|
self._zone_name = data[KEY_NAME]
|
|
self._zone_number = data[KEY_ZONE_NUMBER]
|
|
self._zone_enabled = data[KEY_ENABLED]
|
|
self._manual_run_time = manual_run_time
|
|
self._summary = str()
|
|
super().__init__(controller)
|
|
|
|
# Listen for all zone updates
|
|
dispatcher_connect(hass, SIGNAL_RACHIO_ZONE_UPDATE, self._handle_update)
|
|
|
|
def __str__(self):
|
|
"""Display the zone as a string."""
|
|
return 'Rachio Zone "{}" on {}'.format(self.name, str(self._controller))
|
|
|
|
@property
|
|
def zone_id(self) -> str:
|
|
"""How the Rachio API refers to the zone."""
|
|
return self._id
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
"""Return the friendly name of the zone."""
|
|
return self._zone_name
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return a unique id by combining controller id and zone number."""
|
|
return f"{self._controller.controller_id}-zone-{self.zone_id}"
|
|
|
|
@property
|
|
def icon(self) -> str:
|
|
"""Return the icon to display."""
|
|
return "mdi:water"
|
|
|
|
@property
|
|
def zone_is_enabled(self) -> bool:
|
|
"""Return whether the zone is allowed to run."""
|
|
return self._zone_enabled
|
|
|
|
@property
|
|
def state_attributes(self) -> dict:
|
|
"""Return the optional state attributes."""
|
|
return {ATTR_ZONE_NUMBER: self._zone_number, ATTR_ZONE_SUMMARY: self._summary}
|
|
|
|
def turn_on(self, **kwargs) -> None:
|
|
"""Start watering this zone."""
|
|
# Stop other zones first
|
|
self.turn_off()
|
|
|
|
# Start this zone
|
|
self._controller.rachio.zone.start(self.zone_id, self._manual_run_time.seconds)
|
|
_LOGGER.debug("Watering %s on %s", self.name, self._controller.name)
|
|
|
|
def turn_off(self, **kwargs) -> None:
|
|
"""Stop watering all zones."""
|
|
self._controller.stop_watering()
|
|
|
|
def _poll_update(self, data=None) -> bool:
|
|
"""Poll the API to check whether the zone is running."""
|
|
schedule = self._controller.current_schedule
|
|
return self.zone_id == schedule.get(KEY_ZONE_ID)
|
|
|
|
def _handle_update(self, *args, **kwargs) -> None:
|
|
"""Handle incoming webhook zone data."""
|
|
if args[0][KEY_ZONE_ID] != self.zone_id:
|
|
return
|
|
|
|
self._summary = kwargs.get(KEY_SUMMARY, str())
|
|
|
|
if args[0][KEY_SUBTYPE] == SUBTYPE_ZONE_STARTED:
|
|
self._state = True
|
|
elif args[0][KEY_SUBTYPE] in [SUBTYPE_ZONE_STOPPED, SUBTYPE_ZONE_COMPLETED]:
|
|
self._state = False
|
|
|
|
self.schedule_update_ha_state()
|