Rewrite rfxtrx init logic to do away with global object (#37699)

* Rewrite init logic to do away with global object

* Put constant at end

* Use a set instead of list for device_ids
This commit is contained in:
Joakim Plate 2020-07-10 14:52:07 +02:00 committed by GitHub
parent b45a952d61
commit 67038c6ba8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 365 additions and 334 deletions

View file

@ -8,10 +8,8 @@ import voluptuous as vol
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_NAME,
ATTR_STATE,
CONF_DEVICE,
CONF_DEVICES,
CONF_HOST,
CONF_PORT,
EVENT_HOMEASSISTANT_START,
@ -25,6 +23,8 @@ import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from .const import DEVICE_PACKET_TYPE_LIGHTING4
DOMAIN = "rfxtrx"
DEFAULT_SIGNAL_REPETITIONS = 1
@ -80,7 +80,6 @@ DATA_TYPES = OrderedDict(
]
)
RFX_DEVICES = {}
_LOGGER = logging.getLogger(__name__)
DATA_RFXOBJECT = "rfxobject"
@ -207,31 +206,15 @@ def get_pt2262_cmd(device_id, data_bits):
return hex(data[-1] & mask)
def get_pt2262_device(device_id):
def find_possible_pt2262_device(device_ids, device_id):
"""Look for the device which id matches the given device_id parameter."""
for device in RFX_DEVICES.values():
if (
hasattr(device, "is_lighting4")
and device.masked_id is not None
and device.masked_id == get_pt2262_deviceid(device_id, device.data_bits)
):
_LOGGER.debug(
"rfxtrx: found matching device %s for %s", device_id, device.masked_id,
)
return device
return None
def find_possible_pt2262_device(device_id):
"""Look for the device which id matches the given device_id parameter."""
for dev_id, device in RFX_DEVICES.items():
if hasattr(device, "is_lighting4") and len(dev_id) == len(device_id):
for dev_id in device_ids:
if len(dev_id) == len(device_id):
size = None
for i, (char1, char2) in enumerate(zip(dev_id, device_id)):
if char1 != char2:
break
size = i
if size is not None:
size = len(dev_id) - size - 1
_LOGGER.info(
@ -246,60 +229,19 @@ def find_possible_pt2262_device(device_id):
dev_id[-size:],
device_id[-size:],
)
return device
return dev_id
return None
def get_devices_from_config(config, device):
"""Read rfxtrx configuration."""
signal_repetitions = config[CONF_SIGNAL_REPETITIONS]
def get_device_id(device, data_bits=None):
"""Calculate a device id for device."""
id_string = device.id_string
if data_bits and device.packettype == DEVICE_PACKET_TYPE_LIGHTING4:
masked_id = get_pt2262_deviceid(id_string, data_bits)
if masked_id:
id_string = str(masked_id)
devices = []
for packet_id, entity_info in config[CONF_DEVICES].items():
event = get_rfx_object(packet_id)
if event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
device_id = slugify(event.device.id_string.lower())
if device_id in RFX_DEVICES:
continue
_LOGGER.debug("Add %s rfxtrx", entity_info[ATTR_NAME])
# Check if i must fire event
fire_event = entity_info[ATTR_FIRE_EVENT]
datas = {ATTR_STATE: False, ATTR_FIRE_EVENT: fire_event}
new_device = device(
entity_info[ATTR_NAME], event.device, datas, signal_repetitions
)
RFX_DEVICES[device_id] = new_device
devices.append(new_device)
return devices
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 in RFX_DEVICES:
return
if not config[ATTR_AUTOMATIC_ADD]:
return
pkt_id = "".join(f"{x:02x}" for x in event.data)
_LOGGER.debug(
"Automatic add %s rfxtrx device (Class: %s Sub: %s Packet_id: %s)",
device_id,
event.device.__class__.__name__,
event.device.subtype,
pkt_id,
)
datas = {ATTR_STATE: False, ATTR_FIRE_EVENT: False}
signal_repetitions = config[CONF_SIGNAL_REPETITIONS]
new_device = device(pkt_id, event.device, datas, signal_repetitions, event=event)
RFX_DEVICES[device_id] = new_device
return new_device
return (f"{device.packettype:x}", f"{device.subtype:x}", id_string)
def fire_command_event(hass, entity_id, command):
@ -330,7 +272,8 @@ class RfxtrxDevice(Entity):
self._device = device
self._state = datas[ATTR_STATE]
self._should_fire_event = datas[ATTR_FIRE_EVENT]
self._unique_id = f"{device.packettype:x}_{device.subtype:x}_{device.id_string}"
self._device_id = get_device_id(device)
self._unique_id = "_".join(x for x in self._device_id)
if event:
self._apply_event(event)

