231 lines
6.8 KiB
Python
231 lines
6.8 KiB
Python
"""
|
|
Integration with the Rachio Iro sprinkler system controller.
|
|
|
|
For more details about this platform, please refer to the documentation at
|
|
https://home-assistant.io/components/switch.rachio/
|
|
"""
|
|
from datetime import timedelta
|
|
import logging
|
|
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
|
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
|
import homeassistant.helpers.config_validation as cv
|
|
import homeassistant.util as util
|
|
|
|
REQUIREMENTS = ['rachiopy==0.1.2']
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
CONF_MANUAL_RUN_MINS = 'manual_run_mins'
|
|
|
|
DATA_RACHIO = 'rachio'
|
|
|
|
DEFAULT_MANUAL_RUN_MINS = 10
|
|
|
|
MIN_UPDATE_INTERVAL = timedelta(seconds=30)
|
|
MIN_FORCED_UPDATE_INTERVAL = timedelta(seconds=1)
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
vol.Required(CONF_ACCESS_TOKEN): cv.string,
|
|
vol.Optional(CONF_MANUAL_RUN_MINS, default=DEFAULT_MANUAL_RUN_MINS):
|
|
cv.positive_int,
|
|
})
|
|
|
|
|
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
|
"""Set up the Rachio switches."""
|
|
from rachiopy import Rachio
|
|
|
|
# Get options
|
|
manual_run_mins = config.get(CONF_MANUAL_RUN_MINS)
|
|
_LOGGER.debug("Rachio run time is %d min", manual_run_mins)
|
|
|
|
access_token = config.get(CONF_ACCESS_TOKEN)
|
|
|
|
# Configure API
|
|
_LOGGER.debug("Configuring Rachio API")
|
|
rachio = Rachio(access_token)
|
|
|
|
person = None
|
|
try:
|
|
person = _get_person(rachio)
|
|
except KeyError:
|
|
_LOGGER.error(
|
|
"Could not reach the Rachio API. Is your access token valid?")
|
|
return
|
|
|
|
# Get and persist devices
|
|
devices = _list_devices(rachio, manual_run_mins)
|
|
if not devices:
|
|
_LOGGER.error(
|
|
"No Rachio devices found in account %s", person['username'])
|
|
return
|
|
|
|
hass.data[DATA_RACHIO] = devices[0]
|
|
|
|
if len(devices) > 1:
|
|
_LOGGER.warning("Multiple Rachio devices found in account, "
|
|
"using %s", hass.data[DATA_RACHIO].device_id)
|
|
else:
|
|
_LOGGER.debug("Found Rachio device")
|
|
|
|
hass.data[DATA_RACHIO].update()
|
|
add_devices(hass.data[DATA_RACHIO].list_zones())
|
|
|
|
|
|
def _get_person(rachio):
|
|
"""Pull the account info of the person whose access token was provided."""
|
|
person_id = rachio.person.getInfo()[1]['id']
|
|
return rachio.person.get(person_id)[1]
|
|
|
|
|
|
def _list_devices(rachio, manual_run_mins):
|
|
"""Pull a list of devices on the account."""
|
|
return [RachioIro(rachio, d['id'], manual_run_mins)
|
|
for d in _get_person(rachio)['devices']]
|
|
|
|
|
|
class RachioIro(object):
|
|
"""Representation of a Rachio Iro."""
|
|
|
|
def __init__(self, rachio, device_id, manual_run_mins):
|
|
"""Initialize a Rachio device."""
|
|
self.rachio = rachio
|
|
self._device_id = device_id
|
|
self.manual_run_mins = manual_run_mins
|
|
self._device = None
|
|
self._running = None
|
|
self._zones = None
|
|
|
|
def __str__(self):
|
|
"""Display the device as a string."""
|
|
return "Rachio Iro {}".format(self.serial_number)
|
|
|
|
@property
|
|
def device_id(self):
|
|
"""Return the Rachio API device ID."""
|
|
return self._device['id']
|
|
|
|
@property
|
|
def status(self):
|
|
"""Return the current status of the device."""
|
|
return self._device['status']
|
|
|
|
@property
|
|
def serial_number(self):
|
|
"""Return the serial number of the device."""
|
|
return self._device['serialNumber']
|
|
|
|
@property
|
|
def is_paused(self):
|
|
"""Return whether the device is temporarily disabled."""
|
|
return self._device['paused']
|
|
|
|
@property
|
|
def is_on(self):
|
|
"""Return whether the device is powered on and connected."""
|
|
return self._device['on']
|
|
|
|
@property
|
|
def current_schedule(self):
|
|
"""Return the schedule that the device is running right now."""
|
|
return self._running
|
|
|
|
def list_zones(self, include_disabled=False):
|
|
"""Return a list of the zones connected to the device, incl. data."""
|
|
if not self._zones:
|
|
self._zones = [RachioZone(self.rachio, self, zone['id'],
|
|
self.manual_run_mins)
|
|
for zone in self._device['zones']]
|
|
|
|
if include_disabled:
|
|
return self._zones
|
|
|
|
self.update(no_throttle=True)
|
|
return [z for z in self._zones if z.is_enabled]
|
|
|
|
@util.Throttle(MIN_UPDATE_INTERVAL, MIN_FORCED_UPDATE_INTERVAL)
|
|
def update(self, **kwargs):
|
|
"""Pull updated device info from the Rachio API."""
|
|
self._device = self.rachio.device.get(self._device_id)[1]
|
|
self._running = self.rachio.device\
|
|
.getCurrentSchedule(self._device_id)[1]
|
|
|
|
# Possibly update all zones
|
|
for zone in self.list_zones(include_disabled=True):
|
|
zone.update()
|
|
|
|
_LOGGER.debug("Updated %s", str(self))
|
|
|
|
|
|
class RachioZone(SwitchDevice):
|
|
"""Representation of one zone of sprinklers connected to the Rachio Iro."""
|
|
|
|
def __init__(self, rachio, device, zone_id, manual_run_mins):
|
|
"""Initialize a new Rachio Zone."""
|
|
self.rachio = rachio
|
|
self._device = device
|
|
self._zone_id = zone_id
|
|
self._zone = None
|
|
self._manual_run_secs = manual_run_mins * 60
|
|
|
|
def __str__(self):
|
|
"""Display the zone as a string."""
|
|
return "Rachio Zone {}".format(self.name)
|
|
|
|
@property
|
|
def zone_id(self):
|
|
"""How the Rachio API refers to the zone."""
|
|
return self._zone['id']
|
|
|
|
@property
|
|
def unique_id(self):
|
|
"""Return the unique string ID for the zone."""
|
|
return '{iro}-{zone}'.format(
|
|
iro=self._device.device_id, zone=self.zone_id)
|
|
|
|
@property
|
|
def number(self):
|
|
"""Return the physical connection of the zone pump."""
|
|
return self._zone['zoneNumber']
|
|
|
|
@property
|
|
def name(self):
|
|
"""Return the friendly name of the zone."""
|
|
return self._zone['name']
|
|
|
|
@property
|
|
def is_enabled(self):
|
|
"""Return whether the zone is allowed to run."""
|
|
return self._zone['enabled']
|
|
|
|
@property
|
|
def is_on(self):
|
|
"""Return whether the zone is currently running."""
|
|
schedule = self._device.current_schedule
|
|
return self.zone_id == schedule.get('zoneId')
|
|
|
|
def update(self):
|
|
"""Pull updated zone info from the Rachio API."""
|
|
self._zone = self.rachio.zone.get(self._zone_id)[1]
|
|
|
|
# Possibly update device
|
|
self._device.update()
|
|
|
|
_LOGGER.debug("Updated %s", str(self))
|
|
|
|
def turn_on(self, **kwargs):
|
|
"""Start the zone."""
|
|
# Stop other zones first
|
|
self.turn_off()
|
|
|
|
_LOGGER.info("Watering %s for %d s", self.name, self._manual_run_secs)
|
|
self.rachio.zone.start(self.zone_id, self._manual_run_secs)
|
|
|
|
def turn_off(self, **kwargs):
|
|
"""Stop all zones."""
|
|
_LOGGER.info("Stopping watering of all zones")
|
|
self.rachio.device.stopWater(self._device.device_id)
|