DDS238-2 zigbee?

Hi all,

Recently I ordered a few DDS238-2 (zigbee, NOT WiFi) energy meters/circuit breakers.
I use ZHA with a Conbee II USB stick.
Pairing went fine (they show up as model TS0601),
but the only entities that showed up, are LQI and RSSI.
No energy reading (Watt / Volt etc …), not even a switch !

I can’t use the Tuya (v2) integration, because I don’t have a Tuya zigbee gateway …

Any way to add the missing entities in HA ?
(I’m only interested in reading the power consumption, no need to switch them on / off)

Anybody out there with a little guideline to help me out ?

Best regards from Belgium,
Kris

In the meanwhile, I learned what “quirks” are !

However, my TS0601 is from a newer type: tze200_bkkmqmyo
I think I need to change some things in the following Python code, but I’m lost in the woods …
Anyone got any idea on what needs to be changed in the following code ?
It seems there is no code available anywhere, that can read-out this new type of TS0601 :frowning:
(or my google-fu is not anymore what is used to be …)

I can “see” power measurement with this code, but it returns always “unknown”.
Also the switch is doing nothing, when pulled in HA …

"""Tuya Din Power Meter."""
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
from zigpy.zcl.clusters.smartenergy import Metering

from zhaquirks import Bus, LocalDataCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import TuyaManufClusterAttributes, TuyaOnOff, TuyaSwitch

TUYA_TOTAL_ENERGY_ATTR = 0x0201
TUYA_CURRENT_ATTR = 0x0266
TUYA_FREQ_ATTR = 0x0269
TUYA_POWER_ATTR = 0x0267
TUYA_VOLTAGE_ATTR = 0x0006
TUYA_DIN_SWITCH_ATTR = 0x0210
TUYA_PFACT_ATTR = 0x026F

SWITCH_EVENT = "switch_event"


class TuyaManufClusterDinPower(TuyaManufClusterAttributes):
    """Manufacturer Specific Cluster of the Tuya Power Meter device."""

    attributes = {
        TUYA_TOTAL_ENERGY_ATTR: ("energy", t.uint16_t),
        TUYA_CURRENT_ATTR: ("current", t.int16s),
        TUYA_FREQ_ATTR: ("freq", t.uint16_t),
        TUYA_PFACT_ATTR: ("pfact", t.int16s),
        TUYA_POWER_ATTR: ("power", t.uint16_t),
        TUYA_VOLTAGE_ATTR: ("voltage", t.uint16_t),
        TUYA_DIN_SWITCH_ATTR: ("switch", t.uint8_t),
    }

    def _update_attribute(self, attrid, value):
        super()._update_attribute(attrid, value)
        if attrid == TUYA_TOTAL_ENERGY_ATTR:
            self.endpoint.smartenergy_metering.energy_reported(value / 100)
        elif attrid == TUYA_CURRENT_ATTR:
            self.endpoint.electrical_measurement.current_reported(value)
        elif attrid == TUYA_FREQ_ATTR:
            self.endpoint.electrical_measurement.freq_reported(value / 100)
        elif attrid == TUYA_POWER_ATTR:
            self.endpoint.electrical_measurement.power_reported(value)
        elif attrid == TUYA_PFACT_ATTR:
            self.endpoint.electrical_measurement.pfact_reported(value / 10)
        elif attrid == TUYA_VOLTAGE_ATTR:
            self.endpoint.electrical_measurement.voltage_reported(value / 10)
        elif attrid == TUYA_DIN_SWITCH_ATTR:
            self.endpoint.device.switch_bus.listener_event(
                SWITCH_EVENT, self.endpoint.endpoint_id, value
            )


class TuyaPowerMeasurement(LocalDataCluster, ElectricalMeasurement):
    """Custom class for power, voltage and current measurement."""

    cluster_id = ElectricalMeasurement.cluster_id

    POWER_ID = 0x050B
    VOLTAGE_ID = 0x0505
    CURRENT_ID = 0x0508
    FREQ_ID = 0x0300
    PFACT_ID = 0x0510

    AC_CURRENT_MULTIPLIER = 0x0602
    AC_CURRENT_DIVISOR = 0x0603
    AC_FREQ_MULTIPLIER = 0x0400
    AC_FREQ_DIVISOR = 0x0401

    _CONSTANT_ATTRIBUTES = {AC_CURRENT_MULTIPLIER: 1, AC_CURRENT_DIVISOR: 1000}

    def voltage_reported(self, value):
        """Voltage reported."""
        self._update_attribute(self.VOLTAGE_ID, value)

    def power_reported(self, value):
        """Power reported."""
        self._update_attribute(self.POWER_ID, value)

    def current_reported(self, value):
        """Ampers reported."""
        self._update_attribute(self.CURRENT_ID, value)
        
    def freq_reported(self, value):
        """Freq reported."""
        self._update_attribute(self.FREQ_ID, value)
        
    def pfact_reported(self, value):
        """Power factor reported."""
        self._update_attribute(self.PFACT_ID, value)


