Skip to content

Commit

Permalink
Merge pull request #68 from grimmpp/feature-branch
Browse files Browse the repository at this point in the history
Version 1.3.7 Restore Device States after HA Restart
  • Loading branch information
grimmpp authored Feb 16, 2024
2 parents 2c54651 + 9cb120e commit fbfe525
Show file tree
Hide file tree
Showing 21 changed files with 839 additions and 240 deletions.
8 changes: 6 additions & 2 deletions changes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Changes and Feature List

## Version 1.3.7 Import Cleanup
* Trial to remove import warnings (Reported Issue: https://github.com/grimmpp/home-assistant-eltako/issues/61)
## Version 1.3.7 Restore Device States after HA Restart
* Trial to remove import warnings
Reported Issue: https://github.com/grimmpp/home-assistant-eltako/issues/61
* 🐞 Removed entity_id bug from GatewayConnectionState 🐞 => Requires removing and adding gateway again ❗
* Added state cache of device entities. When restarting HA entities like temperature sensors will show previouse state/value after restart.
Reported Feature: https://github.com/grimmpp/home-assistant-eltako/issues/63

## Version 1.3.6 Dependencies fixed for 1.3.5
* 🐞 Wrong dependency in manifest 🐞
Expand Down
56 changes: 34 additions & 22 deletions custom_components/eltako/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"""Support for Eltako binary sensors."""
from __future__ import annotations
from typing import Literal, final

from eltakobus.util import AddressExpression, b2a, b2s
from eltakobus.eep import *

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant import config_entries
from homeassistant.const import CONF_DEVICE_CLASS
from homeassistant.const import CONF_DEVICE_CLASS, STATE_ON, STATE_OFF
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import DeviceInfo
Expand All @@ -33,13 +34,13 @@ async def async_setup_entry(

platform = Platform.BINARY_SENSOR

for platform in [Platform.BINARY_SENSOR, Platform.SENSOR]:
if platform in config:
for entity_config in config[platform]:
for platform_id in [Platform.BINARY_SENSOR, Platform.SENSOR]:
if platform_id in config:
for entity_config in config[platform_id]:
try:
dev_conf = config_helpers.DeviceConf(entity_config, [CONF_DEVICE_CLASS, CONF_INVERT_SIGNAL])
if dev_conf.eep.eep_string in CONF_EEP_SUPPORTED_BINARY_SENSOR:
entities.append(EltakoBinarySensor(platform, gateway, dev_conf.id, dev_conf.name, dev_conf.eep,
entities.append(EltakoBinarySensor(platform_id, gateway, dev_conf.id, dev_conf.name, dev_conf.eep,
dev_conf.get(CONF_DEVICE_CLASS), dev_conf.get(CONF_INVERT_SIGNAL)))

except Exception as e:
Expand All @@ -52,9 +53,29 @@ async def async_setup_entry(
# dev_id validation not possible because there can be bus sensors as well as decentralized sensors.
log_entities_to_be_added(entities, platform)
async_add_entities(entities)


class AbstractBinarySensor(EltakoEntity, RestoreEntity, BinarySensorEntity):

def load_value_initially(self, latest_state:State):
try:
if 'unknown' == latest_state.state:
self._attr_is_on = None
else:
if latest_state.state in ['on', 'off']:
self._attr_is_on = 'on' == latest_state.state
else:
self._attr_is_on = None

except Exception as e:
self._attr_is_on = None
raise e

self.schedule_update_ha_state()

class EltakoBinarySensor(EltakoEntity, BinarySensorEntity):
LOGGER.debug(f"[binary_sensor {self.dev_id}] value initially loaded: [is_on: {self.is_on}, state: {self.state}]")

class EltakoBinarySensor(AbstractBinarySensor):
"""Representation of Eltako binary sensors such as wall switches.
Supported EEPs (EnOcean Equipment Profiles):
Expand All @@ -69,16 +90,7 @@ def __init__(self, platform: str, gateway: EnOceanGateway, dev_id: AddressExpres
super().__init__(platform, gateway, dev_id, dev_name, dev_eep)
self.invert_signal = invert_signal
self._attr_device_class = device_class

@property
def last_received_signal(self):
"""Return timestamp of last received signal."""
return self._attr_last_received_signal

@property
def data(self):
"""Return telegram data for rocker switch."""
return self._attr_data


def value_changed(self, msg: ESP2Message):
"""Fire an event with the data that have changed.
Expand Down Expand Up @@ -229,17 +241,17 @@ def value_changed(self, msg: ESP2Message):
},
)

class GatewayConnectionState(EltakoEntity, BinarySensorEntity):
class GatewayConnectionState(AbstractBinarySensor):
"""Protocols last time when message received"""

def __init__(self, platform: str, gateway: EnOceanGateway):
super().__init__(platform, gateway, gateway.base_id, "Connected" )
key = "Gateway_Connection_State"

self._attr_unique_id = f"{self.identifier}_Last Received Message - Gateway "+str(gateway.dev_id)
self._attr_icon = "mdi:connection"
self._attr_name = "Connected"

super().__init__(platform, gateway, gateway.base_id, dev_name="Connected", description_key=key)
self.gateway.set_connection_state_changed_handler(self.async_value_changed)
self.icon = "mdi:connection"
self.name = "Connected"
self.has_entity_name = True

@property
def device_info(self) -> DeviceInfo:
Expand Down
10 changes: 4 additions & 6 deletions custom_components/eltako/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,16 @@ def __init__(self, platform: str, gateway: EnOceanGateway, dev_id: AddressExpres
_dev_name = dev_name
if _dev_name == "":
_dev_name = "temperature-controller-teach-in-button"
super().__init__(platform, gateway, dev_id, _dev_name, dev_eep)
self.entity_description = ButtonEntityDescription(
key="teach_in_button",
name="Send teach-in telegram from "+sender_id.plain_address().hex(),
icon="mdi:button-cursor",
device_class=ButtonDeviceClass.UPDATE,
has_entity_name= True,
)
self._attr_unique_id = f"{self.identifier}_{self.entity_description.key}"
self.sender_id = sender_id

super().__init__(platform, gateway, dev_id, _dev_name, dev_eep)

async def async_press(self) -> None:
"""
Handle the button press.
Expand All @@ -101,15 +100,14 @@ class GatewayReconnectButton(EltakoEntity, ButtonEntity):
"""Button for reconnecting serial bus"""

def __init__(self, platform: str, gateway: EnOceanGateway):
super().__init__(platform, gateway, gateway.base_id, gateway.dev_name, None)
self.entity_description = ButtonEntityDescription(
key="gateway_" + str(gateway.dev_id) + "Serial Reconnection",
name="Reconnect Gateway "+str(gateway.dev_id),
icon="mdi:button-pointer",
device_class=ButtonDeviceClass.UPDATE,
has_entity_name= True,
)
self._attr_unique_id = f"{self.identifier}_{self.entity_description.key}"

super().__init__(platform, gateway, gateway.base_id, gateway.dev_name, None)

@property
def device_info(self) -> DeviceInfo:
Expand Down
39 changes: 35 additions & 4 deletions custom_components/eltako/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@
ClimateEntityFeature
)
from homeassistant import config_entries
from homeassistant.const import CONF_ID, CONF_NAME, Platform, TEMP_CELSIUS, CONF_TEMPERATURE_UNIT, Platform
from homeassistant.const import Platform, CONF_TEMPERATURE_UNIT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.typing import ConfigType

from .gateway import EnOceanGateway
Expand Down Expand Up @@ -83,13 +82,13 @@ async def async_setup_entry(
async_add_entities(entities)


def validate_ids_of_climate(entities:[EltakoEntity]):
def validate_ids_of_climate(entities:list[EltakoEntity]):
for e in entities:
e.validate_dev_id()
e.validate_sender_id()
if hasattr(e, "cooling_sender_id"):
e.validate_sender_id(e.cooling_sender_id)
class ClimateController(EltakoEntity, ClimateEntity):
class ClimateController(EltakoEntity, ClimateEntity, RestoreEntity):
"""Representation of an Eltako heating and cooling actor."""

_update_frequency = 55 # sec
Expand Down Expand Up @@ -146,6 +145,38 @@ def __init__(self, platform: str, gateway: EnOceanGateway, dev_id: AddressExpres
self._update_task = asyncio.ensure_future(self._wrapped_update(), loop=self._loop)


def load_value_initially(self, latest_state:State):
# LOGGER.debug(f"[climate {self.dev_id}] eneity unique_id: {self.unique_id}")
# LOGGER.debug(f"[climate {self.dev_id}] latest state - state: {latest_state.state}")
# LOGGER.debug(f"[climate {self.dev_id}] latest state - attributes: {latest_state.attributes}")

try:
self.hvac_modes = []
for m_str in latest_state.attributes.get('hvac_modes', []):
for m_enum in HVACMode:
if m_str == m_enum.value:
self.hvac_modes.append(m_enum)

self._attr_current_temperature = latest_state.attributes.get('current_temperature', None)
self._attr_target_temperature = latest_state.attributes.get('temperature', None)

self._attr_hvac_mode = None
for m_enum in HVACMode:
if latest_state.state == m_enum.value:
self._attr_hvac_mode = m_enum
break

except Exception as e:
self._attr_hvac_mode = None
self._attr_current_temperature = None
self._attr_target_temperature = None
raise e

self.schedule_update_ha_state()

LOGGER.debug(f"[climate {self.dev_id}] value initially loaded: [state: {self.state}, modes: [{self.hvac_modes}], current temp: {self.current_temperature}, target temp: {self.target_temperature}]")


async def _wrapped_update(self, *args) -> None:
while True:
try:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/eltako/config_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def get_gateway_name(dev_name:str, dev_type:str, dev_id: int, base_id:AddressExp
return f"{dev_name} - {dev_type} (Id: {dev_id}, BaseId: {format_address(base_id)})"

def format_address(address: AddressExpression, separator:str='-') -> str:
return b2a(address[0], '-').upper()
return b2a(address[0], separator).upper()

def get_device_name(dev_name: str, dev_id: AddressExpression, general_config: dict) -> str:
if general_config[CONF_SHOW_DEV_ID_IN_DEV_NAME]:
Expand Down
Loading

0 comments on commit fbfe525

Please sign in to comment.