Make RainMachine async (#14879)

* Make RainMachine async

* Updated requirements

* Dispatcher adjustments

* Small verbiage change

* Member-requested changes

* Style consistency

* Updated requirements
This commit is contained in:
Aaron Bach 2018-06-10 02:23:07 -06:00 committed by Martin Hjelmare
parent f3e55ce330
commit 8aca2e84dc
5 changed files with 156 additions and 134 deletions

View file

@ -8,12 +8,12 @@ import logging
from homeassistant.components.rainmachine import (
CONF_ZONE_RUN_TIME, DATA_RAINMACHINE, DEFAULT_ZONE_RUN,
PROGRAM_UPDATE_TOPIC, RainMachineEntity)
PROGRAM_UPDATE_TOPIC, ZONE_UPDATE_TOPIC, RainMachineEntity)
from homeassistant.const import ATTR_ID
from homeassistant.components.switch import SwitchDevice
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect, dispatcher_send)
async_dispatcher_connect, async_dispatcher_send)
DEPENDENCIES = ['rainmachine']
@ -39,20 +39,11 @@ ATTR_VEGETATION_TYPE = 'vegetation_type'
ATTR_ZONES = 'zones'
DAYS = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
'Sunday'
]
PROGRAM_STATUS_MAP = {
0: 'Not Running',
1: 'Running',
2: 'Queued'
}
PROGRAM_STATUS_MAP = {0: 'Not Running', 1: 'Running', 2: 'Queued'}
SOIL_TYPE_MAP = {
0: 'Not Set',
@ -108,7 +99,8 @@ VEGETATION_MAP = {
}
def setup_platform(hass, config, add_devices, discovery_info=None):
async def async_setup_platform(
hass, config, async_add_devices, discovery_info=None):
"""Set up the RainMachine Switch platform."""
if discovery_info is None:
return
@ -120,21 +112,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
rainmachine = hass.data[DATA_RAINMACHINE]
entities = []
for program in rainmachine.client.programs.all().get('programs', {}):
programs = await rainmachine.client.programs.all()
for program in programs:
if not program.get('active'):
continue
_LOGGER.debug('Adding program: %s', program)
entities.append(RainMachineProgram(rainmachine, program))
for zone in rainmachine.client.zones.all().get('zones', {}):
zones = await rainmachine.client.zones.all()
for zone in zones:
if not zone.get('active'):
continue
_LOGGER.debug('Adding zone: %s', zone)
entities.append(RainMachineZone(rainmachine, zone, zone_run_time))
add_devices(entities, True)
async_add_devices(entities, True)
class RainMachineSwitch(RainMachineEntity, SwitchDevice):
@ -163,10 +158,14 @@ class RainMachineSwitch(RainMachineEntity, SwitchDevice):
def unique_id(self) -> str:
"""Return a unique, HASS-friendly identifier for this entity."""
return '{0}_{1}_{2}'.format(
self.rainmachine.device_mac.replace(':', ''),
self._switch_type,
self.rainmachine.device_mac.replace(':', ''), self._switch_type,
self._rainmachine_entity_id)
@callback
def _program_updated(self):
"""Update state, trigger updates."""
self.async_schedule_update_ha_state(True)
class RainMachineProgram(RainMachineSwitch):
"""A RainMachine program."""
@ -185,34 +184,42 @@ class RainMachineProgram(RainMachineSwitch):
"""Return a list of active zones associated with this program."""
return [z for z in self._obj['wateringTimes'] if z['active']]
def turn_off(self, **kwargs) -> None:
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass, PROGRAM_UPDATE_TOPIC, self._program_updated)
async def async_turn_off(self, **kwargs) -> None:
"""Turn the program off."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self.rainmachine.client.programs.stop(self._rainmachine_entity_id)
dispatcher_send(self.hass, PROGRAM_UPDATE_TOPIC)
except RainMachineError as exc_info:
_LOGGER.error('Unable to turn off program "%s"', self.unique_id)
_LOGGER.debug(exc_info)
await self.rainmachine.client.programs.stop(
self._rainmachine_entity_id)
async_dispatcher_send(self.hass, PROGRAM_UPDATE_TOPIC)
except RequestError as err:
_LOGGER.error(
'Unable to turn off program "%s": %s', self.unique_id,
str(err))
def turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs) -> None:
"""Turn the program on."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self.rainmachine.client.programs.start(self._rainmachine_entity_id)
dispatcher_send(self.hass, PROGRAM_UPDATE_TOPIC)
except RainMachineError as exc_info:
_LOGGER.error('Unable to turn on program "%s"', self.unique_id)
_LOGGER.debug(exc_info)
await self.rainmachine.client.programs.start(
self._rainmachine_entity_id)
async_dispatcher_send(self.hass, PROGRAM_UPDATE_TOPIC)
except RequestError as err:
_LOGGER.error(
'Unable to turn on program "%s": %s', self.unique_id, str(err))
def update(self) -> None:
async def async_update(self) -> None:
"""Update info for the program."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self._obj = self.rainmachine.client.programs.get(
self._obj = await self.rainmachine.client.programs.get(
self._rainmachine_entity_id)
self._attrs.update({
@ -221,10 +228,10 @@ class RainMachineProgram(RainMachineSwitch):
ATTR_STATUS: PROGRAM_STATUS_MAP[self._obj.get('status')],
ATTR_ZONES: ', '.join(z['name'] for z in self.zones)
})
except RainMachineError as exc_info:
_LOGGER.error('Unable to update info for program "%s"',
self.unique_id)
_LOGGER.debug(exc_info)
except RequestError as err:
_LOGGER.error(
'Unable to update info for program "%s": %s', self.unique_id,
str(err))
class RainMachineZone(RainMachineSwitch):
@ -242,62 +249,65 @@ class RainMachineZone(RainMachineSwitch):
"""Return whether the zone is running."""
return bool(self._obj.get('state'))
@callback
def _program_updated(self):
"""Update state, trigger updates."""
self.async_schedule_update_ha_state(True)
async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(self.hass, PROGRAM_UPDATE_TOPIC,
self._program_updated)
async_dispatcher_connect(
self.hass, PROGRAM_UPDATE_TOPIC, self._program_updated)
async_dispatcher_connect(
self.hass, ZONE_UPDATE_TOPIC, self._program_updated)
def turn_off(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs) -> None:
"""Turn the zone off."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self.rainmachine.client.zones.stop(self._rainmachine_entity_id)
except RainMachineError as exc_info:
_LOGGER.error('Unable to turn off zone "%s"', self.unique_id)
_LOGGER.debug(exc_info)
await self.rainmachine.client.zones.stop(
self._rainmachine_entity_id)
except RequestError as err:
_LOGGER.error(
'Unable to turn off zone "%s": %s', self.unique_id, str(err))
def turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs) -> None:
"""Turn the zone on."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self.rainmachine.client.zones.start(self._rainmachine_entity_id,
self._run_time)
except RainMachineError as exc_info:
_LOGGER.error('Unable to turn on zone "%s"', self.unique_id)
_LOGGER.debug(exc_info)
await self.rainmachine.client.zones.start(
self._rainmachine_entity_id, self._run_time)
except RequestError as err:
_LOGGER.error(
'Unable to turn on zone "%s": %s', self.unique_id, str(err))
def update(self) -> None:
async def async_update(self) -> None:
"""Update info for the zone."""
from regenmaschine.exceptions import RainMachineError
from regenmaschine.errors import RequestError
try:
self._obj = self.rainmachine.client.zones.get(
self._obj = await self.rainmachine.client.zones.get(
self._rainmachine_entity_id)
self._properties_json = self.rainmachine.client.zones.get(
self._rainmachine_entity_id, properties=True)
self._properties_json = await self.rainmachine.client.zones.get(
self._rainmachine_entity_id, details=True)
self._attrs.update({
ATTR_ID: self._obj['uid'],
ATTR_AREA: self._properties_json.get('waterSense').get('area'),
ATTR_CURRENT_CYCLE: self._obj.get('cycle'),
ATTR_ID:
self._obj['uid'],
ATTR_AREA:
self._properties_json.get('waterSense').get('area'),
ATTR_CURRENT_CYCLE:
self._obj.get('cycle'),
ATTR_FIELD_CAPACITY:
self._properties_json.get(
'waterSense').get('fieldCapacity'),
ATTR_NO_CYCLES: self._obj.get('noOfCycles'),
self._properties_json.get('waterSense')
.get('fieldCapacity'),
ATTR_NO_CYCLES:
self._obj.get('noOfCycles'),
ATTR_PRECIP_RATE:
self._properties_json.get(
'waterSense').get('precipitationRate'),
ATTR_RESTRICTIONS: self._obj.get('restriction'),
ATTR_SLOPE: SLOPE_TYPE_MAP.get(
self._properties_json.get('slope')),
self._properties_json.get('waterSense')
.get('precipitationRate'),
ATTR_RESTRICTIONS:
self._obj.get('restriction'),
ATTR_SLOPE:
SLOPE_TYPE_MAP.get(self._properties_json.get('slope')),
ATTR_SOIL_TYPE:
SOIL_TYPE_MAP.get(self._properties_json.get('sun')),
ATTR_SPRINKLER_TYPE:
@ -308,7 +318,7 @@ class RainMachineZone(RainMachineSwitch):
ATTR_VEGETATION_TYPE:
VEGETATION_MAP.get(self._obj.get('type')),
})
except RainMachineError as exc_info:
_LOGGER.error('Unable to update info for zone "%s"',
self.unique_id)
_LOGGER.debug(exc_info)
except RequestError as err:
_LOGGER.error(
'Unable to update info for zone "%s": %s', self.unique_id,
str(err))