class TuyaElectricalMeasurement(LocalDataCluster, Metering):
    """Custom class for total energy measurement."""

    cluster_id = Metering.cluster_id
    CURRENT_ID = 0x0000
    POWER_WATT = 0x0000

    """Setting unit of measurement."""
    _CONSTANT_ATTRIBUTES = {0x0300: POWER_WATT}

    def energy_reported(self, value):
        """Summation Energy reported."""
        self._update_attribute(self.CURRENT_ID, value)


class TuyaPowerMeter(TuyaSwitch):
    """Tuya power meter device."""

    def __init__(self, *args, **kwargs):
        """Init device."""
        self.switch_bus = Bus()
        super().__init__(*args, **kwargs)

    signature = {
        # "node_descriptor": "<NodeDescriptor byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4098
        #                       maximum_buffer_size=82 maximum_incoming_transfer_size=82 server_mask=11264
        #                       maximum_outgoing_transfer_size=82 descriptor_capability_field=0>",
        # device_version=1
        # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
        # output_clusters=[0x000a, 0x0019]
        MODELS_INFO: [
            ("_TZE200_bkkmqmyo", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=51
            # device_version=1
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaManufClusterAttributes.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaManufClusterDinPower,
                    TuyaPowerMeasurement,
                    TuyaElectricalMeasurement,
                    TuyaOnOff,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

This morning, when I woke up, all entities where visible :slight_smile:
I guess it does take some time before they appear.
Switch does not work to turn breaker on/off, but that’s OK, I’m using them only for energy monitoring, so I can’t accidently turn them off :slight_smile:

Happy happy happy !

Hi

How exactly did you manage to integrate it with HA?
Can you provide some steps?

Thx from Poland
Sławomir

  1. add this device as a regular zigbee device.
  2. create a folder “quirks” in your /config/ folder, and save the underneath content as
    “ts0601_din_power.py” into this folder.
  3. look into “device info” of your DDS238-2 device, and note the second line, it shoud be something
    like “TZE200_bkkmqmyo”.
    into the PY-file, on line 133, overwrite the identifier with your identical device info.
  4. Save file.
  5. add the following lines to your configuration.yaml (and reboot HA)
zha: 
  enable_quirks: true
  custom_quirks_path: /config/quirks
  1. Go back into the “device info”, if everything is OK, you should now see a new line added to the
    bottom of this info: “Quirk: ts0601_din_power.TuyaPowerMeter”
  2. If your entities of this device are not yet visible, reboot HA once again
  3. Enjoy :slight_smile:
"""Tuya Din Power Meter."""
from zigpy.profiles import zha
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
from zigpy.zcl.clusters.smartenergy import Metering

from zhaquirks import Bus, LocalDataCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import TuyaManufClusterAttributes, TuyaOnOff, TuyaSwitch

TUYA_TOTAL_ENERGY_ATTR = 0x0201
TUYA_CURRENT_ATTR = 0x0266
TUYA_FREQ_ATTR = 0x0269
TUYA_POWER_ATTR = 0x0267
TUYA_VOLTAGE_ATTR = 0x0006
TUYA_DIN_SWITCH_ATTR = 0x0210
TUYA_PFACT_ATTR = 0x026F

SWITCH_EVENT = "switch_event"


class TuyaManufClusterDinPower(TuyaManufClusterAttributes):
    """Manufacturer Specific Cluster of the Tuya Power Meter device."""

    attributes = {
        TUYA_TOTAL_ENERGY_ATTR: ("energy", t.uint16_t),
        TUYA_CURRENT_ATTR: ("current", t.int16s),
        TUYA_FREQ_ATTR: ("freq", t.uint16_t),
        TUYA_PFACT_ATTR: ("pfact", t.int16s),
        TUYA_POWER_ATTR: ("power", t.uint16_t),
        TUYA_VOLTAGE_ATTR: ("voltage", t.uint16_t),
        TUYA_DIN_SWITCH_ATTR: ("switch", t.uint8_t),
    }

    def _update_attribute(self, attrid, value):
        super()._update_attribute(attrid, value)
        if attrid == TUYA_TOTAL_ENERGY_ATTR:
            self.endpoint.smartenergy_metering.energy_reported(value / 100)
        elif attrid == TUYA_CURRENT_ATTR:
            self.endpoint.electrical_measurement.current_reported(value)
        elif attrid == TUYA_FREQ_ATTR:
            self.endpoint.electrical_measurement.freq_reported(value / 100)
        elif attrid == TUYA_POWER_ATTR:
            self.endpoint.electrical_measurement.power_reported(value)
        elif attrid == TUYA_PFACT_ATTR:
            self.endpoint.electrical_measurement.pfact_reported(value / 10)
        elif attrid == TUYA_VOLTAGE_ATTR:
            self.endpoint.electrical_measurement.voltage_reported(value / 10)
        elif attrid == TUYA_DIN_SWITCH_ATTR:
            self.endpoint.device.switch_bus.listener_event(
                SWITCH_EVENT, self.endpoint.endpoint_id, value
            )


class TuyaPowerMeasurement(LocalDataCluster, ElectricalMeasurement):
    """Custom class for power, voltage and current measurement."""

    cluster_id = ElectricalMeasurement.cluster_id

    POWER_ID = 0x050B
    VOLTAGE_ID = 0x0505
    CURRENT_ID = 0x0508
    FREQ_ID = 0x0300
    PFACT_ID = 0x0510

    AC_CURRENT_MULTIPLIER = 0x0602
    AC_CURRENT_DIVISOR = 0x0603
    AC_FREQ_MULTIPLIER = 0x0400
    AC_FREQ_DIVISOR = 0x0401

    _CONSTANT_ATTRIBUTES = {AC_CURRENT_MULTIPLIER: 1, AC_CURRENT_DIVISOR: 1000}

    def voltage_reported(self, value):
        """Voltage reported."""
        self._update_attribute(self.VOLTAGE_ID, value)

    def power_reported(self, value):
        """Power reported."""
        self._update_attribute(self.POWER_ID, value)

    def current_reported(self, value):
        """Ampers reported."""
        self._update_attribute(self.CURRENT_ID, value)
        
    def freq_reported(self, value):
        """Freq reported."""
        self._update_attribute(self.FREQ_ID, value)
        
    def pfact_reported(self, value):
        """Power factor reported."""
        self._update_attribute(self.PFACT_ID, value)


class TuyaElectricalMeasurement(LocalDataCluster, Metering):
    """Custom class for total energy measurement."""

    cluster_id = Metering.cluster_id
    CURRENT_ID = 0x0000
    POWER_WATT = 0x0000

    """Setting unit of measurement."""
    _CONSTANT_ATTRIBUTES = {0x0300: POWER_WATT}

    def energy_reported(self, value):
        """Summation Energy reported."""
        self._update_attribute(self.CURRENT_ID, value)


class TuyaPowerMeter(TuyaSwitch):
    """Tuya power meter device."""

    def __init__(self, *args, **kwargs):
        """Init device."""
        self.switch_bus = Bus()
        super().__init__(*args, **kwargs)

    signature = {
        # "node_descriptor": "<NodeDescriptor byte1=1 byte2=64 mac_capability_flags=142 manufacturer_code=4098
        #                       maximum_buffer_size=82 maximum_incoming_transfer_size=82 server_mask=11264
        #                       maximum_outgoing_transfer_size=82 descriptor_capability_field=0>",
        # device_version=1
        # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
        # output_clusters=[0x000a, 0x0019]
        MODELS_INFO: [
            ("_TZE200_bkkmqmyo", "TS0601"),
        ],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=51
            # device_version=1
            # input_clusters=[0, 4, 5, 61184]
            # output_clusters=[10, 25]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaManufClusterAttributes.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TuyaManufClusterDinPower,
                    TuyaPowerMeasurement,
                    TuyaElectricalMeasurement,
                    TuyaOnOff,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

Hi
I followed your steps and everything is fine except that the switch is invalid and the current value is always zero.

The new code from here

seems to fix the switch issue

Thanks, indeed, switch now works fine :slight_smile: