Skip to content

Commit

Permalink
Port Sinope changes over from core/zha (#153)
Browse files Browse the repository at this point in the history
* Port sinope changes over from core/zha

* fix led on-state color selector

* rename trx key

* fix ruff issues

* remove icon entries

* review

* test sinope switches

* update to new node desc format

* add _value_attribute

* add _value_attribute

* fix entity model name to match device

---------

Co-authored-by: Steve Venzerul <[email protected]>
  • Loading branch information
ckm2k1 and Steve Venzerul authored Aug 24, 2024
1 parent 98bb62f commit a7b5516
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 0 deletions.
104 changes: 104 additions & 0 deletions tests/zha_devices_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -7347,4 +7347,108 @@
},
},
},
{
DEV_SIG_DEV_NO: 102,
SIG_MANUFACTURER: "Sinope Technologies",
SIG_MODEL: "DM2550ZB",
SIG_NODE_DESC: {
"logical_type": 1,
"complex_descriptor_available": 0,
"user_descriptor_available": 1,
"reserved": 0,
"aps_flags": 0,
"frequency_band": 8,
"mac_capability_flags": 142,
"manufacturer_code": 4508,
"maximum_buffer_size": 71,
"maximum_incoming_transfer_size": 43,
"server_mask": 10752,
"maximum_outgoing_transfer_size": 43,
"descriptor_capability_field": 0,
},
SIG_ENDPOINTS: {
1: {
SIG_EP_TYPE: 257,
DEV_SIG_EP_ID: 1,
SIG_EP_INPUT: [0, 2, 3, 4, 5, 6, 8, 1794, 2820, 2821, 65281],
SIG_EP_OUTPUT: [3, 4, 10, 25],
SIG_EP_PROFILE: 260,
}
},
DEV_SIG_EVT_CLUSTER_HANDLERS: ["1:0x0019"],
DEV_SIG_ENT_MAP: {
("button", "00:11:22:33:44:55:66:77-1-3"): {
DEV_SIG_CLUSTER_HANDLERS: ["identify"],
DEV_SIG_ENT_MAP_CLASS: "IdentifyButton",
DEV_SIG_ENT_MAP_ID: "button.sinope_technologies_dm2550zb_identify",
},
("light", "00:11:22:33:44:55:66:77-1"): {
DEV_SIG_CLUSTER_HANDLERS: ["level", "on_off"],
DEV_SIG_ENT_MAP_CLASS: "Light",
DEV_SIG_ENT_MAP_ID: "light.sinope_technologies_dm2550zb_light",
},
("sensor", "00:11:22:33:44:55:66:77-1-2820"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "PolledElectricalMeasurement",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_power",
},
("sensor", "00:11:22:33:44:55:66:77-1-2820-apparent_power"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementApparentPower",
DEV_SIG_ENT_MAP_ID: (
"sensor.sinope_technologies_dm2550zb_apparent_power"
),
},
("sensor", "00:11:22:33:44:55:66:77-1-2820-rms_current"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementRMSCurrent",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_current",
},
("sensor", "00:11:22:33:44:55:66:77-1-2820-rms_voltage"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementRMSVoltage",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_voltage",
},
("sensor", "00:11:22:33:44:55:66:77-1-2820-ac_frequency"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementFrequency",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_ac_frequency",
},
("sensor", "00:11:22:33:44:55:66:77-1-2820-power_factor"): {
DEV_SIG_CLUSTER_HANDLERS: ["electrical_measurement"],
DEV_SIG_ENT_MAP_CLASS: "ElectricalMeasurementPowerFactor",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_power_factor",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-rssi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "RSSISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_rssi",
},
("sensor", "00:11:22:33:44:55:66:77-1-0-lqi"): {
DEV_SIG_CLUSTER_HANDLERS: ["basic"],
DEV_SIG_ENT_MAP_CLASS: "LQISensor",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_lqi",
},
("update", "00:11:22:33:44:55:66:77-1-25-firmware_update"): {
DEV_SIG_CLUSTER_HANDLERS: ["ota"],
DEV_SIG_ENT_MAP_CLASS: "FirmwareUpdateEntity",
DEV_SIG_ENT_MAP_ID: "update.sinope_technologies_dm2550zb_firmware",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergyMetering",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_instantaneous_demand",
},
("sensor", "00:11:22:33:44:55:66:77-1-1794-summation_delivered"): {
DEV_SIG_CLUSTER_HANDLERS: ["smartenergy_metering"],
DEV_SIG_ENT_MAP_CLASS: "SmartEnergySummation",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_summation_delivered",
},
("sensor", "00:11:22:33:44:55:66:77-1-2"): {
DEV_SIG_CLUSTER_HANDLERS: ["device_temperature"],
DEV_SIG_ENT_MAP_CLASS: "DeviceTemperature",
DEV_SIG_ENT_MAP_ID: "sensor.sinope_technologies_dm2550zb_device_temperature",
},
},
},
]
59 changes: 59 additions & 0 deletions zha/application/platforms/number/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,3 +969,62 @@ class DanfossRegulationSetpointOffset(NumberConfigurationEntity):
_attr_native_max_value: float = 2.5
_attr_native_step: float = 0.1
_attr_multiplier = 1 / 10


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={"DM2500ZB", "DM2500ZB-G2", "DM2550ZB", "DM2550ZB-G2"},
)
class SinopeDimmerOnLevelConfigurationEntity(NumberConfigurationEntity):
"""Representation of a Sinope dimmer switch on level."""

