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 For more details about this component, please refer to the documentation
at https://home-assistant.io/components/switch/ at https://home-assistant.io/components/switch/
""" """
import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
import os import os
@ -69,38 +70,50 @@ def toggle(hass, entity_id=None):
hass.services.call(DOMAIN, SERVICE_TOGGLE, data) 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.""" """Track states and offer events for switches."""
component = EntityComponent( component = EntityComponent(
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_SWITCHES) _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.""" """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: for switch in target_switches:
if service.service == SERVICE_TURN_ON: if service.service == SERVICE_TURN_ON:
switch.turn_on() yield from switch.async_turn_on()
elif service.service == SERVICE_TOGGLE: elif service.service == SERVICE_TOGGLE:
switch.toggle() yield from switch.async_toggle()
else: else:
switch.turn_off() yield from switch.async_turn_off()
if switch.should_poll: 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( if update_tasks:
os.path.join(os.path.dirname(__file__), 'services.yaml')) yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_switch_service,
descriptions.get(SERVICE_TURN_OFF), descriptions = yield from hass.loop.run_in_executor(
schema=SWITCH_SERVICE_SCHEMA) None, load_yaml_config_file, os.path.join(
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_switch_service, os.path.dirname(__file__), 'services.yaml'))
descriptions.get(SERVICE_TURN_ON),
schema=SWITCH_SERVICE_SCHEMA) hass.services.async_register(
hass.services.register(DOMAIN, SERVICE_TOGGLE, handle_switch_service, DOMAIN, SERVICE_TURN_OFF, async_handle_switch_service,
descriptions.get(SERVICE_TOGGLE), descriptions.get(SERVICE_TURN_OFF), schema=SWITCH_SERVICE_SCHEMA)
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 return True

View File

@ -149,11 +149,11 @@ class CommandSwitch(SwitchDevice):
if (CommandSwitch._switch(self._command_on) and if (CommandSwitch._switch(self._command_on) and
not self._command_state): not self._command_state):
self._state = True self._state = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
if (CommandSwitch._switch(self._command_off) and if (CommandSwitch._switch(self._command_off) and
not self._command_state): not self._command_state):
self._state = False 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): def turn_on(self, **kwargs):
"""Turn the switch on.""" """Turn the switch on."""
self._state = True self._state = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
self._state = False 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): def value_changed(self, val):
"""Update the internal state of the switch.""" """Update the internal state of the switch."""
self._on_state = val 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._state = True
self.unsub_tracker = track_utc_time_change(self.hass, self.flux_update, self.unsub_tracker = track_utc_time_change(self.hass, self.flux_update,
second=[0, 30]) second=[0, 30])
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn off flux.""" """Turn off flux."""
@ -146,7 +146,7 @@ class FluxSwitch(SwitchDevice):
self.unsub_tracker = None self.unsub_tracker = None
self._state = False self._state = False
self.update_ha_state() self.shedule_update_ha_state()
def flux_update(self, now=None): def flux_update(self, now=None):
"""Update all the lights using flux.""" """Update all the lights using flux."""

View File

@ -40,7 +40,7 @@ class KNXSwitch(KNXGroupAddress, SwitchDevice):
self.group_write(1) self.group_write(1)
self._state = [1] self._state = [1]
if not self.should_poll: if not self.should_poll:
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the switch off. """Turn the switch off.
@ -50,4 +50,4 @@ class KNXSwitch(KNXGroupAddress, SwitchDevice):
self.group_write(0) self.group_write(0)
self._state = [0] self._state = [0]
if not self.should_poll: 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: if self._optimistic:
# Optimistically assume that switch has changed state. # Optimistically assume that switch has changed state.
self._state = True self._state = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
@ -126,4 +126,4 @@ class MqttSwitch(SwitchDevice):
if self._optimistic: if self._optimistic:
# Optimistically assume that switch has changed state. # Optimistically assume that switch has changed state.
self._state = False 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: if self.gateway.optimistic:
# optimistically assume that switch has changed state # optimistically assume that switch has changed state
self._values[self.value_type] = STATE_ON self._values[self.value_type] = STATE_ON
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self): def turn_off(self):
"""Turn the switch off.""" """Turn the switch off."""
@ -146,7 +146,7 @@ class MySensorsSwitch(mysensors.MySensorsDeviceEntity, SwitchDevice):
if self.gateway.optimistic: if self.gateway.optimistic:
# optimistically assume that switch has changed state # optimistically assume that switch has changed state
self._values[self.value_type] = STATE_OFF self._values[self.value_type] = STATE_OFF
self.update_ha_state() self.shedule_update_ha_state()
class MySensorsIRSwitch(MySensorsSwitch): class MySensorsIRSwitch(MySensorsSwitch):
@ -182,7 +182,7 @@ class MySensorsIRSwitch(MySensorsSwitch):
# optimistically assume that switch has changed state # optimistically assume that switch has changed state
self._values[self.value_type] = self._ir_code self._values[self.value_type] = self._ir_code
self._values[set_req.V_LIGHT] = STATE_ON 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 # turn off switch after switch was turned on
self.turn_off() self.turn_off()
@ -198,7 +198,7 @@ class MySensorsIRSwitch(MySensorsSwitch):
if self.gateway.optimistic: if self.gateway.optimistic:
# optimistically assume that switch has changed state # optimistically assume that switch has changed state
self._values[set_req.V_LIGHT] = STATE_OFF self._values[set_req.V_LIGHT] = STATE_OFF
self.update_ha_state() self.shedule_update_ha_state()
def update(self): def update(self):
"""Update the controller with the latest value from a sensor.""" """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' val[self.outlet - 1] = '1' if value else '0'
self.netio.get('port list %s' % ''.join(val)) self.netio.get('port list %s' % ''.join(val))
self.netio.states[self.outlet - 1] = value self.netio.states[self.outlet - 1] = value
self.update_ha_state() self.shedule_update_ha_state()
@property @property
def is_on(self): def is_on(self):

