New configuration for rfxtrx sensor

This commit is contained in:
Daniel 2016-04-23 19:55:05 +02:00
parent 2333c0ca3b
commit 74022a3978
2 changed files with 128 additions and 92 deletions

View File

@ -5,6 +5,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/rfxtrx/ https://home-assistant.io/components/rfxtrx/
""" """
import logging import logging
from collections import OrderedDict
import voluptuous as vol import voluptuous as vol
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
@ -22,7 +23,6 @@ ATTR_DEVICE = 'device'
ATTR_DEBUG = 'debug' ATTR_DEBUG = 'debug'
ATTR_STATE = 'state' ATTR_STATE = 'state'
ATTR_NAME = 'name' ATTR_NAME = 'name'
ATTR_PACKETID = 'packetid'
ATTR_FIREEVENT = 'fire_event' ATTR_FIREEVENT = 'fire_event'
ATTR_DATA_TYPE = 'data_type' ATTR_DATA_TYPE = 'data_type'
ATTR_DUMMY = 'dummy' ATTR_DUMMY = 'dummy'
@ -45,21 +45,39 @@ def validate_packetid(value):
else: else:
raise vol.Invalid('invalid packet id for {}'.format(value)) 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({ DEVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_NAME): cv.string, vol.Required(ATTR_NAME): cv.string,
vol.Required(ATTR_PACKETID): validate_packetid,
vol.Optional(ATTR_FIREEVENT, default=False): cv.boolean, 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({ DEFAULT_SCHEMA = vol.Schema({
vol.Required("platform"): DOMAIN, 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(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
vol.Optional(CONF_SIGNAL_REPETITIONS, default=DEFAULT_SIGNAL_REPETITIONS): vol.Optional(CONF_SIGNAL_REPETITIONS, default=DEFAULT_SIGNAL_REPETITIONS):
vol.Coerce(int), vol.Coerce(int),
@ -141,7 +159,9 @@ def get_devices_from_config(config, device):
signal_repetitions = config[CONF_SIGNAL_REPETITIONS] signal_repetitions = config[CONF_SIGNAL_REPETITIONS]
devices = [] 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: if device_id in RFX_DEVICES:
continue continue
_LOGGER.info("Add %s rfxtrx", entity_info[ATTR_NAME]) _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] fire_event = entity_info[ATTR_FIREEVENT]
datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event} datas = {ATTR_STATE: False, ATTR_FIREEVENT: fire_event}
rfxobject = get_rfx_object(entity_info[ATTR_PACKETID]) new_device = device(entity_info[ATTR_NAME], event, datas,
new_device = device(entity_info[ATTR_NAME], rfxobject, datas,
signal_repetitions) signal_repetitions)
RFX_DEVICES[device_id] = new_device RFX_DEVICES[device_id] = new_device
devices.append(new_device) devices.append(new_device)
@ -161,68 +180,67 @@ def get_devices_from_config(config, device):
def get_new_device(event, config, device): def get_new_device(event, config, device):
"""Add entity if not exist and the automatic_add is True.""" """Add entity if not exist and the automatic_add is True."""
device_id = slugify(event.device.id_string.lower()) device_id = slugify(event.device.id_string.lower())
if device_id not in RFX_DEVICES: if device_id in RFX_DEVICES:
automatic_add = config[ATTR_AUTOMATIC_ADD] return
if not automatic_add:
return
_LOGGER.info( automatic_add = config[ATTR_AUTOMATIC_ADD]
"Automatic add %s rfxtrx device (Class: %s Sub: %s)", if not automatic_add:
device_id, return
event.device.__class__.__name__,
event.device.subtype _LOGGER.info(
) "Automatic add %s rfxtrx device (Class: %s Sub: %s)",
pkt_id = "".join("{0:02x}".format(x) for x in event.data) device_id,
entity_name = "%s : %s" % (device_id, pkt_id) event.device.__class__.__name__,
datas = {ATTR_STATE: False, ATTR_FIREEVENT: False} event.device.subtype
signal_repetitions = config[CONF_SIGNAL_REPETITIONS] )
new_device = device(entity_name, event, datas, pkt_id = "".join("{0:02x}".format(x) for x in event.data)
signal_repetitions) entity_name = "%s : %s" % (device_id, pkt_id)
RFX_DEVICES[device_id] = new_device datas = {ATTR_STATE: False, ATTR_FIREEVENT: False}
return new_device 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): def apply_received_command(event):
"""Apply command from rfxtrx.""" """Apply command from rfxtrx."""
device_id = slugify(event.device.id_string.lower()) device_id = slugify(event.device.id_string.lower())
# Check if entity exists or previously added automatically # Check if entity exists or previously added automatically
if device_id in RFX_DEVICES: if device_id not in RFX_DEVICES:
_LOGGER.debug( return
"EntityID: %s device_update. Command: %s",
device_id, _LOGGER.debug(
event.values['Command'] "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): class RfxtrxDevice(Entity):
"""Represents a Rfxtrx device. """Represents a Rfxtrx device.
@ -233,11 +251,11 @@ class RfxtrxDevice(Entity):
def __init__(self, name, event, datas, signal_repetitions): def __init__(self, name, event, datas, signal_repetitions):
"""Initialize the device.""" """Initialize the device."""
self.signal_repetitions = signal_repetitions
self._name = name self._name = name
self._event = event self._event = event
self._state = datas[ATTR_STATE] self._state = datas[ATTR_STATE]
self._should_fire_event = datas[ATTR_FIREEVENT] self._should_fire_event = datas[ATTR_FIREEVENT]
self.signal_repetitions = signal_repetitions
self._brightness = 0 self._brightness = 0
@property @property
@ -269,6 +287,12 @@ class RfxtrxDevice(Entity):
"""Turn the device off.""" """Turn the device off."""
self._send_command("turn_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): def _send_command(self, command, brightness=0):
if not self._event: if not self._event:
return return

View File

@ -14,7 +14,7 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify from homeassistant.util import slugify
from homeassistant.components.rfxtrx import ( from homeassistant.components.rfxtrx import (
ATTR_AUTOMATIC_ADD, ATTR_PACKETID, ATTR_NAME, ATTR_AUTOMATIC_ADD, ATTR_NAME,
CONF_DEVICES, ATTR_DATA_TYPE) CONF_DEVICES, ATTR_DATA_TYPE)
DEPENDENCIES = ['rfxtrx'] DEPENDENCIES = ['rfxtrx']
@ -31,18 +31,27 @@ _LOGGER = logging.getLogger(__name__)
DEVICE_SCHEMA = vol.Schema({ DEVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_NAME, default=None): cv.string, vol.Optional(ATTR_NAME, default=None): cv.string,
vol.Required(ATTR_PACKETID): rfxtrx.validate_packetid, vol.Optional(ATTR_DATA_TYPE, default=[]):
vol.Optional(ATTR_DATA_TYPE, default=None): vol.All(cv.ensure_list, [vol.In(DATA_TYPES.keys())]),
vol.In(list(DATA_TYPES.keys())),
}) })
def _valid_device(value): def _valid_sensor(value):
"""Validate a dictionary of devices definitions.""" """Validate a dictionary of devices definitions."""
config = OrderedDict() config = OrderedDict()
for key, device in value.items(): 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: try:
key = rfxtrx.VALID_SENSOR_DEVICE_ID(key) key = rfxtrx.validate_packetid(key)
config[key] = DEVICE_SCHEMA(device) config[key] = DEVICE_SCHEMA(device)
if not config[key][ATTR_NAME]: if not config[key][ATTR_NAME]:
config[key][ATTR_NAME] = key config[key][ATTR_NAME] = key
@ -54,7 +63,7 @@ def _valid_device(value):
PLATFORM_SCHEMA = vol.Schema({ PLATFORM_SCHEMA = vol.Schema({
vol.Required("platform"): rfxtrx.DOMAIN, 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, vol.Optional(ATTR_AUTOMATIC_ADD, default=False): cv.boolean,
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
@ -64,15 +73,20 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None):
from RFXtrx import SensorEvent from RFXtrx import SensorEvent
sensors = [] 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: if device_id in rfxtrx.RFX_DEVICES:
continue continue
_LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME]) _LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME])
event = rfxtrx.get_rfx_object(entity_info[ATTR_PACKETID]) sub_sensors = {}
new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME], for _data_type in cv.ensure_list(entity_info[ATTR_DATA_TYPE]):
entity_info[ATTR_DATA_TYPE]) new_sensor = RfxtrxSensor(event, entity_info[ATTR_NAME],
rfxtrx.RFX_DEVICES[slugify(device_id)] = new_sensor _data_type)
sensors.append(new_sensor) sensors.append(new_sensor)
sub_sensors[_data_type] = new_sensor
rfxtrx.RFX_DEVICES[slugify(device_id)] = sub_sensors
add_devices_callback(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()) device_id = "sensor_" + slugify(event.device.id_string.lower())
if device_id in rfxtrx.RFX_DEVICES: if device_id in rfxtrx.RFX_DEVICES:
rfxtrx.RFX_DEVICES[device_id].event = event sensors = rfxtrx.RFX_DEVICES[device_id]
k = 2 for key in sensors:
_device_id = device_id + "_" + str(k) sensors[key].event = event
while _device_id in rfxtrx.RFX_DEVICES:
rfxtrx.RFX_DEVICES[_device_id].event = event
k = k + 1
_device_id = device_id + "_" + str(k)
return return
# Add entity if not exist and the automatic_add is True # 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) pkt_id)
new_sensor = RfxtrxSensor(event, entity_name) 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]) add_devices_callback([new_sensor])
if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS: if sensor_update not in rfxtrx.RECEIVED_EVT_SUBSCRIBERS:
@ -117,16 +129,16 @@ class RfxtrxSensor(Entity):
"""Initialize the sensor.""" """Initialize the sensor."""
self.event = event self.event = event
self._unit_of_measurement = None self._unit_of_measurement = None
self._data_type = None self.data_type = None
self._name = name self._name = name
if data_type: if data_type:
self._data_type = data_type self.data_type = data_type
self._unit_of_measurement = DATA_TYPES[data_type] self._unit_of_measurement = DATA_TYPES[data_type]
return return
for data_type in DATA_TYPES: for data_type in DATA_TYPES:
if data_type in self.event.values: if data_type in self.event.values:
self._unit_of_measurement = DATA_TYPES[data_type] self._unit_of_measurement = DATA_TYPES[data_type]
self._data_type = data_type self.data_type = data_type
break break
def __str__(self): def __str__(self):
@ -136,8 +148,8 @@ class RfxtrxSensor(Entity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
if self._data_type: if self.data_type:
return self.event.values[self._data_type] return self.event.values[self.data_type]
return None return None
@property @property