From 321a603bfe5bd843a5fe97942cdbcb2b4250f75f Mon Sep 17 00:00:00 2001 From: badele Date: Sun, 27 Sep 2015 11:13:49 +0200 Subject: [PATCH 01/88] Add a light & switch rfxtrx support --- homeassistant/components/light/rfxtrx.py | 102 ++++++++++++++++++++++ homeassistant/components/rfxtrx.py | 41 +++++++++ homeassistant/components/sensor/rfxtrx.py | 33 ++++--- homeassistant/components/switch/rfxtrx.py | 102 ++++++++++++++++++++++ 4 files changed, 261 insertions(+), 17 deletions(-) create mode 100644 homeassistant/components/light/rfxtrx.py create mode 100644 homeassistant/components/rfxtrx.py create mode 100644 homeassistant/components/switch/rfxtrx.py diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py new file mode 100644 index 00000000000..69ea23a6d4b --- /dev/null +++ b/homeassistant/components/light/rfxtrx.py @@ -0,0 +1,102 @@ +""" +homeassistant.components.light.rfxtrx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for Rfxtrx lights. + +Configuration: + +To use Rfxtrx lights you will need to add the following to your +configuration.yaml file. + +light: + platform: rfxtrx + + devices: + ac09c4f1: Bedroom Light + ac09c4f2: Kitchen Light + ac09c4f3: Bathroom Light + +*Optional* + + # Automatic add new light + automatic_add: True + +""" +import logging +import homeassistant.components.rfxtrx as rfxtrx +from RFXtrx import LightingDevice + +from homeassistant.components.light import Light +from homeassistant.util import slugify + +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] + +DOMAIN = "rfxtrx" + +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Setup the RFXtrx platform. """ + # Add light from config file + devices = config.get('devices') + for entity_id, entity_name in devices.items(): + if entity_id not in rfxtrx.RFX_DEVICES: + new_light = RfxtrxLight(entity_name, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + + add_devices_callback(rfxtrx.RFX_DEVICES.values()) + + def light_update(event): + """ Callback for sensor updates from the RFXtrx gateway. """ + if isinstance(event.device, LightingDevice): + entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) + + # Add entity if not exist and the automatic_add is True + if entity_id not in rfxtrx.RFX_DEVICES: + automatic_add = config.get('automatic_add', False) + if automatic_add: + new_light = RfxtrxLight(entity_id, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + add_devices_callback([new_light]) + + # Check if entity exists (previous automatic added) + if entity_id in rfxtrx.RFX_DEVICES: + if event.values['Command'] == 'On' or event.values['Command'] == 'Off': + if event.values['Command'] == 'On': + rfxtrx.RFX_DEVICES[entity_id].turn_on() + else: + rfxtrx.RFX_DEVICES[entity_id].turn_off() + + if light_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: + rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(light_update) + + +class RfxtrxLight(Light): + """ Provides a demo switch. """ + def __init__(self, name, state): + self._name = name + self._state = state + + @property + def should_poll(self): + """ No polling needed for a demo light. """ + return False + + @property + def name(self): + """ Returns the name of the device if any. """ + return self._name + + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + """ Turn the device on. """ + self._state = True + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + self._state = False + self.update_ha_state() \ No newline at end of file diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py new file mode 100644 index 00000000000..788778debeb --- /dev/null +++ b/homeassistant/components/rfxtrx.py @@ -0,0 +1,41 @@ +""" +homeassistant.components.rfxtrx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Connects Home Assistant to a RFXtrx device. +""" + +import logging + +DEPENDENCIES = [] +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] + +DOMAIN = "rfxtrx" +CONF_DEVICE = 'device' +RECEIVED_EVT_SUBSCRIBERS = [] +RFX_DEVICES = {} + +def setup(hass, config): + """ Setup the Rfxtrx component. """ + + # Init logger + logger = logging.getLogger(__name__) + + # Declare the Handle event + def handle_receive(event): + """ Callback all subscribers for RFXtrx gateway. """ + for subscriber in RECEIVED_EVT_SUBSCRIBERS: + subscriber(event) + + # Try to load the RFXtrx module + try: + import RFXtrx as rfxtrxmod + except ImportError: + logger.exception("Failed to import rfxtrx") + return False + + # Init the rfxtrx module + device = config[DOMAIN][CONF_DEVICE] + rfxtrxmod.Core(device, handle_receive) + + return True \ No newline at end of file diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 4cb8a939d5e..7dc6496555c 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -24,6 +24,9 @@ from collections import OrderedDict from homeassistant.const import (TEMP_CELCIUS) from homeassistant.helpers.entity import Entity +import homeassistant.components.rfxtrx as rfxtrx +from RFXtrx import SensorEvent +from homeassistant.util import slugify REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] @@ -36,7 +39,7 @@ DATA_TYPES = OrderedDict([ ('Rain rate', '')]) -def setup_platform(hass, config, add_devices, discovery_info=None): +def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ logger = logging.getLogger(__name__) @@ -44,23 +47,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): def sensor_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if event.device.id_string in sensors: - sensors[event.device.id_string].event = event - else: - logger.info("adding new sensor: %s", event.device.type_string) - new_sensor = RfxtrxSensor(event) - sensors[event.device.id_string] = new_sensor - add_devices([new_sensor]) - try: - import RFXtrx as rfxtrx - except ImportError: - logger.exception( - "Failed to import rfxtrx") - return False - - device = config.get("device", "") - rfxtrx.Core(device, sensor_update) + if isinstance(event.device, SensorEvent): + entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) + if entity_id in rfxtrx.RFX_DEVICES: + rfxtrx.RFX_DEVICES[entity_id].event = event + else: + automatic_add = config.get('automatic_add', False) + if automatic_add: + new_light = RfxtrxSensor(entity_id, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + add_devices_callback([new_light]) + if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: + rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(sensor_update) class RfxtrxSensor(Entity): """ Represents a RFXtrx sensor. """ diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py new file mode 100644 index 00000000000..a7b39655249 --- /dev/null +++ b/homeassistant/components/switch/rfxtrx.py @@ -0,0 +1,102 @@ +""" +homeassistant.components.switch.rfxtrx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for Rfxtrx switch. + +Configuration: + +To use Rfxtrx switchs you will need to add the following to your +configuration.yaml file. + +switch: + platform: rfxtrx + + devices: + ac09c4f1: Bedroom Door + ac09c4f2: Kitchen Door + ac09c4f3: Bathroom Door + +*Optional* + + # Automatic add new switch + automatic_add: True + +""" +import logging +import homeassistant.components.rfxtrx as rfxtrx +from RFXtrx import LightingDevice + +from homeassistant.components.switch import SwitchDevice +from homeassistant.util import slugify + +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] + +DOMAIN = "rfxtrx" + +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Setup the RFXtrx platform. """ + # Add switch from config file + devices = config.get('devices') + for entity_id, entity_name in devices.items(): + if entity_id not in rfxtrx.RFX_DEVICES: + new_switch = RfxtrxSwitch(entity_name, False) + rfxtrx.RFX_DEVICES[entity_id] = new_switch + + add_devices_callback(rfxtrx.RFX_DEVICES.values()) + + def switch_update(event): + """ Callback for sensor updates from the RFXtrx gateway. """ + if isinstance(event.device, LightingDevice): + entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) + + # Add entity if not exist and the automatic_add is True + if entity_id not in rfxtrx.RFX_DEVICES: + automatic_add = config.get('automatic_add', False) + if automatic_add: + new_switch = RfxtrxSwitch(entity_id, False) + rfxtrx.RFX_DEVICES[entity_id] = new_switch + add_devices_callback([new_switch]) + + # Check if entity exists (previous automatic added) + if entity_id in rfxtrx.RFX_DEVICES: + if event.values['Command'] == 'On' or event.values['Command'] == 'Off': + if event.values['Command'] == 'On': + rfxtrx.RFX_DEVICES[entity_id].turn_on() + else: + rfxtrx.RFX_DEVICES[entity_id].turn_off() + + if switch_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: + rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(switch_update) + + +class RfxtrxSwitch(SwitchDevice): + """ Provides a demo switch. """ + def __init__(self, name, state): + self._name = name + self._state = state + + @property + def should_poll(self): + """ No polling needed for a demo switch. """ + return False + + @property + def name(self): + """ Returns the name of the device if any. """ + return self._name + + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + """ Turn the device on. """ + self._state = True + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + self._state = False + self.update_ha_state() \ No newline at end of file From 174aeacd768bdbf85e6f9454f358e766a49d8f83 Mon Sep 17 00:00:00 2001 From: badele Date: Sun, 27 Sep 2015 23:51:19 +0200 Subject: [PATCH 02/88] Fix duplicate devices insertion --- homeassistant/components/light/rfxtrx.py | 10 ++++++++-- homeassistant/components/switch/rfxtrx.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 69ea23a6d4b..ed9c8f99dfc 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -36,24 +36,30 @@ DOMAIN = "rfxtrx" def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + logger = logging.getLogger(__name__) + # Add light from config file + lights = [] devices = config.get('devices') for entity_id, entity_name in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: + logger.info("Add %s rfxtrx.light" % entity_name) new_light = RfxtrxLight(entity_name, False) rfxtrx.RFX_DEVICES[entity_id] = new_light + lights.append(new_light) - add_devices_callback(rfxtrx.RFX_DEVICES.values()) + add_devices_callback(lights) def light_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ if isinstance(event.device, LightingDevice): - entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) + entity_id = slugify(event.device.id_string.lower()) # Add entity if not exist and the automatic_add is True if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: + logger.info("Automatic add %s rfxtrx.light" % entity_name) new_light = RfxtrxLight(entity_id, False) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index a7b39655249..3f2b957cc25 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -36,24 +36,30 @@ DOMAIN = "rfxtrx" def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ + logger = logging.getLogger(__name__) + # Add switch from config file + switchs = [] devices = config.get('devices') for entity_id, entity_name in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: + logger.info("Add %s rfxtrx.switch" % entity_name) new_switch = RfxtrxSwitch(entity_name, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch + switchs.append(new_switch) - add_devices_callback(rfxtrx.RFX_DEVICES.values()) + add_devices_callback(switchs) def switch_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ if isinstance(event.device, LightingDevice): - entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) + entity_id = slugify(event.device.id_string.lower()) # Add entity if not exist and the automatic_add is True if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: + logger.info("Automatic add %s rfxtrx.switch" % entity_name) new_switch = RfxtrxSwitch(entity_id, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) From d64f0ddd412b4a8bbcbe0ea5cc07df2355ad1633 Mon Sep 17 00:00:00 2001 From: badele Date: Tue, 29 Sep 2015 08:20:25 +0200 Subject: [PATCH 03/88] Refactoring the code for pylint & flake test --- homeassistant/components/light/rfxtrx.py | 12 +++++++----- homeassistant/components/rfxtrx.py | 9 ++++----- homeassistant/components/sensor/rfxtrx.py | 22 ++++++++++++---------- homeassistant/components/switch/rfxtrx.py | 12 +++++++----- 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index ed9c8f99dfc..ad3fbb03d4a 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -33,17 +33,18 @@ REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] DOMAIN = "rfxtrx" +_LOGGER = logging.getLogger(__name__) + def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - logger = logging.getLogger(__name__) # Add light from config file lights = [] devices = config.get('devices') for entity_id, entity_name in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - logger.info("Add %s rfxtrx.light" % entity_name) + _LOGGER.info("Add %s rfxtrx.light", entity_name) new_light = RfxtrxLight(entity_name, False) rfxtrx.RFX_DEVICES[entity_id] = new_light lights.append(new_light) @@ -59,14 +60,15 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - logger.info("Automatic add %s rfxtrx.light" % entity_name) + _LOGGER.info("Automatic add %s rfxtrx.light", entity_id) new_light = RfxtrxLight(entity_id, False) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) # Check if entity exists (previous automatic added) if entity_id in rfxtrx.RFX_DEVICES: - if event.values['Command'] == 'On' or event.values['Command'] == 'Off': + if event.values['Command'] == 'On'\ + or event.values['Command'] == 'Off': if event.values['Command'] == 'On': rfxtrx.RFX_DEVICES[entity_id].turn_on() else: @@ -105,4 +107,4 @@ class RfxtrxLight(Light): def turn_off(self, **kwargs): """ Turn the device off. """ self._state = False - self.update_ha_state() \ No newline at end of file + self.update_ha_state() diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 788778debeb..d72a9fe02ec 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -14,13 +14,12 @@ DOMAIN = "rfxtrx" CONF_DEVICE = 'device' RECEIVED_EVT_SUBSCRIBERS = [] RFX_DEVICES = {} +_LOGGER = logging.getLogger(__name__) + def setup(hass, config): """ Setup the Rfxtrx component. """ - # Init logger - logger = logging.getLogger(__name__) - # Declare the Handle event def handle_receive(event): """ Callback all subscribers for RFXtrx gateway. """ @@ -31,11 +30,11 @@ def setup(hass, config): try: import RFXtrx as rfxtrxmod except ImportError: - logger.exception("Failed to import rfxtrx") + _LOGGER.exception("Failed to import rfxtrx") return False # Init the rfxtrx module device = config[DOMAIN][CONF_DEVICE] rfxtrxmod.Core(device, handle_receive) - return True \ No newline at end of file + return True diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 7dc6496555c..625fd50de84 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -37,30 +37,32 @@ DATA_TYPES = OrderedDict([ ('Barometer', ''), ('Wind direction', ''), ('Rain rate', '')]) +_LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - logger = logging.getLogger(__name__) - - sensors = {} # keep track of sensors added to HA def sensor_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ if isinstance(event.device, SensorEvent): - entity_id = '%s-%s' % (event.device.type_string.lower(), slugify(event.device.id_string.lower())) - if entity_id in rfxtrx.RFX_DEVICES: - rfxtrx.RFX_DEVICES[entity_id].event = event - else: + entity_id = slugify(event.device.id_string.lower()) + + # Add entity if not exist and the automatic_add is True + if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - new_light = RfxtrxSensor(entity_id, False) - rfxtrx.RFX_DEVICES[entity_id] = new_light - add_devices_callback([new_light]) + _LOGGER.info("Automatic add %s rfxtrx.light", entity_id) + new_sensor = RfxtrxSensor(event) + rfxtrx.RFX_DEVICES[entity_id] = new_sensor + add_devices_callback([new_sensor]) + else: + rfxtrx.RFX_DEVICES[entity_id].event = event if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(sensor_update) + class RfxtrxSensor(Entity): """ Represents a RFXtrx sensor. """ diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 3f2b957cc25..b64d0d9bf7a 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -33,17 +33,18 @@ REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] DOMAIN = "rfxtrx" +_LOGGER = logging.getLogger(__name__) + def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - logger = logging.getLogger(__name__) # Add switch from config file switchs = [] devices = config.get('devices') for entity_id, entity_name in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - logger.info("Add %s rfxtrx.switch" % entity_name) + _LOGGER.info("Add %s rfxtrx.switch", entity_name) new_switch = RfxtrxSwitch(entity_name, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch switchs.append(new_switch) @@ -59,14 +60,15 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - logger.info("Automatic add %s rfxtrx.switch" % entity_name) + _LOGGER.info("Automatic add %s rfxtrx.switch", entity_id) new_switch = RfxtrxSwitch(entity_id, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) # Check if entity exists (previous automatic added) if entity_id in rfxtrx.RFX_DEVICES: - if event.values['Command'] == 'On' or event.values['Command'] == 'Off': + if event.values['Command'] == 'On'\ + or event.values['Command'] == 'Off': if event.values['Command'] == 'On': rfxtrx.RFX_DEVICES[entity_id].turn_on() else: @@ -105,4 +107,4 @@ class RfxtrxSwitch(SwitchDevice): def turn_off(self, **kwargs): """ Turn the device off. """ self._state = False - self.update_ha_state() \ No newline at end of file + self.update_ha_state() From cc47e39006bcacf1bb1d3e6c227d0586aa0ad079 Mon Sep 17 00:00:00 2001 From: badele Date: Tue, 29 Sep 2015 22:47:22 +0200 Subject: [PATCH 04/88] Add send capability --- homeassistant/components/light/rfxtrx.py | 23 +++++++++++++++++------ homeassistant/components/rfxtrx.py | 5 +++-- homeassistant/components/switch/rfxtrx.py | 21 +++++++++++++++------ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index ad3fbb03d4a..190aae55da6 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -42,10 +42,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # Add light from config file lights = [] devices = config.get('devices') - for entity_id, entity_name in devices.items(): + for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.light", entity_name) - new_light = RfxtrxLight(entity_name, False) + _LOGGER.info("Add %s rfxtrx.light", entity_info['name']) + new_light = RfxtrxLight(entity_info['name'], None, False) rfxtrx.RFX_DEVICES[entity_id] = new_light lights.append(new_light) @@ -60,8 +60,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - _LOGGER.info("Automatic add %s rfxtrx.light", entity_id) - new_light = RfxtrxLight(entity_id, False) + _LOGGER.info("Automatic add %s rfxtrx.light (class: %s subtype: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + new_light = RfxtrxLight(entity_id, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) @@ -80,8 +84,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxLight(Light): """ Provides a demo switch. """ - def __init__(self, name, state): + def __init__(self, name, event, state): self._name = name + self._event = event self._state = state @property @@ -101,10 +106,16 @@ class RfxtrxLight(Light): def turn_on(self, **kwargs): """ Turn the device on. """ + + self._event.device.send_on(rfxtrx.RFXOBJECT.transport) + self._state = True self.update_ha_state() def turn_off(self, **kwargs): """ Turn the device off. """ + + self._event.device.send_off(rfxtrx.RFXOBJECT.transport) + self._state = False self.update_ha_state() diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index d72a9fe02ec..afd74c907d1 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -15,7 +15,7 @@ CONF_DEVICE = 'device' RECEIVED_EVT_SUBSCRIBERS = [] RFX_DEVICES = {} _LOGGER = logging.getLogger(__name__) - +RFXOBJECT = None def setup(hass, config): """ Setup the Rfxtrx component. """ @@ -34,7 +34,8 @@ def setup(hass, config): return False # Init the rfxtrx module + global RFXOBJECT device = config[DOMAIN][CONF_DEVICE] - rfxtrxmod.Core(device, handle_receive) + RFXOBJECT = rfxtrxmod.Core(device, handle_receive) return True diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index b64d0d9bf7a..cdc7f64f360 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -42,10 +42,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # Add switch from config file switchs = [] devices = config.get('devices') - for entity_id, entity_name in devices.items(): + for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.switch", entity_name) - new_switch = RfxtrxSwitch(entity_name, False) + _LOGGER.info("Add %s rfxtrx.switch", entity_info['name']) + new_switch = RfxtrxSwitch(entity_info['name'], None, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch switchs.append(new_switch) @@ -60,8 +60,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - _LOGGER.info("Automatic add %s rfxtrx.switch", entity_id) - new_switch = RfxtrxSwitch(entity_id, False) + _LOGGER.info("Automatic add %s rfxtrx.switch (class: %s subtype: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + new_switch = RfxtrxSwitch(entity_id, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) @@ -80,8 +84,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxSwitch(SwitchDevice): """ Provides a demo switch. """ - def __init__(self, name, state): + def __init__(self, name, event, state): self._name = name + self._event = event self._state = state @property @@ -101,10 +106,14 @@ class RfxtrxSwitch(SwitchDevice): def turn_on(self, **kwargs): """ Turn the device on. """ + self._event.device.send_on(rfxtrx.RFXOBJECT.transport) + self._state = True self.update_ha_state() def turn_off(self, **kwargs): """ Turn the device off. """ + self._event.device.send_off(rfxtrx.RFXOBJECT.transport) + self._state = False self.update_ha_state() From db509ccf181a2488815ace2c663811dff938d12c Mon Sep 17 00:00:00 2001 From: badele Date: Fri, 2 Oct 2015 22:39:30 +0200 Subject: [PATCH 05/88] Add a light & switch rfxtrx sender capability --- homeassistant/components/light/rfxtrx.py | 31 +++++++++------- homeassistant/components/rfxtrx.py | 45 ++++++++++++++++++++++- homeassistant/components/sensor/rfxtrx.py | 2 +- homeassistant/components/switch/rfxtrx.py | 24 +++++++----- 4 files changed, 78 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 190aae55da6..d4008682c4c 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -24,7 +24,7 @@ light: """ import logging import homeassistant.components.rfxtrx as rfxtrx -from RFXtrx import LightingDevice +import RFXtrx as rfxtrxmod from homeassistant.components.light import Light from homeassistant.util import slugify @@ -38,22 +38,23 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - # Add light from config file lights = [] - devices = config.get('devices') - for entity_id, entity_info in devices.items(): - if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.light", entity_info['name']) - new_light = RfxtrxLight(entity_info['name'], None, False) - rfxtrx.RFX_DEVICES[entity_id] = new_light - lights.append(new_light) + devices = config.get('devices', None) + if devices: + for entity_id, entity_info in devices.items(): + if entity_id not in rfxtrx.RFX_DEVICES: + _LOGGER.info("Add %s rfxtrx.light", entity_info['name']) + rfxobject = rfxtrx.getRFXObject(entity_info['packetid']) + new_light = RfxtrxLight(entity_info['name'], rfxobject, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + lights.append(new_light) add_devices_callback(lights) def light_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if isinstance(event.device, LightingDevice): + if isinstance(event.device, rfxtrxmod.LightingDevice): entity_id = slugify(event.device.id_string.lower()) # Add entity if not exist and the automatic_add is True @@ -65,7 +66,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): event.device.__class__.__name__, event.device.subtype ) - new_light = RfxtrxLight(entity_id, event, False) + packet_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%(entity_id)s : %(packet_id)s" % locals() + new_light = RfxtrxLight(entity_name, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) @@ -107,7 +110,8 @@ class RfxtrxLight(Light): def turn_on(self, **kwargs): """ Turn the device on. """ - self._event.device.send_on(rfxtrx.RFXOBJECT.transport) + if self._event: + self._event.device.send_on(rfxtrx.RFXOBJECT.transport) self._state = True self.update_ha_state() @@ -115,7 +119,8 @@ class RfxtrxLight(Light): def turn_off(self, **kwargs): """ Turn the device off. """ - self._event.device.send_off(rfxtrx.RFXOBJECT.transport) + if self._event: + self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False self.update_ha_state() diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index afd74c907d1..d4eb379b04f 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -4,6 +4,24 @@ homeassistant.components.rfxtrx Connects Home Assistant to a RFXtrx device. """ +""" +homeassistant.components.rfxtrx +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Connects Home Assistant to a RFXtrx device. + +Configuration: + +To use Rfxtrx device you will need to add the following to your +configuration.yaml file. + +rfxtrx: + device: /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1YVC1P0-if00-port0 + +*Optional* + + debug: True + +""" import logging DEPENDENCIES = [] @@ -12,6 +30,7 @@ REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + DOMAIN = "rfxtrx" CONF_DEVICE = 'device' +CONF_DEBUG = 'debug' RECEIVED_EVT_SUBSCRIBERS = [] RFX_DEVICES = {} _LOGGER = logging.getLogger(__name__) @@ -23,6 +42,7 @@ def setup(hass, config): # Declare the Handle event def handle_receive(event): """ Callback all subscribers for RFXtrx gateway. """ + for subscriber in RECEIVED_EVT_SUBSCRIBERS: subscriber(event) @@ -35,7 +55,30 @@ def setup(hass, config): # Init the rfxtrx module global RFXOBJECT + device = config[DOMAIN][CONF_DEVICE] - RFXOBJECT = rfxtrxmod.Core(device, handle_receive) + try: + debug = config[DOMAIN][CONF_DEBUG] + except KeyError: + debug = False + + RFXOBJECT = rfxtrxmod.Core(device, handle_receive, debug=debug) return True + +def getRFXObject(packetid): + """ return the RFXObject with the packetid""" + binarypacket = bytearray.fromhex(packetid) + + pkt = rfxtrxmod.lowlevel.parse(binarypacket) + if pkt is not None: + if isinstance(pkt, rfxtrxmod.lowlevel.SensorPacket): + obj = rfxtrxmod.SensorEvent(pkt) + elif isinstance(pkt, rfxtrxmod.lowlevel.Status): + obj = rfxtrxmod.StatusEvent(pkt) + else: + obj = rfxtrxmod.ControlEvent(pkt) + + return obj + + return None diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 625fd50de84..d0514c93a51 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -50,7 +50,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # Add entity if not exist and the automatic_add is True if entity_id not in rfxtrx.RFX_DEVICES: - automatic_add = config.get('automatic_add', False) + automatic_add = config.get('automatic_add', True) if automatic_add: _LOGGER.info("Automatic add %s rfxtrx.light", entity_id) new_sensor = RfxtrxSensor(event) diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index cdc7f64f360..d13e298f1b7 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -42,12 +42,14 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): # Add switch from config file switchs = [] devices = config.get('devices') - for entity_id, entity_info in devices.items(): - if entity_id not in rfxtrx.RFX_DEVICES: - _LOGGER.info("Add %s rfxtrx.switch", entity_info['name']) - new_switch = RfxtrxSwitch(entity_info['name'], None, False) - rfxtrx.RFX_DEVICES[entity_id] = new_switch - switchs.append(new_switch) + if devices: + for entity_id, entity_info in devices.items(): + if entity_id not in rfxtrx.RFX_DEVICES: + _LOGGER.info("Add %s rfxtrx.switch", entity_info['name']) + rfxobject = rfxtrx.getRFXObject(entity_info['packetid']) + new_switch = RfxtrxSwitch(entity_info['name'], rfxobject, False) + rfxtrx.RFX_DEVICES[entity_id] = new_switch + switchs.append(new_switch) add_devices_callback(switchs) @@ -65,7 +67,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): event.device.__class__.__name__, event.device.subtype ) - new_switch = RfxtrxSwitch(entity_id, event, False) + packet_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%(entity_id)s : %(packet_id)s" % locals() + new_switch = RfxtrxSwitch(entity_name, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) @@ -106,14 +110,16 @@ class RfxtrxSwitch(SwitchDevice): def turn_on(self, **kwargs): """ Turn the device on. """ - self._event.device.send_on(rfxtrx.RFXOBJECT.transport) + if self._event: + self._event.device.send_on(rfxtrx.RFXOBJECT.transport) self._state = True self.update_ha_state() def turn_off(self, **kwargs): """ Turn the device off. """ - self._event.device.send_off(rfxtrx.RFXOBJECT.transport) + if self._event: + self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False self.update_ha_state() From 7f71706f08122e309ff04bf6e765513bcfde3658 Mon Sep 17 00:00:00 2001 From: badele Date: Sat, 3 Oct 2015 11:26:18 +0200 Subject: [PATCH 06/88] Log RFXCOM events --- homeassistant/components/rfxtrx.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index d4eb379b04f..2ec4fd3d25d 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -23,6 +23,7 @@ rfxtrx: """ import logging +from homeassistant.util import slugify DEPENDENCIES = [] REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + @@ -43,6 +44,13 @@ def setup(hass, config): def handle_receive(event): """ Callback all subscribers for RFXtrx gateway. """ + # Log RFXCOM event + entity_id = slugify(event.device.id_string.lower()) + packet_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%(entity_id)s : %(packet_id)s" % locals() + _LOGGER.info("Receive RFXCOM event from %s => %s" % (event.device, entity_name)) + + # Callback to HA registered components for subscriber in RECEIVED_EVT_SUBSCRIBERS: subscriber(event) @@ -68,6 +76,12 @@ def setup(hass, config): def getRFXObject(packetid): """ return the RFXObject with the packetid""" + try: + import RFXtrx as rfxtrxmod + except ImportError: + _LOGGER.exception("Failed to import rfxtrx") + return False + binarypacket = bytearray.fromhex(packetid) pkt = rfxtrxmod.lowlevel.parse(binarypacket) From 6d53944fa1f1c0fdb743f748ac85fbb59c6d1a72 Mon Sep 17 00:00:00 2001 From: Alan Bowman Date: Sun, 4 Oct 2015 20:28:11 +0100 Subject: [PATCH 07/88] Support RGB colors --- homeassistant/components/light/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index 8d09910093b..c1b1579b4b5 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -246,6 +246,7 @@ def setup(hass, config): rgb_color = dat.get(ATTR_RGB_COLOR) if len(rgb_color) == 3: + params[ATTR_RGB_COLOR] = [int(val) for val in rgb_color] params[ATTR_XY_COLOR] = \ color_util.color_RGB_to_xy(int(rgb_color[0]), int(rgb_color[1]), From 3b49d1e876bcefb9f0729affe40f4b94036f8641 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 5 Oct 2015 22:31:21 -0700 Subject: [PATCH 08/88] Update version to 0.7.5dev0 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 2d272ca3a0b..35e3e2c93ae 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.4" +__version__ = "0.7.5dev0" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' From 32f1791c5ae50ba5ff2fcad3b4c614f7ae07ed12 Mon Sep 17 00:00:00 2001 From: badele Date: Tue, 6 Oct 2015 08:44:15 +0200 Subject: [PATCH 09/88] Check flake & pylint style --- homeassistant/components/light/rfxtrx.py | 15 ++++++++------- homeassistant/components/rfxtrx.py | 15 ++++++--------- homeassistant/components/switch/rfxtrx.py | 21 +++++++++++---------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index d4008682c4c..7c8bdb7a93a 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -45,7 +45,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: _LOGGER.info("Add %s rfxtrx.light", entity_info['name']) - rfxobject = rfxtrx.getRFXObject(entity_info['packetid']) + rfxobject = rfxtrx.get_rfx_object(entity_info['packetid']) new_light = RfxtrxLight(entity_info['name'], rfxobject, False) rfxtrx.RFX_DEVICES[entity_id] = new_light lights.append(new_light) @@ -61,13 +61,14 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - _LOGGER.info("Automatic add %s rfxtrx.light (class: %s subtype: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype + _LOGGER.info( + "Automatic add %s rfxtrx.light (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype ) - packet_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%(entity_id)s : %(packet_id)s" % locals() + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) new_light = RfxtrxLight(entity_name, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_light add_devices_callback([new_light]) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 2ec4fd3d25d..0c3f377150b 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -1,9 +1,3 @@ -""" -homeassistant.components.rfxtrx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Connects Home Assistant to a RFXtrx device. -""" - """ homeassistant.components.rfxtrx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -37,6 +31,7 @@ RFX_DEVICES = {} _LOGGER = logging.getLogger(__name__) RFXOBJECT = None + def setup(hass, config): """ Setup the Rfxtrx component. """ @@ -47,8 +42,9 @@ def setup(hass, config): # Log RFXCOM event entity_id = slugify(event.device.id_string.lower()) packet_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%(entity_id)s : %(packet_id)s" % locals() - _LOGGER.info("Receive RFXCOM event from %s => %s" % (event.device, entity_name)) + entity_name = "%s : %s" % (entity_id, packet_id) + _LOGGER.info("Receive RFXCOM event from %s => %s", + event.device, entity_name) # Callback to HA registered components for subscriber in RECEIVED_EVT_SUBSCRIBERS: @@ -74,7 +70,8 @@ def setup(hass, config): return True -def getRFXObject(packetid): + +def get_rfx_object(packetid): """ return the RFXObject with the packetid""" try: import RFXtrx as rfxtrxmod diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index d13e298f1b7..7eefc6fb229 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -46,10 +46,10 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): for entity_id, entity_info in devices.items(): if entity_id not in rfxtrx.RFX_DEVICES: _LOGGER.info("Add %s rfxtrx.switch", entity_info['name']) - rfxobject = rfxtrx.getRFXObject(entity_info['packetid']) - new_switch = RfxtrxSwitch(entity_info['name'], rfxobject, False) - rfxtrx.RFX_DEVICES[entity_id] = new_switch - switchs.append(new_switch) + rfxobject = rfxtrx.get_rfx_object(entity_info['packetid']) + newswitch = RfxtrxSwitch(entity_info['name'], rfxobject, False) + rfxtrx.RFX_DEVICES[entity_id] = newswitch + switchs.append(newswitch) add_devices_callback(switchs) @@ -62,13 +62,14 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) if automatic_add: - _LOGGER.info("Automatic add %s rfxtrx.switch (class: %s subtype: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype + _LOGGER.info( + "Automatic add %s rfxtrx.switch (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype ) - packet_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%(entity_id)s : %(packet_id)s" % locals() + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) new_switch = RfxtrxSwitch(entity_name, event, False) rfxtrx.RFX_DEVICES[entity_id] = new_switch add_devices_callback([new_switch]) From 047cff6596f50b29b3885eb88a44c91627645775 Mon Sep 17 00:00:00 2001 From: Alan Bowman Date: Tue, 29 Sep 2015 21:32:28 +0100 Subject: [PATCH 10/88] Add blinkstick support --- .coveragerc | 1 + .../components/light/blinksticklight.py | 72 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 76 insertions(+) create mode 100644 homeassistant/components/light/blinksticklight.py diff --git a/.coveragerc b/.coveragerc index 95816fa55a9..1cdbb2b4838 100644 --- a/.coveragerc +++ b/.coveragerc @@ -44,6 +44,7 @@ omit = homeassistant/components/keyboard.py homeassistant/components/light/hue.py homeassistant/components/light/limitlessled.py + homeassistant/components/light/blinksticklight.py homeassistant/components/media_player/cast.py homeassistant/components/media_player/denon.py homeassistant/components/media_player/itunes.py diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/light/blinksticklight.py new file mode 100644 index 00000000000..364b5f030c6 --- /dev/null +++ b/homeassistant/components/light/blinksticklight.py @@ -0,0 +1,72 @@ +""" +homeassistant.components.light.blinksticklight +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for Blinkstick lights. +""" + +from blinkstick import blinkstick +import logging + +_LOGGER = logging.getLogger(__name__) + +from homeassistant.components.light import (Light, ATTR_RGB_COLOR) + +REQUIREMENTS = ["blinkstick==1.1.7"] +DEPENDENCIES = [] + +# pylint: disable=unused-argument + + +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add device specified by serial number """ + stick = blinkstick.find_by_serial(config['serial']) + + add_devices_callback([BlinkStickLight(stick, config['name'])]) + + +class BlinkStickLight(Light): + """ Represents a BlinkStick light """ + + def __init__(self, stick, name): + """ Initialise """ + self._stick = stick + self._name = name + self._serial = stick.get_serial() + self._rgb_color = stick.get_color() + + @property + def should_poll(self): + return True + + @property + def name(self): + return self._name + + @property + def rgb_color(self): + """ Read back the color of the light """ + return self._rgb_color + + @property + def is_on(self): + """ Check whether any of the LEDs colors are non-zero """ + return sum(self._rgb_color) > 0 + + def update(self): + """ Read back the device state """ + self._rgb_color = self._stick.get_color() + + def turn_on(self, **kwargs): + """ Turn the device on. """ + if ATTR_RGB_COLOR in kwargs: + self._rgb_color = kwargs[ATTR_RGB_COLOR] + else: + self._rgb_color = [255, 255, 255] + + self._stick.set_color(red=self._rgb_color[0], + green=self._rgb_color[1], + blue=self._rgb_color[2]) + + def turn_off(self, **kwargs): + """ Turn the device off """ + self._stick.turn_off() diff --git a/requirements_all.txt b/requirements_all.txt index 4610100b161..1921f975996 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -137,3 +137,6 @@ SoCo==0.11.1 # PlexAPI (media_player.plex) https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 + +# Blinkstick +blinkstick==1.1.7 From 0fb9e1b16c22662f8b32083040150ca04fc7cc7c Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 6 Oct 2015 21:26:32 +0000 Subject: [PATCH 11/88] Initial commit of snmp device tracker --- .../components/device_tracker/snmp.py | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 homeassistant/components/device_tracker/snmp.py diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py new file mode 100644 index 00000000000..0c4e6bb1ee2 --- /dev/null +++ b/homeassistant/components/device_tracker/snmp.py @@ -0,0 +1,125 @@ +""" +homeassistant.components.device_tracker.demo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Fetch wifi associations through snmp + +device_tracker: + platform: snmp + host: YOUR_WAP_IP + community: SNMP_COMMUNITY + baseoid: BASE_OID + + +Little help with base oids: + Microtik: 1.3.6.1.4.1.14988.1.1.1.2.1.1 (confirmed) + Aruba: 1.3.6.1.4.1.14823.2.3.3.1.2.4.1.2 (untested) + +""" +import logging +from datetime import timedelta +import threading +import binascii + +from homeassistant.const import CONF_HOST, CONF_COMMUNITY, CONF_BASEOID +from homeassistant.helpers import validate_config +from homeassistant.util import Throttle +from homeassistant.components.device_tracker import DOMAIN + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = ['pysnmp'] + +def setup_scanner(hass, config): + """ Setup snmp scanning """ + if not validate_config(config, + {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, + _LOGGER): + return None + + scanner = SnmpScanner(config[DOMAIN]) + + return scanner if scanner.success_init else None + + +class SnmpScanner(object): + """ This class queries any SNMP capable Acces Point for connected devices. """ + def __init__(self, config): + self.host = config[CONF_HOST] + self.community = config[CONF_USERNAME] + self.baseoid = config[CONF_BASEOID] + + self.lock = threading.Lock() + + self.last_results = [] + + # Test the router is accessible + data = self.get_snmp_data() + self.success_init = data is not None + + def scan_devices(self): + """ + Scans for new devices and return a list containing found device IDs. + """ + + self._update_info() + return self.last_results + + def get_device_name(self, device): + """ Returns the name of the given device or None if we don't know. """ + return None + + @Throttle(MIN_TIME_BETWEEN_SCANS) + def _update_info(self): + """ + Ensures the information from the WAP is up to date. + Returns boolean if scanning successful. + """ + if not self.success_init: + return False + + with self.lock: + data = self.get_snmp_data() + if not data: + return False + + self.last_results = data + return True + + def get_snmp_data(self): + """ Fetch mac addresses from WAP via SNMP. """ + devices = [] + + + from pysnmp.entity.rfc3413.oneliner import cmdgen + + oid='1.3.6.1.4.1.14988.1.1.1.2.1.1' + cmdGen = cmdgen.CommandGenerator() + + errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.nextCmd( + cmdgen.CommunityData( self.community ), + cmdgen.UdpTransportTarget( ( self.host , 161) ), + cmdgen.MibVariable( self.baseoid ) + ) + + if errorIndication: + _LOGGER.exception( "SNMPLIB error: {}".format( errorIndication ) ) + return + else: + if errorStatus: + _LOGGER.exception( "SNMP error: {} at {}".format( errorStatus.prettyPrint(), errorIndex and varBindTable[-1][int(errorIndex)-1] or '?' ) ) + return + else: + for varBindTableRow in varBindTable: + for val in varBindTableRow.values(): + devices.append( convertMac( val ) ) + return devices + + def convertMac(octect): + ''' Convert a binary mac address to a string ''' + mac = [] + for x in list(octet): + mac.append(binascii.b2a_hex(x)) + return ":".join(mac) + From df7fbf664e70dc180dbd4999e225dd5a5d688038 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Tue, 6 Oct 2015 21:27:04 +0000 Subject: [PATCH 12/88] Added constants needed for snmp --- homeassistant/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/const.py b/homeassistant/const.py index c256aa921d1..f156a9399d5 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -24,6 +24,8 @@ CONF_USERNAME = "username" CONF_PASSWORD = "password" CONF_API_KEY = "api_key" CONF_ACCESS_TOKEN = "access_token" +CONF_COMMUNITY = "community" +CONF_BASEOID = "baseoid" # #### EVENTS #### EVENT_HOMEASSISTANT_START = "homeassistant_start" From 87599df41b9889e536482fcd72936033692bfecd Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:00:29 -0400 Subject: [PATCH 13/88] add some new media types --- homeassistant/components/media_player/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 143473e2fde..af5dddb8651 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -53,6 +53,9 @@ ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands' MEDIA_TYPE_MUSIC = 'music' MEDIA_TYPE_TVSHOW = 'tvshow' MEDIA_TYPE_VIDEO = 'movie' +MEDIA_TYPE_EPISODE = 'episode' +MEDIA_TYPE_CHANNEL = 'channel' +MEDIA_TYPE_PLAYLIST = 'playlist' SUPPORT_PAUSE = 1 SUPPORT_SEEK = 2 From dcf52332ca18c1d3bb2f12589943d9346378a047 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:09:53 -0400 Subject: [PATCH 14/88] add new properties for Channel or Playlist --- homeassistant/components/media_player/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index af5dddb8651..cd6d367f1f7 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -46,6 +46,8 @@ ATTR_MEDIA_TRACK = 'media_track' ATTR_MEDIA_SERIES_TITLE = 'media_series_title' ATTR_MEDIA_SEASON = 'media_season' ATTR_MEDIA_EPISODE = 'media_episode' +ATTR_MEDIA_CHANNEL = 'media_channel' +ATTR_MEDIA_PLAYLIST = 'media_playlist' ATTR_APP_ID = 'app_id' ATTR_APP_NAME = 'app_name' ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands' @@ -95,6 +97,8 @@ ATTR_TO_PROPERTY = [ ATTR_MEDIA_SERIES_TITLE, ATTR_MEDIA_SEASON, ATTR_MEDIA_EPISODE, + ATTR_MEDIA_CHANNEL, + ATTR_MEDIA_PLAYLIST, ATTR_APP_ID, ATTR_APP_NAME, ATTR_SUPPORTED_MEDIA_COMMANDS, @@ -376,6 +380,16 @@ class MediaPlayerDevice(Entity): """ Episode of current playing media. (TV Show only) """ return None + @property + def media_channel(self): + """ Channel currently playing. """ + return None + + @property + def media_playlist(self): + """ Title of Playlist currently playing. """ + return None + @property def app_id(self): """ ID of the current running app. """ From d454cad5a6a90c02f4b14b4b84eb0ac5dfe7e029 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:10:39 -0400 Subject: [PATCH 15/88] add a play_media function --- homeassistant/components/media_player/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index cd6d367f1f7..3bc542c04c4 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -450,6 +450,10 @@ class MediaPlayerDevice(Entity): """ Plays a YouTube media. """ raise NotImplementedError() + def play_media(self, media_type, media_id): + """ Plays a piece of media. """ + raise NotImplementedError() + # No need to overwrite these. @property def support_pause(self): From e64846e2fdc9bd32f377e104a61cb72aad3d1aaa Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:11:09 -0400 Subject: [PATCH 16/88] add ability to support play_media --- homeassistant/components/media_player/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 3bc542c04c4..7ad38d68c87 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -31,6 +31,7 @@ DISCOVERY_PLATFORMS = { } SERVICE_YOUTUBE_VIDEO = 'play_youtube_video' +SERVICE_PLAY_MEDIA = 'play_media' ATTR_MEDIA_VOLUME_LEVEL = 'volume_level' ATTR_MEDIA_VOLUME_MUTED = 'is_volume_muted' @@ -68,6 +69,7 @@ SUPPORT_NEXT_TRACK = 32 SUPPORT_YOUTUBE = 64 SUPPORT_TURN_ON = 128 SUPPORT_TURN_OFF = 256 +SUPPORT_PLAY_MEDIA = 512 YOUTUBE_COVER_URL_FORMAT = 'https://img.youtube.com/vi/{}/1.jpg' @@ -490,6 +492,11 @@ class MediaPlayerDevice(Entity): """ Boolean if YouTube is supported. """ return bool(self.supported_media_commands & SUPPORT_YOUTUBE) + @property + def support_play_media(self): + """ Boolean if play media command supported. """ + return bool(self.supported_media_commands & SUPPORT_PLAY_MEDIA) + def volume_up(self): """ volume_up media player. """ if self.volume_level < 1: From d17174d43db2fcf6a025626d39b039d060dcba00 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:11:21 -0400 Subject: [PATCH 17/88] play_media as a service --- .../components/media_player/__init__.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 7ad38d68c87..fec4515756c 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -83,6 +83,7 @@ SERVICE_TO_METHOD = { SERVICE_MEDIA_PAUSE: 'media_pause', SERVICE_MEDIA_NEXT_TRACK: 'media_next_track', SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track', + SERVICE_PLAY_MEDIA: 'play_media', } ATTR_TO_PROPERTY = [ @@ -284,6 +285,23 @@ def setup(hass, config): if player.should_poll: player.update_ha_state(True) + def play_media_service(service): + """ Plays specified media_id on the media player. """ + media_type = service.data['media_type'] + media_id = service.data['media_id'] + + if media_type is None: + return + + if media_id is None: + return + + for player in component.extract_from_service(service): + player.play_media(media_type, media_id) + + if player.should_poll: + player.update_ha_state(True) + hass.services.register( DOMAIN, "start_fireplace", lambda service: play_youtube_video_service(service, "eyU3bRy2x44"), @@ -298,6 +316,10 @@ def setup(hass, config): DOMAIN, SERVICE_YOUTUBE_VIDEO, play_youtube_video_service, descriptions.get(SERVICE_YOUTUBE_VIDEO)) + hass.services.register( + DOMAIN, SERVICE_PLAY_MEDIA, play_media_service, + descriptions.get(SERVICE_PLAY_MEDIA)) + return True From 4be33bb15b4149110c74b491ae340e4809d1cfd4 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:12:20 -0400 Subject: [PATCH 18/88] add a way to play a playlist with the client --- homeassistant/components/media_player/itunes.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index ecbb144e033..d3c992d267b 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -118,6 +118,16 @@ class Itunes(object): """ Skips back and returns the current state. """ return self._command('previous') + def play_playlist(self, playlist_id_or_name): + """ Sets a playlist to be current and returns the current state. """ + response = self._request('GET', '/playlists') + playlists = response.get('playlists', []) + + found_playlists = [playlist for playlist in playlists if playlist["name"] == playlist_id_or_name or playlist["id"] == playlist_id_or_name] + if len(found_playlists) > 0: + playlist = found_playlists[0] + return self._request('PUT', '/playlists/' + playlist['id'] + '/play') + def artwork_url(self): """ Returns a URL of the current track's album art. """ return self._base_url + '/artwork' From e84ddb036f63dc63d99ffa4ae2e4e2aa3c3009bb Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:12:30 -0400 Subject: [PATCH 19/88] return what playlist is playing --- homeassistant/components/media_player/itunes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index d3c992d267b..a94dc0d1585 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -304,6 +304,11 @@ class ItunesDevice(MediaPlayerDevice): """ Album of current playing media. (Music track only) """ return self.current_album + @property + def media_playlist(self): + """ Title of the currently playing playlist. """ + return self.current_playlist + @property def supported_media_commands(self): """ Flags of media commands that are supported. """ From 1b22f71a191372b8f3a8bd970154e52e781c0935 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:12:41 -0400 Subject: [PATCH 20/88] implement play_media --- homeassistant/components/media_player/itunes.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index a94dc0d1585..22f987e0845 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -344,6 +344,11 @@ class ItunesDevice(MediaPlayerDevice): response = self.client.previous() self.update_state(response) + def play_media(self, media_type:None, media_id:None): + """ play_media media player. """ + response = self.client.play_playlist(media_id) + self.update_state(response) + class AirPlayDevice(MediaPlayerDevice): """ Represents an AirPlay device via an iTunes-API instance. """ From bdb42bf4a2f12dc7b0b3dcaf23302d78534ded5a Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:12:48 -0400 Subject: [PATCH 21/88] support play_media --- homeassistant/components/media_player/itunes.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 22f987e0845..82038c3134a 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -35,9 +35,10 @@ URL of your running version of iTunes-API. Example: http://192.168.1.50:8181 import logging from homeassistant.components.media_player import ( - MediaPlayerDevice, MEDIA_TYPE_MUSIC, SUPPORT_PAUSE, SUPPORT_SEEK, - SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, SUPPORT_PREVIOUS_TRACK, - SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON, SUPPORT_TURN_OFF, + MediaPlayerDevice, MEDIA_TYPE_MUSIC, MEDIA_TYPE_PLAYLIST, SUPPORT_PAUSE, + SUPPORT_SEEK, SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, + SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, SUPPORT_TURN_ON, + SUPPORT_TURN_OFF, SUPPORT_PLAY_MEDIA, ATTR_ENTITY_PICTURE, ATTR_SUPPORTED_MEDIA_COMMANDS) from homeassistant.const import ( STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_ON) @@ -47,7 +48,8 @@ import requests _LOGGER = logging.getLogger(__name__) SUPPORT_ITUNES = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \ - SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK | \ + SUPPORT_PLAY_MEDIA SUPPORT_AIRPLAY = SUPPORT_VOLUME_SET | SUPPORT_TURN_ON | SUPPORT_TURN_OFF From 9012ba53fdc3ac129db9a48a4a2b0bca02471387 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Tue, 6 Oct 2015 23:18:24 -0400 Subject: [PATCH 22/88] add play_media service to tests --- tests/components/test_media_player.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/components/test_media_player.py b/tests/components/test_media_player.py index 28d39206c47..7c8dc60fff2 100644 --- a/tests/components/test_media_player.py +++ b/tests/components/test_media_player.py @@ -12,7 +12,8 @@ from homeassistant.const import ( STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, - SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, ATTR_ENTITY_ID) + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_PLAY_MEDIA, + ATTR_ENTITY_ID) import homeassistant.components.media_player as media_player from tests.common import mock_service @@ -52,6 +53,7 @@ class TestMediaPlayer(unittest.TestCase): SERVICE_MEDIA_PAUSE: media_player.media_pause, SERVICE_MEDIA_NEXT_TRACK: media_player.media_next_track, SERVICE_MEDIA_PREVIOUS_TRACK: media_player.media_previous_track + SERVICE_PLAY_MEDIA: media_player.play_media } for service_name, service_method in services.items(): From ad549be353f43809a2d01be7eff69c1ba9c9c8f0 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 00:39:38 -0400 Subject: [PATCH 23/88] support play_media for state restoration (for scenes) --- homeassistant/helpers/state.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 909b86a67ed..db33eda960d 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -13,6 +13,8 @@ from homeassistant.const import ( SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, STATE_PLAYING, STATE_PAUSED, ATTR_ENTITY_ID) +from homeassistant.components.media_player import (SERVICE_PLAY_MEDIA) + _LOGGER = logging.getLogger(__name__) @@ -61,6 +63,8 @@ def reproduce_state(hass, states, blocking=False): service = SERVICE_MEDIA_PAUSE elif state.domain == 'media_player' and state.state == STATE_PLAYING: service = SERVICE_MEDIA_PLAY + elif state.domain == 'media_player' and state.attributes and state.attributes['media_type'] and state.attributes['media_id']: + service = SERVICE_PLAY_MEDIA elif state.state == STATE_ON: service = SERVICE_TURN_ON elif state.state == STATE_OFF: From 6afb846d04f8d7a43d495d8415ed4f8dc8ba3bbe Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 00:56:14 -0400 Subject: [PATCH 24/88] avoid key errors --- homeassistant/components/media_player/__init__.py | 4 ++-- homeassistant/helpers/state.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index fec4515756c..1b65ce57912 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -287,8 +287,8 @@ def setup(hass, config): def play_media_service(service): """ Plays specified media_id on the media player. """ - media_type = service.data['media_type'] - media_id = service.data['media_id'] + media_type = service.data.get('media_type') + media_id = service.data.get('media_id') if media_type is None: return diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index db33eda960d..bf3c5f05d7a 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -63,7 +63,8 @@ def reproduce_state(hass, states, blocking=False): service = SERVICE_MEDIA_PAUSE elif state.domain == 'media_player' and state.state == STATE_PLAYING: service = SERVICE_MEDIA_PLAY - elif state.domain == 'media_player' and state.attributes and state.attributes['media_type'] and state.attributes['media_id']: + elif state.domain == 'media_player' and state.attributes and + 'media_type' in state.attributes and 'media_id' in state.attributes service = SERVICE_PLAY_MEDIA elif state.state == STATE_ON: service = SERVICE_TURN_ON From c4f8017a3fefac7999117947d5b5cb0d29249846 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 00:56:36 -0400 Subject: [PATCH 25/88] silence warning --- homeassistant/components/media_player/cast.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/media_player/cast.py b/homeassistant/components/media_player/cast.py index 61223446e5f..6f622c9e0cc 100644 --- a/homeassistant/components/media_player/cast.py +++ b/homeassistant/components/media_player/cast.py @@ -90,6 +90,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class CastDevice(MediaPlayerDevice): """ Represents a Cast device on the network. """ + # pylint: disable=abstract-method # pylint: disable=too-many-public-methods def __init__(self, host): From 6c4b2fd63854fa88a307aa24628b4fa54890951c Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:01:25 -0400 Subject: [PATCH 26/88] derp --- homeassistant/helpers/state.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index bf3c5f05d7a..ac80da98555 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -63,8 +63,8 @@ def reproduce_state(hass, states, blocking=False): service = SERVICE_MEDIA_PAUSE elif state.domain == 'media_player' and state.state == STATE_PLAYING: service = SERVICE_MEDIA_PLAY - elif state.domain == 'media_player' and state.attributes and - 'media_type' in state.attributes and 'media_id' in state.attributes + elif state.domain == 'media_player' and state.attributes and \ + 'media_type' in state.attributes and 'media_id' in state.attributes: service = SERVICE_PLAY_MEDIA elif state.state == STATE_ON: service = SERVICE_TURN_ON From bb997deb85d5261fb07f46d2bf975db44a9d8499 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:06:27 -0400 Subject: [PATCH 27/88] COMMMMAAAAAAAAAAAA --- tests/components/test_media_player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/test_media_player.py b/tests/components/test_media_player.py index 7c8dc60fff2..5d7da542eee 100644 --- a/tests/components/test_media_player.py +++ b/tests/components/test_media_player.py @@ -52,7 +52,7 @@ class TestMediaPlayer(unittest.TestCase): SERVICE_MEDIA_PLAY: media_player.media_play, SERVICE_MEDIA_PAUSE: media_player.media_pause, SERVICE_MEDIA_NEXT_TRACK: media_player.media_next_track, - SERVICE_MEDIA_PREVIOUS_TRACK: media_player.media_previous_track + SERVICE_MEDIA_PREVIOUS_TRACK: media_player.media_previous_track, SERVICE_PLAY_MEDIA: media_player.play_media } From 25a690691bdd8c143eb80e232ca50482b4081420 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:11:19 -0400 Subject: [PATCH 28/88] import it from the right place --- tests/components/test_media_player.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/components/test_media_player.py b/tests/components/test_media_player.py index 5d7da542eee..f1c56e5a0b0 100644 --- a/tests/components/test_media_player.py +++ b/tests/components/test_media_player.py @@ -12,8 +12,8 @@ from homeassistant.const import ( STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, - SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, SERVICE_PLAY_MEDIA, - ATTR_ENTITY_ID) + SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, ATTR_ENTITY_ID) +from homeassistant.components.media_player import (SERVICE_PLAY_MEDIA) import homeassistant.components.media_player as media_player from tests.common import mock_service From 1c4ac6017dd06ee4194ce521f51c9ba35a64f1a9 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:21:41 -0400 Subject: [PATCH 29/88] fix typo while were in here --- tests/components/test_media_player.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/test_media_player.py b/tests/components/test_media_player.py index f1c56e5a0b0..7d7ddb33ac0 100644 --- a/tests/components/test_media_player.py +++ b/tests/components/test_media_player.py @@ -41,7 +41,7 @@ class TestMediaPlayer(unittest.TestCase): def test_services(self): """ - Test if the call service methods conver to correct service calls. + Test if the call service methods convert to correct service calls. """ services = { SERVICE_TURN_ON: media_player.turn_on, From faa3e98921f6b2f1a04d3ffd83f128c4594bf3b3 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:28:58 -0400 Subject: [PATCH 30/88] module level play_media --- homeassistant/components/media_player/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 1b65ce57912..9fcb3d5523d 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -189,6 +189,15 @@ def media_previous_track(hass, entity_id=None): data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} hass.services.call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data) +def play_media(hass, media_type, media_id, entity_id=None): + """ Send the media player the command for playing media. """ + data = {"media_type": media_type, "media_id": media_id} + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.services.call(DOMAIN, SERVICE_PLAY_MEDIA, data) + def setup(hass, config): """ Track states and offer events for media_players. """ From dbcc3a76ea58e345e896c9707d5a72db1a9cd9bb Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:29:55 -0400 Subject: [PATCH 31/88] style --- homeassistant/helpers/state.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index ac80da98555..2f688647a5a 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -64,7 +64,8 @@ def reproduce_state(hass, states, blocking=False): elif state.domain == 'media_player' and state.state == STATE_PLAYING: service = SERVICE_MEDIA_PLAY elif state.domain == 'media_player' and state.attributes and \ - 'media_type' in state.attributes and 'media_id' in state.attributes: + 'media_type' in state.attributes and \ + 'media_id' in state.attributes: service = SERVICE_PLAY_MEDIA elif state.state == STATE_ON: service = SERVICE_TURN_ON From c83324d4cfa806fbe721c1fcb915702fe2fe3130 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:34:37 -0400 Subject: [PATCH 32/88] nope --- tests/components/test_media_player.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/components/test_media_player.py b/tests/components/test_media_player.py index 7d7ddb33ac0..211626ea3fb 100644 --- a/tests/components/test_media_player.py +++ b/tests/components/test_media_player.py @@ -13,7 +13,6 @@ from homeassistant.const import ( SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, SERVICE_VOLUME_DOWN, SERVICE_MEDIA_PLAY_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_NEXT_TRACK, SERVICE_MEDIA_PREVIOUS_TRACK, ATTR_ENTITY_ID) -from homeassistant.components.media_player import (SERVICE_PLAY_MEDIA) import homeassistant.components.media_player as media_player from tests.common import mock_service @@ -52,8 +51,7 @@ class TestMediaPlayer(unittest.TestCase): SERVICE_MEDIA_PLAY: media_player.media_play, SERVICE_MEDIA_PAUSE: media_player.media_pause, SERVICE_MEDIA_NEXT_TRACK: media_player.media_next_track, - SERVICE_MEDIA_PREVIOUS_TRACK: media_player.media_previous_track, - SERVICE_PLAY_MEDIA: media_player.play_media + SERVICE_MEDIA_PREVIOUS_TRACK: media_player.media_previous_track } for service_name, service_method in services.items(): From 26939ce554a270ddd4d15c630b67a60634f2e5b2 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:37:40 -0400 Subject: [PATCH 33/88] style --- homeassistant/components/media_player/__init__.py | 1 + homeassistant/helpers/state.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 9fcb3d5523d..294fccbb1f5 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -189,6 +189,7 @@ def media_previous_track(hass, entity_id=None): data = {ATTR_ENTITY_ID: entity_id} if entity_id else {} hass.services.call(DOMAIN, SERVICE_MEDIA_PREVIOUS_TRACK, data) + def play_media(hass, media_type, media_id, entity_id=None): """ Send the media player the command for playing media. """ data = {"media_type": media_type, "media_id": media_id} diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 2f688647a5a..6526fe2d90b 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -65,7 +65,7 @@ def reproduce_state(hass, states, blocking=False): service = SERVICE_MEDIA_PLAY elif state.domain == 'media_player' and state.attributes and \ 'media_type' in state.attributes and \ - 'media_id' in state.attributes: + 'media_id' in state.attributes: service = SERVICE_PLAY_MEDIA elif state.state == STATE_ON: service = SERVICE_TURN_ON From 9a3c76c2636d93e8f350f3f0bcd5747e3782a1d6 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:41:57 -0400 Subject: [PATCH 34/88] these are required --- homeassistant/components/media_player/itunes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 82038c3134a..022dccdbb25 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -346,7 +346,7 @@ class ItunesDevice(MediaPlayerDevice): response = self.client.previous() self.update_state(response) - def play_media(self, media_type:None, media_id:None): + def play_media(self, media_type, media_id): """ play_media media player. """ response = self.client.play_playlist(media_id) self.update_state(response) From 85338887b48ae78f42ec273030c1a03f2a7e9cec Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:42:50 -0400 Subject: [PATCH 35/88] wrap it --- homeassistant/components/media_player/itunes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 022dccdbb25..85b5e4dedc6 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -125,7 +125,10 @@ class Itunes(object): response = self._request('GET', '/playlists') playlists = response.get('playlists', []) - found_playlists = [playlist for playlist in playlists if playlist["name"] == playlist_id_or_name or playlist["id"] == playlist_id_or_name] + found_playlists = [playlist for playlist in \ + playlists if playlist["name"] == playlist_id_or_name or \ + playlist["id"] == playlist_id_or_name] + if len(found_playlists) > 0: playlist = found_playlists[0] return self._request('PUT', '/playlists/' + playlist['id'] + '/play') From c2fe97777833bb25aa46d891023017d0328ebc32 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 01:55:15 -0400 Subject: [PATCH 36/88] style --- homeassistant/components/media_player/itunes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 85b5e4dedc6..f0049e1ff18 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -125,13 +125,14 @@ class Itunes(object): response = self._request('GET', '/playlists') playlists = response.get('playlists', []) - found_playlists = [playlist for playlist in \ - playlists if playlist["name"] == playlist_id_or_name or \ + found_playlists = [playlist for playlist in + playlists if playlist["name"] == playlist_id_or_name or playlist["id"] == playlist_id_or_name] - + if len(found_playlists) > 0: playlist = found_playlists[0] - return self._request('PUT', '/playlists/' + playlist['id'] + '/play') + path = '/playlists/' + playlist['id'] + '/play' + return self._request('PUT', path) def artwork_url(self): """ Returns a URL of the current track's album art. """ @@ -351,8 +352,9 @@ class ItunesDevice(MediaPlayerDevice): def play_media(self, media_type, media_id): """ play_media media player. """ - response = self.client.play_playlist(media_id) - self.update_state(response) + if media_type == MEDIA_TYPE_PLAYLIST: + response = self.client.play_playlist(media_id) + self.update_state(response) class AirPlayDevice(MediaPlayerDevice): From 3b58e8628dee4887054ba29b614ec3a6957a3ac0 Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 02:02:25 -0400 Subject: [PATCH 37/88] style --- homeassistant/components/media_player/itunes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index f0049e1ff18..7bf5e5eefc6 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -125,8 +125,9 @@ class Itunes(object): response = self._request('GET', '/playlists') playlists = response.get('playlists', []) - found_playlists = [playlist for playlist in - playlists if playlist["name"] == playlist_id_or_name or + found_playlists = \ + [playlist for playlist in playlists if + playlist["name"] == playlist_id_or_name or playlist["id"] == playlist_id_or_name] if len(found_playlists) > 0: From ffbaf0cd5a3069989952c21d014088fd76ad9dbe Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Wed, 7 Oct 2015 02:13:13 -0400 Subject: [PATCH 38/88] simpler --- homeassistant/components/media_player/itunes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/itunes.py b/homeassistant/components/media_player/itunes.py index 7bf5e5eefc6..70def719146 100644 --- a/homeassistant/components/media_player/itunes.py +++ b/homeassistant/components/media_player/itunes.py @@ -127,8 +127,7 @@ class Itunes(object): found_playlists = \ [playlist for playlist in playlists if - playlist["name"] == playlist_id_or_name or - playlist["id"] == playlist_id_or_name] + (playlist_id_or_name in [playlist["name"], playlist["id"]])] if len(found_playlists) > 0: playlist = found_playlists[0] From 9d4aa7e519236f4da326a92f00114dc842d52bef Mon Sep 17 00:00:00 2001 From: Alan Bowman Date: Wed, 7 Oct 2015 13:58:21 +0100 Subject: [PATCH 39/88] Update tests for RGB color support --- tests/components/test_light.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/components/test_light.py b/tests/components/test_light.py index 515b79b6fc0..156ba51e59a 100644 --- a/tests/components/test_light.py +++ b/tests/components/test_light.py @@ -152,9 +152,13 @@ class TestLight(unittest.TestCase): data) method, data = dev2.last_call('turn_on') - self.assertEqual( - {light.ATTR_XY_COLOR: color_util.color_RGB_to_xy(255, 255, 255)}, - data) + self.assertEquals( + data[light.ATTR_XY_COLOR], + color_util.color_RGB_to_xy(255, 255, 255)) + + self.assertEquals( + data[light.ATTR_RGB_COLOR], + [255, 255, 255]) method, data = dev3.last_call('turn_on') self.assertEqual({light.ATTR_XY_COLOR: [.4, .6]}, data) From a58382e7630d4bc39f52a1c4f501c922f1421795 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 16:57:01 +0000 Subject: [PATCH 40/88] Fixed b/octet to mac adress conversion --- .../components/device_tracker/snmp.py | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 0c4e6bb1ee2..81d9d1750c1 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -26,19 +26,18 @@ from homeassistant.helpers import validate_config from homeassistant.util import Throttle from homeassistant.components.device_tracker import DOMAIN -MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pysnmp'] -def setup_scanner(hass, config): +def setup_scanner(hass, config, see): """ Setup snmp scanning """ - if not validate_config(config, - {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, - _LOGGER): - return None + #if not validate_config(config, {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, _LOGGER): + # return None - scanner = SnmpScanner(config[DOMAIN]) + #scanner = SnmpScanner(config[DOMAIN]) + scanner = SnmpScanner(config) return scanner if scanner.success_init else None @@ -47,12 +46,12 @@ class SnmpScanner(object): """ This class queries any SNMP capable Acces Point for connected devices. """ def __init__(self, config): self.host = config[CONF_HOST] - self.community = config[CONF_USERNAME] + self.community = config[CONF_COMMUNITY] self.baseoid = config[CONF_BASEOID] self.lock = threading.Lock() - self.last_results = [] + self.last_results = {} # Test the router is accessible data = self.get_snmp_data() @@ -64,7 +63,7 @@ class SnmpScanner(object): """ self._update_info() - return self.last_results + return [client['mac'] for client in self.last_results] def get_device_name(self, device): """ Returns the name of the given device or None if we don't know. """ @@ -89,37 +88,26 @@ class SnmpScanner(object): def get_snmp_data(self): """ Fetch mac addresses from WAP via SNMP. """ - devices = [] - - from pysnmp.entity.rfc3413.oneliner import cmdgen - oid='1.3.6.1.4.1.14988.1.1.1.2.1.1' - cmdGen = cmdgen.CommandGenerator() + devices = {} + cmdGen = cmdgen.CommandGenerator() errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.nextCmd( cmdgen.CommunityData( self.community ), cmdgen.UdpTransportTarget( ( self.host , 161) ), cmdgen.MibVariable( self.baseoid ) ) - if errorIndication: _LOGGER.exception( "SNMPLIB error: {}".format( errorIndication ) ) return - else: - if errorStatus: - _LOGGER.exception( "SNMP error: {} at {}".format( errorStatus.prettyPrint(), errorIndex and varBindTable[-1][int(errorIndex)-1] or '?' ) ) - return - else: - for varBindTableRow in varBindTable: - for val in varBindTableRow.values(): - devices.append( convertMac( val ) ) + if errorStatus: + _LOGGER.exception( "SNMP error: {} at {}".format( errorStatus.prettyPrint(), errorIndex and varBindTable[-1][int(errorIndex)-1] or '?' ) ) + return + for varBindTableRow in varBindTable: + for key,val in varBindTableRow: + mac = binascii.hexlify( val.asOctets() ).decode('utf-8') + mac = ':'.join( [ mac[i:i+2] for i in range( 0, len(mac), 2 ) ] ) + devices[mac] = { 'mac' : mac } return devices - def convertMac(octect): - ''' Convert a binary mac address to a string ''' - mac = [] - for x in list(octet): - mac.append(binascii.b2a_hex(x)) - return ":".join(mac) - From 11fc521e60a0618323e06bc351c86844286c8019 Mon Sep 17 00:00:00 2001 From: badele Date: Wed, 7 Oct 2015 19:04:03 +0200 Subject: [PATCH 41/88] Replace REQUIREMENTS by DEPENDENCIES variable --- homeassistant/components/light/rfxtrx.py | 3 +-- homeassistant/components/rfxtrx.py | 3 +-- homeassistant/components/sensor/rfxtrx.py | 3 +-- homeassistant/components/switch/rfxtrx.py | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 7c8bdb7a93a..ceb6fe97f8c 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -29,8 +29,7 @@ import RFXtrx as rfxtrxmod from homeassistant.components.light import Light from homeassistant.util import slugify -REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + - 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] +DEPENDENCIES = ['rfxtrx'] DOMAIN = "rfxtrx" _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 0c3f377150b..f2b962c7ba9 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -20,8 +20,7 @@ import logging from homeassistant.util import slugify DEPENDENCIES = [] -REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + - 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip#RFXtrx==0.2'] DOMAIN = "rfxtrx" CONF_DEVICE = 'device' diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index d0514c93a51..b09d9d4a09c 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -28,8 +28,7 @@ import homeassistant.components.rfxtrx as rfxtrx from RFXtrx import SensorEvent from homeassistant.util import slugify -REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + - 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] +DEPENDENCIES = ['rfxtrx'] DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 7eefc6fb229..29120c7d93c 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -29,8 +29,7 @@ from RFXtrx import LightingDevice from homeassistant.components.switch import SwitchDevice from homeassistant.util import slugify -REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/' + - 'ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15'] +DEPENDENCIES = ['rfxtrx'] DOMAIN = "rfxtrx" _LOGGER = logging.getLogger(__name__) From 496e4cf78426328d72a5083ddddcd4036538f35b Mon Sep 17 00:00:00 2001 From: badele Date: Wed, 7 Oct 2015 19:07:19 +0200 Subject: [PATCH 42/88] Exclude rfxtrx component files --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.coveragerc b/.coveragerc index 7ebab01d399..3b464e20269 100644 --- a/.coveragerc +++ b/.coveragerc @@ -69,7 +69,10 @@ omit = homeassistant/components/sensor/glances.py homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/openweathermap.py + homeassistant/components/rfxtrx.py + homeassistant/components/light/rfxtrx.py homeassistant/components/sensor/rfxtrx.py + homeassistant/components/switch/rfxtrx.py homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py homeassistant/components/sensor/swiss_public_transport.py From 46f5ef54a19c0a02fef7ddd02bfad26aa3701b74 Mon Sep 17 00:00:00 2001 From: badele Date: Wed, 7 Oct 2015 19:15:50 +0200 Subject: [PATCH 43/88] Refactoring test instance type --- homeassistant/components/light/rfxtrx.py | 52 ++++++++++++----------- homeassistant/components/switch/rfxtrx.py | 50 +++++++++++----------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index ceb6fe97f8c..58ba3759f94 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -53,34 +53,36 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): def light_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ - if isinstance(event.device, rfxtrxmod.LightingDevice): - entity_id = slugify(event.device.id_string.lower()) + if not isinstance(event.device, rfxtrxmod.LightingDevice): + return - # Add entity if not exist and the automatic_add is True - if entity_id not in rfxtrx.RFX_DEVICES: - automatic_add = config.get('automatic_add', False) - if automatic_add: - _LOGGER.info( - "Automatic add %s rfxtrx.light (Class: %s Sub: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype - ) - pkt_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%s : %s" % (entity_id, pkt_id) - new_light = RfxtrxLight(entity_name, event, False) - rfxtrx.RFX_DEVICES[entity_id] = new_light - add_devices_callback([new_light]) + # Add entity if not exist and the automatic_add is True + entity_id = slugify(event.device.id_string.lower()) + if entity_id not in rfxtrx.RFX_DEVICES: + automatic_add = config.get('automatic_add', False) + if automatic_add: + _LOGGER.info( + "Automatic add %s rfxtrx.light (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) + new_light = RfxtrxLight(entity_name, event, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + add_devices_callback([new_light]) - # Check if entity exists (previous automatic added) - if entity_id in rfxtrx.RFX_DEVICES: - if event.values['Command'] == 'On'\ - or event.values['Command'] == 'Off': - if event.values['Command'] == 'On': - rfxtrx.RFX_DEVICES[entity_id].turn_on() - else: - rfxtrx.RFX_DEVICES[entity_id].turn_off() + # Check if entity exists (previous automatic added) + if entity_id in rfxtrx.RFX_DEVICES: + if event.values['Command'] == 'On'\ + or event.values['Command'] == 'Off': + if event.values['Command'] == 'On': + rfxtrx.RFX_DEVICES[entity_id].turn_on() + else: + rfxtrx.RFX_DEVICES[entity_id].turn_off() + # Subscribe to main rfxtrx events if light_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(light_update) diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 29120c7d93c..d97759d4e86 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -55,33 +55,35 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): def switch_update(event): """ Callback for sensor updates from the RFXtrx gateway. """ if isinstance(event.device, LightingDevice): - entity_id = slugify(event.device.id_string.lower()) + return - # Add entity if not exist and the automatic_add is True - if entity_id not in rfxtrx.RFX_DEVICES: - automatic_add = config.get('automatic_add', False) - if automatic_add: - _LOGGER.info( - "Automatic add %s rfxtrx.switch (Class: %s Sub: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype - ) - pkt_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%s : %s" % (entity_id, pkt_id) - new_switch = RfxtrxSwitch(entity_name, event, False) - rfxtrx.RFX_DEVICES[entity_id] = new_switch - add_devices_callback([new_switch]) + # Add entity if not exist and the automatic_add is True + entity_id = slugify(event.device.id_string.lower()) + if entity_id not in rfxtrx.RFX_DEVICES: + automatic_add = config.get('automatic_add', False) + if automatic_add: + _LOGGER.info( + "Automatic add %s rfxtrx.switch (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) + new_switch = RfxtrxSwitch(entity_name, event, False) + rfxtrx.RFX_DEVICES[entity_id] = new_switch + add_devices_callback([new_switch]) - # Check if entity exists (previous automatic added) - if entity_id in rfxtrx.RFX_DEVICES: - if event.values['Command'] == 'On'\ - or event.values['Command'] == 'Off': - if event.values['Command'] == 'On': - rfxtrx.RFX_DEVICES[entity_id].turn_on() - else: - rfxtrx.RFX_DEVICES[entity_id].turn_off() + # Check if entity exists (previous automatic added) + if entity_id in rfxtrx.RFX_DEVICES: + if event.values['Command'] == 'On'\ + or event.values['Command'] == 'Off': + if event.values['Command'] == 'On': + rfxtrx.RFX_DEVICES[entity_id].turn_on() + else: + rfxtrx.RFX_DEVICES[entity_id].turn_off() + # Subscribe to main rfxtrx events if switch_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: rfxtrx.RECEIVED_EVT_SUBSCRIBERS.append(switch_update) From a5dae78155d8a6309fdd2a6906b0c82263911333 Mon Sep 17 00:00:00 2001 From: badele Date: Wed, 7 Oct 2015 19:57:40 +0200 Subject: [PATCH 44/88] Refactoring the rfxtrx components --- homeassistant/components/light/rfxtrx.py | 33 ++++++++++++----------- homeassistant/components/rfxtrx.py | 15 +++++++---- homeassistant/components/switch/rfxtrx.py | 29 ++++++++++---------- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index 58ba3759f94..f76d9f7ed5b 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -31,7 +31,6 @@ from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] -DOMAIN = "rfxtrx" _LOGGER = logging.getLogger(__name__) @@ -60,20 +59,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): entity_id = slugify(event.device.id_string.lower()) if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) - if automatic_add: - _LOGGER.info( - "Automatic add %s rfxtrx.light (Class: %s Sub: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype - ) - pkt_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%s : %s" % (entity_id, pkt_id) - new_light = RfxtrxLight(entity_name, event, False) - rfxtrx.RFX_DEVICES[entity_id] = new_light - add_devices_callback([new_light]) + if not automatic_add: + return - # Check if entity exists (previous automatic added) + _LOGGER.info( + "Automatic add %s rfxtrx.light (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) + new_light = RfxtrxLight(entity_name, event, False) + rfxtrx.RFX_DEVICES[entity_id] = new_light + add_devices_callback([new_light]) + + # Check if entity exists or previously added automatically if entity_id in rfxtrx.RFX_DEVICES: if event.values['Command'] == 'On'\ or event.values['Command'] == 'Off': @@ -112,7 +113,7 @@ class RfxtrxLight(Light): def turn_on(self, **kwargs): """ Turn the device on. """ - if self._event: + if hasattr(self, '_event') and self._event: self._event.device.send_on(rfxtrx.RFXOBJECT.transport) self._state = True @@ -121,7 +122,7 @@ class RfxtrxLight(Light): def turn_off(self, **kwargs): """ Turn the device off. """ - if self._event: + if hasattr(self, '_event') and self._event: self._event.device.send_off(rfxtrx.RFXOBJECT.transport) self._state = False diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index f2b962c7ba9..79378b85e78 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -20,7 +20,8 @@ import logging from homeassistant.util import slugify DEPENDENCIES = [] -REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip#RFXtrx==0.2'] +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip' + + '#RFXtrx==0.2'] DOMAIN = "rfxtrx" CONF_DEVICE = 'device' @@ -59,11 +60,15 @@ def setup(hass, config): # Init the rfxtrx module global RFXOBJECT + if CONF_DEVICE not in config[DOMAIN]: + _LOGGER.exception( + "can found device parameter in %s YAML configuration section", + DOMAIN + ) + return False + device = config[DOMAIN][CONF_DEVICE] - try: - debug = config[DOMAIN][CONF_DEBUG] - except KeyError: - debug = False + debug = config[DOMAIN].get(CONF_DEBUG, False) RFXOBJECT = rfxtrxmod.Core(device, handle_receive, debug=debug) diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index d97759d4e86..98963beb769 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -31,7 +31,6 @@ from homeassistant.util import slugify DEPENDENCIES = ['rfxtrx'] -DOMAIN = "rfxtrx" _LOGGER = logging.getLogger(__name__) @@ -61,20 +60,22 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): entity_id = slugify(event.device.id_string.lower()) if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', False) - if automatic_add: - _LOGGER.info( - "Automatic add %s rfxtrx.switch (Class: %s Sub: %s)", - entity_id, - event.device.__class__.__name__, - event.device.subtype - ) - pkt_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%s : %s" % (entity_id, pkt_id) - new_switch = RfxtrxSwitch(entity_name, event, False) - rfxtrx.RFX_DEVICES[entity_id] = new_switch - add_devices_callback([new_switch]) + if not automatic_add: + return - # Check if entity exists (previous automatic added) + _LOGGER.info( + "Automatic add %s rfxtrx.switch (Class: %s Sub: %s)", + entity_id, + event.device.__class__.__name__, + event.device.subtype + ) + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (entity_id, pkt_id) + new_switch = RfxtrxSwitch(entity_name, event, False) + rfxtrx.RFX_DEVICES[entity_id] = new_switch + add_devices_callback([new_switch]) + + # Check if entity exists or previously added automatically if entity_id in rfxtrx.RFX_DEVICES: if event.values['Command'] == 'On'\ or event.values['Command'] == 'Off': From 17865c78c4474da0d0f0f9a33baa3da327802054 Mon Sep 17 00:00:00 2001 From: Carlo Costanzo Date: Wed, 7 Oct 2015 17:02:07 -0400 Subject: [PATCH 45/88] Added # comment for Sensor Comments for unique sensor labels. --- config/configuration.yaml.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/configuration.yaml.example b/config/configuration.yaml.example index ae9959a193d..fae945b05e4 100644 --- a/config/configuration.yaml.example +++ b/config/configuration.yaml.example @@ -134,6 +134,9 @@ automation: service: light.turn_off entity_id: group.all_lights +# Sensors need to be added into the configuration.yaml as sensor:, sensor 2:, sensor 3:, etc. +# Each sensor label should be unique or your sensors might not load correctly. + sensor: platform: systemmonitor resources: From 469f35d25f0012e8a99712aee67067f091b0a7bc Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 21:04:34 +0000 Subject: [PATCH 46/88] various fixes, initial working version --- .../components/device_tracker/snmp.py | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 81d9d1750c1..f89c3cb8d5a 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -1,8 +1,12 @@ """ -homeassistant.components.device_tracker.demo +homeassistant.components.device_tracker.snmp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Device tracker platform that supports fetching WiFi assiciations +through SNMP -Fetch wifi associations through snmp +This device tracker needs SNMP to be enabled on the WRT or WAP + +Configuration: device_tracker: platform: snmp @@ -10,10 +14,22 @@ device_tracker: community: SNMP_COMMUNITY baseoid: BASE_OID +Variables: + Host + *required + The IP address of the router, e.g. 192.168.1.1 -Little help with base oids: - Microtik: 1.3.6.1.4.1.14988.1.1.1.2.1.1 (confirmed) - Aruba: 1.3.6.1.4.1.14823.2.3.3.1.2.4.1.2 (untested) + community + *Required + The SNMP community. Read-only is fine + + baseoid + *Required + The OID at which WiFi associations can be found + + Little help with base oids: + Microtik: 1.3.6.1.4.1.14988.1.1.1.2.1.1 (confirmed) + Aruba: 1.3.6.1.4.1.14823.2.3.3.1.2.4.1.2 (untested) """ import logging @@ -26,18 +42,19 @@ from homeassistant.helpers import validate_config from homeassistant.util import Throttle from homeassistant.components.device_tracker import DOMAIN +# Return cached results if last scan was less then this time ago MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pysnmp'] -def setup_scanner(hass, config, see): - """ Setup snmp scanning """ - #if not validate_config(config, {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, _LOGGER): - # return None +# pylint: disable=unused-argument +def get_scanner(hass, config): + """ Validates config and returns an snmp scanner """ + if not validate_config(config, {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, _LOGGER): + return None - #scanner = SnmpScanner(config[DOMAIN]) - scanner = SnmpScanner(config) + scanner = SnmpScanner(config[DOMAIN]) return scanner if scanner.success_init else None @@ -63,6 +80,7 @@ class SnmpScanner(object): """ self._update_info() + _LOGGER.error( self.last_results ) return [client['mac'] for client in self.last_results] def get_device_name(self, device): @@ -90,7 +108,7 @@ class SnmpScanner(object): """ Fetch mac addresses from WAP via SNMP. """ from pysnmp.entity.rfc3413.oneliner import cmdgen - devices = {} + devices = [] cmdGen = cmdgen.CommandGenerator() errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.nextCmd( @@ -108,6 +126,6 @@ class SnmpScanner(object): for key,val in varBindTableRow: mac = binascii.hexlify( val.asOctets() ).decode('utf-8') mac = ':'.join( [ mac[i:i+2] for i in range( 0, len(mac), 2 ) ] ) - devices[mac] = { 'mac' : mac } + devices.append( { 'mac' : mac } ) return devices From 9377b647f551047f4fcab41c2a4bca0351759a74 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 21:05:27 +0000 Subject: [PATCH 47/88] removed debug logging --- homeassistant/components/device_tracker/snmp.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index f89c3cb8d5a..e03fe134c8d 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -80,7 +80,6 @@ class SnmpScanner(object): """ self._update_info() - _LOGGER.error( self.last_results ) return [client['mac'] for client in self.last_results] def get_device_name(self, device): From d149f9d64c55ea38c7be20a1c759dc802a16a793 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Wed, 7 Oct 2015 23:28:04 +0200 Subject: [PATCH 48/88] Update doc string (Fix #491) --- homeassistant/components/notify/smtp.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/notify/smtp.py b/homeassistant/components/notify/smtp.py index ee2413eda30..fbddd8d1d26 100644 --- a/homeassistant/components/notify/smtp.py +++ b/homeassistant/components/notify/smtp.py @@ -1,15 +1,15 @@ """ -homeassistant.components.notify.mail +homeassistant.components.notify.smtp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mail (SMTP) notification service. Configuration: -To use the Mail notifier you will need to add something like the following +To use the smtp notifier you will need to add something like the following to your configuration.yaml file. notify: - platform: mail + platform: smtp server: MAIL_SERVER port: YOUR_SMTP_PORT sender: SENDER_EMAIL_ADDRESS From d556e5979a40d88dbef0c95fd5fa072757bab1ea Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 21:45:24 +0000 Subject: [PATCH 49/88] Updated misc files and code styling --- .coveragerc | 1 + README.md | 2 +- .../components/device_tracker/snmp.py | 36 ++++++++++--------- requirements_all.txt | 4 +++ 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/.coveragerc b/.coveragerc index 1ff145e2de3..433a43bea10 100644 --- a/.coveragerc +++ b/.coveragerc @@ -39,6 +39,7 @@ omit = homeassistant/components/device_tracker/thomson.py homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py + homeassistant/components/device_tracker/snmp.py homeassistant/components/discovery.py homeassistant/components/downloader.py homeassistant/components/keyboard.py diff --git a/README.md b/README.md index 6b1b1353392..6d1baa5c50f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat Examples of devices it can interface it: - * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), and [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) + * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable WAP/WRT * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)) * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index e03fe134c8d..c170bedf79e 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -36,6 +36,7 @@ import logging from datetime import timedelta import threading import binascii +from pysnmp.entity.rfc3413.oneliner import cmdgen from homeassistant.const import CONF_HOST, CONF_COMMUNITY, CONF_BASEOID from homeassistant.helpers import validate_config @@ -48,10 +49,13 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) REQUIREMENTS = ['pysnmp'] + # pylint: disable=unused-argument def get_scanner(hass, config): """ Validates config and returns an snmp scanner """ - if not validate_config(config, {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, _LOGGER): + if not validate_config(config, + {DOMAIN: [CONF_HOST, CONF_COMMUNITY, CONF_BASEOID]}, + _LOGGER): return None scanner = SnmpScanner(config[DOMAIN]) @@ -60,7 +64,9 @@ def get_scanner(hass, config): class SnmpScanner(object): - """ This class queries any SNMP capable Acces Point for connected devices. """ + """ + This class queries any SNMP capable Acces Point for connected devices. + """ def __init__(self, config): self.host = config[CONF_HOST] self.community = config[CONF_COMMUNITY] @@ -105,26 +111,24 @@ class SnmpScanner(object): def get_snmp_data(self): """ Fetch mac addresses from WAP via SNMP. """ - from pysnmp.entity.rfc3413.oneliner import cmdgen devices = [] cmdGen = cmdgen.CommandGenerator() - errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.nextCmd( - cmdgen.CommunityData( self.community ), - cmdgen.UdpTransportTarget( ( self.host , 161) ), - cmdgen.MibVariable( self.baseoid ) + errIndication, errStatus, errIndex, varBindTable = cmdGen.nextCmd( + cmdgen.CommunityData(self.community), + cmdgen.UdpTransportTarget((self.host, 161)), + cmdgen.MibVariable(self.baseoid) ) - if errorIndication: - _LOGGER.exception( "SNMPLIB error: {}".format( errorIndication ) ) + if errIndication: + _LOGGER.exception("SNMPLIB error: {}".format(errIndication)) return - if errorStatus: - _LOGGER.exception( "SNMP error: {} at {}".format( errorStatus.prettyPrint(), errorIndex and varBindTable[-1][int(errorIndex)-1] or '?' ) ) + if errStatus: + _LOGGER.exception("SNMP error: {} at {}".format(errStatus.prettyPrint(), errIndex and varBindTable[-1][int(errIndex)-1] or '?')) return for varBindTableRow in varBindTable: - for key,val in varBindTableRow: - mac = binascii.hexlify( val.asOctets() ).decode('utf-8') - mac = ':'.join( [ mac[i:i+2] for i in range( 0, len(mac), 2 ) ] ) - devices.append( { 'mac' : mac } ) + for val in varBindTableRow.values(): + mac = binascii.hexlify(val.asOctets()).decode('utf-8') + mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)]) + devices.append({'mac': mac}) return devices - diff --git a/requirements_all.txt b/requirements_all.txt index 2b7074d91cd..14ba1985c7e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -137,3 +137,7 @@ SoCo==0.11.1 # PlexAPI (media_player.plex) https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 + +# python-pysnmp (device_tracker.snmp) +pysnmp + From 7cb0f805ee90b23c64e0163326784efb3fb1b6c5 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 22:17:49 +0000 Subject: [PATCH 50/88] fixed loop --- homeassistant/components/device_tracker/snmp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index c170bedf79e..0ec0663b7c5 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -127,7 +127,7 @@ class SnmpScanner(object): _LOGGER.exception("SNMP error: {} at {}".format(errStatus.prettyPrint(), errIndex and varBindTable[-1][int(errIndex)-1] or '?')) return for varBindTableRow in varBindTable: - for val in varBindTableRow.values(): + for _,val in varBindTableRow: mac = binascii.hexlify(val.asOctets()).decode('utf-8') mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)]) devices.append({'mac': mac}) From ae6f651c7df2239cb00650c4cc16b2eeca6a276b Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Wed, 7 Oct 2015 23:22:29 +0000 Subject: [PATCH 51/88] styling and version for requirement --- .coveragerc | 1 + homeassistant/components/device_tracker/snmp.py | 8 +++++--- requirements_all.txt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.coveragerc b/.coveragerc index 433a43bea10..d9b4a29a1b5 100644 --- a/.coveragerc +++ b/.coveragerc @@ -40,6 +40,7 @@ omit = homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py homeassistant/components/device_tracker/snmp.py + homeassistant/components/discovery.py homeassistant/components/downloader.py homeassistant/components/keyboard.py diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 0ec0663b7c5..9113a1ab98d 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -47,7 +47,7 @@ from homeassistant.components.device_tracker import DOMAIN MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pysnmp'] +REQUIREMENTS = ['pysnmp<=4.2.5'] # pylint: disable=unused-argument @@ -124,10 +124,12 @@ class SnmpScanner(object): _LOGGER.exception("SNMPLIB error: {}".format(errIndication)) return if errStatus: - _LOGGER.exception("SNMP error: {} at {}".format(errStatus.prettyPrint(), errIndex and varBindTable[-1][int(errIndex)-1] or '?')) + _LOGGER.exception("SNMP error: {} at {}".format( + errStatus.prettyPrint(), + errIndex and varBindTable[-1][int(errIndex)-1] or '?')) return for varBindTableRow in varBindTable: - for _,val in varBindTableRow: + for _, val in varBindTableRow: mac = binascii.hexlify(val.asOctets()).decode('utf-8') mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)]) devices.append({'mac': mac}) diff --git a/requirements_all.txt b/requirements_all.txt index 14ba1985c7e..1c59b62fabd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -139,5 +139,5 @@ SoCo==0.11.1 https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 # python-pysnmp (device_tracker.snmp) -pysnmp +pysnmp>=4.2.5 From 213a1fe4ba26984be3bc1aff8392991b0cfb79a3 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 8 Oct 2015 08:00:30 +0000 Subject: [PATCH 52/88] Various fixes, CI validation --- .coveragerc | 1 - .../components/device_tracker/snmp.py | 34 ++++++++++++------- homeassistant/const.py | 2 -- requirements_all.txt | 2 +- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.coveragerc b/.coveragerc index d9b4a29a1b5..433a43bea10 100644 --- a/.coveragerc +++ b/.coveragerc @@ -40,7 +40,6 @@ omit = homeassistant/components/device_tracker/tomato.py homeassistant/components/device_tracker/tplink.py homeassistant/components/device_tracker/snmp.py - homeassistant/components/discovery.py homeassistant/components/downloader.py homeassistant/components/keyboard.py diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 9113a1ab98d..6d8f5113df0 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -36,9 +36,8 @@ import logging from datetime import timedelta import threading import binascii -from pysnmp.entity.rfc3413.oneliner import cmdgen -from homeassistant.const import CONF_HOST, CONF_COMMUNITY, CONF_BASEOID +from homeassistant.const import CONF_HOST from homeassistant.helpers import validate_config from homeassistant.util import Throttle from homeassistant.components.device_tracker import DOMAIN @@ -47,7 +46,10 @@ from homeassistant.components.device_tracker import DOMAIN MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) _LOGGER = logging.getLogger(__name__) -REQUIREMENTS = ['pysnmp<=4.2.5'] +REQUIREMENTS = ['pysnmp==4.2.5'] + +CONF_COMMUNITY = "community" +CONF_BASEOID = "baseoid" # pylint: disable=unused-argument @@ -90,6 +92,7 @@ class SnmpScanner(object): def get_device_name(self, device): """ Returns the name of the given device or None if we don't know. """ + # We have no names return None @Throttle(MIN_TIME_BETWEEN_SCANS) @@ -111,25 +114,30 @@ class SnmpScanner(object): def get_snmp_data(self): """ Fetch mac addresses from WAP via SNMP. """ + from pysnmp.entity.rfc3413.oneliner import cmdgen devices = [] - cmdGen = cmdgen.CommandGenerator() - errIndication, errStatus, errIndex, varBindTable = cmdGen.nextCmd( + snmp = cmdgen.CommandGenerator() + errindication, errstatus, errindex, restable = snmp.nextCmd( cmdgen.CommunityData(self.community), cmdgen.UdpTransportTarget((self.host, 161)), cmdgen.MibVariable(self.baseoid) ) - if errIndication: - _LOGGER.exception("SNMPLIB error: {}".format(errIndication)) + + if errindication: + _LOGGER.error("SNMPLIB error: {}".format(errindication)) return - if errStatus: - _LOGGER.exception("SNMP error: {} at {}".format( - errStatus.prettyPrint(), - errIndex and varBindTable[-1][int(errIndex)-1] or '?')) + if errstatus: + err = "SNMP error: {} at {}" + _LOGGER.error(err.format(errstatus.prettyPrint(), + errindex and + restable[-1][int(errindex)-1] + or '?')) return - for varBindTableRow in varBindTable: - for _, val in varBindTableRow: + + for resrow in restable: + for _, val in resrow: mac = binascii.hexlify(val.asOctets()).decode('utf-8') mac = ':'.join([mac[i:i+2] for i in range(0, len(mac), 2)]) devices.append({'mac': mac}) diff --git a/homeassistant/const.py b/homeassistant/const.py index 53ccd3c67db..2d272ca3a0b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -24,8 +24,6 @@ CONF_USERNAME = "username" CONF_PASSWORD = "password" CONF_API_KEY = "api_key" CONF_ACCESS_TOKEN = "access_token" -CONF_COMMUNITY = "community" -CONF_BASEOID = "baseoid" # #### EVENTS #### EVENT_HOMEASSISTANT_START = "homeassistant_start" diff --git a/requirements_all.txt b/requirements_all.txt index 1c59b62fabd..7e3a3acc458 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -139,5 +139,5 @@ SoCo==0.11.1 https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 # python-pysnmp (device_tracker.snmp) -pysnmp>=4.2.5 +pysnmp==4.2.5 From bf9b1794412724f270d24e73e526673dd7401ab7 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 10:23:19 +0200 Subject: [PATCH 53/88] Update docstrings --- .../components/light/blinksticklight.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/light/blinksticklight.py b/homeassistant/components/light/blinksticklight.py index 364b5f030c6..3e8ba8b505d 100644 --- a/homeassistant/components/light/blinksticklight.py +++ b/homeassistant/components/light/blinksticklight.py @@ -1,34 +1,36 @@ """ homeassistant.components.light.blinksticklight -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Support for Blinkstick lights. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.blinksticklight.html """ +import logging from blinkstick import blinkstick -import logging + +from homeassistant.components.light import (Light, ATTR_RGB_COLOR) _LOGGER = logging.getLogger(__name__) -from homeassistant.components.light import (Light, ATTR_RGB_COLOR) REQUIREMENTS = ["blinkstick==1.1.7"] DEPENDENCIES = [] + # pylint: disable=unused-argument - - def setup_platform(hass, config, add_devices_callback, discovery_info=None): - """ Add device specified by serial number """ + """ Add device specified by serial number. """ stick = blinkstick.find_by_serial(config['serial']) add_devices_callback([BlinkStickLight(stick, config['name'])]) class BlinkStickLight(Light): - """ Represents a BlinkStick light """ + """ Represents a BlinkStick light. """ def __init__(self, stick, name): - """ Initialise """ self._stick = stick self._name = name self._serial = stick.get_serial() @@ -36,20 +38,22 @@ class BlinkStickLight(Light): @property def should_poll(self): + """ Polling needed. """ return True @property def name(self): + """ The name of the light. """ return self._name @property def rgb_color(self): - """ Read back the color of the light """ + """ Read back the color of the light. """ return self._rgb_color @property def is_on(self): - """ Check whether any of the LEDs colors are non-zero """ + """ Check whether any of the LEDs colors are non-zero. """ return sum(self._rgb_color) > 0 def update(self): From 6d3f18d09440031c27cdf44f648d93ec5bf71d21 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 11:08:17 +0200 Subject: [PATCH 54/88] Update docstrings --- homeassistant/components/light/rfxtrx.py | 39 +++++++----------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py index f76d9f7ed5b..5d6f41fe509 100644 --- a/homeassistant/components/light/rfxtrx.py +++ b/homeassistant/components/light/rfxtrx.py @@ -1,26 +1,10 @@ """ homeassistant.components.light.rfxtrx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Support for Rfxtrx lights. - -Configuration: - -To use Rfxtrx lights you will need to add the following to your -configuration.yaml file. - -light: - platform: rfxtrx - - devices: - ac09c4f1: Bedroom Light - ac09c4f2: Kitchen Light - ac09c4f3: Bathroom Light - -*Optional* - - # Automatic add new light - automatic_add: True +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for RFXtrx lights. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.rfxtrx.html """ import logging import homeassistant.components.rfxtrx as rfxtrx @@ -36,7 +20,6 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Setup the RFXtrx platform. """ - # Add light from config file lights = [] devices = config.get('devices', None) if devices: @@ -51,7 +34,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): add_devices_callback(lights) def light_update(event): - """ Callback for sensor updates from the RFXtrx gateway. """ + """ Callback for light updates from the RFXtrx gateway. """ if not isinstance(event.device, rfxtrxmod.LightingDevice): return @@ -89,7 +72,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxLight(Light): - """ Provides a demo switch. """ + """ Provides a RFXtrx light. """ def __init__(self, name, event, state): self._name = name self._event = event @@ -97,21 +80,21 @@ class RfxtrxLight(Light): @property def should_poll(self): - """ No polling needed for a demo light. """ + """ No polling needed for a light. """ return False @property def name(self): - """ Returns the name of the device if any. """ + """ Returns the name of the light if any. """ return self._name @property def is_on(self): - """ True if device is on. """ + """ True if light is on. """ return self._state def turn_on(self, **kwargs): - """ Turn the device on. """ + """ Turn the light on. """ if hasattr(self, '_event') and self._event: self._event.device.send_on(rfxtrx.RFXOBJECT.transport) @@ -120,7 +103,7 @@ class RfxtrxLight(Light): self.update_ha_state() def turn_off(self, **kwargs): - """ Turn the device off. """ + """ Turn the light off. """ if hasattr(self, '_event') and self._event: self._event.device.send_off(rfxtrx.RFXOBJECT.transport) From 06cac7f9ef0a63c8db21bd016b681c9ada3aab50 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 11:08:32 +0200 Subject: [PATCH 55/88] Update docstrings --- homeassistant/components/rfxtrx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 79378b85e78..f6bb5c6bc71 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -1,6 +1,6 @@ """ homeassistant.components.rfxtrx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Connects Home Assistant to a RFXtrx device. Configuration: @@ -33,7 +33,7 @@ RFXOBJECT = None def setup(hass, config): - """ Setup the Rfxtrx component. """ + """ Setup the RFXtrx component. """ # Declare the Handle event def handle_receive(event): @@ -76,7 +76,7 @@ def setup(hass, config): def get_rfx_object(packetid): - """ return the RFXObject with the packetid""" + """ Return the RFXObject with the packetid. """ try: import RFXtrx as rfxtrxmod except ImportError: From d8aefb5d55ef3c4b717bf87f84d5475f22357b0f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 11:08:47 +0200 Subject: [PATCH 56/88] Update docstrings --- homeassistant/components/sensor/rfxtrx.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index b09d9d4a09c..1dac53015b3 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -3,21 +3,8 @@ homeassistant.components.sensor.rfxtrx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Shows sensor values from RFXtrx sensors. -Configuration: - -To use the rfxtrx sensors you will need to add something like the following to -your configuration.yaml file. - -sensor: - platform: rfxtrx - device: PATH_TO_DEVICE - -Variables: - -device -*Required -Path to your RFXtrx device. -E.g. /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1Y0NJGR-if00-port0 +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.rfxtrx.html """ import logging from collections import OrderedDict @@ -67,7 +54,6 @@ class RfxtrxSensor(Entity): def __init__(self, event): self.event = event - self._unit_of_measurement = None self._data_type = None for data_type in DATA_TYPES: @@ -86,13 +72,14 @@ class RfxtrxSensor(Entity): @property def state(self): + """ Returns the state of the device. """ if self._data_type: return self.event.values[self._data_type] return None @property def name(self): - """ Get the mame of the sensor. """ + """ Get the name of the sensor. """ return self._name @property From 3b7f6d3b6771644722d0983af0ba1ea641ba030c Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 11:09:00 +0200 Subject: [PATCH 57/88] Update docstrings --- homeassistant/components/switch/rfxtrx.py | 28 +++++------------------ 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py index 98963beb769..49a788d0d04 100644 --- a/homeassistant/components/switch/rfxtrx.py +++ b/homeassistant/components/switch/rfxtrx.py @@ -1,26 +1,10 @@ """ homeassistant.components.switch.rfxtrx -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Support for Rfxtrx switch. - -Configuration: - -To use Rfxtrx switchs you will need to add the following to your -configuration.yaml file. - -switch: - platform: rfxtrx - - devices: - ac09c4f1: Bedroom Door - ac09c4f2: Kitchen Door - ac09c4f3: Bathroom Door - -*Optional* - - # Automatic add new switch - automatic_add: True +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Support for RFXtrx switches. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/switch.rfxtrx.html """ import logging import homeassistant.components.rfxtrx as rfxtrx @@ -90,7 +74,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): class RfxtrxSwitch(SwitchDevice): - """ Provides a demo switch. """ + """ Provides a RFXtrx switch. """ def __init__(self, name, event, state): self._name = name self._event = event @@ -98,7 +82,7 @@ class RfxtrxSwitch(SwitchDevice): @property def should_poll(self): - """ No polling needed for a demo switch. """ + """ No polling needed for a RFXtrx switch. """ return False @property From 9f10ab5e7ad14685caaaeae4ceaf8392e523f31a Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Thu, 8 Oct 2015 11:10:05 +0200 Subject: [PATCH 58/88] Update logger output --- homeassistant/components/sensor/rfxtrx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index 1dac53015b3..07912b719d2 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -38,7 +38,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if entity_id not in rfxtrx.RFX_DEVICES: automatic_add = config.get('automatic_add', True) if automatic_add: - _LOGGER.info("Automatic add %s rfxtrx.light", entity_id) + _LOGGER.info("Automatic add %s rfxtrx.sensor", entity_id) new_sensor = RfxtrxSensor(event) rfxtrx.RFX_DEVICES[entity_id] = new_sensor add_devices_callback([new_sensor]) From 85bf6cb5686fc547eadd831f7cbd1597d9a3ed2e Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 8 Oct 2015 10:01:10 +0000 Subject: [PATCH 59/88] Added pylint disables --- homeassistant/components/device_tracker/snmp.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 6d8f5113df0..8fe80cf85d6 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -76,7 +76,7 @@ class SnmpScanner(object): self.lock = threading.Lock() - self.last_results = {} + self.last_results = [] # Test the router is accessible data = self.get_snmp_data() @@ -90,6 +90,8 @@ class SnmpScanner(object): self._update_info() return [client['mac'] for client in self.last_results] + # Ignoring no-self-use warning + # pylint: disable=R0201 def get_device_name(self, device): """ Returns the name of the given device or None if we don't know. """ # We have no names @@ -126,10 +128,12 @@ class SnmpScanner(object): ) if errindication: + #pylint: disable=W1202 _LOGGER.error("SNMPLIB error: {}".format(errindication)) return if errstatus: err = "SNMP error: {} at {}" + #pylint: disable=W1202 _LOGGER.error(err.format(errstatus.prettyPrint(), errindex and restable[-1][int(errindex)-1] From 721c1d0f547293fe16f5487de03575ab06244356 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 8 Oct 2015 10:24:55 +0000 Subject: [PATCH 60/88] styling fix for flake --- homeassistant/components/device_tracker/snmp.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 8fe80cf85d6..012c7250c33 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -90,7 +90,7 @@ class SnmpScanner(object): self._update_info() return [client['mac'] for client in self.last_results] - # Ignoring no-self-use warning + # Supressing no-self-use warning # pylint: disable=R0201 def get_device_name(self, device): """ Returns the name of the given device or None if we don't know. """ @@ -128,12 +128,14 @@ class SnmpScanner(object): ) if errindication: - #pylint: disable=W1202 + # Supressing logging-format-interpolation + # pylint: disable=W1202 _LOGGER.error("SNMPLIB error: {}".format(errindication)) return if errstatus: err = "SNMP error: {} at {}" - #pylint: disable=W1202 + # Supressing logging-format-interpolation + # pylint: disable=W1202 _LOGGER.error(err.format(errstatus.prettyPrint(), errindex and restable[-1][int(errindex)-1] From a015df7b0156329e245b6bfeab224f70d3e2fb3a Mon Sep 17 00:00:00 2001 From: magnusknutas Date: Thu, 8 Oct 2015 13:41:58 +0200 Subject: [PATCH 61/88] Test for media_content_id KeyError --- homeassistant/components/media_player/kodi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 7bfd385f65b..b25055ec6ec 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -166,8 +166,9 @@ class KodiDevice(MediaPlayerDevice): @property def media_content_id(self): """ Content ID of current playing media. """ + _LOGGER.debug(self._item) if self._item is not None: - return self._item['uniqueid'] + return self._item.get('uniqueid', None) @property def media_content_type(self): From 61c955779b9021d1c0e9de6f57585b41d5a5ff07 Mon Sep 17 00:00:00 2001 From: magnusknutas Date: Thu, 8 Oct 2015 13:55:01 +0200 Subject: [PATCH 62/88] Logging with info --- homeassistant/components/media_player/kodi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index b25055ec6ec..6d5d90549a3 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -166,7 +166,7 @@ class KodiDevice(MediaPlayerDevice): @property def media_content_id(self): """ Content ID of current playing media. """ - _LOGGER.debug(self._item) + _LOGGER.info(self._item) if self._item is not None: return self._item.get('uniqueid', None) From 75c3e42064109012fec3e92c36c1f1db5ebb94bb Mon Sep 17 00:00:00 2001 From: magnusknutas Date: Thu, 8 Oct 2015 14:00:23 +0200 Subject: [PATCH 63/88] Removes log for cleanup --- homeassistant/components/media_player/kodi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/media_player/kodi.py b/homeassistant/components/media_player/kodi.py index 6d5d90549a3..2fe42e2e707 100644 --- a/homeassistant/components/media_player/kodi.py +++ b/homeassistant/components/media_player/kodi.py @@ -166,7 +166,6 @@ class KodiDevice(MediaPlayerDevice): @property def media_content_id(self): """ Content ID of current playing media. """ - _LOGGER.info(self._item) if self._item is not None: return self._item.get('uniqueid', None) From 5322789c1448ba042d07bfa08cabaa8f0fc175c2 Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 8 Oct 2015 14:10:33 +0000 Subject: [PATCH 64/88] Ability to store icons/pictures in config_dir/www for e.g. device_tracker pictures --- homeassistant/components/frontend/__init__.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 419e48d55b5..b327e510cd8 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -11,6 +11,7 @@ import logging from . import version import homeassistant.util as util from homeassistant.const import URL_ROOT, HTTP_OK +from homeassistant.config import get_default_config_dir DOMAIN = 'frontend' DEPENDENCIES = ['api'] @@ -19,7 +20,6 @@ INDEX_PATH = os.path.join(os.path.dirname(__file__), 'index.html.template') _LOGGER = logging.getLogger(__name__) - FRONTEND_URLS = [ URL_ROOT, '/logbook', '/history', '/map', '/devService', '/devState', '/devEvent'] @@ -44,6 +44,9 @@ def setup(hass, config): hass.http.register_path( 'HEAD', re.compile(r'/static/(?P[a-zA-Z\._\-0-9/]+)'), _handle_get_static, False) + hass.http.register_path( + 'GET', re.compile(r'/local/(?P[a-zA-Z\._\-0-9/]+)'), + _handle_get_local, False) return True @@ -84,3 +87,16 @@ def _handle_get_static(handler, path_match, data): path = os.path.join(os.path.dirname(__file__), 'www_static', req_file) handler.write_file(path) + + +def _handle_get_local(handler, path_match, data): + """ + Returns a static file from the hass.config.path/www for the frontend. + """ + req_file = util.sanitize_path(path_match.group('file')) + + path = os.path.join(get_default_config_dir(), 'www', req_file) + if not os.path.isfile(path): + return False + + handler.write_file(path) From ee23c0fe14a8eb6b3db60f55773a64864330325d Mon Sep 17 00:00:00 2001 From: Tom Duijf Date: Thu, 8 Oct 2015 14:54:20 +0000 Subject: [PATCH 65/88] cleaner logging --- homeassistant/components/device_tracker/snmp.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 012c7250c33..2fbc03b980a 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -128,18 +128,12 @@ class SnmpScanner(object): ) if errindication: - # Supressing logging-format-interpolation - # pylint: disable=W1202 - _LOGGER.error("SNMPLIB error: {}".format(errindication)) + _LOGGER.error("SNMPLIB error: %s", errindication) return if errstatus: - err = "SNMP error: {} at {}" - # Supressing logging-format-interpolation - # pylint: disable=W1202 - _LOGGER.error(err.format(errstatus.prettyPrint(), - errindex and - restable[-1][int(errindex)-1] - or '?')) + _LOGGER.error('SNMP error: %s at %s', errstatus.prettyPrint(), + errindex and restable[-1][int(errindex)-1] + or '?') return for resrow in restable: From 28b107ffa9b67edafdc9b3a1ba51e20b1dc88b39 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 00:27:29 +0200 Subject: [PATCH 66/88] Move details from header to docs --- .../components/device_tracker/snmp.py | 33 +++---------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/device_tracker/snmp.py b/homeassistant/components/device_tracker/snmp.py index 2fbc03b980a..21bcdfb2a93 100644 --- a/homeassistant/components/device_tracker/snmp.py +++ b/homeassistant/components/device_tracker/snmp.py @@ -1,36 +1,11 @@ """ homeassistant.components.device_tracker.snmp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Device tracker platform that supports fetching WiFi assiciations -through SNMP - -This device tracker needs SNMP to be enabled on the WRT or WAP - -Configuration: - -device_tracker: - platform: snmp - host: YOUR_WAP_IP - community: SNMP_COMMUNITY - baseoid: BASE_OID - -Variables: - Host - *required - The IP address of the router, e.g. 192.168.1.1 - - community - *Required - The SNMP community. Read-only is fine - - baseoid - *Required - The OID at which WiFi associations can be found - - Little help with base oids: - Microtik: 1.3.6.1.4.1.14988.1.1.1.2.1.1 (confirmed) - Aruba: 1.3.6.1.4.1.14823.2.3.3.1.2.4.1.2 (untested) +Device tracker platform that supports fetching WiFi associations +through SNMP. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/device_tracker.snmp.html """ import logging from datetime import timedelta From 45f0911640455384359a508bc246dcfdd75e5a2c Mon Sep 17 00:00:00 2001 From: Jon Maddox Date: Thu, 8 Oct 2015 20:37:59 -0400 Subject: [PATCH 67/88] move play_media to the top so it catches first --- homeassistant/helpers/state.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 6526fe2d90b..24a37c5b5ea 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -59,14 +59,14 @@ def reproduce_state(hass, states, blocking=False): state.entity_id) continue - if state.domain == 'media_player' and state.state == STATE_PAUSED: - service = SERVICE_MEDIA_PAUSE - elif state.domain == 'media_player' and state.state == STATE_PLAYING: - service = SERVICE_MEDIA_PLAY - elif state.domain == 'media_player' and state.attributes and \ + if state.domain == 'media_player' and state.attributes and \ 'media_type' in state.attributes and \ 'media_id' in state.attributes: service = SERVICE_PLAY_MEDIA + elif state.domain == 'media_player' and state.state == STATE_PAUSED: + service = SERVICE_MEDIA_PAUSE + elif state.domain == 'media_player' and state.state == STATE_PLAYING: + service = SERVICE_MEDIA_PLAY elif state.state == STATE_ON: service = SERVICE_TURN_ON elif state.state == STATE_OFF: From dc5f0ef314b049551215d5463bec6756c26dcfa5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 21:01:38 -0700 Subject: [PATCH 68/88] NMap: fix hostname resolver Fixes #482 --- homeassistant/components/device_tracker/nmap_tracker.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/device_tracker/nmap_tracker.py index 8d9c2e72c20..247cbbfe7e8 100644 --- a/homeassistant/components/device_tracker/nmap_tracker.py +++ b/homeassistant/components/device_tracker/nmap_tracker.py @@ -123,9 +123,9 @@ class NmapDeviceScanner(object): for host in self.last_results: if host.last_update + self.home_interval > now: exclude_targets.add(host) - if len(exclude_targets) > 0: - target_list = [t.ip for t in exclude_targets] - options += " --exclude {}".format(",".join(target_list)) + if exclude_targets: + options += " --exclude {}".format(",".join(t.ip for t + in exclude_targets)) try: result = scanner.scan(hosts=self.hosts, arguments=options) @@ -137,7 +137,7 @@ class NmapDeviceScanner(object): for ipv4, info in result['scan'].items(): if info['status']['state'] != 'up': continue - name = info['hostnames'][0] if info['hostnames'] else ipv4 + name = info['hostnames'][0]['name'] if info['hostnames'] else ipv4 # Mac address only returned if nmap ran as root mac = info['addresses'].get('mac') or _arp(ipv4) if mac is None: From 0624725e21f0f26e1989c063ee8653b0bfffd8ab Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 21:45:51 -0700 Subject: [PATCH 69/88] Ignore nmap style issue - pylint bug --- .../components/device_tracker/nmap_tracker.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/device_tracker/nmap_tracker.py b/homeassistant/components/device_tracker/nmap_tracker.py index 247cbbfe7e8..6f993f0fc7e 100644 --- a/homeassistant/components/device_tracker/nmap_tracker.py +++ b/homeassistant/components/device_tracker/nmap_tracker.py @@ -117,15 +117,18 @@ class NmapDeviceScanner(object): scanner = PortScanner() options = "-F --host-timeout 5" - exclude_targets = set() + if self.home_interval: - now = dt_util.now() - for host in self.last_results: - if host.last_update + self.home_interval > now: - exclude_targets.add(host) - if exclude_targets: - options += " --exclude {}".format(",".join(t.ip for t - in exclude_targets)) + boundary = dt_util.now() - self.home_interval + last_results = [device for device in self.last_results + if device.last_update > boundary] + if last_results: + # Pylint is confused here. + # pylint: disable=no-member + options += " --exclude {}".format(",".join(device.ip for device + in last_results)) + else: + last_results = [] try: result = scanner.scan(hosts=self.hosts, arguments=options) @@ -133,7 +136,6 @@ class NmapDeviceScanner(object): return False now = dt_util.now() - self.last_results = [] for ipv4, info in result['scan'].items(): if info['status']['state'] != 'up': continue @@ -142,9 +144,9 @@ class NmapDeviceScanner(object): mac = info['addresses'].get('mac') or _arp(ipv4) if mac is None: continue - device = Device(mac.upper(), name, ipv4, now) - self.last_results.append(device) - self.last_results.extend(exclude_targets) + last_results.append(Device(mac.upper(), name, ipv4, now)) + + self.last_results = last_results _LOGGER.info("nmap scan successful") return True From 9f33b8f541cd2de14abfb7021c3f19acaf1a81c4 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 22:15:12 -0700 Subject: [PATCH 70/88] DDWRT - match multiple output variants Fixes #481 --- .../components/device_tracker/ddwrt.py | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/device_tracker/ddwrt.py b/homeassistant/components/device_tracker/ddwrt.py index a9a4ac8e3f5..947876c85b5 100644 --- a/homeassistant/components/device_tracker/ddwrt.py +++ b/homeassistant/components/device_tracker/ddwrt.py @@ -46,6 +46,7 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5) _LOGGER = logging.getLogger(__name__) _DDWRT_DATA_REGEX = re.compile(r'\{(\w+)::([^\}]*)\}') +_MAC_REGEX = re.compile(r'(([0-9A-Fa-f]{1,2}\:){5}[0-9A-Fa-f]{1,2})') # pylint: disable=unused-argument @@ -77,7 +78,7 @@ class DdWrtDeviceScanner(object): self.last_results = {} - self.mac2name = None + self.mac2name = {} # Test the router is accessible url = 'http://{}/Status_Wireless.live.asp'.format(self.host) @@ -98,30 +99,33 @@ class DdWrtDeviceScanner(object): with self.lock: # if not initialised and not already scanned and not found - if self.mac2name is None or device not in self.mac2name: + if device not in self.mac2name: url = 'http://{}/Status_Lan.live.asp'.format(self.host) data = self.get_ddwrt_data(url) if not data: - return + return None dhcp_leases = data.get('dhcp_leases', None) - if dhcp_leases: - # remove leading and trailing single quotes - cleaned_str = dhcp_leases.strip().strip('"') - elements = cleaned_str.split('","') - num_clients = int(len(elements)/5) - self.mac2name = {} - for idx in range(0, num_clients): - # this is stupid but the data is a single array - # every 5 elements represents one hosts, the MAC - # is the third element and the name is the first - mac_index = (idx * 5) + 2 - if mac_index < len(elements): - mac = elements[mac_index] - self.mac2name[mac] = elements[idx * 5] - return self.mac2name.get(device, None) + if not dhcp_leases: + return None + + # remove leading and trailing single quotes + cleaned_str = dhcp_leases.strip().strip('"') + elements = cleaned_str.split('","') + num_clients = int(len(elements)/5) + self.mac2name = {} + for idx in range(0, num_clients): + # this is stupid but the data is a single array + # every 5 elements represents one hosts, the MAC + # is the third element and the name is the first + mac_index = (idx * 5) + 2 + if mac_index < len(elements): + mac = elements[mac_index] + self.mac2name[mac] = elements[idx * 5] + + return self.mac2name.get(device) @Throttle(MIN_TIME_BETWEEN_SCANS) def _update_info(self): @@ -141,29 +145,25 @@ class DdWrtDeviceScanner(object): if not data: return False - if data: - self.last_results = [] - active_clients = data.get('active_wireless', None) - if active_clients: - # This is really lame, instead of using JSON the DD-WRT UI - # uses its own data format for some reason and then - # regex's out values so I guess I have to do the same, - # LAME!!! + self.last_results = [] - # remove leading and trailing single quotes - clean_str = active_clients.strip().strip("'") - elements = clean_str.split("','") + active_clients = data.get('active_wireless', None) + if not active_clients: + return False - num_clients = int(len(elements)/9) - for idx in range(0, num_clients): - # get every 9th element which is the MAC address - index = idx * 9 - if index < len(elements): - self.last_results.append(elements[index]) + # This is really lame, instead of using JSON the DD-WRT UI + # uses its own data format for some reason and then + # regex's out values so I guess I have to do the same, + # LAME!!! - return True + # remove leading and trailing single quotes + clean_str = active_clients.strip().strip("'") + elements = clean_str.split("','") - return False + self.last_results.extend(item for item in elements + if _MAC_REGEX.match(item)) + + return True def get_ddwrt_data(self, url): """ Retrieve data from DD-WRT and return parsed result. """ From 8a04e1f5f40612ee9dfb398d837ed1ed101e8db3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 22:19:15 -0700 Subject: [PATCH 71/88] Device tracker configuration fix Fixes #498 --- homeassistant/components/device_tracker/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index 55359b393ec..9fe18585418 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -114,6 +114,8 @@ def setup(hass, config): os.remove(csv_path) conf = config.get(DOMAIN, {}) + if isinstance(conf, list): + conf = conf[0] consider_home = timedelta( seconds=util.convert(conf.get(CONF_CONSIDER_HOME), int, DEFAULT_CONSIDER_HOME)) From 47fc1deecbd29b9154cbcdf03f165299a8af642c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 23:49:55 -0700 Subject: [PATCH 72/88] Fix throttle to work on instance-level --- homeassistant/util/__init__.py | 31 +++++++++++++++++++------------ tests/util/test_init.py | 11 +++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/homeassistant/util/__init__.py b/homeassistant/util/__init__.py index 805937376a0..2d449285493 100644 --- a/homeassistant/util/__init__.py +++ b/homeassistant/util/__init__.py @@ -233,35 +233,42 @@ class Throttle(object): self.limit_no_throttle = limit_no_throttle def __call__(self, method): - lock = threading.Lock() - if self.limit_no_throttle is not None: method = Throttle(self.limit_no_throttle)(method) + # We want to be able to differentiate between function and method calls + # All methods have the classname in their qualname seperated by a '.' + # Functions have a '.' in their qualname if defined inline, but will + # be prefixed by '..' so we strip that out. + is_func = '.' not in method.__qualname__.split('..')[-1] + @wraps(method) def wrapper(*args, **kwargs): """ Wrapper that allows wrapped to be called only once per min_time. If we cannot acquire the lock, it is running so return None. """ - if not lock.acquire(False): + # pylint: disable=protected-access + host = wrapper if is_func else args[0] + if not hasattr(host, '_throttle_lock'): + host._throttle_lock = threading.Lock() + + if not host._throttle_lock.acquire(False): return None + + last_call = getattr(host, '_throttle_last_call', None) + # Check if method is never called or no_throttle is given + force = not last_call or kwargs.pop('no_throttle', False) + try: - last_call = wrapper.last_call - - # Check if method is never called or no_throttle is given - force = not last_call or kwargs.pop('no_throttle', False) - if force or utcnow() - last_call > self.min_time: result = method(*args, **kwargs) - wrapper.last_call = utcnow() + host._throttle_last_call = utcnow() return result else: return None finally: - lock.release() - - wrapper.last_call = None + host._throttle_lock.release() return wrapper diff --git a/tests/util/test_init.py b/tests/util/test_init.py index 94358f5eb51..2bf917f4e25 100644 --- a/tests/util/test_init.py +++ b/tests/util/test_init.py @@ -218,3 +218,14 @@ class TestUtil(unittest.TestCase): self.assertEqual(3, len(calls1)) self.assertEqual(2, len(calls2)) + + def test_throttle_per_instance(self): + """ Test that the throttle method is done per instance of a class. """ + + class Tester(object): + @util.Throttle(timedelta(seconds=1)) + def hello(self): + return True + + self.assertTrue(Tester().hello()) + self.assertTrue(Tester().hello()) From be8089bcde5f28a531fd086e47deb4d564766562 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Oct 2015 23:50:04 -0700 Subject: [PATCH 73/88] Cleanup arest --- homeassistant/components/sensor/arest.py | 63 +++++++++++++----------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index cfe88e0f0d6..7d2192e8920 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -47,7 +47,7 @@ Format of a default JSON response by aREST: } """ import logging -from requests import get, exceptions +import requests from datetime import timedelta from homeassistant.util import Throttle @@ -58,36 +58,42 @@ _LOGGER = logging.getLogger(__name__) # Return cached results if last scan was less then this time ago MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) +CONF_RESOURCE = 'resource' +CONF_MONITORED_VARIABLES = 'monitored_variables' + def setup_platform(hass, config, add_devices, discovery_info=None): """ Get the aREST sensor. """ - resource = config.get('resource', None) + resource = config.get(CONF_RESOURCE) + var_conf = config.get(CONF_MONITORED_VARIABLES) + + if None in (resource, var_conf): + _LOGGER.error('Not all required config keys present: %s', + ', '.join((CONF_RESOURCE, CONF_MONITORED_VARIABLES))) + return False try: - response = get(resource, timeout=10) - except exceptions.MissingSchema: + response = requests.get(resource, timeout=10).json() + except requests.exceptions.MissingSchema: _LOGGER.error("Missing resource or schema in configuration. " "Add http:// to your URL.") return False - except exceptions.ConnectionError: + except requests.exceptions.ConnectionError: _LOGGER.error("No route to device. " "Please check the IP address in the configuration file.") return False - rest = ArestData(resource) + arest = ArestData(resource) dev = [] for variable in config['monitored_variables']: - if 'unit' not in variable: - variable['unit'] = ' ' - if variable['name'] not in response.json()['variables']: + if variable['name'] not in response['variables']: _LOGGER.error('Variable: "%s" does not exist', variable['name']) - else: - dev.append(ArestSensor(rest, - response.json()['name'], - variable['name'], - variable['unit'])) + continue + + dev.append(ArestSensor(arest, response['name'], variable['name'], + variable.get('unit'))) add_devices(dev) @@ -95,8 +101,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ArestSensor(Entity): """ Implements an aREST sensor. """ - def __init__(self, rest, location, variable, unit_of_measurement): - self.rest = rest + def __init__(self, arest, location, variable, unit_of_measurement): + self.arest = arest self._name = '{} {}'.format(location.title(), variable.title()) self._variable = variable self._state = 'n/a' @@ -116,17 +122,16 @@ class ArestSensor(Entity): @property def state(self): """ Returns the state of the device. """ - return self._state - - def update(self): - """ Gets the latest data from aREST API and updates the state. """ - self.rest.update() - values = self.rest.data + values = self.arest.data if 'error' in values: - self._state = values['error'] + return values['error'] else: - self._state = values[self._variable] + return values.get(self._variable, 'n/a') + + def update(self): + """ Gets the latest data from aREST API. """ + self.arest.update() # pylint: disable=too-few-public-methods @@ -135,16 +140,14 @@ class ArestData(object): def __init__(self, resource): self.resource = resource - self.data = dict() + self.data = {} @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """ Gets the latest data from aREST device. """ try: - response = get(self.resource, timeout=10) - if 'error' in self.data: - del self.data['error'] + response = requests.get(self.resource, timeout=10) self.data = response.json()['variables'] - except exceptions.ConnectionError: + except requests.exceptions.ConnectionError: _LOGGER.error("No route to device. Is device offline?") - self.data['error'] = 'n/a' + self.data = {'error': 'error fetching'} From fe5bb89a686ac7e5d129190a4cb5b610a6ed7b43 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:04:29 +0200 Subject: [PATCH 74/88] Add telegram notifier --- homeassistant/components/notify/telegram.py | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 homeassistant/components/notify/telegram.py diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/notify/telegram.py new file mode 100644 index 00000000000..c65d9ccf6cb --- /dev/null +++ b/homeassistant/components/notify/telegram.py @@ -0,0 +1,66 @@ +""" +homeassistant.components.notify.telegram +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Telegram platform for notify component. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/notify.xmpp.html +""" +import logging +import urllib + +from homeassistant.helpers import validate_config +from homeassistant.components.notify import ( + DOMAIN, ATTR_TITLE, BaseNotificationService) +from homeassistant.const import CONF_API_KEY + +_LOGGER = logging.getLogger(__name__) +REQUIREMENTS = ['python-telegram-bot==2.8.7'] + + +def get_service(hass, config): + """ Get the Telegram notification service. """ + + if not validate_config(config, + {DOMAIN: [CONF_API_KEY, 'chat_id']}, + _LOGGER): + return None + + try: + import telegram + except ImportError: + _LOGGER.exception( + "Unable to import python-telegram-bot. " + "Did you maybe not install the 'python-telegram-bot' package?") + return None + + try: + bot = telegram.Bot(token=config[DOMAIN][CONF_API_KEY]) + username = bot.getMe()['username'] + _LOGGER.info("Telegram bot is' %s'", username) + except urllib.error.HTTPError: + _LOGGER.error("Please check your access token.") + return None + + return TelegramNotificationService( + config[DOMAIN][CONF_API_KEY], + config[DOMAIN]['chat_id']) + + +# pylint: disable=too-few-public-methods +class TelegramNotificationService(BaseNotificationService): + """ Implements notification service for Telegram. """ + + def __init__(self, api_key, chat_id): + import telegram + self._api_key = api_key + self._chat_id = chat_id + self.bot = telegram.Bot(token=self._api_key) + + def send_message(self, message="", **kwargs): + """ Send a message to a user. """ + + title = kwargs.get(ATTR_TITLE) + + self.bot.sendMessage(chat_id=self._chat_id, + text=title + " " + message) From 3ef5e7c16143fc2d3750ae04a01c0ab08ae3259e Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:12:49 +0200 Subject: [PATCH 75/88] Add telegram --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 26fae1ba3f9..2364cb4d8d1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -65,6 +65,7 @@ omit = homeassistant/components/notify/slack.py homeassistant/components/notify/smtp.py homeassistant/components/notify/syslog.py + homeassistant/components/notify/telegram.py homeassistant/components/notify/xmpp.py homeassistant/components/sensor/arest.py homeassistant/components/sensor/bitcoin.py From 9f6ce868e23e8d28606a144bbfd289ab1d61dd73 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:13:05 +0200 Subject: [PATCH 76/88] Add telegram --- requirements_all.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements_all.txt b/requirements_all.txt index e78effedfb8..5fa48f3c10d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -143,3 +143,6 @@ pysnmp==4.2.5 # Blinkstick blinkstick==1.1.7 + +# Telegram (notify.telegram) +python-telegram-bot==2.8.7 From e29f857f43328790446b88e1723acc96319048ea Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:40:48 +0200 Subject: [PATCH 77/88] Update header (docstring) --- homeassistant/components/rfxtrx.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index f6bb5c6bc71..d381fac0f61 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -1,20 +1,10 @@ """ homeassistant.components.rfxtrx ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Connects Home Assistant to a RFXtrx device. - -Configuration: - -To use Rfxtrx device you will need to add the following to your -configuration.yaml file. - -rfxtrx: - device: /dev/serial/by-id/usb-RFXCOM_RFXtrx433_A1YVC1P0-if00-port0 - -*Optional* - - debug: True +Provides support for RFXtrx components. +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/switch.rfxtrx.html """ import logging from homeassistant.util import slugify From 526a163563ff53426d45aab04572e8a143e088b2 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:41:35 +0200 Subject: [PATCH 78/88] Update link --- homeassistant/components/rfxtrx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index d381fac0f61..0788986c91d 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -4,7 +4,7 @@ homeassistant.components.rfxtrx Provides support for RFXtrx components. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/switch.rfxtrx.html +https://home-assistant.io/components/rfxtrx.html """ import logging from homeassistant.util import slugify From db53e46705cf64af4daaa172302a16417d00b5af Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:44:59 +0200 Subject: [PATCH 79/88] Add link to docs and remove configuration details --- homeassistant/components/switch/arest.py | 33 ++---------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/switch/arest.py b/homeassistant/components/switch/arest.py index 239e24a4925..ec04e3c210f 100644 --- a/homeassistant/components/switch/arest.py +++ b/homeassistant/components/switch/arest.py @@ -3,38 +3,9 @@ homeassistant.components.switch.arest ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The arest switch can control the digital pins of a device running with the aREST RESTful framework for Arduino, the ESP8266, and the Raspberry Pi. -Only tested with Arduino boards so far. -Configuration: - -To use the arest switch you will need to add something like the following -to your configuration.yaml file. - -sensor: - platform: arest - resource: http://IP_ADDRESS - pins: - 11: - name: Fan Office - 12: - name: Light Desk - -Variables: - -resource: -*Required -IP address of the device that is exposing an aREST API. - -pins: -The number of the digital pin to switch. - -These are the variables for the pins array: - -name -*Required -The name for the pin that will be used in the frontend. - -Details for the API: http://arest.io +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/switch.arest.html """ import logging from requests import get, exceptions From f8efe3f00f9d0346e1e9686992ac6c447949c844 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 14:48:58 +0200 Subject: [PATCH 80/88] Update link to docs --- homeassistant/components/notify/telegram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/notify/telegram.py b/homeassistant/components/notify/telegram.py index c65d9ccf6cb..23b915baf1e 100644 --- a/homeassistant/components/notify/telegram.py +++ b/homeassistant/components/notify/telegram.py @@ -4,7 +4,7 @@ homeassistant.components.notify.telegram Telegram platform for notify component. For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/notify.xmpp.html +https://home-assistant.io/components/notify.telegram.html """ import logging import urllib From 8fc2f5fe36e512e03ec72d1a569ac32b9b44bc07 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 17:41:07 +0200 Subject: [PATCH 81/88] Update and equalize comments --- requirements_all.txt | 56 +++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/requirements_all.txt b/requirements_all.txt index 5fa48f3c10d..c9afec10ad0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -10,77 +10,77 @@ vincenty==0.1.2 # Sun (sun) astral==0.8.1 -# Philips Hue library (lights.hue) +# Philips Hue (lights.hue) phue==0.8 -# Limitlessled/Easybulb/Milight library (lights.limitlessled) +# Limitlessled/Easybulb/Milight (lights.limitlessled) ledcontroller==1.1.0 -# Chromecast bindings (media_player.cast) +# Chromecast (media_player.cast) pychromecast==0.6.12 # Keyboard (keyboard) pyuserinput==0.1.9 -# Tellstick bindings (*.tellstick) +# Tellstick (*.tellstick) tellcore-py==1.1.2 -# Nmap bindings (device_tracker.nmap) +# Nmap (device_tracker.nmap) python-nmap==0.4.3 -# PushBullet bindings (notify.pushbullet) +# PushBullet (notify.pushbullet) pushbullet.py==0.7.1 -# Nest Thermostat bindings (thermostat.nest) +# Nest Thermostat (thermostat.nest) python-nest==2.6.0 # Z-Wave (*.zwave) pydispatcher==2.0.5 -# ISY994 bindings (*.isy994) +# ISY994 (isy994) PyISY==1.0.5 # PSutil (sensor.systemmonitor) psutil==3.0.0 -# Pushover bindings (notify.pushover) +# Pushover (notify.pushover) python-pushover==0.2 # Transmission Torrent Client (*.transmission) transmissionrpc==0.11 -# OpenWeatherMap Web API (sensor.openweathermap) +# OpenWeatherMap (sensor.openweathermap) pyowm==2.2.1 -# XMPP Bindings (notify.xmpp) +# XMPP (notify.xmpp) sleekxmpp==1.3.1 dnspython3==1.12.0 # Blockchain (sensor.bitcoin) blockchain==1.1.2 -# MPD Bindings (media_player.mpd) +# Music Player Daemon (media_player.mpd) python-mpd2==0.5.4 # Hikvision (switch.hikvisioncam) hikvision==0.4 -# console log coloring +# Console log coloring colorlog==2.6.0 # JSON-RPC interface (media_player.kodi) jsonrpc-requests==0.1 -# Forecast.io Bindings (sensor.forecast) +# Forecast.io (sensor.forecast) python-forecastio==1.3.3 -# Firmata Bindings (*.arduino) +# Firmata (*.arduino) PyMata==2.07a -# Rfxtrx sensor (sensor.rfxtrx) +# Rfxtrx (rfxtrx) https://github.com/Danielhiversen/pyRFXtrx/archive/ec7a1aaddf8270db6e5da1c13d58c1547effd7cf.zip#RFXtrx==0.15 -# Mysensors +# Mysensors (sensor.mysensors) https://github.com/theolind/pymysensors/archive/35b87d880147a34107da0d40cb815d75e6cb4af7.zip#pymysensors==0.2 # Netgear (device_tracker.netgear) @@ -101,18 +101,18 @@ slacker==0.6.8 # Temper sensors (sensor.temper) https://github.com/rkabadi/temper-python/archive/3dbdaf2d87b8db9a3cd6e5585fc704537dd2d09b.zip#temperusb==1.2.3 -# PyEdimax +# PyEdimax (switch.edimax) https://github.com/rkabadi/pyedimax/archive/365301ce3ff26129a7910c501ead09ea625f3700.zip#pyedimax==0.1 # RPI-GPIO platform (*.rpi_gpio) # Uncomment for Raspberry Pi # RPi.GPIO==0.5.11 -# Adafruit temperature/humidity sensor -# uncomment on a Raspberry Pi / Beaglebone +# Adafruit temperature/humidity sensor (sensor.dht) +# Uncomment on a Raspberry Pi / Beaglebone # http://github.com/mala-zaba/Adafruit_Python_DHT/archive/4101340de8d2457dd194bca1e8d11cbfc237e919.zip#Adafruit_DHT==1.1.0 -# PAHO MQTT Binding (mqtt) +# PAHO MQTT (mqtt) paho-mqtt==1.1 # PyModbus (modbus) @@ -121,27 +121,25 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6 # Verisure (verisure) https://github.com/persandstrom/python-verisure/archive/9873c4527f01b1ba1f72ae60f7f35854390d59be.zip#python-verisure==0.2.6 -# Python tools for interacting with IFTTT Maker Channel (ifttt) +# IFTTT Maker Channel (ifttt) pyfttt==0.3 -# sensor.sabnzbd +# SABnzbd (sensor.sabnzbd) https://github.com/balloob/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1 -# switch.vera -# sensor.vera -# light.vera +# Vera (*.vera) https://github.com/balloob/home-assistant-vera-api/archive/a8f823066ead6c7da6fb5e7abaf16fef62e63364.zip#python-vera==0.1 -# Sonos bindings (media_player.sonos) +# Sonos (media_player.sonos) SoCo==0.11.1 # PlexAPI (media_player.plex) https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 -# python-pysnmp (device_tracker.snmp) +# SNMP (device_tracker.snmp) pysnmp==4.2.5 -# Blinkstick +# Blinkstick (light.blinksticklight) blinkstick==1.1.7 # Telegram (notify.telegram) From a8a172c8b730d79372199a81f3158c5cc0b265b4 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 23:24:26 +0200 Subject: [PATCH 82/88] Add link to docs and remove configuration details from file header --- homeassistant/components/sensor/arest.py | 44 ++---------------------- 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 7d2192e8920..6a11aa7189c 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -3,48 +3,8 @@ homeassistant.components.sensor.arest ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The arest sensor will consume an exposed aREST API of a device. -Configuration: - -To use the arest sensor you will need to add something like the following -to your configuration.yaml file. - -sensor: - platform: arest - resource: http://IP_ADDRESS - monitored_variables: - - name: temperature - unit: '°C' - - name: humidity - unit: '%' - -Variables: - -resource: -*Required -IP address of the device that is exposing an aREST API. - -These are the variables for the monitored_variables array: - -name -*Required -The name of the variable you wish to monitor. - -unit -*Optional -Defines the units of measurement of the sensor, if any. - -Details for the API: http://arest.io - -Format of a default JSON response by aREST: -{ - "variables":{ - "temperature":21, - "humidity":89 - }, - "id":"device008", - "name":"Bedroom", - "connected":true -} +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.arest.html """ import logging import requests From 47f994b86791653e1d6c6277940f805e19e0e696 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 23:33:59 +0200 Subject: [PATCH 83/88] Move configuration details to docs --- homeassistant/components/media_player/plex.py | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/media_player/plex.py b/homeassistant/components/media_player/plex.py index 46eb8947d99..5fac9ecb0f0 100644 --- a/homeassistant/components/media_player/plex.py +++ b/homeassistant/components/media_player/plex.py @@ -1,34 +1,11 @@ """ homeassistant.components.media_player.plex ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Provides an interface to the Plex API. -Provides an interface to the Plex API - -Configuration: - -To use Plex add something like this to your configuration: - -media_player: - platform: plex - name: plex_server - user: plex - password: my_secure_password - -Variables: - -name -*Required -The name of the backend device (Under Plex Media Server > settings > server). - -user -*Required -The Plex username - -password -*Required -The Plex password +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.plex.html """ - import logging from datetime import timedelta @@ -49,10 +26,8 @@ _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK -# pylint: disable=abstract-method -# pylint: disable=unused-argument - +# pylint: disable=abstract-method, unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the plex platform. """ from plexapi.myplex import MyPlexUser @@ -68,7 +43,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_devices(): - """ Updates the devices objects """ + """ Updates the devices objects. """ try: devices = plexuser.devices() except BadRequest: @@ -94,7 +69,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_sessions(): - """ Updates the sessions objects """ + """ Updates the sessions objects. """ try: sessions = plexserver.sessions() except BadRequest: @@ -113,7 +88,6 @@ class PlexClient(MediaPlayerDevice): """ Represents a Plex device. """ # pylint: disable=too-many-public-methods - def __init__(self, device, plex_sessions, update_devices, update_sessions): self.plex_sessions = plex_sessions self.update_devices = update_devices @@ -121,12 +95,12 @@ class PlexClient(MediaPlayerDevice): self.set_device(device) def set_device(self, device): - """ Sets the device property """ + """ Sets the device property. """ self.device = device @property def session(self): - """ Returns the session, if any """ + """ Returns the session, if any. """ if self.device.clientIdentifier not in self.plex_sessions: return None @@ -196,21 +170,21 @@ class PlexClient(MediaPlayerDevice): @property def media_season(self): - """ Season of curent playing media. (TV Show only) """ + """ Season of curent playing media (TV Show only). """ from plexapi.video import Show if isinstance(self.session, Show): return self.session.seasons()[0].index @property def media_series_title(self): - """ Series title of current playing media. (TV Show only)""" + """ Series title of current playing media (TV Show only). """ from plexapi.video import Show if isinstance(self.session, Show): return self.session.grandparentTitle @property def media_episode(self): - """ Episode of current playing media. (TV Show only) """ + """ Episode of current playing media (TV Show only). """ from plexapi.video import Show if isinstance(self.session, Show): return self.session.index From f07d07432d021cf1db599e8fea1fb000772e2892 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 23:38:28 +0200 Subject: [PATCH 84/88] Add telegram --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d1baa5c50f..0079cdaf14b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Built home automation on top of your devices: * Turn on lights slowly during sun set to compensate for less light * Turn off all lights and devices when everybody leaves the house * Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects - * Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), and [Jabber (XMPP)](http://xmpp.org) + * Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), [Telegram](https://telegram.org/), and [Jabber (XMPP)](http://xmpp.org) The system is built modular so support for other devices or actions can be implemented easily. See also the [section on architecture](https://home-assistant.io/developers/architecture.html) and the [section on creating your own components](https://home-assistant.io/developers/creating_components.html). From c3de67041aafd929ffc6c9d72660183ec5355cd6 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 23:40:06 +0200 Subject: [PATCH 85/88] Add plex --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0079cdaf14b..1d5dd5711b2 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Examples of devices it can interface it: * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable WAP/WRT * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors - * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)) + * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)) * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) * Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org). * [See full list of supported devices](https://home-assistant.io/components/) From d3c472252920b76feddd4bec3b7bb16794be0bdc Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 9 Oct 2015 23:45:36 +0200 Subject: [PATCH 86/88] Add some other components --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1d5dd5711b2..36777acc517 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,12 @@ Check out [the website](https://home-assistant.io) for [a demo][demo], installat Examples of devices it can interface it: - * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable WAP/WRT + * Monitoring connected devices to a wireless router: [OpenWrt](https://openwrt.org/), [Tomato](http://www.polarcloud.com/tomato), [Netgear](http://netgear.com), [DD-WRT](http://www.dd-wrt.com/site/index), [TPLink](http://www.tp-link.us/), [ASUSWRT](http://event.asus.com/2013/nw/ASUSWRT/) and any SNMP capable Linksys WAP/WRT + * * [Philips Hue](http://meethue.com) lights, [WeMo](http://www.belkin.com/us/Products/home-automation/c/wemo-home-automation/) switches, [Edimax](http://www.edimax.com/) switches, [Efergy](https://efergy.com) energy monitoring, RFXtrx sensors, and [Tellstick](http://www.telldus.se/products/tellstick) devices and sensors * [Google Chromecasts](http://www.google.com/intl/en/chrome/devices/chromecast), [Music Player Daemon](http://www.musicpd.org/), [Logitech Squeezebox](https://en.wikipedia.org/wiki/Squeezebox_%28network_music_player%29), [Plex](https://plex.tv/), [Kodi (XBMC)](http://kodi.tv/), and iTunes (by way of [itunes-api](https://github.com/maddox/itunes-api)) - * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) + * Support for [ISY994](https://www.universal-devices.com/residential/isy994i-series/) (Insteon and X10 devices), [Z-Wave](http://www.z-wave.com/), [Nest Thermostats](https://nest.com/), [RFXtrx](http://www.rfxcom.com/), [Arduino](https://www.arduino.cc/), [Raspberry Pi](https://www.raspberrypi.org/), and [Modbus](http://www.modbus.org/) + * Interaction with [IFTTT](https://ifttt.com/) * Integrate data from the [Bitcoin](https://bitcoin.org) network, meteorological data from [OpenWeatherMap](http://openweathermap.org/) and [Forecast.io](https://forecast.io/), [Transmission](http://www.transmissionbt.com/), or [SABnzbd](http://sabnzbd.org). * [See full list of supported devices](https://home-assistant.io/components/) @@ -29,7 +31,7 @@ Built home automation on top of your devices: * Turn on the lights when people get home after sun set * Turn on lights slowly during sun set to compensate for less light * Turn off all lights and devices when everybody leaves the house - * Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects + * Offers a [REST API](https://home-assistant.io/developers/api.html) and can interface with MQTT for easy integration with other projects like [OwnTracks](http://owntracks.org/) * Allow sending notifications using [Instapush](https://instapush.im), [Notify My Android (NMA)](http://www.notifymyandroid.com/), [PushBullet](https://www.pushbullet.com/), [PushOver](https://pushover.net/), [Slack](https://slack.com/), [Telegram](https://telegram.org/), and [Jabber (XMPP)](http://xmpp.org) The system is built modular so support for other devices or actions can be implemented easily. See also the [section on architecture](https://home-assistant.io/developers/architecture.html) and the [section on creating your own components](https://home-assistant.io/developers/creating_components.html). From 3a3b8bbb45541ee4a94f87c522093ed8da81c19a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 10 Oct 2015 10:33:09 -0700 Subject: [PATCH 87/88] Fix packaging issues --- MANIFEST.in | 7 +++++-- .../components/frontend/www_static/__init__.py | 0 .../frontend/www_static/images/__init__.py | 0 homeassistant/const.py | 2 +- homeassistant/startup/__init__.py | 0 setup.py | 13 +++++++------ 6 files changed, 13 insertions(+), 9 deletions(-) delete mode 100644 homeassistant/components/frontend/www_static/__init__.py delete mode 100644 homeassistant/components/frontend/www_static/images/__init__.py delete mode 100644 homeassistant/startup/__init__.py diff --git a/MANIFEST.in b/MANIFEST.in index 53d3a9e22a9..8233015e646 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,5 @@ -recursive-exclude tests * -recursive-include homeassistant services.yaml +include README.md +include LICENSE +graft homeassistant +prune homeassistant/components/frontend/www_static/home-assistant-polymer +recursive-exclude * *.py[co] diff --git a/homeassistant/components/frontend/www_static/__init__.py b/homeassistant/components/frontend/www_static/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/homeassistant/components/frontend/www_static/images/__init__.py b/homeassistant/components/frontend/www_static/images/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/homeassistant/const.py b/homeassistant/const.py index 35e3e2c93ae..0573a0cbbf7 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.5dev0" +__version__ = "0.7.5.dev0" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*' diff --git a/homeassistant/startup/__init__.py b/homeassistant/startup/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/setup.py b/setup.py index 044d5428809..b9b5cdd0d5d 100755 --- a/setup.py +++ b/setup.py @@ -9,11 +9,12 @@ DOWNLOAD_URL = ('https://github.com/balloob/home-assistant/archive/' PACKAGES = find_packages(exclude=['tests', 'tests.*']) -PACKAGE_DATA = \ - {'homeassistant.components.frontend': ['index.html.template'], - 'homeassistant.components.frontend.www_static': ['*.*'], - 'homeassistant.components.frontend.www_static.images': ['*.*'], - 'homeassistant.startup': ['*.*']} +# PACKAGE_DATA = \ +# {'homeassistant.components.frontend': ['index.html.template'], +# 'homeassistant.components.frontend.www_static': ['*.*'], +# 'homeassistant.components.frontend.www_static.images': ['*.*'], +# 'homeassistant.components.mqtt': ['*.crt'], +# 'homeassistant.startup': ['*.*']} REQUIRES = [ 'requests>=2,<3', @@ -23,6 +24,7 @@ REQUIRES = [ 'vincenty==0.1.2' ] + # package_data=PACKAGE_DATA, setup( name=PACKAGE_NAME, version=__version__, @@ -34,7 +36,6 @@ setup( description='Open-source home automation platform running on Python 3.', packages=PACKAGES, include_package_data=True, - package_data=PACKAGE_DATA, zip_safe=False, platforms='any', install_requires=REQUIRES, From 6a18205d2ea8f97a9c8509e87e679ce98fdca00e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 10 Oct 2015 11:39:29 -0700 Subject: [PATCH 88/88] Update to version 0.7.5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 0573a0cbbf7..a84f4571b8e 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Constants used by Home Assistant components. """ -__version__ = "0.7.5.dev0" +__version__ = "0.7.5" # Can be used to specify a catch all when registering state or event listeners. MATCH_ALL = '*'