core/homeassistant/components/switch/rachio.py

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)