View file

@ -13,31 +13,29 @@ from homeassistant.const import (
CONF_COMMAND_OFF,
CONF_COMMAND_ON,
CONF_DEVICE_CLASS,
CONF_DEVICES,
CONF_NAME,
)
from homeassistant.helpers import config_validation as cv, event as evt
from homeassistant.util import slugify
from . import (
ATTR_NAME,
CONF_AUTOMATIC_ADD,
CONF_DATA_BITS,
CONF_DEVICES,
CONF_FIRE_EVENT,
CONF_OFF_DELAY,
RFX_DEVICES,
SIGNAL_EVENT,
find_possible_pt2262_device,
fire_command_event,
get_device_id,
get_pt2262_cmd,
get_pt2262_device,
get_pt2262_deviceid,
get_rfx_object,
)
from .const import COMMAND_OFF_LIST, COMMAND_ON_LIST
from .const import COMMAND_OFF_LIST, COMMAND_ON_LIST, DEVICE_PACKET_TYPE_LIGHTING4
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_DEVICES, default={}): {
@ -61,28 +59,40 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
)
def _get_device_data_bits(device, device_bits):
"""Deduce data bits for device based on a cache of device bits."""
data_bits = None
if device.packettype == DEVICE_PACKET_TYPE_LIGHTING4:
for id_masked, bits in device_bits.items():
if get_pt2262_deviceid(device.id_string, bits) == id_masked:
data_bits = bits
break
return data_bits
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Binary Sensor platform to RFXtrx."""
sensors = []
device_ids = set()
device_bits = {}
pt2262_devices = []
for packet_id, entity 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 event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
if entity.get(CONF_DATA_BITS) is not None:
_LOGGER.debug(
"Masked device id: %s",
get_pt2262_deviceid(device_id, entity.get(CONF_DATA_BITS)),
)
device_id = get_device_id(event.device, data_bits=entity.get(CONF_DATA_BITS))
if device_id in device_ids:
continue
device_ids.add(device_id)
_LOGGER.debug(
"Add %s rfxtrx.binary_sensor (class %s)",
entity[ATTR_NAME],
entity.get(CONF_DEVICE_CLASS),
)
if event.device.packettype == DEVICE_PACKET_TYPE_LIGHTING4:
find_possible_pt2262_device(pt2262_devices, event.device.id_string)
pt2262_devices.append(event.device.id_string)
device = RfxtrxBinarySensor(
event.device,
@ -95,7 +105,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
entity.get(CONF_COMMAND_OFF),
)
sensors.append(device)
RFX_DEVICES[device_id] = device
add_entities(sensors)
@ -104,35 +113,28 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
if not isinstance(event, rfxtrxmod.ControlEvent):
return
device_id = slugify(event.device.id_string.lower())
data_bits = _get_device_data_bits(event.device, device_bits)
sensor = RFX_DEVICES.get(device_id, get_pt2262_device(device_id))
device_id = get_device_id(event.device, data_bits=data_bits)
if device_id in device_ids:
return
device_ids.add(device_id)
if sensor is None:
# Add the entity if not exists and automatic_add is True
if not config[CONF_AUTOMATIC_ADD]:
return
if event.device.packettype == 0x13:
poss_dev = find_possible_pt2262_device(device_id)
if poss_dev is not None:
poss_id = slugify(poss_dev.event.device.id_string.lower())
_LOGGER.debug("Found possible matching device ID: %s", poss_id)
pkt_id = "".join(f"{x:02x}" for x in event.data)
sensor = RfxtrxBinarySensor(event.device, pkt_id, event=event)
RFX_DEVICES[device_id] = sensor
add_entities([sensor])
_LOGGER.info(
"Added binary sensor %s (Device ID: %s Class: %s Sub: %s)",
pkt_id,
slugify(event.device.id_string.lower()),
event.device.__class__.__name__,
event.device.subtype,
)
_LOGGER.info(
"Added binary sensor (Device ID: %s Class: %s Sub: %s)",
event.device.id_string.lower(),
event.device.__class__.__name__,
event.device.subtype,
)
pkt_id = "".join(f"{x:02x}" for x in event.data)
sensor = RfxtrxBinarySensor(
event.device, pkt_id, data_bits=data_bits, event=event
)
add_entities([sensor])
# Subscribe to main RFXtrx events
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, binary_sensor_update)
if config[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, binary_sensor_update)
class RfxtrxBinarySensor(BinarySensorEntity):
@ -158,22 +160,13 @@ class RfxtrxBinarySensor(BinarySensorEntity):
self._device_class = device_class
self._off_delay = off_delay
self._state = False
self.is_lighting4 = device.packettype == 0x13
self.delay_listener = None
self._data_bits = data_bits
self._cmd_on = cmd_on
self._cmd_off = cmd_off
if data_bits is not None:
self._masked_id = get_pt2262_deviceid(device.id_string.lower(), data_bits)
self._unique_id = (
f"{device.packettype:x}_{device.subtype:x}_{self._masked_id}"
)
else:
self._masked_id = None
self._unique_id = (
f"{device.packettype:x}_{device.subtype:x}_{device.id_string}"
)
self._device_id = get_device_id(device, data_bits=data_bits)
self._unique_id = "_".join(x for x in self._device_id)
if event:
self._apply_event(event)
@ -193,11 +186,6 @@ class RfxtrxBinarySensor(BinarySensorEntity):
"""Return the device name."""
return self._name
@property
def masked_id(self):
"""Return the masked device id (isolated address bits)."""
return self._masked_id
@property
def data_bits(self):
"""Return the number of data bits."""
@ -263,20 +251,15 @@ class RfxtrxBinarySensor(BinarySensorEntity):
def _apply_event(self, event):
"""Apply command from rfxtrx."""
if self.is_lighting4:
if event.device.packettype == DEVICE_PACKET_TYPE_LIGHTING4:
self._apply_event_lighting4(event)
else:
self._apply_event_standard(event)
def _handle_event(self, event):
"""Check if event applies to me and update."""
if self._masked_id:
masked_id = get_pt2262_deviceid(event.device.id_string, self._data_bits)
if masked_id != self._masked_id:
return
else:
if event.device.id_string != self._device.id_string:
return
if get_device_id(event.device, data_bits=self._data_bits) != self._device_id:
return
_LOGGER.debug(
"Binary sensor update (Device ID: %s Class: %s Sub: %s)",

View file

@ -14,3 +14,4 @@ COMMAND_OFF_LIST = [
"Down",
"Close (inline relay)",
]
DEVICE_PACKET_TYPE_LIGHTING4 = 0x13

View file

@ -1,23 +1,25 @@
"""Support for RFXtrx covers."""
import logging
import RFXtrx as rfxtrxmod
import voluptuous as vol
from homeassistant.components.cover import PLATFORM_SCHEMA, CoverEntity
from homeassistant.const import CONF_NAME, STATE_OPEN
from homeassistant.const import ATTR_STATE, CONF_DEVICES, CONF_NAME, STATE_OPEN
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.restore_state import RestoreEntity
from . import (
ATTR_FIRE_EVENT,
CONF_AUTOMATIC_ADD,
CONF_DEVICES,
CONF_FIRE_EVENT,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
SIGNAL_EVENT,
RfxtrxDevice,
fire_command_event,
get_devices_from_config,
get_new_device,
get_device_id,
get_rfx_object,
)
from .const import COMMAND_OFF_LIST, COMMAND_ON_LIST
@ -38,11 +40,32 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
}
)
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the RFXtrx cover."""
covers = get_devices_from_config(config, RfxtrxCover)
add_entities(covers)
device_ids = set()
entities = []
for packet_id, entity_info in config[CONF_DEVICES].items():
event = get_rfx_object(packet_id)
if event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
device_id = get_device_id(event.device)
if device_id in device_ids:
continue
device_ids.add(device_id)
datas = {ATTR_STATE: None, ATTR_FIRE_EVENT: entity_info[CONF_FIRE_EVENT]}
entity = RfxtrxCover(
entity_info[CONF_NAME], event.device, datas, config[CONF_SIGNAL_REPETITIONS]
)
entities.append(entity)
add_entities(entities)
def cover_update(event):
"""Handle cover updates from the RFXtrx gateway."""
@ -53,12 +76,28 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
):
return
new_device = get_new_device(event, config, RfxtrxCover)
if new_device:
add_entities([new_device])
device_id = get_device_id(event.device)
if device_id in device_ids:
return
device_ids.add(device_id)
_LOGGER.info(
"Added cover (Device ID: %s Class: %s Sub: %s)",
event.device.id_string.lower(),
event.device.__class__.__name__,
event.device.subtype,
)
pkt_id = "".join(f"{x:02x}" for x in event.data)
datas = {ATTR_STATE: False, ATTR_FIRE_EVENT: False}
entity = RfxtrxCover(
pkt_id, event.device, datas, DEFAULT_SIGNAL_REPETITIONS, event=event
)
add_entities([entity])
# Subscribe to main RFXtrx events
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, cover_update)
if config[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, cover_update)
class RfxtrxCover(RfxtrxDevice, CoverEntity, RestoreEntity):

