Skip to content

Commit

Permalink
Allow undefined generic entities besides CE (#1627)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukramJ authored Aug 6, 2024
1 parent 3382828 commit ec33a8e
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.5.5
rev: v0.5.6
hooks:
- id: ruff
args:
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Version 2024.8.1(2024-08-02)

- Refactor entity path
- Allow undefined generic entities besides CE

# Version 2024.8.0(2024-08-01)

Expand Down
1 change: 1 addition & 0 deletions hahomematic/platforms/custom/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class DeviceProfile(StrEnum):
class ED(StrEnum):
"""Enum for entity definitions."""

ALLOW_UNDEFINED_GENERIC_ENTITIES = "allow_undefined_generic_entities"
DEFAULT_ENTITIES = "default_entities"
INCLUDE_DEFAULT_ENTITIES = "include_default_entities"
DEVICE_GROUP = "device_group"
Expand Down
61 changes: 45 additions & 16 deletions hahomematic/platforms/custom/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
SCHEMA_ED_DEVICE_GROUP = vol.Schema(
{
vol.Required(ED.PRIMARY_CHANNEL.value): vol.Any(int, None),
vol.Required(ED.ALLOW_UNDEFINED_GENERIC_ENTITIES.value): bool,
vol.Optional(ED.SECONDARY_CHANNELS.value): (int,),
vol.Optional(ED.REPEATABLE_FIELDS.value): SCHEMA_ED_FIELD_DETAILS,
vol.Optional(ED.VISIBLE_REPEATABLE_FIELDS.value): SCHEMA_ED_FIELD_DETAILS,
Expand Down Expand Up @@ -80,9 +81,19 @@
4: (Parameter.BATTERY_STATE,),
},
ED.DEVICE_DEFINITIONS: {
DeviceProfile.IP_BUTTON_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: True,
ED.REPEATABLE_FIELDS: {
Field.BUTTON_LOCK: Parameter.GLOBAL_BUTTON_LOCK,
},
},
},
DeviceProfile.IP_COVER: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (2, 3),
ED.REPEATABLE_FIELDS: {
Field.COMBINED_PARAMETER: Parameter.COMBINED_PARAMETER,
Expand All @@ -107,6 +118,7 @@
DeviceProfile.IP_DIMMER: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (2, 3),
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Expand All @@ -123,6 +135,7 @@
DeviceProfile.IP_GARAGE: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.DOOR_COMMAND: Parameter.DOOR_COMMAND,
Field.SECTION: Parameter.SECTION,
Expand All @@ -138,6 +151,7 @@
DeviceProfile.IP_HDM: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.FIELDS: {
1: {
Field.DIRECTION: Parameter.ACTIVITY_STATE,
Expand All @@ -151,6 +165,7 @@
DeviceProfile.IP_FIXED_COLOR_LIGHT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (2, 3),
ED.REPEATABLE_FIELDS: {
Field.COLOR: Parameter.COLOR,
Expand All @@ -172,6 +187,7 @@
DeviceProfile.IP_SIMPLE_FIXED_COLOR_LIGHT_WIRED: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.COLOR: Parameter.COLOR,
Field.COLOR_BEHAVIOUR: Parameter.COLOR_BEHAVIOUR,
Expand All @@ -186,6 +202,7 @@
DeviceProfile.IP_SIMPLE_FIXED_COLOR_LIGHT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.COLOR: Parameter.COLOR,
Field.LEVEL: Parameter.LEVEL,
Expand All @@ -199,6 +216,7 @@
DeviceProfile.IP_RGBW_LIGHT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (2, 3, 4),
ED.REPEATABLE_FIELDS: {
Field.COLOR_TEMPERATURE: Parameter.COLOR_TEMPERATURE,
Expand All @@ -224,6 +242,7 @@
DeviceProfile.IP_DRG_DALI: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.COLOR_TEMPERATURE: Parameter.COLOR_TEMPERATURE,
Field.ON_TIME_VALUE: Parameter.DURATION_VALUE,
Expand All @@ -242,6 +261,7 @@
DeviceProfile.IP_SWITCH: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (2, 3),
ED.REPEATABLE_FIELDS: {
Field.STATE: Parameter.STATE,
Expand All @@ -267,6 +287,7 @@
DeviceProfile.IP_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.DIRECTION: Parameter.ACTIVITY_STATE,
Field.LOCK_STATE: Parameter.LOCK_STATE,
Expand All @@ -279,25 +300,10 @@
},
},
},
DeviceProfile.IP_BUTTON_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.REPEATABLE_FIELDS: {
Field.BUTTON_LOCK: Parameter.GLOBAL_BUTTON_LOCK,
},
},
},
DeviceProfile.RF_BUTTON_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: None,
ED.REPEATABLE_FIELDS: {
Field.BUTTON_LOCK: Parameter.GLOBAL_BUTTON_LOCK,
},
},
},
DeviceProfile.IP_SIREN: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 3,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.ACOUSTIC_ALARM_ACTIVE: Parameter.ACOUSTIC_ALARM_ACTIVE,
Field.OPTICAL_ALARM_ACTIVE: Parameter.OPTICAL_ALARM_ACTIVE,
Expand All @@ -311,6 +317,7 @@
DeviceProfile.IP_SIREN_SMOKE: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 1,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.SMOKE_DETECTOR_COMMAND: Parameter.SMOKE_DETECTOR_COMMAND,
},
Expand All @@ -322,6 +329,7 @@
DeviceProfile.IP_THERMOSTAT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.ACTIVE_PROFILE: Parameter.ACTIVE_PROFILE,
Field.BOOST_MODE: Parameter.BOOST_MODE,
Expand Down Expand Up @@ -351,6 +359,7 @@
DeviceProfile.IP_THERMOSTAT_GROUP: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.ACTIVE_PROFILE: Parameter.ACTIVE_PROFILE,
Field.BOOST_MODE: Parameter.BOOST_MODE,
Expand All @@ -377,9 +386,19 @@
},
ED.INCLUDE_DEFAULT_ENTITIES: False,
},
DeviceProfile.RF_BUTTON_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: None,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: True,
ED.REPEATABLE_FIELDS: {
Field.BUTTON_LOCK: Parameter.GLOBAL_BUTTON_LOCK,
},
},
},
DeviceProfile.RF_COVER: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.DIRECTION: Parameter.DIRECTION,
Field.LEVEL: Parameter.LEVEL,
Expand All @@ -392,6 +411,7 @@
DeviceProfile.RF_DIMMER: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Field.ON_TIME_VALUE: Parameter.ON_TIME,
Expand All @@ -402,6 +422,7 @@
DeviceProfile.RF_DIMMER_COLOR: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Field.ON_TIME_VALUE: Parameter.ON_TIME,
Expand All @@ -420,6 +441,7 @@
DeviceProfile.RF_DIMMER_COLOR_FIXED: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Field.ON_TIME_VALUE: Parameter.ON_TIME,
Expand All @@ -430,6 +452,7 @@
DeviceProfile.RF_DIMMER_COLOR_TEMP: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Field.ON_TIME_VALUE: Parameter.ON_TIME,
Expand All @@ -445,6 +468,7 @@
DeviceProfile.RF_DIMMER_WITH_VIRT_CHANNEL: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.SECONDARY_CHANNELS: (1, 2),
ED.REPEATABLE_FIELDS: {
Field.LEVEL: Parameter.LEVEL,
Expand All @@ -456,6 +480,7 @@
DeviceProfile.RF_LOCK: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.DIRECTION: Parameter.DIRECTION,
Field.OPEN: Parameter.OPEN,
Expand All @@ -467,6 +492,7 @@
DeviceProfile.RF_SWITCH: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.STATE: Parameter.STATE,
Field.ON_TIME_VALUE: Parameter.ON_TIME,
Expand All @@ -485,6 +511,7 @@
DeviceProfile.RF_THERMOSTAT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.AUTO_MODE: Parameter.AUTO_MODE,
Field.BOOST_MODE: Parameter.BOOST_MODE,
Expand All @@ -510,6 +537,7 @@
DeviceProfile.RF_THERMOSTAT_GROUP: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.REPEATABLE_FIELDS: {
Field.AUTO_MODE: Parameter.AUTO_MODE,
Field.BOOST_MODE: Parameter.BOOST_MODE,
Expand All @@ -536,6 +564,7 @@
DeviceProfile.SIMPLE_RF_THERMOSTAT: {
ED.DEVICE_GROUP: {
ED.PRIMARY_CHANNEL: 0,
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES: False,
ED.VISIBLE_REPEATABLE_FIELDS: {
Field.HUMIDITY: Parameter.HUMIDITY,
Field.TEMPERATURE: Parameter.TEMPERATURE,
Expand Down
38 changes: 24 additions & 14 deletions hahomematic/platforms/custom/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from hahomematic.const import CALLBACK_TYPE, ENTITY_KEY, INIT_DATETIME, CallSource, EntityUsage
from hahomematic.platforms import device as hmd
from hahomematic.platforms.custom import definition as hmed
from hahomematic.platforms.custom.const import DeviceProfile, Field
from hahomematic.platforms.custom.const import ED, DeviceProfile, Field
from hahomematic.platforms.custom.support import ExtendedConfig
from hahomematic.platforms.decorators import config_property
from hahomematic.platforms.entity import BaseEntity, CallParameterCollector
Expand Down Expand Up @@ -54,11 +54,19 @@ def __init__(
device_type=device.device_type, platform=self.platform
),
)
self._allow_undefined_generic_entities: Final[bool] = self._device_desc[
ED.ALLOW_UNDEFINED_GENERIC_ENTITIES
]
self._extended: Final = extended
self._data_entities: Final[dict[Field, hmge.GenericEntity]] = {}
self._init_entities()
self._init_entity_fields()

@property
def allow_undefined_generic_entities(self) -> bool:
"""Return if undefined generic entities of this device are allowed."""
return self._allow_undefined_generic_entities

@config_property
def base_channel_no(self) -> int | None:
"""Return the base channel no of the entity."""
Expand Down Expand Up @@ -144,12 +152,14 @@ def _get_entity_name(self) -> EntityNameData:
device=self._device,
channel_no=self.channel_no,
is_only_primary_channel=is_only_primary_channel,
usage=self._usage,
usage=self._get_entity_usage(),
postfix=self.entity_name_postfix.replace("_", " ").title(),
)

def _get_entity_usage(self) -> EntityUsage:
"""Generate the usage for the entity."""
if self._forced_usage:
return self._forced_usage
if (
secondary_channels := self._device_desc.get(hmed.ED.SECONDARY_CHANNELS)
) and self.channel_no in secondary_channels:
Expand Down Expand Up @@ -180,7 +190,7 @@ def _init_entities(self) -> None:
entity = self._device.get_generic_entity(
channel_address=self._channel_address, parameter=parameter
)
self._add_entity(field=field_name, entity=entity)
self._add_entity(field=field_name, entity=entity, is_visible=False)

# Add visible repeating fields
for field_name, parameter in self._device_desc.get(
Expand Down Expand Up @@ -221,7 +231,7 @@ def _init_entities(self) -> None:
if hmed.get_include_default_entities(device_profile=self._device_profile):
self._mark_entities(entity_def=hmed.get_default_entities())

def _add_entities(self, field_dict_name: hmed.ED, is_visible: bool = False) -> None:
def _add_entities(self, field_dict_name: hmed.ED, is_visible: bool | None = None) -> None:
"""Add entities to custom entity."""
fields = self._device_desc.get(field_dict_name, {})
for channel_no, channel in fields.items():
Expand All @@ -232,21 +242,22 @@ def _add_entities(self, field_dict_name: hmed.ED, is_visible: bool = False) -> N
if entity := self._device.get_generic_entity(
channel_address=channel_address, parameter=parameter
):
if is_visible and entity.is_forced_sensor is False:
entity.set_usage(EntityUsage.CE_VISIBLE)
self._add_entity(field=field, entity=entity)
self._add_entity(field=field, entity=entity, is_visible=is_visible)

def _add_entity(
self, field: Field, entity: hmge.GenericEntity | None, is_visible: bool = False
self, field: Field, entity: hmge.GenericEntity | None, is_visible: bool | None = None
) -> None:
"""Add entity to collection and register callback."""
if not entity:
return
self.device.add_sub_device_channel(
channel_no=self._channel_no, base_channel_no=self._base_channel_no
)
if is_visible:
entity.set_usage(EntityUsage.CE_VISIBLE)

if is_visible is True and entity.is_forced_sensor is False:
entity.force_usage(forced_usage=EntityUsage.CE_VISIBLE)
elif is_visible is False and entity.is_forced_sensor is False:
entity.force_usage(forced_usage=EntityUsage.NO_CREATE)

self._unregister_callbacks.append(
entity.register_internal_entity_updated_callback(cb=self.fire_entity_updated_callback)
Expand Down Expand Up @@ -279,11 +290,10 @@ def _mark_entity(self, channel_no: int | None, parameters: tuple[str, ...]) -> N
)

for parameter in parameters:
entity = self._device.get_generic_entity(
if entity := self._device.get_generic_entity(
channel_address=channel_address, parameter=parameter
)
if entity:
entity.set_usage(EntityUsage.ENTITY)
):
entity.force_usage(forced_usage=EntityUsage.ENTITY)

def _get_entity[_EntityT: hmge.GenericEntity](
self, field: Field, entity_type: type[_EntityT]
Expand Down
Loading

0 comments on commit ec33a8e

Please sign in to comment.