Skip to content
Merged
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f22ee00
Add support for Tuya DPs mapped to multiple cluster attributes
abmantis Dec 25, 2024
0cf12e5
Add test
abmantis Dec 25, 2024
2b63c35
Add Tuya PC321-Z-TY 3 phase power meter
abmantis Dec 25, 2024
d36c1c2
Merge branch 'dev' into tuya_multi_attr
abmantis Jan 2, 2025
af12ce4
Merge branch 'dev' of github.com:zigpy/zha-device-handlers into TS060…
abmantis Jan 6, 2025
98ff595
Merge branch 'dev' of github.com:zigpy/zha-device-handlers into tuya_…
abmantis Jan 17, 2025
32c3fbb
Make dp_to_attribute backwards compatible
abmantis Jan 20, 2025
47bd335
Merge branch 'dev' of github.com:zigpy/zha-device-handlers into tuya_…
abmantis Jan 20, 2025
6142170
Merge branch 'dev' of github.com:zigpy/zha-device-handlers into tuya_…
abmantis Jan 31, 2025
965a3ac
Move _dp_to_attributes to init; add tests
abmantis Jan 31, 2025
5d58046
Merge branch 'dev' of github.com:zigpy/zha-device-handlers into tuya_…
abmantis Feb 6, 2025
581bc0f
Call tuya_dp_multi on tuya_dp to avoid duplicating
abmantis Feb 6, 2025
08eab65
Merge branch 'tuya_multi_attr' of github.com:abmantis/zha-device-hand…
abmantis Feb 8, 2025
3792e0d
Remove uneeded constant attrs
abmantis Feb 8, 2025
2c09161
Merge branch 'dev' of https://github.com/zigpy/zha-device-handlers in…
abmantis Feb 23, 2025
8700364
Add final tweaks and fixes
abmantis Feb 23, 2025
0ad7fad
Add tests for multi_dp converters; fix casing
abmantis Feb 26, 2025
c93a045
Merge branch 'dev' into TS0601_TZE200_nslr42tt
abmantis Mar 7, 2025
99f3b52
Merge branch 'dev' into TS0601_TZE200_nslr42tt
abmantis Mar 19, 2025
b0e7a9c
Add missing negative power tests
abmantis Mar 19, 2025
a8c3034
Use decimal DPs
abmantis Mar 25, 2025
687dbe8
Revert old method names
abmantis Mar 25, 2025
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
218 changes: 218 additions & 0 deletions zhaquirks/tuya/ts0601_power.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
"""Tuya Power Meter."""

from collections.abc import ByteString

from zigpy.quirks.v2 import SensorDeviceClass, SensorStateClass
from zigpy.quirks.v2.homeassistant import (
UnitOfElectricCurrent,
UnitOfEnergy,
UnitOfPower,
)
import zigpy.types as t
from zigpy.zcl.clusters.general import LevelControl, OnOff
from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement

from zhaquirks.tuya import DPToAttributeMapping, TuyaLocalCluster
from zhaquirks.tuya.builder import TuyaQuirkBuilder


def dpToPower(data: ByteString) -> int:
"""Convert DP data to power value."""
# From https://github.com/Koenkk/zigbee2mqtt/issues/18603#issuecomment-2277697295
power = int(data)
if power > 0x0FFFFFFF:
power = (0x1999999C - power) * -1
return power

Check warning on line 25 in zhaquirks/tuya/ts0601_power.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0601_power.py#L22-L25

Added lines #L22 - L25 were not covered by tests


def multiDpToPower(data: ByteString) -> int:
"""Convert DP data to power value."""
# Support negative power readings
# From https://github.com/Koenkk/zigbee2mqtt/issues/18603#issuecomment-2277697295
power = data[7] | (data[6] << 8)
if power > 0x7FFF:
power = (0x999A - power) * -1
return power

Check warning on line 35 in zhaquirks/tuya/ts0601_power.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0601_power.py#L32-L35

Added lines #L32 - L35 were not covered by tests


def multiDpToCurrent(data: ByteString) -> int:
"""Convert DP data to current value."""
return data[4] | (data[3] << 8)

Check warning on line 40 in zhaquirks/tuya/ts0601_power.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0601_power.py#L40

Added line #L40 was not covered by tests


def multiDpToVoltage(data: ByteString) -> int:
"""Convert DP data to voltage value."""
return data[1] | (data[0] << 8)

