diff --git a/homeassistant/components/rfxtrx/__init__.py b/homeassistant/components/rfxtrx/__init__.py index e4b565cd5d9..54863f86332 100644 --- a/homeassistant/components/rfxtrx/__init__.py +++ b/homeassistant/components/rfxtrx/__init__.py @@ -24,7 +24,7 @@ from homeassistant.const import ( UV_INDEX, ) import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.restore_state import RestoreEntity from .const import ( ATTR_EVENT, @@ -92,9 +92,15 @@ def _bytearray_string(data): raise vol.Invalid("Data must be a hex string with multiple of two characters") +def _ensure_device(value): + if value is None: + return DEVICE_DATA_SCHEMA({}) + return DEVICE_DATA_SCHEMA(value) + + SERVICE_SEND_SCHEMA = vol.Schema({ATTR_EVENT: _bytearray_string}) -DEVICE_SCHEMA = vol.Schema( +DEVICE_DATA_SCHEMA = vol.Schema( { vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean, @@ -110,7 +116,7 @@ BASE_SCHEMA = vol.Schema( { vol.Optional(CONF_DEBUG, default=False): cv.boolean, vol.Optional(CONF_AUTOMATIC_ADD, default=False): cv.boolean, - vol.Optional(CONF_DEVICES, default={}): {cv.string: DEVICE_SCHEMA}, + vol.Optional(CONF_DEVICES, default={}): {cv.string: _ensure_device}, } ) @@ -337,7 +343,7 @@ def get_device_id(device, data_bits=None): return (f"{device.packettype:x}", f"{device.subtype:x}", id_string) -class RfxtrxDevice(Entity): +class RfxtrxDevice(RestoreEntity): """Represents a Rfxtrx device. Contains the common logic for Rfxtrx lights and switches. @@ -348,6 +354,7 @@ class RfxtrxDevice(Entity): self.signal_repetitions = signal_repetitions self._name = f"{device.type_string} {device.id_string}" self._device = device + self._event = None self._state = None self._device_id = device_id self._unique_id = "_".join(x for x in self._device_id) @@ -355,6 +362,17 @@ class RfxtrxDevice(Entity): if event: self._apply_event(event) + async def async_added_to_hass(self): + """Restore RFXtrx device state (ON/OFF).""" + if self._event: + return + + old_state = await self.async_get_last_state() + if old_state is not None: + event = old_state.attributes.get(ATTR_EVENT) + if event: + self._apply_event(get_rfx_object(event)) + @property def should_poll(self): """No polling needed for a RFXtrx switch.""" @@ -365,6 +383,13 @@ class RfxtrxDevice(Entity): """Return the name of the device if any.""" return self._name + @property + def device_state_attributes(self): + """Return the device state attributes.""" + if not self._event: + return None + return {ATTR_EVENT: "".join(f"{x:02x}" for x in self._event.data)} + @property def is_on(self): """Return true if device is on.""" @@ -391,6 +416,7 @@ class RfxtrxDevice(Entity): def _apply_event(self, event): """Apply a received event.""" + self._event = event def _send_command(self, command, brightness=0): rfx_object = self.hass.data[DATA_RFXOBJECT] diff --git a/homeassistant/components/rfxtrx/binary_sensor.py b/homeassistant/components/rfxtrx/binary_sensor.py index 8ec67d4a902..33ef6893c52 100644 --- a/homeassistant/components/rfxtrx/binary_sensor.py +++ b/homeassistant/components/rfxtrx/binary_sensor.py @@ -12,6 +12,7 @@ from homeassistant.const import ( ) from homeassistant.core import callback from homeassistant.helpers import event as evt +from homeassistant.helpers.restore_state import RestoreEntity from . import ( CONF_AUTOMATIC_ADD, @@ -25,6 +26,7 @@ from . import ( get_rfx_object, ) from .const import ( + ATTR_EVENT, COMMAND_OFF_LIST, COMMAND_ON_LIST, DATA_RFXTRX_CONFIG, @@ -105,7 +107,7 @@ async def async_setup_entry( ) -class RfxtrxBinarySensor(BinarySensorEntity): +class RfxtrxBinarySensor(BinarySensorEntity, RestoreEntity): """A representation of a RFXtrx binary sensor.""" def __init__( @@ -120,7 +122,7 @@ class RfxtrxBinarySensor(BinarySensorEntity): event=None, ): """Initialize the RFXtrx sensor.""" - self.event = None + self._event = None self._device = device self._name = f"{device.type_string} {device.id_string}" self._device_class = device_class @@ -141,6 +143,13 @@ class RfxtrxBinarySensor(BinarySensorEntity): """Restore RFXtrx switch device state (ON/OFF).""" await super().async_added_to_hass() + if self._event is None: + old_state = await self.async_get_last_state() + if old_state is not None: + event = old_state.attributes.get(ATTR_EVENT) + if event: + self._apply_event(get_rfx_object(event)) + self.async_on_remove( self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_EVENT, self._handle_event @@ -152,6 +161,13 @@ class RfxtrxBinarySensor(BinarySensorEntity): """Return the device name.""" return self._name + @property + def device_state_attributes(self): + """Return the device state attributes.""" + if not self._event: + return None + return {ATTR_EVENT: "".join(f"{x:02x}" for x in self._event.data)} + @property def data_bits(self): """Return the number of data bits.""" @@ -221,6 +237,7 @@ class RfxtrxBinarySensor(BinarySensorEntity): def _apply_event(self, event): """Apply command from rfxtrx.""" + self._event = event if event.device.packettype == DEVICE_PACKET_TYPE_LIGHTING4: self._apply_event_lighting4(event) else: diff --git a/homeassistant/components/rfxtrx/cover.py b/homeassistant/components/rfxtrx/cover.py index 829ff9c8110..a3cefb42cb7 100644 --- a/homeassistant/components/rfxtrx/cover.py +++ b/homeassistant/components/rfxtrx/cover.py @@ -2,7 +2,7 @@ import logging from homeassistant.components.cover import CoverEntity -from homeassistant.const import CONF_DEVICES, STATE_OPEN +from homeassistant.const import CONF_DEVICES from homeassistant.core import callback from homeassistant.helpers.restore_state import RestoreEntity @@ -86,10 +86,6 @@ class RfxtrxCover(RfxtrxDevice, CoverEntity, RestoreEntity): """Restore RFXtrx cover device state (OPEN/CLOSE).""" await super().async_added_to_hass() - old_state = await self.async_get_last_state() - if old_state is not None: - self._state = old_state.state == STATE_OPEN - self.async_on_remove( self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_EVENT, self._handle_event @@ -120,6 +116,7 @@ class RfxtrxCover(RfxtrxDevice, CoverEntity, RestoreEntity): def _apply_event(self, event): """Apply command from rfxtrx.""" + super()._apply_event(event) if event.values["Command"] in COMMAND_ON_LIST: self._state = True elif event.values["Command"] in COMMAND_OFF_LIST: diff --git a/homeassistant/components/rfxtrx/light.py b/homeassistant/components/rfxtrx/light.py index c248d8b8307..649be7be3fe 100644 --- a/homeassistant/components/rfxtrx/light.py +++ b/homeassistant/components/rfxtrx/light.py @@ -8,9 +8,8 @@ from homeassistant.components.light import ( SUPPORT_BRIGHTNESS, LightEntity, ) -from homeassistant.const import CONF_DEVICES, STATE_ON +from homeassistant.const import CONF_DEVICES from homeassistant.core import callback -from homeassistant.helpers.restore_state import RestoreEntity from . import ( CONF_AUTOMATIC_ADD, @@ -93,7 +92,7 @@ async def async_setup_entry( hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, light_update) -class RfxtrxLight(RfxtrxDevice, LightEntity, RestoreEntity): +class RfxtrxLight(RfxtrxDevice, LightEntity): """Representation of a RFXtrx light.""" _brightness = 0 @@ -102,17 +101,6 @@ class RfxtrxLight(RfxtrxDevice, LightEntity, RestoreEntity): """Restore RFXtrx device state (ON/OFF).""" await super().async_added_to_hass() - old_state = await self.async_get_last_state() - if old_state is not None: - self._state = old_state.state == STATE_ON - - # Restore the brightness of dimmable devices - if ( - old_state is not None - and old_state.attributes.get(ATTR_BRIGHTNESS) is not None - ): - self._brightness = int(old_state.attributes[ATTR_BRIGHTNESS]) - self.async_on_remove( self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_EVENT, self._handle_event @@ -147,6 +135,7 @@ class RfxtrxLight(RfxtrxDevice, LightEntity, RestoreEntity): def _apply_event(self, event): """Apply command from rfxtrx.""" + super()._apply_event(event) if event.values["Command"] in COMMAND_ON_LIST: self._state = True elif event.values["Command"] in COMMAND_OFF_LIST: diff --git a/homeassistant/components/rfxtrx/sensor.py b/homeassistant/components/rfxtrx/sensor.py index 7c540672c9a..e105c463b3b 100644 --- a/homeassistant/components/rfxtrx/sensor.py +++ b/homeassistant/components/rfxtrx/sensor.py @@ -11,7 +11,7 @@ from homeassistant.components.sensor import ( ) from homeassistant.const import CONF_DEVICES from homeassistant.core import callback -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.restore_state import RestoreEntity from . import ( CONF_AUTOMATIC_ADD, @@ -21,7 +21,7 @@ from . import ( get_device_id, get_rfx_object, ) -from .const import DATA_RFXTRX_CONFIG +from .const import ATTR_EVENT, DATA_RFXTRX_CONFIG _LOGGER = logging.getLogger(__name__) @@ -113,12 +113,12 @@ async def async_setup_entry( hass.helpers.dispatcher.async_dispatcher_connect(SIGNAL_EVENT, sensor_update) -class RfxtrxSensor(Entity): +class RfxtrxSensor(RestoreEntity): """Representation of a RFXtrx sensor.""" def __init__(self, device, device_id, data_type, event=None): """Initialize the sensor.""" - self.event = None + self._event = None self._device = device self._name = f"{device.type_string} {device.id_string} {data_type}" self.data_type = data_type @@ -136,6 +136,13 @@ class RfxtrxSensor(Entity): """Restore RFXtrx switch device state (ON/OFF).""" await super().async_added_to_hass() + if self._event is None: + old_state = await self.async_get_last_state() + if old_state is not None: + event = old_state.attributes.get(ATTR_EVENT) + if event: + self._apply_event(get_rfx_object(event)) + self.async_on_remove( self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_EVENT, self._handle_event @@ -149,9 +156,9 @@ class RfxtrxSensor(Entity): @property def state(self): """Return the state of the sensor.""" - if not self.event: + if not self._event: return None - value = self.event.values.get(self.data_type) + value = self._event.values.get(self.data_type) return self._convert_fun(value) @property @@ -162,9 +169,9 @@ class RfxtrxSensor(Entity): @property def device_state_attributes(self): """Return the device state attributes.""" - if not self.event: + if not self._event: return None - return self.event.values + return {ATTR_EVENT: "".join(f"{x:02x}" for x in self._event.data)} @property def unit_of_measurement(self): @@ -192,7 +199,7 @@ class RfxtrxSensor(Entity): def _apply_event(self, event): """Apply command from rfxtrx.""" - self.event = event + self._event = event @callback def _handle_event(self, event, device_id): diff --git a/homeassistant/components/rfxtrx/switch.py b/homeassistant/components/rfxtrx/switch.py index c890e162b2c..7b2a23c1624 100644 --- a/homeassistant/components/rfxtrx/switch.py +++ b/homeassistant/components/rfxtrx/switch.py @@ -4,7 +4,7 @@ import logging import RFXtrx as rfxtrxmod from homeassistant.components.switch import SwitchEntity -from homeassistant.const import CONF_DEVICES, STATE_ON +from homeassistant.const import CONF_DEVICES from homeassistant.core import callback from homeassistant.helpers.restore_state import RestoreEntity @@ -96,10 +96,6 @@ class RfxtrxSwitch(RfxtrxDevice, SwitchEntity, RestoreEntity): """Restore RFXtrx switch device state (ON/OFF).""" await super().async_added_to_hass() - old_state = await self.async_get_last_state() - if old_state is not None: - self._state = old_state.state == STATE_ON - self.async_on_remove( self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_EVENT, self._handle_event @@ -108,6 +104,7 @@ class RfxtrxSwitch(RfxtrxDevice, SwitchEntity, RestoreEntity): def _apply_event(self, event): """Apply command from rfxtrx.""" + super()._apply_event(event) if event.values["Command"] in COMMAND_ON_LIST: self._state = True elif event.values["Command"] in COMMAND_OFF_LIST: