core/homeassistant/components/switch/rachio.py

231 lines
6.8 KiB
Python
Raw Normal View History

"""
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)