Rfxtrx use previous received event to do complete restore (#37819)

* Add event attribute to display last received event

* Restore state using event attribute

* Allow empty dict for device config

* Must also validate normal case

* Do early return
This commit is contained in:
Joakim Plate 2020-07-17 10:27:07 +02:00 committed by GitHub
parent f5b628c04f
commit 7c9be024bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 39 deletions

View File

@ -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]

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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):

View File

@ -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: