Convert switch to AsnycIO (#4382)

* Convert switch to AsnycIO

* Move update entity to service

* use time better for faster handling

* Change to suggestion from paulus

* Use new shedule_update_ha_state

* fix lint

* minimize executor calls
This commit is contained in:
Pascal Vizeli 2016-11-16 06:06:50 +01:00 committed by Paulus Schoutsen
parent 5d8a465c18
commit 41aaeb715a
18 changed files with 80 additions and 56 deletions

View File

@ -4,6 +4,7 @@ Component to interface with various switches that can be controlled remotely.
For more details about this component, please refer to the documentation
at https://home-assistant.io/components/switch/
"""
import asyncio
from datetime import timedelta
import logging
import os
@ -69,38 +70,50 @@ def toggle(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_TOGGLE, data)
def setup(hass, config):
@asyncio.coroutine
def async_setup(hass, config):
"""Track states and offer events for switches."""
component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES)
component.setup(config)
yield from component.async_setup(config)
def handle_switch_service(service):
@asyncio.coroutine
def async_handle_switch_service(service):
"""Handle calls to the switch services."""
target_switches = component.extract_from_service(service)
target_switches = component.async_extract_from_service(service)
update_tasks = []
for switch in target_switches:
if service.service == SERVICE_TURN_ON:
switch.turn_on()
yield from switch.async_turn_on()
elif service.service == SERVICE_TOGGLE:
switch.toggle()
yield from switch.async_toggle()
else:
switch.turn_off()
yield from switch.async_turn_off()
if switch.should_poll:
switch.update_ha_state(True)
update_coro = switch.async_update_ha_state(True)
if hasattr(switch, 'async_update'):
update_tasks.append(hass.loop.create_task(update_coro))
else:
yield from update_coro
descriptions = load_yaml_config_file(
os.path.join(os.path.dirname(__file__), 'services.yaml'))
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service,
descriptions.get(SERVICE_TURN_OFF),
schema=SWITCH_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service,
descriptions.get(SERVICE_TURN_ON),
schema=SWITCH_SERVICE_SCHEMA)
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service,
descriptions.get(SERVICE_TOGGLE),
schema=SWITCH_SERVICE_SCHEMA)
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
descriptions = yield from hass.loop.run_in_executor(
None, load_yaml_config_file, os.path.join(
os.path.dirname(__file__), 'services.yaml'))
hass.services.async_register(
DOMAIN, SERVICE_TURN_OFF, async_handle_switch_service,
descriptions.get(SERVICE_TURN_OFF), schema=SWITCH_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_switch_service,
descriptions.get(SERVICE_TURN_ON), schema=SWITCH_SERVICE_SCHEMA)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_switch_service,
descriptions.get(SERVICE_TOGGLE), schema=SWITCH_SERVICE_SCHEMA)
return True

View File

@ -149,11 +149,11 @@ class CommandSwitch(SwitchDevice):
if (CommandSwitch._switch(self._command_on) and
not self._command_state):
self._state = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the device off."""
if (CommandSwitch._switch(self._command_off) and
not self._command_state):
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -66,9 +66,9 @@ class DemoSwitch(SwitchDevice):
def turn_on(self, **kwargs):
"""Turn the switch on."""
self._state = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the device off."""
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -79,4 +79,4 @@ class EnOceanSwitch(enocean.EnOceanDevice, ToggleEntity):
def value_changed(self, val):
"""Update the internal state of the switch."""
self._on_state = val
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -137,7 +137,7 @@ class FluxSwitch(SwitchDevice):
self._state = True
self.unsub_tracker = track_utc_time_change(self.hass, self.flux_update,
second=[0, 30])
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn off flux."""
@ -146,7 +146,7 @@ class FluxSwitch(SwitchDevice):
self.unsub_tracker = None
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()
def flux_update(self, now=None):
"""Update all the lights using flux."""

View File

@ -40,7 +40,7 @@ class KNXSwitch(KNXGroupAddress, SwitchDevice):
self.group_write(1)
self._state = [1]
if not self.should_poll:
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the switch off.
@ -50,4 +50,4 @@ class KNXSwitch(KNXGroupAddress, SwitchDevice):
self.group_write(0)
self._state = [0]
if not self.should_poll:
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -117,7 +117,7 @@ class MqttSwitch(SwitchDevice):
if self._optimistic:
# Optimistically assume that switch has changed state.
self._state = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the device off."""
@ -126,4 +126,4 @@ class MqttSwitch(SwitchDevice):
if self._optimistic:
# Optimistically assume that switch has changed state.
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -137,7 +137,7 @@ class MySensorsSwitch(mysensors.MySensorsDeviceEntity, SwitchDevice):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[self.value_type] = STATE_ON
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self):
"""Turn the switch off."""
@ -146,7 +146,7 @@ class MySensorsSwitch(mysensors.MySensorsDeviceEntity, SwitchDevice):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[self.value_type] = STATE_OFF
self.update_ha_state()
self.shedule_update_ha_state()
class MySensorsIRSwitch(MySensorsSwitch):
@ -182,7 +182,7 @@ class MySensorsIRSwitch(MySensorsSwitch):
# optimistically assume that switch has changed state
self._values[self.value_type] = self._ir_code
self._values[set_req.V_LIGHT] = STATE_ON
self.update_ha_state()
self.shedule_update_ha_state()
# turn off switch after switch was turned on
self.turn_off()
@ -198,7 +198,7 @@ class MySensorsIRSwitch(MySensorsSwitch):
if self.gateway.optimistic:
# optimistically assume that switch has changed state
self._values[set_req.V_LIGHT] = STATE_OFF
self.update_ha_state()
self.shedule_update_ha_state()
def update(self):
"""Update the controller with the latest value from a sensor."""

View File

@ -156,7 +156,7 @@ class NetioSwitch(SwitchDevice):
val[self.outlet - 1] = '1' if value else '0'
self.netio.get('port list %s' % ''.join(val))
self.netio.states[self.outlet - 1] = value
self.update_ha_state()
self.shedule_update_ha_state()
@property
def is_on(self):

View File

@ -127,11 +127,11 @@ class PilightSwitch(SwitchDevice):
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
self._code_on, blocking=True)
self._state = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self):
"""Turn the switch on by calling pilight.send service with off code."""
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
self._code_off, blocking=True)
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -170,7 +170,7 @@ class PALoopbackSwitch(SwitchDevice):
self._pa_svr.update_module_state(no_throttle=True)
self._module_idx = self._pa_svr.get_module_idx(
self._sink_name, self._source_name)
self.update_ha_state()
self.shedule_update_ha_state()
else:
_LOGGER.warning(IGNORED_SWITCH_WARN)
@ -181,7 +181,7 @@ class PALoopbackSwitch(SwitchDevice):
self._pa_svr.update_module_state(no_throttle=True)
self._module_idx = self._pa_svr.get_module_idx(
self._sink_name, self._source_name)
self.update_ha_state()
self.shedule_update_ha_state()
else:
_LOGGER.warning(IGNORED_SWITCH_WARN)

View File

@ -77,10 +77,10 @@ class RPiGPIOSwitch(ToggleEntity):
"""Turn the device on."""
rpi_gpio.write_output(self._port, 0 if self._invert_logic else 1)
self._state = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self):
"""Turn the device off."""
rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0)
self._state = False
self.update_ha_state()
self.shedule_update_ha_state()

View File

@ -119,7 +119,7 @@ class SCSGateSwitch(SwitchDevice):
ToggleStatusTask(target=self._scs_id, toggled=True))
self._toggled = True
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn the device off."""
@ -129,7 +129,7 @@ class SCSGateSwitch(SwitchDevice):
ToggleStatusTask(target=self._scs_id, toggled=False))
self._toggled = False
self.update_ha_state()
self.shedule_update_ha_state()
def process_event(self, message):
"""Handle a SCSGate message related with this switch."""