View file

@ -10,21 +10,21 @@ from homeassistant.components.light import (
SUPPORT_BRIGHTNESS,
LightEntity,
)
from homeassistant.const import CONF_NAME, STATE_ON
from homeassistant.const import ATTR_STATE, CONF_DEVICES, CONF_NAME, STATE_ON
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.restore_state import RestoreEntity
from . import (
ATTR_FIRE_EVENT,
CONF_AUTOMATIC_ADD,
CONF_DEVICES,
CONF_FIRE_EVENT,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
SIGNAL_EVENT,
RfxtrxDevice,
fire_command_event,
get_devices_from_config,
get_new_device,
get_device_id,
get_rfx_object,
)
from .const import COMMAND_OFF_LIST, COMMAND_ON_LIST
@ -52,8 +52,29 @@ SUPPORT_RFXTRX = SUPPORT_BRIGHTNESS
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the RFXtrx platform."""
lights = get_devices_from_config(config, RfxtrxLight)
add_entities(lights)
device_ids = set()
# Add switch from config file
entities = []
for packet_id, entity_info in config[CONF_DEVICES].items():
event = get_rfx_object(packet_id)
if event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
device_id = get_device_id(event.device)
if device_id in device_ids:
continue
device_ids.add(device_id)
datas = {ATTR_STATE: None, ATTR_FIRE_EVENT: entity_info[CONF_FIRE_EVENT]}
entity = RfxtrxLight(
entity_info[CONF_NAME], event.device, datas, config[CONF_SIGNAL_REPETITIONS]
)
entities.append(entity)
add_entities(entities)
def light_update(event):
"""Handle light updates from the RFXtrx gateway."""
@ -63,12 +84,29 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
):
return
new_device = get_new_device(event, config, RfxtrxLight)
if new_device:
add_entities([new_device])
device_id = get_device_id(event.device)
if device_id in device_ids:
return
device_ids.add(device_id)
_LOGGER.debug(
"Added light (Device ID: %s Class: %s Sub: %s)",
event.device.id_string.lower(),
event.device.__class__.__name__,
event.device.subtype,
)
pkt_id = "".join(f"{x:02x}" for x in event.data)
datas = {ATTR_STATE: None, ATTR_FIRE_EVENT: False}
entity = RfxtrxLight(
pkt_id, event.device, datas, DEFAULT_SIGNAL_REPETITIONS, event=event
)
add_entities([entity])
# Subscribe to main RFXtrx events
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, light_update)
if config[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, light_update)
class RfxtrxLight(RfxtrxDevice, LightEntity, RestoreEntity):

View file

@ -5,21 +5,17 @@ from RFXtrx import SensorEvent
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME, CONF_NAME
from homeassistant.const import ATTR_ENTITY_ID, CONF_DEVICES, CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import slugify
from . import (
ATTR_DATA_TYPE,
ATTR_FIRE_EVENT,
CONF_AUTOMATIC_ADD,
CONF_DATA_TYPE,
CONF_DEVICES,
CONF_FIRE_EVENT,
DATA_TYPES,
RFX_DEVICES,
SIGNAL_EVENT,
get_device_id,
get_rfx_object,
)
@ -46,64 +42,63 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the RFXtrx platform."""
sensors = []
data_ids = set()
entities = []
for packet_id, entity_info in config[CONF_DEVICES].items():
event = get_rfx_object(packet_id)
device_id = "sensor_{}".format(slugify(event.device.id_string.lower()))
if device_id in RFX_DEVICES:
if event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
_LOGGER.info("Add %s rfxtrx.sensor", entity_info[ATTR_NAME])
sub_sensors = {}
data_types = entity_info[ATTR_DATA_TYPE]
if not data_types:
data_types = [""]
for data_type in DATA_TYPES:
if data_type in event.values:
data_types = [data_type]
break
for _data_type in data_types:
new_sensor = RfxtrxSensor(
if entity_info[CONF_DATA_TYPE]:
data_types = entity_info[CONF_DATA_TYPE]
else:
data_types = list(set(event.values) & set(DATA_TYPES))
device_id = get_device_id(event.device)
for data_type in data_types:
data_id = (*device_id, data_type)
if data_id in data_ids:
continue
data_ids.add(data_id)
entity = RfxtrxSensor(
event.device,
entity_info[ATTR_NAME],
_data_type,
entity_info[ATTR_FIRE_EVENT],
entity_info[CONF_NAME],
data_type,
entity_info[CONF_FIRE_EVENT],
)
sensors.append(new_sensor)
sub_sensors[_data_type] = new_sensor
RFX_DEVICES[device_id] = sub_sensors
add_entities(sensors)
entities.append(entity)
add_entities(entities)
def sensor_update(event):
"""Handle sensor updates from the RFXtrx gateway."""
if not isinstance(event, SensorEvent):
return
device_id = f"sensor_{slugify(event.device.id_string.lower())}"
if device_id in RFX_DEVICES:
return
# Add entity if not exist and the automatic_add is True
if not config[CONF_AUTOMATIC_ADD]:
return
pkt_id = "".join(f"{x:02x}" for x in event.data)
_LOGGER.info("Automatic add rfxtrx.sensor: %s", pkt_id)
device_id = get_device_id(event.device)
for data_type in set(event.values) & set(DATA_TYPES):
data_id = (*device_id, data_type)
if data_id in data_ids:
continue
data_ids.add(data_id)
data_type = ""
for _data_type in DATA_TYPES:
if _data_type in event.values:
data_type = _data_type
break
new_sensor = RfxtrxSensor(event.device, pkt_id, data_type, event=event)
sub_sensors = {}
sub_sensors[new_sensor.data_type] = new_sensor
RFX_DEVICES[device_id] = sub_sensors
add_entities([new_sensor])
_LOGGER.debug(
"Added sensor (Device ID: %s Class: %s Sub: %s)",
event.device.id_string.lower(),
event.device.__class__.__name__,
event.device.subtype,
)
entity = RfxtrxSensor(event.device, pkt_id, data_type, event=event)
add_entities([entity])
# Subscribe to main RFXtrx events
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, sensor_update)
if config[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, sensor_update)
class RfxtrxSensor(Entity):
@ -117,9 +112,8 @@ class RfxtrxSensor(Entity):
self.should_fire_event = should_fire_event
self.data_type = data_type
self._unit_of_measurement = DATA_TYPES.get(data_type, "")
self._unique_id = (
f"{device.packettype:x}_{device.subtype:x}_{device.id_string}_{data_type}"
)
self._device_id = get_device_id(device)
self._unique_id = "_".join(x for x in (*self._device_id, data_type))
if event:
self._apply_event(event)

View file

@ -5,21 +5,21 @@ import RFXtrx as rfxtrxmod
import voluptuous as vol
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
from homeassistant.const import CONF_NAME, STATE_ON
from homeassistant.const import ATTR_STATE, CONF_DEVICES, CONF_NAME, STATE_ON
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.restore_state import RestoreEntity
from . import (
ATTR_FIRE_EVENT,
CONF_AUTOMATIC_ADD,
CONF_DEVICES,
CONF_FIRE_EVENT,
CONF_SIGNAL_REPETITIONS,
DEFAULT_SIGNAL_REPETITIONS,
SIGNAL_EVENT,
RfxtrxDevice,
fire_command_event,
get_devices_from_config,
get_new_device,
get_device_id,
get_rfx_object,
)
from .const import COMMAND_OFF_LIST, COMMAND_ON_LIST
@ -45,9 +45,28 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
def setup_platform(hass, config, add_entities_callback, discovery_info=None):
"""Set up the RFXtrx platform."""
device_ids = set()
# Add switch from config file
switches = get_devices_from_config(config, RfxtrxSwitch)
add_entities_callback(switches)
entities = []
for packet_id, entity_info in config[CONF_DEVICES].items():
event = get_rfx_object(packet_id)
if event is None:
_LOGGER.error("Invalid device: %s", packet_id)
continue
device_id = get_device_id(event.device)
if device_id in device_ids:
continue
device_ids.add(device_id)
datas = {ATTR_STATE: None, ATTR_FIRE_EVENT: entity_info[CONF_FIRE_EVENT]}
entity = RfxtrxSwitch(
entity_info[CONF_NAME], event.device, datas, config[CONF_SIGNAL_REPETITIONS]
)
entities.append(entity)
add_entities_callback(entities)
def switch_update(event):
"""Handle sensor updates from the RFXtrx gateway."""
@ -58,12 +77,28 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None):
):
return
new_device = get_new_device(event, config, RfxtrxSwitch)
if new_device:
add_entities_callback([new_device])
device_id = get_device_id(event.device)
if device_id in device_ids:
return
device_ids.add(device_id)
_LOGGER.info(
"Added switch (Device ID: %s Class: %s Sub: %s)",
event.device.id_string.lower(),
event.device.__class__.__name__,
event.device.subtype,
)
pkt_id = "".join(f"{x:02x}" for x in event.data)
datas = {ATTR_STATE: None, ATTR_FIRE_EVENT: False}
entity = RfxtrxSwitch(
pkt_id, event.device, datas, DEFAULT_SIGNAL_REPETITIONS, event=event
)
add_entities_callback([entity])
# Subscribe to main RFXtrx events
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, switch_update)
if config[CONF_AUTOMATIC_ADD]:
hass.helpers.dispatcher.dispatcher_connect(SIGNAL_EVENT, switch_update)
class RfxtrxSwitch(RfxtrxDevice, SwitchEntity, RestoreEntity):