_unique_id_suffix = "on_intensity"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 1
_attr_native_max_value: float = 255
_attribute_name = "on_intensity"
_attr_translation_key: str = "on_level"


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
"SW2500ZB",
"SW2500ZB-G2",
},
)
class SinopeLightLEDOnIntensityConfigurationEntity(NumberConfigurationEntity):
"""Representation of a Sinope switch LED on-level brightness."""

_unique_id_suffix = "on_led_intensity"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 1
_attr_native_max_value: float = 100
_attribute_name = "on_led_intensity"
_attr_translation_key: str = "on_led_intensity"


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
"SW2500ZB",
"SW2500ZB-G2",
},
)
class SinopeLightLEDOffIntensityConfigurationEntity(NumberConfigurationEntity):
"""Representation of a Sinope switch LED off-level brightness."""

_unique_id_suffix = "off_led_intensity"
_attr_entity_category = EntityCategory.CONFIG
_attr_native_min_value: float = 1
_attr_native_max_value: float = 100
_attribute_name = "off_led_intensity"
_attr_translation_key: str = "off_led_intensity"
50 changes: 50 additions & 0 deletions zha/application/platforms/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -819,3 +819,53 @@ class DanfossViewingDirection(ZCLEnumSelectEntity):
_attribute_name = "viewing_direction"
_attr_translation_key: str = "viewing_direction"
_enum = danfoss_thermostat.DanfossViewingDirectionEnum


class SinopeLightLedColors(types.enum32):
"""Color values for Sinope light switch status LEDs."""

Lim = 0x0AFFDC
Amber = 0x000A4B
Fushia = 0x0100A5
Perle = 0x64FFFF
Blue = 0xFFFF00


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
"SW2500ZB",
"SW2500ZB-G2",
},
)
class SinopeLightLEDOffColorSelect(ZCLEnumSelectEntity):
"""Representation of the marker LED Off-state color of Sinope light switches."""

_unique_id_suffix = "off_led_color"
_attribute_name = "off_led_color"
_attr_translation_key: str = "off_led_color"
_enum = SinopeLightLedColors


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
"SW2500ZB",
"SW2500ZB-G2",
},
)
class SinopeLightLEDOnColorSelect(ZCLEnumSelectEntity):
"""Representation of the marker LED On-state color of Sinope light switches."""

_unique_id_suffix = "on_led_color"
_attribute_name = "on_led_color"
_attr_translation_key: str = "on_led_color"
_enum = SinopeLightLedColors
17 changes: 17 additions & 0 deletions zha/application/platforms/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,20 @@ class DanfossAdaptationRunSettings(SwitchConfigurationEntity):
_unique_id_suffix = "adaptation_run_settings"
_attribute_name: str = "adaptation_run_settings"
_attr_translation_key: str = "adaptation_run_enabled"


