Migrate mqtt tracker and arwn sensor to async / cleanup owntrack (#6373)

* Migrate mqtt tracker and arwn sensor to async / cleanup owntrack

* Fix tests / lint
This commit is contained in:
Pascal Vizeli 2017-03-03 12:09:10 +01:00 committed by GitHub
parent 55f8ec8866
commit ed9e93c29f
4 changed files with 35 additions and 25 deletions

View File

@ -4,11 +4,13 @@ Support for tracking MQTT enabled devices.
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/device_tracker.mqtt/ https://home-assistant.io/components/device_tracker.mqtt/
""" """
import asyncio
import logging import logging
import voluptuous as vol import voluptuous as vol
import homeassistant.components.mqtt as mqtt import homeassistant.components.mqtt as mqtt
from homeassistant.core import callback
from homeassistant.const import CONF_DEVICES from homeassistant.const import CONF_DEVICES
from homeassistant.components.mqtt import CONF_QOS from homeassistant.components.mqtt import CONF_QOS
from homeassistant.components.device_tracker import PLATFORM_SCHEMA from homeassistant.components.device_tracker import PLATFORM_SCHEMA
@ -23,19 +25,23 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend({
}) })
def setup_scanner(hass, config, see, discovery_info=None): @asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Setup the MQTT tracker.""" """Setup the MQTT tracker."""
devices = config[CONF_DEVICES] devices = config[CONF_DEVICES]
qos = config[CONF_QOS] qos = config[CONF_QOS]
dev_id_lookup = {} dev_id_lookup = {}
def device_tracker_message_received(topic, payload, qos): @callback
def async_tracker_message_received(topic, payload, qos):
"""MQTT message received.""" """MQTT message received."""
see(dev_id=dev_id_lookup[topic], location_name=payload) hass.async_add_job(
async_see(dev_id=dev_id_lookup[topic], location_name=payload))
for dev_id, topic in devices.items(): for dev_id, topic in devices.items():
dev_id_lookup[topic] = dev_id dev_id_lookup[topic] = dev_id
mqtt.subscribe(hass, topic, device_tracker_message_received, qos) yield from mqtt.async_subscribe(
hass, topic, async_tracker_message_received, qos)
return True return True

View File

@ -55,7 +55,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
}) })
@callback
def get_cipher(): def get_cipher():
"""Return decryption function and length of key. """Return decryption function and length of key.
@ -81,7 +80,6 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
mobile_beacons_active = defaultdict(list) mobile_beacons_active = defaultdict(list)
regions_entered = defaultdict(list) regions_entered = defaultdict(list)
@callback
def decrypt_payload(topic, ciphertext): def decrypt_payload(topic, ciphertext):
"""Decrypt encrypted payload.""" """Decrypt encrypted payload."""
try: try:
@ -119,7 +117,6 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
return None return None
# pylint: disable=too-many-return-statements # pylint: disable=too-many-return-statements
@callback
def validate_payload(topic, payload, data_type): def validate_payload(topic, payload, data_type):
"""Validate the OwnTracks payload.""" """Validate the OwnTracks payload."""
try: try:
@ -201,7 +198,6 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
dev_id, kwargs = _parse_see_args(topic, data) dev_id, kwargs = _parse_see_args(topic, data)
@callback
def enter_event(): def enter_event():
"""Execute enter event.""" """Execute enter event."""
zone = hass.states.get("zone.{}".format(slugify(location))) zone = hass.states.get("zone.{}".format(slugify(location)))
@ -222,7 +218,6 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
hass.async_add_job(async_see(**kwargs)) hass.async_add_job(async_see(**kwargs))
async_see_beacons(dev_id, kwargs) async_see_beacons(dev_id, kwargs)
@callback
def leave_event(): def leave_event():
"""Execute leave event.""" """Execute leave event."""
regions = regions_entered[dev_id] regions = regions_entered[dev_id]
@ -336,7 +331,6 @@ def async_setup_scanner(hass, config, async_see, discovery_info=None):
return True return True
@callback
def parse_topic(topic, pretty=False): def parse_topic(topic, pretty=False):
"""Parse an MQTT topic owntracks/user/dev, return (user, dev) tuple. """Parse an MQTT topic owntracks/user/dev, return (user, dev) tuple.
@ -353,7 +347,6 @@ def parse_topic(topic, pretty=False):
return (host_name, dev_id) return (host_name, dev_id)
@callback
def _parse_see_args(topic, data): def _parse_see_args(topic, data):
"""Parse the OwnTracks location parameters, into the format see expects. """Parse the OwnTracks location parameters, into the format see expects.
@ -372,7 +365,6 @@ def _parse_see_args(topic, data):
return dev_id, kwargs return dev_id, kwargs
@callback
def _set_gps_from_zone(kwargs, location, zone): def _set_gps_from_zone(kwargs, location, zone):
"""Set the see parameters from the zone parameters. """Set the see parameters from the zone parameters.

View File

@ -4,11 +4,13 @@ Support for collecting data from the ARWN project.
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/sensor.arwn/ https://home-assistant.io/components/sensor.arwn/
""" """
import asyncio
import json import json
import logging import logging
import homeassistant.components.mqtt as mqtt import homeassistant.components.mqtt as mqtt
from homeassistant.const import (TEMP_FAHRENHEIT, TEMP_CELSIUS) from homeassistant.core import callback
from homeassistant.const import TEMP_FAHRENHEIT, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify from homeassistant.util import slugify
@ -17,13 +19,15 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['mqtt'] DEPENDENCIES = ['mqtt']
DOMAIN = 'arwn' DOMAIN = 'arwn'
SENSORS = {} DATA_ARWN = 'arwn'
TOPIC = 'arwn/#' TOPIC = 'arwn/#'
def discover_sensors(topic, payload): def discover_sensors(topic, payload):
"""Given a topic, dynamically create the right sensor type.""" """Given a topic, dynamically create the right sensor type.
Async friendly.
"""
parts = topic.split('/') parts = topic.split('/')
unit = payload.get('units', '') unit = payload.get('units', '')
domain = parts[1] domain = parts[1]
@ -47,9 +51,11 @@ def _slug(name):
return 'sensor.arwn_{}'.format(slugify(name)) return 'sensor.arwn_{}'.format(slugify(name))
def setup_platform(hass, config, add_devices, discovery_info=None): @asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the ARWN platform.""" """Set up the ARWN platform."""
def sensor_event_received(topic, payload, qos): @callback
def async_sensor_event_received(topic, payload, qos):
"""Process events as sensors. """Process events as sensors.
When a new event on our topic (arwn/#) is received we map it When a new event on our topic (arwn/#) is received we map it
@ -67,6 +73,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if not sensors: if not sensors:
return return
store = hass.data.get(DATA_ARWN)
if store is None:
store = hass.data[DATA_ARWN] = {}
if isinstance(sensors, ArwnSensor): if isinstance(sensors, ArwnSensor):
sensors = (sensors, ) sensors = (sensors, )
@ -74,18 +84,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
del event['timestamp'] del event['timestamp']
for sensor in sensors: for sensor in sensors:
if sensor.name not in SENSORS: if sensor.name not in store:
sensor.hass = hass sensor.hass = hass
sensor.set_event(event) sensor.set_event(event)
SENSORS[sensor.name] = sensor store[sensor.name] = sensor
_LOGGER.debug("Registering new sensor %(name)s => %(event)s", _LOGGER.debug("Registering new sensor %(name)s => %(event)s",
dict(name=sensor.name, event=event)) dict(name=sensor.name, event=event))
add_devices((sensor,)) async_add_devices((sensor,), True)
else: else:
SENSORS[sensor.name].set_event(event) store[sensor.name].set_event(event)
SENSORS[sensor.name].update_ha_state()
mqtt.subscribe(hass, TOPIC, sensor_event_received, 0) yield from mqtt.async_subscribe(
hass, TOPIC, async_sensor_event_received, 0)
return True return True

View File

@ -1,4 +1,5 @@
"""The tests for the MQTT device tracker platform.""" """The tests for the MQTT device tracker platform."""
import asyncio
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
import logging import logging
@ -33,12 +34,13 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase):
def test_ensure_device_tracker_platform_validation(self): \ def test_ensure_device_tracker_platform_validation(self): \
# pylint: disable=invalid-name # pylint: disable=invalid-name
"""Test if platform validation was done.""" """Test if platform validation was done."""
@asyncio.coroutine
def mock_setup_scanner(hass, config, see, discovery_info=None): def mock_setup_scanner(hass, config, see, discovery_info=None):
"""Check that Qos was added by validation.""" """Check that Qos was added by validation."""
self.assertTrue('qos' in config) self.assertTrue('qos' in config)
with patch('homeassistant.components.device_tracker.mqtt.' with patch('homeassistant.components.device_tracker.mqtt.'
'setup_scanner', autospec=True, 'async_setup_scanner', autospec=True,
side_effect=mock_setup_scanner) as mock_sp: side_effect=mock_setup_scanner) as mock_sp:
dev_id = 'paulus' dev_id = 'paulus'