Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HA entities for Schneider Electric Thermostats #3575

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 218 additions & 3 deletions zhaquirks/schneiderelectric/thermostat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
from typing import Final

from zigpy.quirks import CustomCluster
from zigpy.quirks.v2 import QuirkBuilder
from zigpy.quirks.v2 import EntityType, QuirkBuilder
from zigpy.quirks.v2.homeassistant import (
EntityPlatform,
UnitOfPower,
UnitOfTemperature,
UnitOfTime,
)
from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass
import zigpy.types as t
from zigpy.zcl.clusters.hvac import SystemMode, Thermostat, UserInterface
from zigpy.zcl.clusters.measurement import TemperatureMeasurement
Expand Down Expand Up @@ -285,7 +292,7 @@ class AttributeDefs(Thermostat.AttributeDefs):
access="r",
is_manufacturer_specific=True,
)
se_local_temperature_souce_select: Final = ZCLAttributeDef(
se_local_temperature_source_select: Final = ZCLAttributeDef(
id=0xE212,
type=SELocalTemperatureSourceSelect,
access="rw",
Expand Down Expand Up @@ -328,7 +335,7 @@ class AttributeDefs(Thermostat.AttributeDefs):
is_manufacturer_specific=True,
)
se_heating_emitter_type: Final = ZCLAttributeDef(
id=0xE218,
id=0xE21A,
type=SEHeatingEmitterType,
access="rw",
is_manufacturer_specific=True,
Expand Down Expand Up @@ -572,5 +579,213 @@ class AttributeDefs(CustomCluster.AttributeDefs):
.replaces(SEMetering, endpoint_id=5)
.replaces(SECycleTime)
.replaces(SEHeatingCoolingOutput)
.number(
cluster_id=SEMetering.cluster_id,
endpoint_id=5,
attribute_name=SEMetering.AttributeDefs.se_fixed_load_demand.name,
translation_key="fixed_load_demand",
fallback_name="Fixed load demand",
device_class=NumberDeviceClass.POWER,
unit=UnitOfPower.WATT,
min_value=0,
max_value=10000,
step=1,
)
.number(
cluster_id=SEUserInterface.cluster_id,
endpoint_id=1,
attribute_name=SEUserInterface.AttributeDefs.se_brightness.name,
translation_key="display_brightness",
fallback_name="Display brightness",
# unit="%",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have PERCENTAGE as a unit for this. It's just a string: PERCENTAGE = "%", so this should already work when uncommented. Can you confirm?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tested, and if I uncomment this line, I get the following error:

    Failed to create platform entity: <class 'zha.application.platforms.number.NumberConfigurationEntity'> [args=('b0:c7:de:ff:fe:be:38:1f-1', [<zha.zigbee.cluster_handlers.hvac.UserInterfaceClusterHandler object at 0x7fe3642a55e0>], <zha.zigbee.endpoint.Endpoint object at 0x7fe3642a4c80>, <CustomDeviceV2 model='EKO07259' manuf='Schneider Electric' nwk=0xDC1A ieee=b0:c7:de:ff:fe:be:38:1f is_initialized=True> - quirk_applied: True - quirk_or_device_class: zigpy.quirks.v2.CustomDeviceV2 - quirk_id: None), kwargs={'entity_metadata': NumberMetadata(entity_platform=<EntityPlatform.NUMBER: 'number'>, entity_type=<EntityType.CONFIG: 'config'>, cluster_id=516, endpoint_id=1, cluster_type=<ClusterType.Server: 0>, initially_disabled=False, attribute_initialized_from_cache=True, translation_key='display_inactive_brightness', fallback_name='Display inactive brightness', attribute_name='se_inactive_brightness', reporting_config=None, min=0, max=100, step=1, unit='%', mode=None, multiplier=None, device_class=None)}]

Traceback (most recent call last):
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/application/gateway.py", line 335, in create_platform_entities
    platform_entity = platform_entity_class.create_platform_entity(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/application/platforms/number/__init__.py", line 229, in create_platform_entity
    return cls(unique_id, cluster_handlers, endpoint, device, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/application/platforms/number/__init__.py", line 242, in __init__
    super().__init__(unique_id, cluster_handlers, endpoint, device, **kwargs)
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/application/platforms/__init__.py", line 297, in __init__
    self._init_from_quirks_metadata(entity_metadata)
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/application/platforms/number/__init__.py", line 269, in _init_from_quirks_metadata
    self._attr_native_unit_of_measurement = validate_unit(
                                            ^^^^^^^^^^^^^^
  File "/nix/store/2dcvclynic8hvxi9lcfrcsy1k1iwbz1p-python3.12-zha-0.0.39/lib/python3.12/site-packages/zha/units.py", line 167, in validate_unit
    return UNITS_OF_MEASURE[type(external_unit).__name__](external_unit.value)
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'str'

Copy link
Contributor Author

@uvNikita uvNikita Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, as of now, unit can't be string since then the code resolves into:

UNITS_OF_MEASURE[type("%").__name__](external_unit.value) -> UNITS_OF_MEASURE['str'](external_unit.value) -> KeyError: 'str'

min_value=0,
max_value=100,
step=1,
)
.number(
cluster_id=SEUserInterface.cluster_id,
endpoint_id=1,
attribute_name=SEUserInterface.AttributeDefs.se_inactive_brightness.name,
translation_key="display_inactive_brightness",
fallback_name="Display inactive brightness",
# unit="%",
min_value=0,
max_value=100,
step=1,
)
.number(
cluster_id=SEUserInterface.cluster_id,
endpoint_id=1,
attribute_name=SEUserInterface.AttributeDefs.se_activity_timeout.name,
translation_key="display_activity_timeout",
fallback_name="Display activity timeout",
device_class=NumberDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
min_value=0,
max_value=3600,
step=1,
)
.binary_sensor(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_open_window_detection_status.name,
translation_key="open_window_detection_status",
fallback_name="Open window detection status",
entity_type=EntityType.DIAGNOSTIC,
)
.number(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_open_window_detection_threshold.name,
translation_key="open_window_detection_threshold",
fallback_name="Open window detection threshold",
device_class=NumberDeviceClass.TEMPERATURE,
unit=UnitOfTemperature.CELSIUS,
min_value=0,
max_value=12,
multiplier=0.1,
step=0.1,
)
.number(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_open_window_event_duration.name,
translation_key="open_window_event_duration",
fallback_name="Open window event duration",
device_class=NumberDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
min_value=0,
max_value=7620,
step=1,
)
.number(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_open_window_detection_guard_period.name,
translation_key="open_window_detection_guard_period",
fallback_name="Open window detection guard period",
device_class=NumberDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
min_value=0,
max_value=7620,
step=1,
)
.number(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_fallback_timeout.name,
translation_key="fallback_timeout",
fallback_name="Fallback timeout",
device_class=NumberDeviceClass.DURATION,
unit=UnitOfTime.SECONDS,
min_value=30,
max_value=10800,
step=1,
)
.number(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_boost_amount.name,
translation_key="boost_amount",
fallback_name="Boost amount",
device_class=NumberDeviceClass.TEMPERATURE,
unit=UnitOfTemperature.CELSIUS,
min_value=0,
max_value=10,
multiplier=0.01,
step=0.5,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_control_status.name,
translation_key="control_status",
fallback_name="Control status",
enum_class=SEControlStatus,
entity_platform=EntityPlatform.SENSOR,
entity_type=EntityType.DIAGNOSTIC,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_local_temperature_source_select.name,
translation_key="local_temperature_source",
fallback_name="Local temperature source",
enum_class=SELocalTemperatureSourceSelect,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_control_type.name,
translation_key="control_type",
fallback_name="Control type",
enum_class=SEControlType,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_thermostat_application.name,
translation_key="thermostat_application",
fallback_name="Thermostat application",
enum_class=SEThermostatApplication,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_heating_fuel.name,
translation_key="heating_fuel",
fallback_name="Heating fuel",
enum_class=SEHeatingFuel,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_heat_transfer_medium.name,
translation_key="heat_transfer_medium",
fallback_name="Heat transfer medium",
enum_class=SEHeatTransferMedium,
)
.enum(
cluster_id=SEThermostat.cluster_id,
endpoint_id=1,
attribute_name=SEThermostat.AttributeDefs.se_heating_emitter_type.name,
translation_key="heating_emitter_type",
fallback_name="Heating emitter type",
enum_class=SEHeatingEmitterType,
)
.number(
cluster_id=SETemperatureMeasurement.cluster_id,
endpoint_id=2,
attribute_name=SETemperatureMeasurement.AttributeDefs.se_sensor_correction.name,
translation_key="ambient_sensor_correction",
fallback_name="Ambient sensor correction",
device_class=NumberDeviceClass.TEMPERATURE,
unit=UnitOfTemperature.CELSIUS,
min_value=-10,
max_value=10,
step=0.1,
multiplier=0.01,
)
.number(
cluster_id=SETemperatureMeasurementExternal.cluster_id,
endpoint_id=3,
attribute_name=SETemperatureMeasurementExternal.AttributeDefs.se_sensor_correction.name,
translation_key="external_sensor_correction",
fallback_name="External sensor correction",
device_class=NumberDeviceClass.TEMPERATURE,
unit=UnitOfTemperature.CELSIUS,
min_value=-10,
max_value=10,
step=0.1,
multiplier=0.01,
)
.enum(
cluster_id=SETemperatureMeasurementExternal.cluster_id,
endpoint_id=3,
attribute_name=SETemperatureMeasurementExternal.AttributeDefs.se_temperature_sensor_type.name,
translation_key="external_temperature_sensor_type",
fallback_name="External temperature sensor type",
enum_class=SETemperatureSensorType,
)
.add_to_registry()
)
Loading