@CONFIG_DIAGNOSTIC_MATCH(
cluster_handler_names="sinope_manufacturer_specific",
models={
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
},
)
class SinopeLightDoubleTapFullSwitch(SwitchConfigurationEntity):
"""Representation of a config option that controls whether Double Tap Full option is enabled on a Sinope light switch."""

_unique_id_suffix = "double_up_full"
_attribute_name = "double_up_full"
_attr_translation_key: str = "double_up_full"
1 change: 1 addition & 0 deletions zha/zigbee/cluster_handlers/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
SONOFF_CLUSTER: Final[int] = 0xFC11
TUYA_MANUFACTURER_CLUSTER: Final[int] = 0xEF00
VOC_LEVEL_CLUSTER: Final[int] = 0x042E
SINOPE_MANUFACTURER_CLUSTER: Final[int] = 0xFF01

CLUSTER_HANDLER_EVENT: Final[str] = "cluster_handler_event"
CLUSTER_HANDLER_ATTRIBUTE_UPDATED: Final[str] = "cluster_handler_attribute_updated"
Expand Down
60 changes: 60 additions & 0 deletions zha/zigbee/cluster_handlers/manufacturerspecific.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
REPORT_CONFIG_IMMEDIATE,
REPORT_CONFIG_MAX_INT,
REPORT_CONFIG_MIN_INT,
REPORT_CONFIG_MIN_INT_IMMEDIATE,
REPORT_CONFIG_RPT_CHANGE,
SIGNAL_ATTR_UPDATED,
SINOPE_MANUFACTURER_CLUSTER,
SMARTTHINGS_ACCELERATION_CLUSTER,
SMARTTHINGS_HUMIDITY_CLUSTER,
SONOFF_CLUSTER,
Expand Down Expand Up @@ -496,3 +499,60 @@ class DanfossDiagnosticClusterHandler(DiagnosticClusterHandler):
AttrReportConfig(attr="sw_error_code", config=REPORT_CONFIG_DEFAULT),
AttrReportConfig(attr="motor_step_counter", config=REPORT_CONFIG_DEFAULT),
)


@registries.CLUSTER_HANDLER_ONLY_CLUSTERS.register(SINOPE_MANUFACTURER_CLUSTER)
@registries.CLUSTER_HANDLER_REGISTRY.register(SINOPE_MANUFACTURER_CLUSTER)
class SinopeManufacturerClusterHandler(ClusterHandler):
"""Sinope Manufacturer cluster handler."""

BIND = True

def __init__(self, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> None:
"""Initialize Sinope cluster handler."""
super().__init__(cluster, endpoint)
self.ZCL_INIT_ATTRS = {
"double_up_full": True,
"on_led_color": True,
"off_led_color": True,
"off_led_intensity": True,
"on_led_intensity": True,
}

if self.cluster.endpoint.model in [
"DM2550ZB",
"DM2550ZB-G2",
"DM2500ZB-G2",
"DM2500ZB",
]:
self.ZCL_INIT_ATTRS["on_intensity"] = True

_value_attribute = "action_report"
REPORT_CONFIG = (
AttrReportConfig(
attr="action_report",
config=(
REPORT_CONFIG_MIN_INT_IMMEDIATE,
REPORT_CONFIG_MIN_INT_IMMEDIATE,
REPORT_CONFIG_RPT_CHANGE,
),
),
)

@classmethod
def matches(cls, cluster: zigpy.zcl.Cluster, endpoint: Endpoint) -> bool:
"""Filter the cluster match for specific devices."""
switches = (
"SW2500ZB",
"SW2500ZB-G2",
"DM2500ZB",
"DM2500ZB-G2",
"DM2550ZB",
"DM2550ZB-G2",
)

_LOGGER.debug(
"matching sinope device to cluster handler %s", cluster.endpoint.model
)

return cluster.endpoint.model in switches

0 comments on commit a7b5516

Please sign in to comment.