View File

@ -92,7 +92,7 @@ class SwitchTemplate(SwitchDevice):
@callback
def template_switch_state_listener(entity, old_state, new_state):
"""Called when the target device changes state."""
hass.async_add_job(self.async_update_ha_state, True)
hass.async_add_job(self.async_update_ha_state(True))
async_track_state_change(
hass, entity_ids, template_switch_state_listener)

View File

@ -36,13 +36,13 @@ class VeraSwitch(VeraDevice, SwitchDevice):
"""Turn device on."""
self.vera_device.switch_on()
self._state = STATE_ON
self.update_ha_state()
self.shedule_update_ha_state()
def turn_off(self, **kwargs):
"""Turn device off."""
self.vera_device.switch_off()
self._state = STATE_OFF
self.update_ha_state()
self.shedule_update_ha_state()
@property
def current_power_mwh(self):

View File

@ -76,13 +76,11 @@ class WOLSwitch(SwitchDevice):
def turn_on(self):
"""Turn the device on."""
self._wol.send_magic_packet(self._mac_address)
self.update_ha_state()
def turn_off(self):
"""Turn the device off if an off action is present."""
if self._off_script is not None:
self._off_script.run()
self.update_ha_state()
def update(self):
"""Check if device is on and update the state."""

View File

@ -150,14 +150,14 @@ class WemoSwitch(SwitchDevice):
def turn_on(self, **kwargs):
"""Turn the switch on."""
self._state = WEMO_ON
self.update_ha_state()
self.wemo.on()
self.shedule_update_ha_state()
def turn_off(self):
"""Turn the switch off."""
self._state = WEMO_OFF
self.update_ha_state()
self.wemo.off()
self.shedule_update_ha_state()
def update(self):
"""Update WeMo state."""

View File

@ -1,6 +1,7 @@
"""An abstract class for entities."""
import asyncio
import logging
import functools as ft
from timeit import default_timer as timer
from typing import Any, Optional, List, Dict
@ -269,6 +270,18 @@ class Entity(object):
self.hass.states.async_set(
self.entity_id, state, attr, self.force_update)
def shedule_update_ha_state(self, force_refresh=False):
"""Shedule a update ha state change task.
That is only needed on executor to not block.
"""
# We're already in a thread, do the force refresh here.
if force_refresh and not hasattr(self, 'async_update'):
self.update()
force_refresh = False
self.hass.add_job(self.async_update_ha_state(force_refresh))
def remove(self) -> None:
"""Remove entitiy from HASS."""
run_coroutine_threadsafe(
@ -324,23 +337,23 @@ class ToggleEntity(Entity):
def turn_on(self, **kwargs) -> None:
"""Turn the entity on."""
run_coroutine_threadsafe(self.async_turn_on(**kwargs),
self.hass.loop).result()
raise NotImplementedError()
@asyncio.coroutine
def async_turn_on(self, **kwargs):
"""Turn the entity on."""
raise NotImplementedError()
yield from self.hass.loop.run_in_executor(
None, ft.partial(self.turn_on, **kwargs))
def turn_off(self, **kwargs) -> None:
"""Turn the entity off."""
run_coroutine_threadsafe(self.async_turn_off(**kwargs),
self.hass.loop).result()
raise NotImplementedError()
@asyncio.coroutine
def async_turn_off(self, **kwargs):
"""Turn the entity off."""
raise NotImplementedError()
yield from self.hass.loop.run_in_executor(
None, ft.partial(self.turn_off, **kwargs))
def toggle(self) -> None:
"""Toggle the entity."""