From 45a7c27280e5349218b498d7b2697c953d46ab6b Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 7 Feb 2017 20:47:11 +0100 Subject: [PATCH] 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 --- .../components/device_tracker/__init__.py | 12 +++- .../components/device_tracker/automatic.py | 2 +- .../device_tracker/bluetooth_le_tracker.py | 2 +- .../device_tracker/bluetooth_tracker.py | 2 +- .../components/device_tracker/demo.py | 2 +- .../components/device_tracker/gpslogger.py | 2 +- .../components/device_tracker/icloud.py | 2 +- .../components/device_tracker/locative.py | 2 +- .../components/device_tracker/mqtt.py | 2 +- .../components/device_tracker/mysensors.py | 60 +++++++++++++++++++ .../components/device_tracker/owntracks.py | 2 +- .../components/device_tracker/ping.py | 2 +- .../components/device_tracker/trackr.py | 2 +- .../components/device_tracker/volvooncall.py | 2 +- homeassistant/components/mysensors.py | 3 + tests/components/device_tracker/test_init.py | 18 ++++++ tests/components/device_tracker/test_mqtt.py | 2 +- 17 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 homeassistant/components/device_tracker/mysensors.py diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 21e7c7b0da1..bb6730a562f 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -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)) diff --git a/homeassistant/components/device_tracker/automatic.py b/homeassistant/components/device_tracker/automatic.py index d47aa818673..3b4612edf6c 100644 --- a/homeassistant/components/device_tracker/automatic.py +++ b/homeassistant/components/device_tracker/automatic.py @@ -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) diff --git a/homeassistant/components/device_tracker/bluetooth_le_tracker.py b/homeassistant/components/device_tracker/bluetooth_le_tracker.py index 454ab127af0..a4a933fe778 100644 --- a/homeassistant/components/device_tracker/bluetooth_le_tracker.py +++ b/homeassistant/components/device_tracker/bluetooth_le_tracker.py @@ -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 diff --git a/homeassistant/components/device_tracker/bluetooth_tracker.py b/homeassistant/components/device_tracker/bluetooth_tracker.py index a8b3861cdc5..1de0629c7c5 100644 --- a/homeassistant/components/device_tracker/bluetooth_tracker.py +++ b/homeassistant/components/device_tracker/bluetooth_tracker.py @@ -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 diff --git a/homeassistant/components/device_tracker/demo.py b/homeassistant/components/device_tracker/demo.py index 08242c2034d..dfd50a2b991 100644 --- a/homeassistant/components/device_tracker/demo.py +++ b/homeassistant/components/device_tracker/demo.py @@ -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.""" diff --git a/homeassistant/components/device_tracker/gpslogger.py b/homeassistant/components/device_tracker/gpslogger.py index 22099630bd1..c76c8fdd51b 100644 --- a/homeassistant/components/device_tracker/gpslogger.py +++ b/homeassistant/components/device_tracker/gpslogger.py @@ -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)) diff --git a/homeassistant/components/device_tracker/icloud.py b/homeassistant/components/device_tracker/icloud.py index 0878f8b005b..f6396ba7c34 100644 --- a/homeassistant/components/device_tracker/icloud.py +++ b/homeassistant/components/device_tracker/icloud.py @@ -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) diff --git a/homeassistant/components/device_tracker/locative.py b/homeassistant/components/device_tracker/locative.py index 32eb033a284..75cebbd95e7 100644 --- a/homeassistant/components/device_tracker/locative.py +++ b/homeassistant/components/device_tracker/locative.py @@ -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)) diff --git a/homeassistant/components/device_tracker/mqtt.py b/homeassistant/components/device_tracker/mqtt.py index f9a85da98b2..a93263fada9 100644 --- a/homeassistant/components/device_tracker/mqtt.py +++ b/homeassistant/components/device_tracker/mqtt.py @@ -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] diff --git a/homeassistant/components/device_tracker/mysensors.py b/homeassistant/components/device_tracker/mysensors.py new file mode 100644 index 00000000000..2ba19924089 --- /dev/null +++ b/homeassistant/components/device_tracker/mysensors.py @@ -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 diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index e2a238a5656..c03041b6317 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -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) diff --git a/homeassistant/components/device_tracker/ping.py b/homeassistant/components/device_tracker/ping.py index f0f2ac9f49c..2af400ba89c 100644 --- a/homeassistant/components/device_tracker/ping.py +++ b/homeassistant/components/device_tracker/ping.py @@ -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()] diff --git a/homeassistant/components/device_tracker/trackr.py b/homeassistant/components/device_tracker/trackr.py index 2eb0def278f..cf66fd33272 100644 --- a/homeassistant/components/device_tracker/trackr.py +++ b/homeassistant/components/device_tracker/trackr.py @@ -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 diff --git a/homeassistant/components/device_tracker/volvooncall.py b/homeassistant/components/device_tracker/volvooncall.py index 1be76d6139c..834ec7e55bd 100644 --- a/homeassistant/components/device_tracker/volvooncall.py +++ b/homeassistant/components/device_tracker/volvooncall.py @@ -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( diff --git a/homeassistant/components/mysensors.py b/homeassistant/components/mysensors.py index 4b4f1798d7f..d9c8584a5e9 100644 --- a/homeassistant/components/mysensors.py +++ b/homeassistant/components/mysensors.py @@ -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) diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index c083557294b..524eda22634 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -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 diff --git a/tests/components/device_tracker/test_mqtt.py b/tests/components/device_tracker/test_mqtt.py index 6eb5ba2381c..9405c944552 100644 --- a/tests/components/device_tracker/test_mqtt.py +++ b/tests/components/device_tracker/test_mqtt.py @@ -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)