diff --git a/homeassistant/components/rachio/const.py b/homeassistant/components/rachio/const.py index a25d329c7c1..943e1b33199 100644 --- a/homeassistant/components/rachio/const.py +++ b/homeassistant/components/rachio/const.py @@ -52,6 +52,7 @@ STATUS_ONLINE = "ONLINE" SCHEDULE_TYPE_FIXED = "FIXED" SCHEDULE_TYPE_FLEX = "FLEX" SERVICE_SET_ZONE_MOISTURE = "set_zone_moisture_percent" +SERVICE_START_MULTIPLE_ZONES = "start_multiple_zone_schedule" SIGNAL_RACHIO_UPDATE = f"{DOMAIN}_update" SIGNAL_RACHIO_CONTROLLER_UPDATE = f"{SIGNAL_RACHIO_UPDATE}_controller" diff --git a/homeassistant/components/rachio/device.py b/homeassistant/components/rachio/device.py index 64066ca7bd7..9d7c3057939 100644 --- a/homeassistant/components/rachio/device.py +++ b/homeassistant/components/rachio/device.py @@ -80,6 +80,10 @@ class RachioPerson: """Get a list of controllers managed by this account.""" return self._controllers + def start_multiple_zones(self, zones) -> None: + """Start multiple zones.""" + self.rachio.zone.start_multiple(zones) + class RachioIro: """Represent a Rachio Iro.""" diff --git a/homeassistant/components/rachio/services.yaml b/homeassistant/components/rachio/services.yaml index 0b22a0953a0..480d53aa454 100644 --- a/homeassistant/components/rachio/services.yaml +++ b/homeassistant/components/rachio/services.yaml @@ -7,3 +7,12 @@ set_zone_moisture_percent: percent: description: Set the desired zone moisture percentage from 0 to 100. [Required] example: 50 +start_multiple_zone_schedule: + description: Create a custom schedule of zones and runtimes. + fields: + entity_id: + description: Name of the zone or zones to run. Zones should all be on the same controller, attempting to start zones on multiple controllers may have undesired results. [Required] + example: "switch.front_yard, switch.side_yard" + duration: + description: Number of minutes to run the zone(s). If only 1 duration is given, that time will be used for all zones. If given a list of durations, the durations will apply to the respective zone listed above. [Required] + example: 15, 20 diff --git a/homeassistant/components/rachio/switch.py b/homeassistant/components/rachio/switch.py index 4af61d4f71b..8009d79b224 100644 --- a/homeassistant/components/rachio/switch.py +++ b/homeassistant/components/rachio/switch.py @@ -6,7 +6,9 @@ import logging import voluptuous as vol from homeassistant.components.switch import SwitchEntity +from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.event import async_track_point_in_utc_time @@ -37,6 +39,7 @@ from .const import ( SCHEDULE_TYPE_FIXED, SCHEDULE_TYPE_FLEX, SERVICE_SET_ZONE_MOISTURE, + SERVICE_START_MULTIPLE_ZONES, SIGNAL_RACHIO_CONTROLLER_UPDATE, SIGNAL_RACHIO_RAIN_DELAY_UPDATE, SIGNAL_RACHIO_SCHEDULE_UPDATE, @@ -63,32 +66,81 @@ from .webhooks import ( _LOGGER = logging.getLogger(__name__) +ATTR_DURATION = "duration" +ATTR_ID = "id" ATTR_PERCENT = "percent" ATTR_SCHEDULE_SUMMARY = "Summary" ATTR_SCHEDULE_ENABLED = "Enabled" ATTR_SCHEDULE_DURATION = "Duration" ATTR_SCHEDULE_TYPE = "Type" +ATTR_SORT_ORDER = "sortOrder" ATTR_ZONE_NUMBER = "Zone number" ATTR_ZONE_SHADE = "Shade" ATTR_ZONE_SLOPE = "Slope" ATTR_ZONE_SUMMARY = "Summary" ATTR_ZONE_TYPE = "Type" +START_MULTIPLE_ZONES_SCHEMA = vol.Schema( + { + vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_DURATION): cv.ensure_list_csv, + } +) + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Rachio switches.""" + zone_entities = [] has_flex_sched = False entities = await hass.async_add_executor_job(_create_entities, hass, config_entry) for entity in entities: + if isinstance(entity, RachioZone): + zone_entities.append(entity) if isinstance(entity, RachioSchedule) and entity.type == SCHEDULE_TYPE_FLEX: has_flex_sched = True - break async_add_entities(entities) _LOGGER.info("%d Rachio switch(es) added", len(entities)) - platform = entity_platform.current_platform.get() + def start_multiple(service): + """Service to start multiple zones in sequence.""" + zones_list = [] + person = hass.data[DOMAIN_RACHIO][config_entry.entry_id] + entity_id = service.data[ATTR_ENTITY_ID] + duration = iter(service.data[ATTR_DURATION]) + default_time = service.data[ATTR_DURATION][0] + entity_to_zone_id = { + entity.entity_id: entity.zone_id for entity in zone_entities + } + + for (count, data) in enumerate(entity_id): + if data in entity_to_zone_id: + # Time can be passed as a list per zone, + # or one time for all zones + time = int(next(duration, default_time)) * 60 + zones_list.append( + { + ATTR_ID: entity_to_zone_id.get(data), + ATTR_DURATION: time, + ATTR_SORT_ORDER: count, + } + ) + + if len(zones_list) != 0: + person.start_multiple_zones(zones_list) + _LOGGER.debug("Starting zone(s) %s", entity_id) + else: + raise HomeAssistantError("No matching zones found in given entity_ids") + + hass.services.async_register( + DOMAIN_RACHIO, + SERVICE_START_MULTIPLE_ZONES, + start_multiple, + schema=START_MULTIPLE_ZONES_SCHEMA, + ) + if has_flex_sched: + platform = entity_platform.current_platform.get() platform.async_register_entity_service( SERVICE_SET_ZONE_MOISTURE, {vol.Required(ATTR_PERCENT): cv.positive_int}, @@ -289,7 +341,7 @@ class RachioZone(RachioSwitch): def __init__(self, person, controller, data, current_schedule): """Initialize a new Rachio Zone.""" - self._id = data[KEY_ID] + self.id = data[KEY_ID] self._zone_name = data[KEY_NAME] self._zone_number = data[KEY_ZONE_NUMBER] self._zone_enabled = data[KEY_ENABLED] @@ -309,7 +361,7 @@ class RachioZone(RachioSwitch): @property def zone_id(self) -> str: """How the Rachio API refers to the zone.""" - return self._id + return self.id @property def name(self) -> str: @@ -381,7 +433,7 @@ class RachioZone(RachioSwitch): def set_moisture_percent(self, percent) -> None: """Set the zone moisture percent.""" _LOGGER.debug("Setting %s moisture to %s percent", self._zone_name, percent) - self._controller.rachio.zone.set_moisture_percent(self._id, percent / 100) + self._controller.rachio.zone.set_moisture_percent(self.id, percent / 100) @callback def _async_handle_update(self, *args, **kwargs) -> None: