Tellstick Duo acync callback fix (#10384)

* Reverted commit 1c8f179690. This fixes issue: #10329

* convert callback to async

* fix lint

* cleanup

* cleanup

* cleanups

* optimize initial handling

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint

* Update tellstick.py

* Fixed code errors and lint problems.

* fix bug

* Reduce logic, migrate to dispatcher

* Update tellstick.py

* Update tellstick.py

* fix lint

* fix lint
This commit is contained in:
Stefan Jonasson 2017-11-09 15:03:35 +01:00 committed by Pascal Vizeli
parent ee265394a6
commit 62c1b542ed
3 changed files with 68 additions and 100 deletions

View file

@ -4,15 +4,13 @@ Support for Tellstick lights.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.tellstick/ https://home-assistant.io/components/light.tellstick/
""" """
import voluptuous as vol
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light)
from homeassistant.components.tellstick import ( from homeassistant.components.tellstick import (
DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES, ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES, ATTR_DISCOVER_CONFIG,
DOMAIN, TellstickDevice) DATA_TELLSTICK, TellstickDevice)
PLATFORM_SCHEMA = vol.Schema({vol.Required("platform"): DOMAIN})
SUPPORT_TELLSTICK = SUPPORT_BRIGHTNESS SUPPORT_TELLSTICK = SUPPORT_BRIGHTNESS
@ -27,17 +25,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
signal_repetitions = discovery_info.get( signal_repetitions = discovery_info.get(
ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS) ATTR_DISCOVER_CONFIG, DEFAULT_SIGNAL_REPETITIONS)
add_devices(TellstickLight(tellcore_id, hass.data['tellcore_registry'], add_devices([TellstickLight(hass.data[DATA_TELLSTICK][tellcore_id],
signal_repetitions) signal_repetitions)
for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]) for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]],
True)
class TellstickLight(TellstickDevice, Light): class TellstickLight(TellstickDevice, Light):
"""Representation of a Tellstick light.""" """Representation of a Tellstick light."""
def __init__(self, tellcore_id, tellcore_registry, signal_repetitions): def __init__(self, tellcore_device, signal_repetitions):
"""Initialize the Tellstick light.""" """Initialize the Tellstick light."""
super().__init__(tellcore_id, tellcore_registry, signal_repetitions) super().__init__(tellcore_device, signal_repetitions)
self._brightness = 255 self._brightness = 255
@ -57,9 +56,8 @@ class TellstickLight(TellstickDevice, Light):
def _parse_tellcore_data(self, tellcore_data): def _parse_tellcore_data(self, tellcore_data):
"""Turn the value received from tellcore into something useful.""" """Turn the value received from tellcore into something useful."""
if tellcore_data is not None: if tellcore_data:
brightness = int(tellcore_data) return int(tellcore_data) # brightness
return brightness
return None return None
def _update_model(self, new_state, data): def _update_model(self, new_state, data):

View file

@ -4,16 +4,11 @@ Support for Tellstick switches.
For more details about this platform, please refer to the documentation at For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.tellstick/ https://home-assistant.io/components/switch.tellstick/
""" """
import voluptuous as vol from homeassistant.components.tellstick import (
DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_DEVICES,
from homeassistant.components.tellstick import (DEFAULT_SIGNAL_REPETITIONS, ATTR_DISCOVER_CONFIG, DATA_TELLSTICK, TellstickDevice)
ATTR_DISCOVER_DEVICES,
ATTR_DISCOVER_CONFIG,
DOMAIN, TellstickDevice)
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import ToggleEntity
PLATFORM_SCHEMA = vol.Schema({vol.Required("platform"): DOMAIN})
# pylint: disable=unused-argument # pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
@ -26,9 +21,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG, signal_repetitions = discovery_info.get(ATTR_DISCOVER_CONFIG,
DEFAULT_SIGNAL_REPETITIONS) DEFAULT_SIGNAL_REPETITIONS)
add_devices(TellstickSwitch(tellcore_id, hass.data['tellcore_registry'], add_devices([TellstickSwitch(hass.data[DATA_TELLSTICK][tellcore_id],
signal_repetitions) signal_repetitions)
for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]) for tellcore_id in discovery_info[ATTR_DISCOVER_DEVICES]],
True)
class TellstickSwitch(TellstickDevice, ToggleEntity): class TellstickSwitch(TellstickDevice, ToggleEntity):
@ -36,11 +32,11 @@ class TellstickSwitch(TellstickDevice, ToggleEntity):
def _parse_ha_data(self, kwargs): def _parse_ha_data(self, kwargs):
"""Turn the value from HA into something useful.""" """Turn the value from HA into something useful."""
return None pass
def _parse_tellcore_data(self, tellcore_data): def _parse_tellcore_data(self, tellcore_data):
"""Turn the value received from tellcore into something useful.""" """Turn the value received from tellcore into something useful."""
return None pass
def _update_model(self, new_state, data): def _update_model(self, new_state, data):
"""Update the device entity state to match the arguments.""" """Update the device entity state to match the arguments."""

View file

@ -4,12 +4,14 @@ Tellstick Component.
For more details about this component, please refer to the documentation at For more details about this component, please refer to the documentation at
https://home-assistant.io/components/tellstick/ https://home-assistant.io/components/tellstick/
""" """
import asyncio
import logging import logging
import threading import threading
import voluptuous as vol import voluptuous as vol
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.core import callback
from homeassistant.const import ( from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT) EVENT_HOMEASSISTANT_STOP, CONF_HOST, CONF_PORT)
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -26,6 +28,9 @@ CONF_SIGNAL_REPETITIONS = 'signal_repetitions'
DEFAULT_SIGNAL_REPETITIONS = 1 DEFAULT_SIGNAL_REPETITIONS = 1
DOMAIN = 'tellstick' DOMAIN = 'tellstick'
DATA_TELLSTICK = 'tellstick_device'
SIGNAL_TELLCORE_CALLBACK = 'tellstick_callback'
# Use a global tellstick domain lock to avoid getting Tellcore errors when # Use a global tellstick domain lock to avoid getting Tellcore errors when
# calling concurrently. # calling concurrently.
TELLSTICK_LOCK = threading.RLock() TELLSTICK_LOCK = threading.RLock()
@ -62,7 +67,7 @@ def _discover(hass, config, component_name, found_tellcore_devices):
def setup(hass, config): def setup(hass, config):
"""Set up the Tellstick component.""" """Set up the Tellstick component."""
from tellcore.constants import TELLSTICK_DIM from tellcore.constants import TELLSTICK_DIM
from tellcore.telldus import QueuedCallbackDispatcher from tellcore.telldus import AsyncioCallbackDispatcher
from tellcore.telldus import TelldusCore from tellcore.telldus import TelldusCore
from tellcorenet import TellCoreClient from tellcorenet import TellCoreClient
@ -83,94 +88,57 @@ def setup(hass, config):
try: try:
tellcore_lib = TelldusCore( tellcore_lib = TelldusCore(
callback_dispatcher=QueuedCallbackDispatcher()) callback_dispatcher=AsyncioCallbackDispatcher(hass.loop))
except OSError: except OSError:
_LOGGER.exception("Could not initialize Tellstick") _LOGGER.exception("Could not initialize Tellstick")
return False return False
# Get all devices, switches and lights alike # Get all devices, switches and lights alike
all_tellcore_devices = tellcore_lib.devices() tellcore_devices = tellcore_lib.devices()
# Register devices # Register devices
tellcore_registry = TellstickRegistry(hass, tellcore_lib) hass.data[DATA_TELLSTICK] = {device.id: device for
tellcore_registry.register_tellcore_devices(all_tellcore_devices) device in tellcore_devices}
hass.data['tellcore_registry'] = tellcore_registry
# Discover the switches # Discover the switches
_discover(hass, config, 'switch', _discover(hass, config, 'switch',
[tellcore_device.id for tellcore_device in all_tellcore_devices [device.id for device in tellcore_devices
if not tellcore_device.methods(TELLSTICK_DIM)]) if not device.methods(TELLSTICK_DIM)])
# Discover the lights # Discover the lights
_discover(hass, config, 'light', _discover(hass, config, 'light',
[tellcore_device.id for tellcore_device in all_tellcore_devices [device.id for device in tellcore_devices
if tellcore_device.methods(TELLSTICK_DIM)]) if device.methods(TELLSTICK_DIM)])
@callback
def async_handle_callback(tellcore_id, tellcore_command,
tellcore_data, cid):
"""Handle the actual callback from Tellcore."""
hass.helpers.dispatcher.async_dispatcher_send(
SIGNAL_TELLCORE_CALLBACK, tellcore_id,
tellcore_command, tellcore_data)
# Register callback
callback_id = tellcore_lib.register_device_event(
async_handle_callback)
def clean_up_callback(event):
"""Unregister the callback bindings."""
if callback_id is not None:
tellcore_lib.unregister_callback(callback_id)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, clean_up_callback)
return True return True
class TellstickRegistry(object):
"""Handle everything around Tellstick callbacks.
Keeps a map device ids to the tellcore device object, and
another to the HA device objects (entities).
Also responsible for registering / cleanup of callbacks, and for
dispatching the callbacks to the corresponding HA device object.
All device specific logic should be elsewhere (Entities).
"""
def __init__(self, hass, tellcore_lib):
"""Initialize the Tellstick mappings and callbacks."""
# used when map callback device id to ha entities.
self._id_to_ha_device_map = {}
self._id_to_tellcore_device_map = {}
self._setup_tellcore_callback(hass, tellcore_lib)
def _tellcore_event_callback(self, tellcore_id, tellcore_command,
tellcore_data, cid):
"""Handle the actual callback from Tellcore."""
ha_device = self._id_to_ha_device_map.get(tellcore_id, None)
if ha_device is not None:
# Pass it on to the HA device object
ha_device.update_from_callback(tellcore_command, tellcore_data)
def _setup_tellcore_callback(self, hass, tellcore_lib):
"""Register the callback handler."""
callback_id = tellcore_lib.register_device_event(
self._tellcore_event_callback)
def clean_up_callback(event):
"""Unregister the callback bindings."""
if callback_id is not None:
tellcore_lib.unregister_callback(callback_id)
_LOGGER.debug("Tellstick callback unregistered")
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, clean_up_callback)
def register_ha_device(self, tellcore_id, ha_device):
"""Register a new HA device to receive callback updates."""
self._id_to_ha_device_map[tellcore_id] = ha_device
def register_tellcore_devices(self, tellcore_devices):
"""Register a list of devices."""
self._id_to_tellcore_device_map.update(
{tellcore_device.id: tellcore_device for tellcore_device
in tellcore_devices})
def get_tellcore_device(self, tellcore_id):
"""Return a device by tellcore_id."""
return self._id_to_tellcore_device_map.get(tellcore_id, None)
class TellstickDevice(Entity): class TellstickDevice(Entity):
"""Representation of a Tellstick device. """Representation of a Tellstick device.
Contains the common logic for all Tellstick devices. Contains the common logic for all Tellstick devices.
""" """
def __init__(self, tellcore_id, tellcore_registry, signal_repetitions): def __init__(self, tellcore_device, signal_repetitions):
"""Init the Tellstick device.""" """Init the Tellstick device."""
self._signal_repetitions = signal_repetitions self._signal_repetitions = signal_repetitions
self._state = None self._state = None
@ -179,13 +147,16 @@ class TellstickDevice(Entity):
self._repeats_left = 0 self._repeats_left = 0
# Look up our corresponding tellcore device # Look up our corresponding tellcore device
self._tellcore_device = tellcore_registry.get_tellcore_device( self._tellcore_device = tellcore_device
tellcore_id) self._name = tellcore_device.name
self._name = self._tellcore_device.name
# Query tellcore for the current state @asyncio.coroutine
self._update_from_tellcore() def async_added_to_hass(self):
# Add ourselves to the mapping for callbacks """Register callbacks."""
tellcore_registry.register_ha_device(tellcore_id, self) self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_TELLCORE_CALLBACK,
self.update_from_callback
)
@property @property
def should_poll(self): def should_poll(self):
@ -275,15 +246,19 @@ class TellstickDevice(Entity):
self._update_model(tellcore_command != TELLSTICK_TURNOFF, self._update_model(tellcore_command != TELLSTICK_TURNOFF,
self._parse_tellcore_data(tellcore_data)) self._parse_tellcore_data(tellcore_data))
def update_from_callback(self, tellcore_command, tellcore_data): def update_from_callback(self, tellcore_id, tellcore_command,
tellcore_data):
"""Handle updates from the tellcore callback.""" """Handle updates from the tellcore callback."""
if tellcore_id != self._tellcore_device.id:
return
self._update_model_from_command(tellcore_command, tellcore_data) self._update_model_from_command(tellcore_command, tellcore_data)
self.schedule_update_ha_state() self.schedule_update_ha_state()
# This is a benign race on _repeats_left -- it's checked with the lock # This is a benign race on _repeats_left -- it's checked with the lock
# in _send_repeated_command. # in _send_repeated_command.
if self._repeats_left > 0: if self._repeats_left > 0:
self.hass.async_add_job(self._send_repeated_command) self._send_repeated_command()
def _update_from_tellcore(self): def _update_from_tellcore(self):
"""Read the current state of the device from the tellcore library.""" """Read the current state of the device from the tellcore library."""
@ -303,4 +278,3 @@ class TellstickDevice(Entity):
def update(self): def update(self):
"""Poll the current state of the device.""" """Poll the current state of the device."""
self._update_from_tellcore() self._update_from_tellcore()
self.schedule_update_ha_state()