Skip to content

Commit

Permalink
Add missing ZHA metering device types (#109126)
Browse files Browse the repository at this point in the history
* Update smartenergy.py metering_device_type enums

* Added missing enum 127

* Enum 127 is also electric metering type

* Meter type constants and status enums in smartenergy cluster handler

Addresses 
#109126 (comment)

Whilst I have the code open I've also added status handlers for the non-electrical meter types.

* New tests for different metering device type statuses
  • Loading branch information
jeverley committed Jan 30, 2024
1 parent bcb9a10 commit 4ec3a17
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 7 deletions.
101 changes: 96 additions & 5 deletions homeassistant/components/zha/core/cluster_handlers/smartenergy.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,67 @@ class MeteringClusterHandler(ClusterHandler):
Metering.AttributeDefs.unit_of_measure.name: True,
}

METERING_DEVICE_TYPES_ELECTRIC = {
0,
7,
8,
9,
10,
11,
13,
14,
15,
127,
134,
135,
136,
137,
138,
140,
141,
142,
}
METERING_DEVICE_TYPES_GAS = {1, 128}
METERING_DEVICE_TYPES_WATER = {2, 129}
METERING_DEVICE_TYPES_HEATING_COOLING = {3, 5, 6, 130, 132, 133}

metering_device_type = {
0: "Electric Metering",
1: "Gas Metering",
2: "Water Metering",
3: "Thermal Metering",
3: "Thermal Metering", # depreciated
4: "Pressure Metering",
5: "Heat Metering",
6: "Cooling Metering",
7: "End Use Measurement Device (EUMD) for metering electric vehicle charging",
8: "PV Generation Metering",
9: "Wind Turbine Generation Metering",
10: "Water Turbine Generation Metering",
11: "Micro Generation Metering",
12: "Solar Hot Water Generation Metering",
13: "Electric Metering Element/Phase 1",
14: "Electric Metering Element/Phase 2",
15: "Electric Metering Element/Phase 3",
127: "Mirrored Electric Metering",
128: "Mirrored Gas Metering",
129: "Mirrored Water Metering",
130: "Mirrored Thermal Metering",
130: "Mirrored Thermal Metering", # depreciated
131: "Mirrored Pressure Metering",
132: "Mirrored Heat Metering",
133: "Mirrored Cooling Metering",
134: "Mirrored End Use Measurement Device (EUMD) for metering electric vehicle charging",
135: "Mirrored PV Generation Metering",
136: "Mirrored Wind Turbine Generation Metering",
137: "Mirrored Water Turbine Generation Metering",
138: "Mirrored Micro Generation Metering",
139: "Mirrored Solar Hot Water Generation Metering",
140: "Mirrored Electric Metering Element/Phase 1",
141: "Mirrored Electric Metering Element/Phase 2",
142: "Mirrored Electric Metering Element/Phase 3",
}

class DeviceStatusElectric(enum.IntFlag):
"""Metering Device Status."""
"""Electric Metering Device Status."""

NO_ALARMS = 0
CHECK_METER = 1
Expand All @@ -158,6 +201,45 @@ class DeviceStatusElectric(enum.IntFlag):
SERVICE_DISCONNECT = 64
RESERVED = 128

class DeviceStatusGas(enum.IntFlag):
"""Gas Metering Device Status."""

NO_ALARMS = 0
CHECK_METER = 1
LOW_BATTERY = 2
TAMPER_DETECT = 4
NOT_DEFINED = 8
LOW_PRESSURE = 16
LEAK_DETECT = 32
SERVICE_DISCONNECT = 64
REVERSE_FLOW = 128

class DeviceStatusWater(enum.IntFlag):
"""Water Metering Device Status."""

NO_ALARMS = 0
CHECK_METER = 1
LOW_BATTERY = 2
TAMPER_DETECT = 4
PIPE_EMPTY = 8
LOW_PRESSURE = 16
LEAK_DETECT = 32
SERVICE_DISCONNECT = 64
REVERSE_FLOW = 128

class DeviceStatusHeatingCooling(enum.IntFlag):
"""Heating and Cooling Metering Device Status."""

NO_ALARMS = 0
CHECK_METER = 1
LOW_BATTERY = 2
TAMPER_DETECT = 4
TEMPERATURE_SENSOR = 8
BURST_DETECT = 16
LEAK_DETECT = 32
SERVICE_DISCONNECT = 64
REVERSE_FLOW = 128

class DeviceStatusDefault(enum.IntFlag):
"""Metering Device Status."""

Expand Down Expand Up @@ -198,9 +280,18 @@ def status(self) -> int | None:
"""Return metering device status."""
if (status := self.cluster.get(Metering.AttributeDefs.status.name)) is None:
return None
if self.cluster.get(Metering.AttributeDefs.metering_device_type.name) == 0:
# Electric metering device type

metering_device_type = self.cluster.get(
Metering.AttributeDefs.metering_device_type.name
)
if metering_device_type in self.METERING_DEVICE_TYPES_ELECTRIC:
return self.DeviceStatusElectric(status)
if metering_device_type in self.METERING_DEVICE_TYPES_GAS:
return self.DeviceStatusGas(status)
if metering_device_type in self.METERING_DEVICE_TYPES_WATER:
return self.DeviceStatusWater(status)
if metering_device_type in self.METERING_DEVICE_TYPES_HEATING_COOLING:
return self.DeviceStatusHeatingCooling(status)
return self.DeviceStatusDefault(status)

@property
Expand Down
28 changes: 26 additions & 2 deletions tests/components/zha/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,33 @@ async def async_test_metering(hass: HomeAssistant, cluster, entity_id):
)

await send_attributes_report(
hass, cluster, {"status": 32, "metering_device_type": 1}
hass, cluster, {"status": 64 + 8, "metering_device_type": 1}
)
assert hass.states.get(entity_id).attributes["status"] in (
"SERVICE_DISCONNECT|NOT_DEFINED",
"NOT_DEFINED|SERVICE_DISCONNECT",
)

await send_attributes_report(
hass, cluster, {"status": 64 + 8, "metering_device_type": 2}
)
assert hass.states.get(entity_id).attributes["status"] in (
"SERVICE_DISCONNECT|PIPE_EMPTY",
"PIPE_EMPTY|SERVICE_DISCONNECT",
)

await send_attributes_report(
hass, cluster, {"status": 64 + 8, "metering_device_type": 5}
)
assert hass.states.get(entity_id).attributes["status"] in (
"SERVICE_DISCONNECT|TEMPERATURE_SENSOR",
"TEMPERATURE_SENSOR|SERVICE_DISCONNECT",
)

# Status for other meter types
await send_attributes_report(
hass, cluster, {"status": 32, "metering_device_type": 4}
)
# currently only statuses for electric meters are supported
assert hass.states.get(entity_id).attributes["status"] in ("<bitmap8.32: 32>", "32")


Expand Down

0 comments on commit 4ec3a17

Please sign in to comment.