diff --git a/tests/test_schneiderelectric.py b/tests/test_schneiderelectric.py index 00c8cc050b..8375506c77 100644 --- a/tests/test_schneiderelectric.py +++ b/tests/test_schneiderelectric.py @@ -2,14 +2,13 @@ from unittest import mock -import pytest -from zigpy.zcl import foundation +from zigpy.zcl import ClusterType, foundation from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.smartenergy import Metering from tests.common import ClusterListener +import zhaquirks from zhaquirks.schneiderelectric import SE_MANUF_NAME -import zhaquirks.schneiderelectric.outlet zhaquirks.setup() @@ -86,10 +85,13 @@ async def test_1gang_shutter_1_lift_percentage_updates(zigpy_device_from_v2_quir assert len(cluster_listener.cluster_commands) == 0 -@pytest.mark.parametrize("quirk", (zhaquirks.schneiderelectric.outlet.SocketOutlet,)) -async def test_schneider_device_temp(zigpy_device_from_quirk, quirk): +async def test_schneider_device_temp(zigpy_device_from_v2_quirk): """Test that instant demand is divided by 1000.""" - device = zigpy_device_from_quirk(quirk) + device = zigpy_device_from_v2_quirk( + manufacturer=SE_MANUF_NAME, + model="SOCKET/OUTLET/1", + cluster_ids={6: {Metering.cluster_id: ClusterType.Server}}, + ) metering_cluster = device.endpoints[6].smartenergy_metering metering_listener = ClusterListener(metering_cluster) diff --git a/zhaquirks/schneiderelectric/outlet.py b/zhaquirks/schneiderelectric/outlet.py index 5397a71a3c..7764fcb19c 100644 --- a/zhaquirks/schneiderelectric/outlet.py +++ b/zhaquirks/schneiderelectric/outlet.py @@ -1,31 +1,63 @@ """Schneider Electric (Wiser) Outlet Quirks.""" -from zigpy.profiles import zgp, zha -from zigpy.quirks import CustomCluster, CustomDevice -from zigpy.zcl.clusters.general import ( - Basic, - GreenPowerProxy, - Groups, - Identify, - OnOff, - Ota, - Scenes, -) -from zigpy.zcl.clusters.homeautomation import Diagnostic, ElectricalMeasurement -from zigpy.zcl.clusters.smartenergy import DeviceManagement, Metering - -from zhaquirks.const import ( - DEVICE_TYPE, - ENDPOINTS, - INPUT_CLUSTERS, - MODELS_INFO, - OUTPUT_CLUSTERS, - PROFILE_ID, -) -from zhaquirks.schneiderelectric import SE_MANUF_NAME +from typing import Final + +from zigpy import types as t +from zigpy.quirks import CustomCluster +from zigpy.quirks.v2 import QuirkBuilder +from zigpy.zcl.clusters.smartenergy import Metering +from zigpy.zcl.foundation import DataTypeId, ZCLAttributeDef + +from zhaquirks.schneiderelectric import SE_MANUF_NAME, SEBasic + + +class SEIndicatorMode(t.enum8): + """Indicator mode.""" + + InverseOfOutput = 0x00 + FollowsOutput = 0x01 + AlwaysOff = 0x02 + AlwaysOn = 0x03 + +class SELocalControlMode(t.enum8): + """Local control mode.""" -class MeteringCluster(CustomCluster, Metering): + Active = 0x00 + Inactive = 0x01 + + +class SEOutletConfiguration(CustomCluster): + """Schneider Electric Outlet Configuration cluster.""" + + cluster_id = 0xFC04 + name = "SEOutletConfiguration" + + class AttributeDefs(CustomCluster.AttributeDefs): + """Attribute definitions.""" + + se_indicator_luminance_level: Final = ZCLAttributeDef( + id=0x0000, + type=t.uint8_t, + access="rw", + is_manufacturer_specific=True, + ) + se_indicator_mode: Final = ZCLAttributeDef( + id=0x0002, + type=SEIndicatorMode, + zcl_type=DataTypeId.uint8, + access="rw", + is_manufacturer_specific=True, + ) + se_local_control_mode: Final = ZCLAttributeDef( + id=0x0050, + type=SELocalControlMode, + access="rw", + is_manufacturer_specific=True, + ) + + +class SEMeteringCluster(CustomCluster, Metering): """Custom Metering cluster to fix instantaneous demand value multiplied by 1000.""" def _update_attribute(self, attrid, value): @@ -34,76 +66,37 @@ def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) -class SocketOutlet(CustomDevice): - """Schneider Electric Socket outlet WDE002182, WDE002172.""" - - signature = { - MODELS_INFO: [ - (SE_MANUF_NAME, "SOCKET/OUTLET/1"), - (SE_MANUF_NAME, "SOCKET/OUTLET/2"), - ], - ENDPOINTS: { - # - 6: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.MAIN_POWER_OUTLET, - INPUT_CLUSTERS: [ - Basic.cluster_id, - Identify.cluster_id, - Groups.cluster_id, - Scenes.cluster_id, - OnOff.cluster_id, - Metering.cluster_id, - DeviceManagement.cluster_id, - ElectricalMeasurement.cluster_id, - Diagnostic.cluster_id, - 0xFC04, - ], - OUTPUT_CLUSTERS: [ - Ota.cluster_id, - ], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [ - GreenPowerProxy.cluster_id, - ], - }, - }, - } - - replacement = { - ENDPOINTS: { - 6: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.MAIN_POWER_OUTLET, - INPUT_CLUSTERS: [ - Basic.cluster_id, - Identify.cluster_id, - Groups.cluster_id, - Scenes.cluster_id, - OnOff.cluster_id, - MeteringCluster, - DeviceManagement.cluster_id, - ElectricalMeasurement.cluster_id, - Diagnostic.cluster_id, - 0xFC04, - ], - OUTPUT_CLUSTERS: [ - Ota.cluster_id, - ], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [ - GreenPowerProxy.cluster_id, - ], - }, - }, - } +( + QuirkBuilder(SE_MANUF_NAME, "SOCKET/OUTLET/1") + .applies_to(SE_MANUF_NAME, "SOCKET/OUTLET/2") + .replaces(SEBasic, endpoint_id=6) + .replaces(SEMeteringCluster, endpoint_id=6) + .replaces(SEOutletConfiguration, endpoint_id=6) + .number( + cluster_id=SEOutletConfiguration.cluster_id, + endpoint_id=6, + attribute_name=SEOutletConfiguration.AttributeDefs.se_indicator_luminance_level.name, + min_value=0, + max_value=5, + step=1, + translation_key="indicator_luminance_level", + fallback_name="Indicator luminance level", + ) + .enum( + cluster_id=SEOutletConfiguration.cluster_id, + endpoint_id=6, + attribute_name=SEOutletConfiguration.AttributeDefs.se_indicator_mode.name, + enum_class=SEIndicatorMode, + translation_key="indicator_mode", + fallback_name="Indicator mode", + ) + .enum( + cluster_id=SEOutletConfiguration.cluster_id, + endpoint_id=6, + attribute_name=SEOutletConfiguration.AttributeDefs.se_local_control_mode.name, + enum_class=SELocalControlMode, + translation_key="local_control_mode", + fallback_name="Local control mode", + ) + .add_to_registry() +)