diff --git a/.coveragerc b/.coveragerc index 21536e075a0..e15b96287f1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -26,6 +26,9 @@ omit = homeassistant/components/zwave.py homeassistant/components/*/zwave.py + homeassistant/components/rfxtrx.py + homeassistant/components/*/rfxtrx.py + homeassistant/components/ifttt.py homeassistant/components/browser.py homeassistant/components/camera/* @@ -72,7 +75,6 @@ omit = homeassistant/components/sensor/mysensors.py homeassistant/components/sensor/openweathermap.py homeassistant/components/sensor/rest.py - homeassistant/components/sensor/rfxtrx.py homeassistant/components/sensor/rpi_gpio.py homeassistant/components/sensor/sabnzbd.py homeassistant/components/sensor/swiss_public_transport.py diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index c8d99bc3ea2..6989009b2d5 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit c8d99bc3ea21cdd7bfb39e7700f92ed09f4b9efd +Subproject commit 6989009b2d59e39fd39b3025ff5899877f618bd3 diff --git a/homeassistant/components/light/rfxtrx.py b/homeassistant/components/light/rfxtrx.py new file mode 100644 index 00000000000..f76d9f7ed5b --- /dev/null +++ b/homeassistant/components/light/rfxtrx.py @@ -0,0 +1,129 @@ +""" +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 +import RFXtrx as rfxtrxmod + +from homeassistant.components.light import Light +from homeassistant.util import slugify + +DEPENDENCIES = ['rfxtrx'] + +_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: + 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.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) + + add_devices_callback(lights) + + def light_update(event): + """ Callback for sensor updates from the RFXtrx gateway. """ + if not isinstance(event.device, rfxtrxmod.LightingDevice): + return + + # 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 not automatic_add: + return + + _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': + 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) + + +class RfxtrxLight(Light): + """ Provides a demo switch. """ + def __init__(self, name, event, state): + self._name = name + self._event = event + 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. """ + + if hasattr(self, '_event') and 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. """ + + if hasattr(self, '_event') and 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 new file mode 100644 index 00000000000..79378b85e78 --- /dev/null +++ b/homeassistant/components/rfxtrx.py @@ -0,0 +1,99 @@ +""" +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 +from homeassistant.util import slugify + +DEPENDENCIES = [] +REQUIREMENTS = ['https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip' + + '#RFXtrx==0.2'] + +DOMAIN = "rfxtrx" +CONF_DEVICE = 'device' +CONF_DEBUG = 'debug' +RECEIVED_EVT_SUBSCRIBERS = [] +RFX_DEVICES = {} +_LOGGER = logging.getLogger(__name__) +RFXOBJECT = None + + +def setup(hass, config): + """ Setup the Rfxtrx component. """ + + # Declare the Handle event + 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 = "%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: + 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 + 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] + debug = config[DOMAIN].get(CONF_DEBUG, False) + + RFXOBJECT = rfxtrxmod.Core(device, handle_receive, debug=debug) + + return True + + +def get_rfx_object(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) + 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 4cb8a939d5e..b09d9d4a09c 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -24,9 +24,11 @@ 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'] +DEPENDENCIES = ['rfxtrx'] DATA_TYPES = OrderedDict([ ('Temperature', TEMP_CELCIUS), @@ -34,32 +36,30 @@ DATA_TYPES = OrderedDict([ ('Barometer', ''), ('Wind direction', ''), ('Rain rate', '')]) +_LOGGER = logging.getLogger(__name__) -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__) - - sensors = {} # keep track of sensors added to HA 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 + if isinstance(event.device, SensorEvent): + entity_id = slugify(event.device.id_string.lower()) - device = config.get("device", "") - rfxtrx.Core(device, sensor_update) + # 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', True) + if automatic_add: + _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): diff --git a/homeassistant/components/switch/rfxtrx.py b/homeassistant/components/switch/rfxtrx.py new file mode 100644 index 00000000000..98963beb769 --- /dev/null +++ b/homeassistant/components/switch/rfxtrx.py @@ -0,0 +1,128 @@ +""" +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 + +DEPENDENCIES = ['rfxtrx'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Setup the RFXtrx platform. """ + + # Add switch from config file + switchs = [] + devices = config.get('devices') + 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.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) + + def switch_update(event): + """ Callback for sensor updates from the RFXtrx gateway. """ + if isinstance(event.device, LightingDevice): + return + + # 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 not automatic_add: + return + + _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': + 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) + + +class RfxtrxSwitch(SwitchDevice): + """ Provides a demo switch. """ + def __init__(self, name, event, state): + self._name = name + self._event = event + 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. """ + 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. """ + if self._event: + self._event.device.send_off(rfxtrx.RFXOBJECT.transport) + + self._state = False + self.update_ha_state()