From 74022a39782d4251d30b9942b566fb0eca8d67c1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 23 Apr 2016 19:55:05 +0200 Subject: [PATCH] New configuration for rfxtrx sensor --- homeassistant/components/rfxtrx.py | 156 +++++++++++++--------- homeassistant/components/sensor/rfxtrx.py | 64 +++++---- 2 files changed, 128 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/rfxtrx.py b/homeassistant/components/rfxtrx.py index 69de1d02601..a217570e4f3 100644 --- a/homeassistant/components/rfxtrx.py +++ b/homeassistant/components/rfxtrx.py @@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/rfxtrx/ """ import logging +from collections import OrderedDict import voluptuous as vol import homeassistant.helpers.config_validation as cv @@ -22,7 +23,6 @@ ATTR_DEVICE = 'device' ATTR_DEBUG = 'debug' ATTR_STATE = 'state' ATTR_NAME = 'name' -ATTR_PACKETID = 'packetid' ATTR_FIREEVENT = 'fire_event' ATTR_DATA_TYPE = 'data_type' ATTR_DUMMY = 'dummy' @@ -45,21 +45,39 @@ def validate_packetid(value): else: raise vol.Invalid('invalid packet id for {}'.format(value)) -# Share between rfxtrx platforms -VALID_DEVICE_ID = vol.All(cv.string, vol.Lower) -VALID_SENSOR_DEVICE_ID = vol.All(VALID_DEVICE_ID, - vol.truth(lambda val: - val.startswith('sensor_'))) - DEVICE_SCHEMA = vol.Schema({ vol.Required(ATTR_NAME): cv.string, - vol.Required(ATTR_PACKETID): validate_packetid, vol.Optional(ATTR_FIREEVENT, default=False): cv.boolean, }) + +def _valid_device(value): + """Validate a dictionary of devices definitions.""" + config = OrderedDict() + for key, device in value.items(): + # Still accept old configuration + if 'packetid' in device.keys(): + print(key, device.keys(), device, config) + msg = 'You are using an outdated configuration of the rfxtrx ' +\ + 'devuce, {}. Your new config should be:\n{}: \n\t name:{}\n'\ + .format(key, device.get('packetid'), + device.get(ATTR_NAME, 'deivce_name')) + _LOGGER.warning(msg) + key = device.get('packetid') + device.pop('packetid') + try: + key = validate_packetid(key) + config[key] = DEVICE_SCHEMA(device) + if not config[key][ATTR_NAME]: + config[key][ATTR_NAME] = key + except vol.MultipleInvalid as ex: + raise vol.Invalid('Rfxtrx deive {} is invalid: {}' + .format(key, ex)) + return config + DEFAULT_SCHEMA = vol.Schema({ vol.Required("platform"): DOMAIN, - vol.Required(CONF_DEVICES): {cv.slug: DEVICE_SCHEMA}, + vol.Required(CONF_DEVICES): vol.All(dict, _valid_device), vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean, vol.Optional(CONF_SIGNAL_REPETITIONS, default=DEFAULT_SIGNAL_REPETITIONS): vol.Coerce(int), @@ -141,7 +159,9 @@ def get_devices_from_config(config, device): signal_repetitions = config[CONF_SIGNAL_REPETITIONS] devices = [] - for device_id, entity_info in config[CONF_DEVICES].items(): + for packet_id, entity_info in config[CONF_DEVICES].items(): + event = get_rfx_object(packet_id) + device_id = slugify(event.device.id_string.lower()) if device_id in RFX_DEVICES: continue _LOGGER.info("Add %s rfxtrx", entity_info[ATTR_NAME]) @@ -150,8 +170,7 @@ def get_devices_from_config(config, device): fire_event = entity_info[ATTR_FIREEVENT] datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event} - rfxobject = get_rfx_object(entity_info[ATTR_PACKETID]) - new_device = device(entity_info[ATTR_NAME], rfxobject, datas, + new_device = device(entity_info[ATTR_NAME], event, datas, signal_repetitions) RFX_DEVICES[device_id] = new_device devices.append(new_device) @@ -161,68 +180,67 @@ def get_devices_from_config(config, device): def get_new_device(event, config, device): """Add entity if not exist and the automatic_add is True.""" device_id = slugify(event.device.id_string.lower()) - if device_id not in RFX_DEVICES: - automatic_add = config[ATTR_AUTOMATIC_ADD] - if not automatic_add: - return + if device_id in RFX_DEVICES: + return - _LOGGER.info( - "Automatic add %s rfxtrx device (Class: %s Sub: %s)", - device_id, - event.device.__class__.__name__, - event.device.subtype - ) - pkt_id = "".join("{0:02x}".format(x) for x in event.data) - entity_name = "%s : %s" % (device_id, pkt_id) - datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} - signal_repetitions = config[CONF_SIGNAL_REPETITIONS] - new_device = device(entity_name, event, datas, - signal_repetitions) - RFX_DEVICES[device_id] = new_device - return new_device + automatic_add = config[ATTR_AUTOMATIC_ADD] + if not automatic_add: + return + + _LOGGER.info( + "Automatic add %s rfxtrx device (Class: %s Sub: %s)", + device_id, + event.device.__class__.__name__, + event.device.subtype + ) + pkt_id = "".join("{0:02x}".format(x) for x in event.data) + entity_name = "%s : %s" % (device_id, pkt_id) + datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} + signal_repetitions = config[CONF_SIGNAL_REPETITIONS] + new_device = device(entity_name, event, datas, + signal_repetitions) + RFX_DEVICES[device_id] = new_device + return new_device def apply_received_command(event): """Apply command from rfxtrx.""" device_id = slugify(event.device.id_string.lower()) # Check if entity exists or previously added automatically - if device_id in RFX_DEVICES: - _LOGGER.debug( - "EntityID: %s device_update. Command: %s", - device_id, - event.values['Command'] + if device_id not in RFX_DEVICES: + return + + _LOGGER.debug( + "EntityID: %s device_update. Command: %s", + device_id, + event.values['Command'] + ) + + if event.values['Command'] == 'On'\ + or event.values['Command'] == 'Off': + + # Update the rfxtrx device state + is_on = event.values['Command'] == 'On' + RFX_DEVICES[device_id].update_state(is_on) + + elif hasattr(RFX_DEVICES[device_id], 'brightness')\ + and event.values['Command'] == 'Set level': + _brightness = (event.values['Dim level'] * 255 // 100) + + # Update the rfxtrx device state + is_on = _brightness > 0 + RFX_DEVICES[device_id].update_state(is_on, _brightness) + + # Fire event + if RFX_DEVICES[device_id].should_fire_event: + RFX_DEVICES[device_id].hass.bus.fire( + EVENT_BUTTON_PRESSED, { + ATTR_ENTITY_ID: + RFX_DEVICES[device_id].entity_id, + ATTR_STATE: event.values['Command'].lower() + } ) - if event.values['Command'] == 'On'\ - or event.values['Command'] == 'Off': - - # Update the rfxtrx device state - is_on = event.values['Command'] == 'On' - # pylint: disable=protected-access - RFX_DEVICES[device_id]._state = is_on - RFX_DEVICES[device_id].update_ha_state() - - elif hasattr(RFX_DEVICES[device_id], 'brightness')\ - and event.values['Command'] == 'Set level': - # pylint: disable=protected-access - RFX_DEVICES[device_id]._brightness = \ - (event.values['Dim level'] * 255 // 100) - - # Update the rfxtrx device state - is_on = RFX_DEVICES[device_id]._brightness > 0 - RFX_DEVICES[device_id]._state = is_on - RFX_DEVICES[device_id].update_ha_state() - - # Fire event - if RFX_DEVICES[device_id].should_fire_event: - RFX_DEVICES[device_id].hass.bus.fire( - EVENT_BUTTON_PRESSED, { - ATTR_ENTITY_ID: - RFX_DEVICES[device_id].entity_id, - ATTR_STATE: event.values['Command'].lower() - } - ) - class RfxtrxDevice(Entity): """Represents a Rfxtrx device. @@ -233,11 +251,11 @@ class RfxtrxDevice(Entity): def __init__(self, name, event, datas, signal_repetitions): """Initialize the device.""" + self.signal_repetitions = signal_repetitions self._name = name self._event = event self._state = datas[ATTR_STATE] self._should_fire_event = datas[ATTR_FIREEVENT] - self.signal_repetitions = signal_repetitions self._brightness = 0 @property @@ -269,6 +287,12 @@ class RfxtrxDevice(Entity): """Turn the device off.""" self._send_command("turn_off") + def update_state(self, state, brightness=0): + """Update det state of the device.""" + self._state = state + self._brightness = brightness + self.update_ha_state() + def _send_command(self, command, brightness=0): if not self._event: return diff --git a/homeassistant/components/sensor/rfxtrx.py b/homeassistant/components/sensor/rfxtrx.py index ddd68009c48..7fde6a6dc77 100644 --- a/homeassistant/components/sensor/rfxtrx.py +++ b/homeassistant/components/sensor/rfxtrx.py @@ -14,7 +14,7 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import slugify from homeassistant.components.rfxtrx import ( - ATTR_AUTOMATIC_ADD, ATTR_PACKETID, ATTR_NAME, + ATTR_AUTOMATIC_ADD, ATTR_NAME, CONF_DEVICES, ATTR_DATA_TYPE) DEPENDENCIES = ['rfxtrx'] @@ -31,18 +31,27 @@ _LOGGER = logging.getLogger(__name__) DEVICE_SCHEMA = vol.Schema({ vol.Optional(ATTR_NAME, default=None): cv.string, - vol.Required(ATTR_PACKETID): rfxtrx.validate_packetid, - vol.Optional(ATTR_DATA_TYPE, default=None): - vol.In(list(DATA_TYPES.keys())), + vol.Optional(ATTR_DATA_TYPE, default=[]): + vol.All(cv.ensure_list, [vol.In(DATA_TYPES.keys())]), }) -def _valid_device(value): +def _valid_sensor(value): """Validate a dictionary of devices definitions.""" config = OrderedDict() for key, device in value.items(): + # Still accept old configuration + if 'packetid' in device.keys(): + print(key, device.keys(), device, config) + msg = 'You are using an outdated configuration of the rfxtrx ' +\ + 'sensor, {}. Your new config should be:\n{}: \n\t name:{}\n'\ + .format(key, device.get('packetid'), + device.get(ATTR_NAME, 'sensor_name')) + _LOGGER.warning(msg) + key = device.get('packetid') + device.pop('packetid') try: - key = rfxtrx.VALID_SENSOR_DEVICE_ID(key) + key = rfxtrx.validate_packetid(key) config[key] = DEVICE_SCHEMA(device) if not config[key][ATTR_NAME]: config[key][ATTR_NAME] = key @@ -54,7 +63,7 @@ def _valid_device(value): PLATFORM_SCHEMA = vol.Schema({ vol.Required("platform"): rfxtrx.DOMAIN, - vol.Required(CONF_DEVICES): vol.All(dict, _valid_device), + vol.Required(CONF_DEVICES): vol.All(dict, _valid_sensor), vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean, }, extra=vol.ALLOW_EXTRA) @@ -64,15 +73,20 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): from RFXtrx import SensorEvent sensors = [] - for device_id, entity_info in config['devices'].items(): + for packet_id, entity_info in config['devices'].items(): + event = rfxtrx.get_rfx_object(packet_id) + device_id = "sensor_" + slugify(event.device.id_string.lower()) + if device_id in rfxtrx.RFX_DEVICES: continue _LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME]) - event = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID]) - new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME], - entity_info[ATTR_DATA_TYPE]) - rfxtrx.RFX_DEVICES[slugify(device_id)] = new_sensor - sensors.append(new_sensor) + sub_sensors = {} + for _data_type in cv.ensure_list(entity_info[ATTR_DATA_TYPE]): + new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME], + _data_type) + sensors.append(new_sensor) + sub_sensors[_data_type] = new_sensor + rfxtrx.RFX_DEVICES[slugify(device_id)] = sub_sensors add_devices_callback(sensors) @@ -84,13 +98,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): device_id = "sensor_" + slugify(event.device.id_string.lower()) if device_id in rfxtrx.RFX_DEVICES: - rfxtrx.RFX_DEVICES[device_id].event = event - k = 2 - _device_id = device_id + "_" + str(k) - while _device_id in rfxtrx.RFX_DEVICES: - rfxtrx.RFX_DEVICES[_device_id].event = event - k = k + 1 - _device_id = device_id + "_" + str(k) + sensors = rfxtrx.RFX_DEVICES[device_id] + for key in sensors: + sensors[key].event = event return # Add entity if not exist and the automatic_add is True @@ -103,7 +113,9 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): pkt_id) new_sensor = RfxtrxSensor(event, entity_name) - rfxtrx.RFX_DEVICES[device_id] = new_sensor + sub_sensors = {} + sub_sensors[new_sensor.data_type] = new_sensor + rfxtrx.RFX_DEVICES[device_id] = sub_sensors add_devices_callback([new_sensor]) if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: @@ -117,16 +129,16 @@ class RfxtrxSensor(Entity): """Initialize the sensor.""" self.event = event self._unit_of_measurement = None - self._data_type = None + self.data_type = None self._name = name if data_type: - self._data_type = data_type + self.data_type = data_type self._unit_of_measurement = DATA_TYPES[data_type] return for data_type in DATA_TYPES: if data_type in self.event.values: self._unit_of_measurement = DATA_TYPES[data_type] - self._data_type = data_type + self.data_type = data_type break def __str__(self): @@ -136,8 +148,8 @@ class RfxtrxSensor(Entity): @property def state(self): """Return the state of the sensor.""" - if self._data_type: - return self.event.values[self._data_type] + if self.data_type: + return self.event.values[self.data_type] return None @property