Add mysensors device tracker and platform discovery (#5781)

* Add mysensors device_tracker platform

* Add discovery of device_tracker platforms

* Enable discovery of device_tracker platforms that are not
  DeviceScanner.
* Update signature of setup_scanner function in all affected platforms.
* Add test.
* Use discovery for mysensors device_tracker platform.

* Remove gps accuracy

* Small change to core like schema

* fix depency
pull/5795/head
Martin Hjelmare 2017-02-07 20:47:11 +01:00 committed by Pascal Vizeli
parent c7fd28c10f
commit 45a7c27280
17 changed files with 104 additions and 15 deletions

View File

@ -158,10 +158,11 @@ def async_setup(hass: HomeAssistantType, config: ConfigType):
None, platform.get_scanner, hass, {DOMAIN: p_config})
elif hasattr(platform, 'async_setup_scanner'):
setup = yield from platform.async_setup_scanner(
hass, p_config, tracker.async_see)
hass, p_config, tracker.async_see, disc_info)
elif hasattr(platform, 'setup_scanner'):
setup = yield from hass.loop.run_in_executor(
None, platform.setup_scanner, hass, p_config, tracker.see)
None, platform.setup_scanner, hass, p_config, tracker.see,
disc_info)
else:
raise HomeAssistantError("Invalid device_tracker platform.")
@ -193,6 +194,13 @@ def async_setup(hass: HomeAssistantType, config: ConfigType):
discovery.async_listen(
hass, DISCOVERY_PLATFORMS.keys(), async_device_tracker_discovered)
@asyncio.coroutine
def async_platform_discovered(platform, info):
"""Callback to load a platform."""
yield from async_setup_platform(platform, {}, disc_info=info)
discovery.async_listen_platform(hass, DOMAIN, async_platform_discovered)
# Clean up stale devices
async_track_utc_time_change(
hass, tracker.async_update_stale, second=range(0, 60, 5))

View File

@ -49,7 +49,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config: dict, see):
def setup_scanner(hass, config: dict, see, discovery_info=None):
"""Validate the configuration and return an Automatic scanner."""
try:
AutomaticDeviceScanner(hass, config, see)

View File

@ -25,7 +25,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the Bluetooth LE Scanner."""
# pylint: disable=import-error
from gattlib import DiscoveryService

View File

@ -21,7 +21,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the Bluetooth Scanner."""
# pylint: disable=import-error
import bluetooth

View File

@ -4,7 +4,7 @@ import random
from homeassistant.components.device_tracker import DOMAIN
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the demo tracker."""
def offset():
"""Return random offset."""

View File

@ -19,7 +19,7 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup an endpoint for the GPSLogger application."""
hass.http.register_view(GPSLoggerView(see))

View File

@ -71,7 +71,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config: dict, see):
def setup_scanner(hass, config: dict, see, discovery_info=None):
"""Set up the iCloud Scanner."""
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)

View File

@ -20,7 +20,7 @@ _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup an endpoint for the Locative application."""
hass.http.register_view(LocativeView(see))

View File

@ -23,7 +23,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(mqtt.SCHEMA_BASE).extend({
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the MQTT tracker."""
devices = config[CONF_DEVICES]
qos = config[CONF_QOS]

View File

