2017-08-08 07:49:25 +00:00
|
|
|
"""Implements a RainMachine sprinkler controller for Home Assistant."""
|
|
|
|
|
|
|
|
from logging import getLogger
|
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
2018-04-28 13:46:58 +00:00
|
|
|
from homeassistant.components.rainmachine import (
|
|
|
|
DATA_RAINMACHINE, DEFAULT_ATTRIBUTION, MIN_SCAN_TIME, MIN_SCAN_TIME_FORCED)
|
|
|
|
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
|
|
|
|
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS
|
2017-08-08 07:49:25 +00:00
|
|
|
from homeassistant.util import Throttle
|
|
|
|
|
|
|
|
_LOGGER = getLogger(__name__)
|
2018-04-28 13:46:58 +00:00
|
|
|
DEPENDENCIES = ['rainmachine']
|
2017-08-08 07:49:25 +00:00
|
|
|
|
|
|
|
ATTR_CYCLES = 'cycles'
|
|
|
|
ATTR_TOTAL_DURATION = 'total_duration'
|
|
|
|
|
|
|
|
CONF_ZONE_RUN_TIME = 'zone_run_time'
|
|
|
|
|
|
|
|
DEFAULT_ZONE_RUN_SECONDS = 60 * 10
|
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
|
|
vol.Optional(CONF_ZONE_RUN_TIME, default=DEFAULT_ZONE_RUN_SECONDS):
|
|
|
|
cv.positive_int
|
|
|
|
})
|
2017-08-08 07:49:25 +00:00
|
|
|
|
|
|
|
|
2017-10-17 07:24:52 +00:00
|
|
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Set this component up under its platform."""
|
2018-04-28 13:46:58 +00:00
|
|
|
client = hass.data.get(DATA_RAINMACHINE)
|
|
|
|
device_name = client.provision.device_name()['name']
|
|
|
|
device_mac = client.provision.wifi()['macAddress']
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
_LOGGER.debug('Config received: %s', config)
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
zone_run_time = config[CONF_ZONE_RUN_TIME]
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
entities = []
|
|
|
|
for program in client.programs.all().get('programs', {}):
|
|
|
|
if not program.get('active'):
|
|
|
|
continue
|
2018-02-12 03:55:51 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
_LOGGER.debug('Adding program: %s', program)
|
|
|
|
entities.append(
|
|
|
|
RainMachineProgram(client, device_name, device_mac, program))
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
for zone in client.zones.all().get('zones', {}):
|
|
|
|
if not zone.get('active'):
|
|
|
|
continue
|
2017-08-15 18:03:40 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
_LOGGER.debug('Adding zone: %s', zone)
|
|
|
|
entities.append(
|
|
|
|
RainMachineZone(client, device_name, device_mac, zone,
|
|
|
|
zone_run_time))
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
add_devices(entities, True)
|
2017-08-08 07:49:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RainMachineEntity(SwitchDevice):
|
|
|
|
"""A class to represent a generic RainMachine entity."""
|
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
def __init__(self, client, device_name, device_mac, entity_json):
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Initialize a generic RainMachine entity."""
|
|
|
|
self._api_type = 'remote' if client.auth.using_remote_api else 'local'
|
|
|
|
self._client = client
|
|
|
|
self._entity_json = entity_json
|
2018-04-28 13:46:58 +00:00
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
self.device_mac = device_mac
|
|
|
|
self.device_name = device_name
|
2017-08-08 07:49:25 +00:00
|
|
|
|
|
|
|
self._attrs = {
|
2018-04-28 13:46:58 +00:00
|
|
|
ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION,
|
2018-02-12 03:55:51 +00:00
|
|
|
ATTR_DEVICE_CLASS: self.device_name
|
2017-08-08 07:49:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self) -> dict:
|
|
|
|
"""Return the state attributes."""
|
2018-04-28 13:46:58 +00:00
|
|
|
return self._attrs
|
|
|
|
|
|
|
|
@property
|
|
|
|
def icon(self) -> str:
|
|
|
|
"""Return the icon."""
|
|
|
|
return 'mdi:water'
|
2017-08-08 07:49:25 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def is_enabled(self) -> bool:
|
|
|
|
"""Return whether the entity is enabled."""
|
|
|
|
return self._entity_json.get('active')
|
|
|
|
|
|
|
|
@property
|
2018-02-12 03:55:51 +00:00
|
|
|
def rainmachine_entity_id(self) -> int:
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Return the RainMachine ID for this entity."""
|
|
|
|
return self._entity_json.get('uid')
|
|
|
|
|
|
|
|
|
|
|
|
class RainMachineProgram(RainMachineEntity):
|
|
|
|
"""A RainMachine program."""
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_on(self) -> bool:
|
|
|
|
"""Return whether the program is running."""
|
|
|
|
return bool(self._entity_json.get('status'))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self) -> str:
|
|
|
|
"""Return the name of the program."""
|
2018-04-28 13:46:58 +00:00
|
|
|
return 'Program: {0}'.format(self._entity_json.get('name'))
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
@property
|
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Return a unique, HASS-friendly identifier for this entity."""
|
|
|
|
return '{0}_program_{1}'.format(
|
|
|
|
self.device_mac.replace(':', ''), self.rainmachine_entity_id)
|
|
|
|
|
2017-08-08 07:49:25 +00:00
|
|
|
def turn_off(self, **kwargs) -> None:
|
|
|
|
"""Turn the program off."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._client.programs.stop(self.rainmachine_entity_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.BrokenAPICall:
|
|
|
|
_LOGGER.error('programs.stop currently broken in remote API')
|
|
|
|
except exceptions.HTTPError as exc_info:
|
2018-02-12 03:55:51 +00:00
|
|
|
_LOGGER.error('Unable to turn off program "%s"', self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|
|
|
|
|
|
|
|
def turn_on(self, **kwargs) -> None:
|
|
|
|
"""Turn the program on."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._client.programs.start(self.rainmachine_entity_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.BrokenAPICall:
|
|
|
|
_LOGGER.error('programs.start currently broken in remote API')
|
|
|
|
except exceptions.HTTPError as exc_info:
|
2018-02-12 03:55:51 +00:00
|
|
|
_LOGGER.error('Unable to turn on program "%s"', self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
@Throttle(MIN_SCAN_TIME, MIN_SCAN_TIME_FORCED)
|
|
|
|
def update(self) -> None:
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Update info for the program."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._entity_json = self._client.programs.get(
|
|
|
|
self.rainmachine_entity_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.HTTPError as exc_info:
|
|
|
|
_LOGGER.error('Unable to update info for program "%s"',
|
2018-02-12 03:55:51 +00:00
|
|
|
self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|
|
|
|
|
|
|
|
|
|
|
|
class RainMachineZone(RainMachineEntity):
|
|
|
|
"""A RainMachine zone."""
|
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
def __init__(self, client, device_name, device_mac, zone_json,
|
|
|
|
zone_run_time):
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Initialize a RainMachine zone."""
|
2018-02-12 03:55:51 +00:00
|
|
|
super().__init__(client, device_name, device_mac, zone_json)
|
2017-08-08 07:49:25 +00:00
|
|
|
self._run_time = zone_run_time
|
|
|
|
self._attrs.update({
|
2018-02-12 03:55:51 +00:00
|
|
|
ATTR_CYCLES: self._entity_json.get('noOfCycles'),
|
|
|
|
ATTR_TOTAL_DURATION: self._entity_json.get('userDuration')
|
2017-08-08 07:49:25 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_on(self) -> bool:
|
|
|
|
"""Return whether the zone is running."""
|
|
|
|
return bool(self._entity_json.get('state'))
|
|
|
|
|
|
|
|
@property
|
|
|
|
def name(self) -> str:
|
|
|
|
"""Return the name of the zone."""
|
2018-04-28 13:46:58 +00:00
|
|
|
return 'Zone: {0}'.format(self._entity_json.get('name'))
|
2017-08-08 07:49:25 +00:00
|
|
|
|
2018-02-12 03:55:51 +00:00
|
|
|
@property
|
|
|
|
def unique_id(self) -> str:
|
|
|
|
"""Return a unique, HASS-friendly identifier for this entity."""
|
|
|
|
return '{0}_zone_{1}'.format(
|
|
|
|
self.device_mac.replace(':', ''), self.rainmachine_entity_id)
|
|
|
|
|
2017-08-08 07:49:25 +00:00
|
|
|
def turn_off(self, **kwargs) -> None:
|
|
|
|
"""Turn the zone off."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._client.zones.stop(self.rainmachine_entity_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.HTTPError as exc_info:
|
2018-02-12 03:55:51 +00:00
|
|
|
_LOGGER.error('Unable to turn off zone "%s"', self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|
|
|
|
|
|
|
|
def turn_on(self, **kwargs) -> None:
|
|
|
|
"""Turn the zone on."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._client.zones.start(self.rainmachine_entity_id,
|
|
|
|
self._run_time)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.HTTPError as exc_info:
|
2018-02-12 03:55:51 +00:00
|
|
|
_LOGGER.error('Unable to turn on zone "%s"', self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|
|
|
|
|
2018-04-28 13:46:58 +00:00
|
|
|
@Throttle(MIN_SCAN_TIME, MIN_SCAN_TIME_FORCED)
|
|
|
|
def update(self) -> None:
|
2017-08-08 07:49:25 +00:00
|
|
|
"""Update info for the zone."""
|
|
|
|
import regenmaschine.exceptions as exceptions
|
|
|
|
|
|
|
|
try:
|
2018-02-12 03:55:51 +00:00
|
|
|
self._entity_json = self._client.zones.get(
|
|
|
|
self.rainmachine_entity_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
except exceptions.HTTPError as exc_info:
|
|
|
|
_LOGGER.error('Unable to update info for zone "%s"',
|
2018-02-12 03:55:51 +00:00
|
|
|
self.unique_id)
|
2017-08-08 07:49:25 +00:00
|
|
|
_LOGGER.debug(exc_info)
|