View file

@ -59,8 +59,6 @@ async def rfxtrx_cleanup():
):
yield
rfxtrx_core.RFX_DEVICES.clear()
@pytest.fixture(name="rfxtrx")
async def rfxtrx_fixture(hass):

View file

@ -141,7 +141,7 @@ async def test_fire_event(hass):
assert state
assert state.state == "on"
assert 1 == len(calls)
assert len(calls) == 1
assert calls[0].data == {"entity_id": "switch.test", "state": "on"}
@ -187,5 +187,5 @@ async def test_fire_event_sensor(hass):
hass.bus.async_listen("signal_received", record_event)
await _signal_event(hass, "0a520802060101ff0f0269")
assert 1 == len(calls)
assert calls[0].data == {"entity_id": "sensor.test_temperature"}
assert len(calls) == 5
assert any(call.data == {"entity_id": "sensor.test_temperature"} for call in calls)

View file

@ -57,7 +57,7 @@ async def test_default_config(hass, rfxtrx):
)
await hass.async_block_till_done()
assert 0 == len(rfxtrx_core.RFX_DEVICES)
assert len(hass.states.async_all()) == 0
async def test_one_light(hass, rfxtrx):

View file

@ -55,12 +55,39 @@ async def test_one_sensor_no_datatype(hass, rfxtrx):
)
await hass.async_block_till_done()
state = hass.states.get("sensor.test_temperature")
base_id = "sensor.test"
base_name = "Test"
state = hass.states.get(f"{base_id}_temperature")
assert state
assert state.state == "unknown"
assert state.attributes.get("friendly_name") == "Test Temperature"
assert state.attributes.get("friendly_name") == f"{base_name} Temperature"
assert state.attributes.get("unit_of_measurement") == TEMP_CELSIUS
state = hass.states.get(f"{base_id}_humidity")
assert state
assert state.state == "unknown"
assert state.attributes.get("friendly_name") == f"{base_name} Humidity"
assert state.attributes.get("unit_of_measurement") == UNIT_PERCENTAGE
state = hass.states.get(f"{base_id}_humidity_status")
assert state
assert state.state == "unknown"
assert state.attributes.get("friendly_name") == f"{base_name} Humidity status"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_rssi_numeric")
assert state
assert state.state == "unknown"
assert state.attributes.get("friendly_name") == f"{base_name} Rssi numeric"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_battery_numeric")
assert state
assert state.state == "unknown"
assert state.attributes.get("friendly_name") == f"{base_name} Battery numeric"
assert state.attributes.get("unit_of_measurement") == ""
async def test_several_sensors(hass, rfxtrx):
"""Test with 3 sensors."""
@ -113,61 +140,94 @@ async def test_discover_sensor(hass, rfxtrx):
)
await hass.async_block_till_done()
# 1
await _signal_event(hass, "0a520801070100b81b0279")
state = hass.states.get("sensor.0a520801070100b81b0279_temperature")
base_id = "sensor.0a520801070100b81b0279"
state = hass.states.get(f"{base_id}_humidity")
assert state
assert state.state == "27"
assert state.attributes.get("unit_of_measurement") == UNIT_PERCENTAGE
state = hass.states.get(f"{base_id}_humidity_status")
assert state
assert state.state == "normal"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_rssi_numeric")
assert state
assert state.state == "7"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_temperature")
assert state
assert state.state == "18.4"
assert (
state.attributes.items()
>= {
"friendly_name": "0a520801070100b81b0279 Temperature",
"unit_of_measurement": TEMP_CELSIUS,
"Humidity status": "normal",
"Temperature": 18.4,
"Rssi numeric": 7,
"Humidity": 27,
"Battery numeric": 9,
"Humidity status numeric": 2,
}.items()
)
assert state.attributes.get("unit_of_measurement") == TEMP_CELSIUS
state = hass.states.get(f"{base_id}_battery_numeric")
assert state
assert state.state == "9"
assert state.attributes.get("unit_of_measurement") == ""
# 2
await _signal_event(hass, "0a52080405020095240279")
state = hass.states.get("sensor.0a52080405020095240279_temperature")
base_id = "sensor.0a52080405020095240279"
state = hass.states.get(f"{base_id}_humidity")
assert state
assert state.state == "36"
assert state.attributes.get("unit_of_measurement") == UNIT_PERCENTAGE
state = hass.states.get(f"{base_id}_humidity_status")
assert state
assert state.state == "normal"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_rssi_numeric")
assert state
assert state.state == "7"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_temperature")
assert state
assert state.state == "14.9"
assert (
state.attributes.items()
>= {
"friendly_name": "0a52080405020095240279 Temperature",
"unit_of_measurement": TEMP_CELSIUS,
"Humidity status": "normal",
"Temperature": 14.9,
"Rssi numeric": 7,
"Humidity": 36,
"Battery numeric": 9,
"Humidity status numeric": 2,
}.items()
)
assert state.attributes.get("unit_of_measurement") == TEMP_CELSIUS
state = hass.states.get(f"{base_id}_battery_numeric")
assert state
assert state.state == "9"
assert state.attributes.get("unit_of_measurement") == ""
# 1 Update
await _signal_event(hass, "0a52085e070100b31b0279")
state = hass.states.get("sensor.0a520801070100b81b0279_temperature")
base_id = "sensor.0a520801070100b81b0279"
state = hass.states.get(f"{base_id}_humidity")
assert state
assert state.state == "27"
assert state.attributes.get("unit_of_measurement") == UNIT_PERCENTAGE
state = hass.states.get(f"{base_id}_humidity_status")
assert state
assert state.state == "normal"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_rssi_numeric")
assert state
assert state.state == "7"
assert state.attributes.get("unit_of_measurement") == ""
state = hass.states.get(f"{base_id}_temperature")
assert state
assert state.state == "17.9"
assert (
state.attributes.items()
>= {
"friendly_name": "0a520801070100b81b0279 Temperature",
"unit_of_measurement": TEMP_CELSIUS,
"Humidity status": "normal",
"Temperature": 17.9,
"Rssi numeric": 7,
"Humidity": 27,
"Battery numeric": 9,
"Humidity status numeric": 2,
}.items()
)
assert state.attributes.get("unit_of_measurement") == TEMP_CELSIUS
assert len(hass.states.async_all()) == 2
state = hass.states.get(f"{base_id}_battery_numeric")
assert state
assert state.state == "9"
assert state.attributes.get("unit_of_measurement") == ""
assert len(hass.states.async_all()) == 10
async def test_discover_sensor_noautoadd(hass, rfxtrx):
@ -215,35 +275,14 @@ async def test_update_of_sensors(hass, rfxtrx):
state = hass.states.get("sensor.test_temperature")
assert state
assert state.state == "unknown"
assert (
state.attributes.items()
>= {
"friendly_name": "Test Temperature",
"unit_of_measurement": TEMP_CELSIUS,
}.items()
)
state = hass.states.get("sensor.bath_temperature")
assert state
assert state.state == "unknown"
assert (
state.attributes.items()
>= {
"friendly_name": "Bath Temperature",
"unit_of_measurement": TEMP_CELSIUS,
}.items()
)
state = hass.states.get("sensor.bath_humidity")
assert state
assert state.state == "unknown"
assert (
state.attributes.items()
>= {
"friendly_name": "Bath Humidity",
"unit_of_measurement": UNIT_PERCENTAGE,
}.items()
)
assert len(hass.states.async_all()) == 3
@ -253,52 +292,13 @@ async def test_update_of_sensors(hass, rfxtrx):
state = hass.states.get("sensor.test_temperature")
assert state
assert state.state == "13.3"
assert (
state.attributes.items()
>= {
"friendly_name": "Test Temperature",
"unit_of_measurement": TEMP_CELSIUS,
"Battery numeric": 9,
"Temperature": 13.3,
"Humidity": 34,
"Humidity status": "normal",
"Humidity status numeric": 2,
"Rssi numeric": 6,
}.items()
)
state = hass.states.get("sensor.bath_temperature")
assert state
assert state.state == "51.1"
assert (
state.attributes.items()
>= {
"friendly_name": "Bath Temperature",
"unit_of_measurement": TEMP_CELSIUS,
"Battery numeric": 9,
"Temperature": 51.1,
"Humidity": 15,
"Humidity status": "normal",
"Humidity status numeric": 2,
"Rssi numeric": 6,
}.items()
)
state = hass.states.get("sensor.bath_humidity")
assert state
assert state.state == "15"
assert (
state.attributes.items()
>= {
"friendly_name": "Bath Humidity",
"unit_of_measurement": UNIT_PERCENTAGE,
"Battery numeric": 9,
"Temperature": 51.1,
"Humidity": 15,
"Humidity status": "normal",
"Humidity status numeric": 2,
"Rssi numeric": 6,
}.items()
)
assert len(hass.states.async_all()) == 3