Add Rachio Schedules

This commit is contained in:
brg468
2020-03-29 15:45:21 -04:00
parent 48b5095f22
commit 0a05365b92
3 changed files with 116 additions and 1 deletions

View File

@@ -35,6 +35,7 @@ from .const import (
KEY_TYPE, KEY_TYPE,
KEY_USERNAME, KEY_USERNAME,
KEY_ZONES, KEY_ZONES,
KEY_SCHEDULES,
RACHIO_API_EXCEPTIONS, RACHIO_API_EXCEPTIONS,
) )
@@ -94,7 +95,7 @@ SUBTYPE_ZONE_CYCLING = "ZONE_CYCLING"
SUBTYPE_ZONE_CYCLING_COMPLETED = "ZONE_CYCLING_COMPLETED" SUBTYPE_ZONE_CYCLING_COMPLETED = "ZONE_CYCLING_COMPLETED"
# Webhook callbacks # Webhook callbacks
LISTEN_EVENT_TYPES = ["DEVICE_STATUS_EVENT", "ZONE_STATUS_EVENT"] LISTEN_EVENT_TYPES = ["DEVICE_STATUS_EVENT", "ZONE_STATUS_EVENT", "SCHEDULE_STATUS_EVENT"]
WEBHOOK_CONST_ID = "homeassistant.rachio:" WEBHOOK_CONST_ID = "homeassistant.rachio:"
WEBHOOK_PATH = URL_API + DOMAIN WEBHOOK_PATH = URL_API + DOMAIN
SIGNAL_RACHIO_UPDATE = DOMAIN + "_update" SIGNAL_RACHIO_UPDATE = DOMAIN + "_update"
@@ -258,6 +259,7 @@ class RachioIro:
self.mac_address = data[KEY_MAC_ADDRESS] self.mac_address = data[KEY_MAC_ADDRESS]
self.model = data[KEY_MODEL] self.model = data[KEY_MODEL]
self._zones = data[KEY_ZONES] self._zones = data[KEY_ZONES]
self._schedules = data[KEY_SCHEDULES]
self._init_data = data self._init_data = data
self._webhooks = webhooks self._webhooks = webhooks
_LOGGER.debug('%s has ID "%s"', str(self), self.controller_id) _LOGGER.debug('%s has ID "%s"', str(self), self.controller_id)
@@ -295,6 +297,7 @@ class RachioIro:
for event_type in self.rachio.notification.getWebhookEventType()[1]: for event_type in self.rachio.notification.getWebhookEventType()[1]:
if event_type[KEY_NAME] in LISTEN_EVENT_TYPES: if event_type[KEY_NAME] in LISTEN_EVENT_TYPES:
event_types.append({"id": event_type[KEY_ID]}) event_types.append({"id": event_type[KEY_ID]})
# Register to listen to these events from the device # Register to listen to these events from the device
url = self.rachio.webhook_url url = self.rachio.webhook_url
@@ -341,6 +344,10 @@ class RachioIro:
return zone return zone
return None return None
def list_schedules(self) -> list:
"""Return a list of schedules."""
return self._schedules
def stop_watering(self) -> None: def stop_watering(self) -> None:
"""Stop watering all zones connected to this controller.""" """Stop watering all zones connected to this controller."""

View File