View File

@ -127,11 +127,11 @@ class PilightSwitch(SwitchDevice):
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
self._code_on, blocking=True) self._code_on, blocking=True)
self._state = True self._state = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self): def turn_off(self):
"""Turn the switch on by calling pilight.send service with off code.""" """Turn the switch on by calling pilight.send service with off code."""
self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME, self._hass.services.call(pilight.DOMAIN, pilight.SERVICE_NAME,
self._code_off, blocking=True) self._code_off, blocking=True)
self._state = False 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._pa_svr.update_module_state(no_throttle=True)
self._module_idx = self._pa_svr.get_module_idx( self._module_idx = self._pa_svr.get_module_idx(
self._sink_name, self._source_name) self._sink_name, self._source_name)
self.update_ha_state() self.shedule_update_ha_state()
else: else:
_LOGGER.warning(IGNORED_SWITCH_WARN) _LOGGER.warning(IGNORED_SWITCH_WARN)
@ -181,7 +181,7 @@ class PALoopbackSwitch(SwitchDevice):
self._pa_svr.update_module_state(no_throttle=True) self._pa_svr.update_module_state(no_throttle=True)
self._module_idx = self._pa_svr.get_module_idx( self._module_idx = self._pa_svr.get_module_idx(
self._sink_name, self._source_name) self._sink_name, self._source_name)
self.update_ha_state() self.shedule_update_ha_state()
else: else:
_LOGGER.warning(IGNORED_SWITCH_WARN) _LOGGER.warning(IGNORED_SWITCH_WARN)

View File

@ -77,10 +77,10 @@ class RPiGPIOSwitch(ToggleEntity):
"""Turn the device on.""" """Turn the device on."""
rpi_gpio.write_output(self._port, 0 if self._invert_logic else 1) rpi_gpio.write_output(self._port, 0 if self._invert_logic else 1)
self._state = True self._state = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self): def turn_off(self):
"""Turn the device off.""" """Turn the device off."""
rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0) rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0)
self._state = False 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)) ToggleStatusTask(target=self._scs_id, toggled=True))
self._toggled = True self._toggled = True
self.update_ha_state() self.shedule_update_ha_state()
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the device off.""" """Turn the device off."""
@ -129,7 +129,7 @@ class SCSGateSwitch(SwitchDevice):
ToggleStatusTask(target=self._scs_id, toggled=False)) ToggleStatusTask(target=self._scs_id, toggled=False))
self._toggled = False self._toggled = False
self.update_ha_state() self.shedule_update_ha_state()
def process_event(self, message): def process_event(self, message):
"""Handle a SCSGate message related with this switch.""" """Handle a SCSGate message related with this switch."""

View File

@ -92,7 +92,7 @@ class SwitchTemplate(SwitchDevice):
@callback @callback
def template_switch_state_listener(entity, old_state, new_state): def template_switch_state_listener(entity, old_state, new_state):
"""Called when the target device changes 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( async_track_state_change(
hass, entity_ids, template_switch_state_listener) hass, entity_ids, template_switch_state_listener)

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
"""An abstract class for entities.""" """An abstract class for entities."""
import asyncio import asyncio
import logging import logging
import functools as ft
from timeit import default_timer as timer from timeit import default_timer as timer
from typing import Any, Optional, List, Dict from typing import Any, Optional, List, Dict
@ -269,6 +270,18 @@ class Entity(object):
self.hass.states.async_set( self.hass.states.async_set(
self.entity_id, state, attr, self.force_update) 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: def remove(self) -> None:
"""Remove entitiy from HASS.""" """Remove entitiy from HASS."""
run_coroutine_threadsafe( run_coroutine_threadsafe(
@ -324,23 +337,23 @@ class ToggleEntity(Entity):
def turn_on(self, **kwargs) -> None: def turn_on(self, **kwargs) -> None:
"""Turn the entity on.""" """Turn the entity on."""
run_coroutine_threadsafe(self.async_turn_on(**kwargs), raise NotImplementedError()
self.hass.loop).result()
@asyncio.coroutine @asyncio.coroutine
def async_turn_on(self, **kwargs): def async_turn_on(self, **kwargs):
"""Turn the entity on.""" """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: def turn_off(self, **kwargs) -> None:
"""Turn the entity off.""" """Turn the entity off."""
run_coroutine_threadsafe(self.async_turn_off(**kwargs), raise NotImplementedError()
self.hass.loop).result()
@asyncio.coroutine @asyncio.coroutine
def async_turn_off(self, **kwargs): def async_turn_off(self, **kwargs):
"""Turn the entity off.""" """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: def toggle(self) -> None:
"""Toggle the entity.""" """Toggle the entity."""