1 """Adapter to wrap the rachiopy api for home assistant."""
3 from __future__
import annotations
5 from http
import HTTPStatus
9 from rachiopy
import Rachio
10 import voluptuous
as vol
36 SERVICE_PAUSE_WATERING,
37 SERVICE_RESUME_WATERING,
38 SERVICE_STOP_WATERING,
41 from .coordinator
import RachioScheduleUpdateCoordinator, RachioUpdateCoordinator
43 _LOGGER = logging.getLogger(__name__)
45 ATTR_DEVICES =
"devices"
46 ATTR_DURATION =
"duration"
47 PERMISSION_ERROR =
"7"
49 PAUSE_SERVICE_SCHEMA = vol.Schema(
51 vol.Optional(ATTR_DEVICES): cv.string,
52 vol.Optional(ATTR_DURATION, default=60): cv.positive_int,
56 RESUME_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_DEVICES): cv.string})
58 STOP_SERVICE_SCHEMA = vol.Schema({vol.Optional(ATTR_DEVICES): cv.string})
62 """Represent a Rachio user."""
64 def __init__(self, rachio: Rachio, config_entry: ConfigEntry) ->
None:
65 """Create an object from the provided API instance."""
70 self.
_id_id: str |
None =
None
71 self._controllers: list[RachioIro] = []
72 self._base_stations: list[RachioBaseStation] = []
75 """Create rachio devices and services."""
76 await hass.async_add_executor_job(self.
_setup_setup, hass)
78 for rachio_iro
in self._controllers:
80 if rachio_iro.model.split(
"_")[0] != MODEL_GENERATION_1:
84 all_controllers = [rachio_iro.name
for rachio_iro
in self._controllers]
86 def pause_water(service: ServiceCall) ->
None:
87 """Service to pause watering on all or specific controllers."""
88 duration = service.data[ATTR_DURATION]
89 devices = service.data.get(ATTR_DEVICES, all_controllers)
90 for iro
in self._controllers:
91 if iro.name
in devices:
92 iro.pause_watering(duration)
94 def resume_water(service: ServiceCall) ->
None:
95 """Service to resume watering on all or specific controllers."""
96 devices = service.data.get(ATTR_DEVICES, all_controllers)
97 for iro
in self._controllers:
98 if iro.name
in devices:
101 def stop_water(service: ServiceCall) ->
None:
102 """Service to stop watering on all or specific controllers."""
103 devices = service.data.get(ATTR_DEVICES, all_controllers)
104 for iro
in self._controllers:
105 if iro.name
in devices:
109 if not all_controllers:
112 hass.services.async_register(
114 SERVICE_STOP_WATERING,
116 schema=STOP_SERVICE_SCHEMA,
122 hass.services.async_register(
124 SERVICE_PAUSE_WATERING,
126 schema=PAUSE_SERVICE_SCHEMA,
129 hass.services.async_register(
131 SERVICE_RESUME_WATERING,
133 schema=RESUME_SERVICE_SCHEMA,
136 def _setup(self, hass: HomeAssistant) ->
None:
137 """Rachio device setup."""
138 rachio = self.
rachiorachio
140 response = rachio.person.info()
143 if int(response[0][KEY_STATUS]) != HTTPStatus.OK:
145 self.
_id_id = response[1][KEY_ID]
148 data = rachio.person.get(self.
_id_id)
151 if int(data[0][KEY_STATUS]) != HTTPStatus.OK:
153 self.
usernameusername = data[1][KEY_USERNAME]
154 devices: list[dict[str, Any]] = data[1][KEY_DEVICES]
155 base_station_data = rachio.valve.list_base_stations(self.
_id_id)
156 base_stations: list[dict[str, Any]] = base_station_data[1][KEY_BASE_STATIONS]
158 for controller
in devices:
159 webhooks = rachio.notification.get_device_webhook(controller[KEY_ID])[1]
165 if isinstance(webhooks, dict):
166 if webhooks.get(
"code") == PERMISSION_ERROR:
169 "Not adding controller '%s', only controllers owned by '%s'"
172 controller[KEY_NAME],
177 "Failed to add rachio controller '%s' because of an error: %s",
178 controller[KEY_NAME],
179 webhooks.get(
"error",
"Unknown Error"),
183 rachio_iro =
RachioIro(hass, rachio, controller, webhooks)
185 self._controllers.append(rachio_iro)
187 base_count = len(base_stations)
188 self._base_stations.extend(
193 hass, rachio, self.
config_entryconfig_entry, base, base_count
197 for base
in base_stations
200 _LOGGER.debug(
'Using Rachio API as user "%s"', self.
usernameusername)
204 """Get the user ID as defined by the Rachio API."""
209 """Get a list of controllers managed by this account."""
210 return self._controllers
214 """List of smart hose timer base stations."""
215 return self._base_stations
218 """Start multiple zones."""
219 self.
rachiorachio.zone.start_multiple(zones)
223 """Represent a Rachio Iro."""
229 data: dict[str, Any],
230 webhooks: list[dict[str, Any]],
232 """Initialize a Rachio device."""
235 self.
_id_id = data[KEY_ID]
244 self.
_webhooks_webhooks: list[dict[str, Any]] = webhooks
245 _LOGGER.debug(
'%s has ID "%s"', self, self.
controller_idcontroller_id)
248 """Rachio Iro setup for webhooks."""
253 """Start getting updates from the Rachio API."""
254 current_webhook_id =
None
257 def _deinit_webhooks(_) -> None:
258 """Stop getting updates from the Rachio API."""
267 webhook[KEY_EXTERNAL_ID].startswith(WEBHOOK_CONST_ID)
268 or webhook[KEY_ID] == current_webhook_id
270 self.
rachiorachio.notification.delete(webhook[KEY_ID])
273 _deinit_webhooks(
None)
277 {
"id": event_type[KEY_ID]}
278 for event_type
in self.
rachiorachio.notification.get_webhook_event_type()[1]
279 if event_type[KEY_NAME]
in LISTEN_EVENT_TYPES
283 url = self.
rachiorachio.webhook_url
284 auth = WEBHOOK_CONST_ID + self.
rachiorachio.webhook_auth
285 new_webhook = self.
rachiorachio.notification.add(
289 current_webhook_id = new_webhook[1][KEY_ID]
290 self.
hasshass.bus.listen(EVENT_HOMEASSISTANT_STOP, _deinit_webhooks)
293 """Display the controller as a string."""
294 return f
'Rachio controller "{self.name}"'
298 """Return the Rachio API controller ID."""
303 """Return the schedule that the device is running right now."""
308 """Return the information used to set up the controller."""
312 """Return a list of the zone dicts connected to the device."""
318 return [z
for z
in self.
_zones_zones
if z[KEY_ENABLED]]
321 """Return the zone with the given ID."""
322 for zone
in self.
list_zoneslist_zones(include_disabled=
True):
323 if zone[KEY_ID] == zone_id:
329 """Return a list of fixed schedules."""
333 """Return a list of flex schedules."""
337 """Stop watering all zones connected to this controller."""
339 _LOGGER.debug(
"Stopped watering of all zones on %s", self)
342 """Pause watering on this controller."""
344 _LOGGER.debug(
"Paused watering on %s for %s minutes", self, duration)
347 """Resume paused watering on this controller."""
349 _LOGGER.debug(
"Resuming watering on %s", self)
353 """Represent a smart hose timer base station."""
358 data: dict[str, Any],
359 status_coordinator: RachioUpdateCoordinator,
360 schedule_coordinator: RachioScheduleUpdateCoordinator,
362 """Initialize a smart hose timer base station."""
364 self.
_id_id = data[KEY_ID]
369 """Start watering on this valve."""
370 self.
rachiorachio.valve.start_watering(valve_id, duration)
373 """Stop watering on this valve."""
374 self.
rachiorachio.valve.stop_watering(valve_id)
377 """Create a skip for a scheduled event."""
378 self.
rachiorachio.program.create_skip_overrides(program_id, timestamp)
382 """HTTP status codes that mean invalid auth."""
383 return http_status_code
in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN)
None start_watering(self, str valve_id, int duration)
None stop_watering(self, str valve_id)
None __init__(self, Rachio rachio, dict[str, Any] data, RachioUpdateCoordinator status_coordinator, RachioScheduleUpdateCoordinator schedule_coordinator)
None create_skip(self, str program_id, str timestamp)
list list_schedules(self)
list list_zones(self, include_disabled=False)
None pause_watering(self, duration)
None __init__(self, HomeAssistant hass, Rachio rachio, dict[str, Any] data, list[dict[str, Any]] webhooks)
None _init_webhooks(self)
str current_schedule(self)
dict|None get_zone(self, zone_id)
list list_flex_schedules(self)
None resume_watering(self)
list[RachioBaseStation] base_stations(self)
None _setup(self, HomeAssistant hass)
list[RachioIro] controllers(self)
None __init__(self, Rachio rachio, ConfigEntry config_entry)
None async_setup(self, HomeAssistant hass)
None start_multiple_zones(self, zones)
bool is_invalid_auth_code(int http_status_code)