Check warning on line 45 in zhaquirks/tuya/ts0601_power.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0601_power.py#L45

Added line #L45 was not covered by tests


class Tuya3PhaseElectricalMeasurement(ElectricalMeasurement, TuyaLocalCluster):
"""Tuya Electrical Measurement cluster."""

_CONSTANT_ATTRIBUTES = {
ElectricalMeasurement.AttributeDefs.ac_current_multiplier.id: 1,
ElectricalMeasurement.AttributeDefs.ac_current_divisor.id: 1000,
ElectricalMeasurement.AttributeDefs.ac_voltage_multiplier: 1,
ElectricalMeasurement.AttributeDefs.ac_voltage_divisor.id: 10,
}


(
TuyaQuirkBuilder("_TZE200_nslr42tt", "TS0601")
.tuya_temperature(dp_id=0x85, scale=10)
.tuya_sensor(
dp_id=0x86,
attribute_name="device_status",
type=t.int32s,
fallback_name="Device status",
translation_key="device_status",
)
.tuya_dp(
dp_id=0x84,
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="ac_frequency",
)
# Energy
.tuya_sensor(
dp_id=1,
attribute_name="energy",
type=t.int32s,
divisor=100,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
unit=UnitOfEnergy.KILO_WATT_HOUR,
fallback_name="Total energy",
)
.tuya_sensor(
dp_id=0x65,
attribute_name="energy_ph_a",
type=t.int32s,
divisor=1000,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
unit=UnitOfEnergy.KILO_WATT_HOUR,
translation_key="energy_ph_a",
fallback_name="Energy phase A",
)
.tuya_sensor(
dp_id=0x6F,
attribute_name="energy_ph_b",
type=t.int32s,
divisor=1000,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
unit=UnitOfEnergy.KILO_WATT_HOUR,
translation_key="energy_ph_b",
fallback_name="Energy phase B",
)
.tuya_sensor(
dp_id=0x79,
attribute_name="energy_ph_c",
type=t.int32s,
divisor=1000,
state_class=SensorStateClass.TOTAL,
device_class=SensorDeviceClass.ENERGY,
unit=UnitOfEnergy.KILO_WATT_HOUR,
translation_key="energy_ph_c",
fallback_name="Energy phase C",
)
.tuya_sensor(
dp_id=0x9,
attribute_name="power",
type=t.int32s,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER,
unit=UnitOfPower.WATT,
fallback_name="Total power",
converter=dpToPower,
)
.tuya_sensor(
dp_id=0x83,
attribute_name="current",
type=t.int32s,
divisor=1000,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.CURRENT,
unit=UnitOfElectricCurrent.AMPERE,
fallback_name="Total current",
)
.tuya_dp_multi(
dp_id=0x6,
attribute_mapping=[
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="active_power",
converter=multiDpToPower,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_voltage",
converter=multiDpToVoltage,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_current",
converter=multiDpToCurrent,
),
],
)
.tuya_dp_multi(
dp_id=0x7,
attribute_mapping=[
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="active_power_ph_b",
converter=multiDpToPower,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_voltage_ph_b",
converter=multiDpToVoltage,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_current_ph_b",
converter=multiDpToCurrent,
),
],
)
.tuya_dp_multi(
dp_id=0x8,
attribute_mapping=[
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="active_power_ph_c",
converter=multiDpToPower,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_voltage_ph_c",
converter=multiDpToVoltage,
),
DPToAttributeMapping(
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="rms_current_ph_c",
converter=multiDpToCurrent,
),
],
)
.tuya_dp(
dp_id=0x66,
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="power_factor",
)
.tuya_dp(
dp_id=0x70,
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="power_factor_ph_b",
)
.tuya_dp(
dp_id=0x7A,
ep_attribute=Tuya3PhaseElectricalMeasurement.ep_attribute,
attribute_name="power_factor_ph_c",
)
.adds(Tuya3PhaseElectricalMeasurement)
.removes(LevelControl.cluster_id)
.removes(OnOff.cluster_id)
Comment on lines +214 to +215
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if these clusters have any use at all, but we can now use prevent_default_entity_creation(endpoint_id=1, cluster_id=OnOff.cluster_id) as well (instead of completely "removing" the clusters).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They did not seem to do anything at all. Should prevent_default_entity_creation be used instead for these cases?

.skip_configuration()
.add_to_registry()
)
Loading