@ -0,0 +1,60 @@
"""
Support for tracking MySensors devices.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.mysensors/
"""
import logging
from homeassistant.components import mysensors
from homeassistant.util import slugify
DEPENDENCIES = ['mysensors']
_LOGGER = logging.getLogger(__name__)
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the MySensors tracker."""
def mysensors_callback(gateway, node_id):
"""Callback for mysensors platform."""
node = gateway.sensors[node_id]
if node.sketch_name is None:
_LOGGER.info('No sketch_name: node %s', node_id)
return
pres = gateway.const.Presentation
set_req = gateway.const.SetReq
for child in node.children.values():
position = child.values.get(set_req.V_POSITION)
if child.type != pres.S_GPS or position is None:
continue
try:
latitude, longitude, _ = position.split(',')
except ValueError:
_LOGGER.error('Payload for V_POSITION %s is not of format '
'latitude,longitude,altitude', position)
continue
name = '{} {} {}'.format(
node.sketch_name, node_id, child.id)
attr = {
mysensors.ATTR_CHILD_ID: child.id,
mysensors.ATTR_DESCRIPTION: child.description,
mysensors.ATTR_DEVICE: gateway.device,
mysensors.ATTR_NODE_ID: node_id,
}
see(
dev_id=slugify(name),
host_name=name,
gps=(latitude, longitude),
battery=node.battery_level,
attributes=attr
)
gateways = hass.data.get(mysensors.MYSENSORS_GATEWAYS)
for gateway in gateways:
gateway.platform_callbacks.append(mysensors_callback)
return True

View File

@ -71,7 +71,7 @@ def get_cipher():
return (KEYLEN, decrypt)
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Set up an OwnTracks tracker."""
max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY)
waypoint_import = config.get(CONF_WAYPOINT_IMPORT)

View File

@ -73,7 +73,7 @@ class Host:
_LOGGER.debug("ping KO on ip=%s failed=%d", self.ip_address, failed)
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Setup the Host objects and return the update function."""
hosts = [Host(ip, dev_id, hass, config) for (dev_id, ip) in
config[const.CONF_HOSTS].items()]

View File

@ -23,7 +23,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config: dict, see):
def setup_scanner(hass, config: dict, see, discovery_info=None):
"""Validate the configuration and return a TrackR scanner."""
TrackRDeviceScanner(hass, config, see)
return True

View File

@ -30,7 +30,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
})
def setup_scanner(hass, config, see):
def setup_scanner(hass, config, see, discovery_info=None):
"""Validate the configuration and return a scanner."""
from volvooncall import Connection
connection = Connection(

View File

@ -234,6 +234,9 @@ def setup(hass, config):
'cover']:
discovery.load_platform(hass, component, DOMAIN, {}, config)
discovery.load_platform(
hass, 'device_tracker', DOMAIN, {}, config)
discovery.load_platform(
hass, 'notify', DOMAIN, {CONF_NAME: DOMAIN}, config)

View File

@ -11,6 +11,7 @@ import os
from homeassistant.components import zone
from homeassistant.core import callback
from homeassistant.bootstrap import setup_component
from homeassistant.helpers import discovery
from homeassistant.loader import get_component
from homeassistant.util.async import run_coroutine_threadsafe
import homeassistant.util.dt as dt_util
@ -324,6 +325,23 @@ class TestComponentsDeviceTracker(unittest.TestCase):
fire_service_discovered(self.hass, 'test', {})
self.assertTrue(mock_scan.called)
@patch(
'homeassistant.components.device_tracker.DeviceTracker.see')
@patch(
'homeassistant.components.device_tracker.demo.setup_scanner',
autospec=True)
def test_discover_platform(self, mock_demo_setup_scanner, mock_see):
"""Test discovery of device_tracker demo platform."""
assert device_tracker.DOMAIN not in self.hass.config.components
discovery.load_platform(
self.hass, device_tracker.DOMAIN, 'demo', {'test_key': 'test_val'},
{})
self.hass.block_till_done()
assert device_tracker.DOMAIN in self.hass.config.components
assert mock_demo_setup_scanner.called
assert mock_demo_setup_scanner.call_args[0] == (
self.hass, {}, mock_see, {'test_key': 'test_val'})
def test_update_stale(self):
"""Test stalled update."""
scanner = get_component('device_tracker.test').SCANNER

View File

@ -33,7 +33,7 @@ class TestComponentsDeviceTrackerMQTT(unittest.TestCase):
def test_ensure_device_tracker_platform_validation(self): \
# pylint: disable=invalid-name
"""Test if platform validation was done."""
def mock_setup_scanner(hass, config, see):
def mock_setup_scanner(hass, config, see, discovery_info=None):
"""Check that Qos was added by validation."""
self.assertTrue('qos' in config)