@@ -22,6 +22,7 @@ KEY_ID = "id"
KEY_NAME = "name" KEY_NAME = "name"
KEY_MODEL = "model" KEY_MODEL = "model"
KEY_ON = "on" KEY_ON = "on"
KEY_DURATION = "totalDuration"
KEY_STATUS = "status" KEY_STATUS = "status"
KEY_SUBTYPE = "subType" KEY_SUBTYPE = "subType"
KEY_SUMMARY = "summary" KEY_SUMMARY = "summary"
@@ -33,6 +34,8 @@ KEY_USERNAME = "username"
KEY_ZONE_ID = "zoneId" KEY_ZONE_ID = "zoneId"
KEY_ZONE_NUMBER = "zoneNumber" KEY_ZONE_NUMBER = "zoneNumber"
KEY_ZONES = "zones" KEY_ZONES = "zones"
KEY_SCHEDULES = "scheduleRules"
KEY_SCHEDULE_ID = "scheduleId"
# Yes we really do get all these exceptions (hopefully rachiopy switches to requests) # Yes we really do get all these exceptions (hopefully rachiopy switches to requests)
RACHIO_API_EXCEPTIONS = ( RACHIO_API_EXCEPTIONS = (

View File

@@ -9,11 +9,15 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import ( from . import (
SIGNAL_RACHIO_CONTROLLER_UPDATE, SIGNAL_RACHIO_CONTROLLER_UPDATE,
SIGNAL_RACHIO_ZONE_UPDATE, SIGNAL_RACHIO_ZONE_UPDATE,
SIGNAL_RACHIO_SCHEDULE_UPDATE,
SUBTYPE_SLEEP_MODE_OFF, SUBTYPE_SLEEP_MODE_OFF,
SUBTYPE_SLEEP_MODE_ON, SUBTYPE_SLEEP_MODE_ON,
SUBTYPE_ZONE_COMPLETED, SUBTYPE_ZONE_COMPLETED,
SUBTYPE_ZONE_STARTED, SUBTYPE_ZONE_STARTED,
SUBTYPE_ZONE_STOPPED, SUBTYPE_ZONE_STOPPED,
SUBTYPE_SCHEDULE_COMPLETED,
SUBTYPE_SCHEDULE_STOPPED,
SUBTYPE_SCHEDULE_STARTED,
RachioDeviceInfoProvider, RachioDeviceInfoProvider,
) )
from .const import ( from .const import (
@@ -25,17 +29,23 @@ from .const import (
KEY_ID, KEY_ID,
KEY_IMAGE_URL, KEY_IMAGE_URL,
KEY_NAME, KEY_NAME,
KEY_DURATION,
KEY_ON, KEY_ON,
KEY_SUBTYPE, KEY_SUBTYPE,
KEY_SUMMARY, KEY_SUMMARY,
KEY_ZONE_ID, KEY_ZONE_ID,
KEY_ZONE_NUMBER, KEY_ZONE_NUMBER,
KEY_SCHEDULES,
KEY_SCHEDULE_ID,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATTR_ZONE_SUMMARY = "Summary" ATTR_ZONE_SUMMARY = "Summary"
ATTR_ZONE_NUMBER = "Zone number" ATTR_ZONE_NUMBER = "Zone number"
ATTR_SCHEDULE_SUMMARY = "Summary"
ATTR_SCHEDULE_ENABLED = "Enabled"
ATTR_SCHEDULE_DURATION = "Duration"
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
@@ -54,11 +64,16 @@ def _create_entities(hass, config_entry):
for controller in person.controllers: for controller in person.controllers:
entities.append(RachioStandbySwitch(controller)) entities.append(RachioStandbySwitch(controller))
zones = controller.list_zones() zones = controller.list_zones()
schedules = controller.list_schedules()
current_schedule = controller.current_schedule current_schedule = controller.current_schedule
_LOGGER.debug("Rachio setting up zones: %s", zones) _LOGGER.debug("Rachio setting up zones: %s", zones)
_LOGGER.debug("Added schedule: %s", schedules)
for zone in zones: for zone in zones:
_LOGGER.debug("Rachio setting up zone: %s", zone) _LOGGER.debug("Rachio setting up zone: %s", zone)
entities.append(RachioZone(person, controller, zone, current_schedule)) entities.append(RachioZone(person, controller, zone, current_schedule))
for sched in schedules:
_LOGGER.debug("Added schedule: %s", sched)
entities.append(RachioSchedule(person, controller, sched, current_schedule))
return entities return entities
@@ -265,3 +280,93 @@ class RachioZone(RachioSwitch):
async_dispatcher_connect( async_dispatcher_connect(
self.hass, SIGNAL_RACHIO_ZONE_UPDATE, self._handle_update self.hass, SIGNAL_RACHIO_ZONE_UPDATE, self._handle_update
) )
class RachioSchedule(RachioSwitch):
"""Representation of one fixed schedule on the Rachio Iro."""
def __init__(self, person, controller, data, current_schedule):
"""Initialize a new Rachio Schedule."""
self._id = data[KEY_ID]
self._schedule_name = data[KEY_NAME]
self._duration = data[KEY_DURATION]
self._schedule_enabled = data[KEY_ENABLED]
self._person = person
self._summary = data[KEY_SUMMARY]
self._current_schedule = current_schedule
super().__init__(controller, poll=False)
self._state = self.schedule_id == self._current_schedule.get(KEY_SCHEDULE_ID)
def __str__(self):
"""Display the schedule as a string."""
return 'Rachio Schedule "{}" on {}'.format(self.name, str(self._controller))
@property
def schedule_id(self) -> str:
"""How the Rachio API refers to the schedule."""
return self._id
@property
def name(self) -> str:
"""Return the friendly name of the schedule."""
return self._schedule_name
@property
def unique_id(self) -> str:
"""Return a unique id by combining controller id and schedule."""
return f"{self._controller.controller_id}-schedule-{self.schedule_id}"
@property
def icon(self) -> str:
"""Return the icon to display."""
return "mdi:water"
@property
def state_attributes(self) -> dict:
"""Return the optional state attributes."""
return {ATTR_SCHEDULE_SUMMARY: self._summary, ATTR_SCHEDULE_ENABLED: self.schedule_is_enabled,
ATTR_SCHEDULE_DURATION: self._duration /60}
@property
def schedule_is_enabled(self) -> bool:
"""Return whether the schedule is allowed to run."""
return self._schedule_enabled
def turn_on(self, **kwargs) -> None:
"""Start this schedule."""
self._controller.rachio.schedulerule.start(self.schedule_id)
_LOGGER.debug(
"Schedule %s started 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 schedule is running."""
self._current_schedule = self._controller.current_schedule
return self.schedule_id == self._current_schedule.get(KEY_SCHEDULE_ID)
def _handle_update(self, *args, **kwargs) -> None:
"""Handle incoming webhook schedule data."""
#Schedule ID not passed when running indvidual zones, so we catch that error
try:
if args[0][KEY_SCHEDULE_ID] == self.schedule_id:
if args[0][KEY_SUBTYPE] in [SUBTYPE_SCHEDULE_STARTED]:
self._state = True
elif args[0][KEY_SUBTYPE] in [SUBTYPE_SCHEDULE_STOPPED, SUBTYPE_SCHEDULE_COMPLETED]:
self._state = False
except KeyError:
pass
self.schedule_update_ha_state()
async def async_added_to_hass(self):
"""Subscribe to updates."""
async_dispatcher_connect(
self.hass, SIGNAL_RACHIO_SCHEDULE_UPDATE, self._handle_update
)