zigpy / zha-device-handlers

ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices.
Apache License 2.0
683 stars 634 forks source link

[Device Support Request] _TZE204_ac0fhfiq TS0601 BiDirectional ZigBee Energy Meter with 150A Clamp Current Sensor #2420

Open stombi opened 1 year ago

stombi commented 1 year ago

Problem description

Hi I would like to request device support for the _TZE204_ac0fhfiq TS0601 BiDirectional ZigBee Energy Meter with 150A Clamp Current Sensor.

It apears in HA, but without any sensor.

product link : https://www.aliexpress.com/item/1005005466228073.html

Solution description

Any

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

Device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0051", "in_clusters": [ "0x0000", "0x0004", "0x0005", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] }, "242": { "profile_id": 41440, "device_type": "0x0061", "in_clusters": [], "out_clusters": [ "0x0021" ] } }, "manufacturer": "_TZE204_ac0fhfiq", "model": "TS0601", "class": "zigpy.device.Device" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant Container", "version": "2023.2.5", "dev": false, "hassio": false, "virtualenv": false, "python_version": "3.10.7", "docker": true, "arch": "x86_64", "timezone": "Europe/Paris", "os_name": "Linux", "os_version": "5.10.0-21-amd64", "run_as_root": true }, "custom_components": {}, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows==0.34.7", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.92", "zigpy-deconz==0.19.2", "zigpy==0.53.0", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.2" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus" ] }, { "vid": "10C4", "pid": "EA60", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*zigstar*", "known_devices": [ "ZigStar Coordinators" ] }, { "vid": "1CF1", "pid": "0030", "description": "*conbee*", "known_devices": [ "Conbee II" ] }, { "vid": "10C4", "pid": "8A2A", "description": "*zigbee*", "known_devices": [ "Nortek HUSBZB-1" ] }, { "vid": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "dependencies": [ "file_upload" ], "after_dependencies": [ "onboarding", "usb", "zeroconf" ], "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp" ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 64567, "manufacturer": "_TZE204_ac0fhfiq", "model": "TS0601", "name": "_TZE204_ac0fhfiq TS0601", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4417, "power_source": "Mains", "lqi": 120, "rssi": null, "last_seen": "2023-05-29T13:52:11", "available": true, "device_type": "Router", "signature": { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0051", "in_clusters": [ "0x0000", "0x0004", "0x0005", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] }, "242": { "profile_id": 41440, "device_type": "0x0061", "in_clusters": [], "out_clusters": [ "0x0021" ] } } }, "active_coordinator": false, "entities": [], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "SMART_PLUG" }, { "name": "unknown 97 device_type of 0xa1e0 profile id" } ], "user_given_name": null, "device_reg_id": "9dce80159cb1f043f40e7a51ddbb311c", "area_id": "salon", "cluster_details": { "1": { "device_type": { "name": "SMART_PLUG", "id": 81 }, "profile_id": 260, "in_clusters": { "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": null, "attributes": {}, "unsupported_attributes": {} }, "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 74 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE204_ac0fhfiq" }, "0x0005": { "attribute_name": "model", "value": "TS0601" } }, "unsupported_attributes": {} } }, "out_clusters": { "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} }, "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} } } }, "242": { "device_type": { "name": "unknown", "id": 97 }, "profile_id": 41440, "in_clusters": {}, "out_clusters": { "0x0021": { "endpoint_attribute": "green_power", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

Logs ```python [Paste the logs here] ```

Custom quirk

Custom quirk ```python [Paste your custom quirk here] ```

Additional information

No response

reef-actor commented 1 year ago

I am also looking to get this device supported. The seller mentioned that they could provide configuration assistance for z2m so I have asked them for this information, if I get a response I will share it here.

jkfamillerosier commented 1 year ago

I just got one of those. I tried to make it work by adding a TS0601.js file with the parameters for a _TZE204_cjbofhxw :

`const fz = require('zigbee-herdsman-converters/converters/fromZigbee'); const tz = require('zigbee-herdsman-converters/converters/toZigbee'); const exposes = require('zigbee-herdsman-converters/lib/exposes'); const reporting = require('zigbee-herdsman-converters/lib/reporting'); const extend = require('zigbee-herdsman-converters/lib/extend'); const ota = require('zigbee-herdsman-converters/lib/ota'); const tuya = require('zigbee-herdsman-converters/lib/tuya'); const utils = require('zigbee-herdsman-converters/lib/utils'); const globalStore = require('zigbee-herdsman-converters/lib/store'); const e = exposes.presets; const ea = exposes.access;

const fzLocal = { gateway_connection_status: { cluster: 'manuSpecificTuya', type: ['commandMcuGatewayConnectionStatus'], convert: async (model, msg, publish, options, meta) => { // "payload" can have the following values: // 0x00: The gateway is not connected to the internet. // 0x01: The gateway is connected to the internet. // 0x02: The request timed out after three seconds. const payload = {payloadSize: 1,payload: 1} await msg.endpoint.command('manuSpecificTuya', 'mcuGatewayConnectionStatus', payload, {}); }, }, }

const definition = { fingerprint: tuya.fingerprint('TS0601', ['_TZE204_ac0fhfiq']), model: 'TS0601_clamp_meter', vendor: 'TuYa', description: 'Clamp meter', fromZigbee: [tuya.fz.datapoints, fzLocal.gateway_connection_status], toZigbee: [tuya.tz.datapoints], configure: tuya.configureMagicPacket, exposes: [e.current(), e.power(), e.voltage(), e.energy()], meta: { tuyaDatapoints: [ [6, 'current', tuya.valueConverter.divideBy1000], [103, 'power', tuya.valueConverter.divideBy10], [20, 'voltage', tuya.valueConverter.divideBy10], [101, 'energy', tuya.valueConverter.divideBy1000], ], }, };

module.exports = definition;`

I modified the model. The energy value (datapoint 101) works (seems to work). But the rest is either null or 0 when I try other datapoints.

Hope this helps.

rrozema commented 1 year ago

Most likely the same quirk can be used as the one resulting from #1973. Probably only need to add the additional device name.

stombi commented 1 year ago

I'm really new to this, I know nothing about Zigbee, I couldnt get #1973 to work.

I managed to use @jkfamillerosier TS0601.js , I found out that you need to have current in the wire with the clamp before you pair the device. Then it gave me another datapoint 102 wich is 0 or 0.1 depending on the direction of the clamp.

The energy datapoint 101 seems ok, it gave me 0.03Kwh

The sort of "manual" that came with the device gives a hint about how to see the way the current is going, apparently the end number of power I guess is supposed to be 1 or 0 photo_2023-06-05_20-47-05

reef-actor commented 1 year ago

Most likely the same quirk can be used as the one resulting from #1973. Probably only need to add the additional device name.

I took the quirk from that issue and changed the MODELS_INFO in TuyaPowerMeter to:

MODELS_INFO: [
  ("_TZE204_ac0fhfiq", "TS0601"),
]

After removing the device, deleting pycache, restarting and re-pairing, the quirk is not shown in the device. Any idea what I might need to do to get it applied?

stombi commented 1 year ago

I did some testing with this js , 101 datapoint is definitly the instantaneous power in W (mesured with a fan with 3 speed buttons 35W/41W/49W) and 102 datapoint is 1 or 0 depending of the direction off the clamp

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    // Since a lot of TuYa devices use the same modelID, but use different datapoints
    // it's necessary to provide a fingerprint instead of a zigbeeModel
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE204_ac0fhfiq',
        },
    ],
    model: 'TS0601_TZE204_ac0fhfiq',
    vendor: 'TuYa',
    description: 'Bidirectional clamp meter',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        // Here you should put all functionality that your device exposes
        e.current(),
        e.power(),
        e.voltage(),
        e.energy()
    ],
    meta: {
        // All datapoints go in here
        tuyaDatapoints: [
            [6, 'current', tuya.valueConverter.raw],
            [101, 'power', tuya.valueConverter.raw], // 101 - Instantaneous measured power
            [103, 'voltage', tuya.valueConverter.raw], // nok : 214,212,103
            [102, 'energy', tuya.valueConverter.raw], // 102 - current direction in clamp 0 or 1
        ],
    },
};

module.exports = definition;``
rrozema commented 1 year ago

After removing the device, deleting pycache, restarting and re-pairing, the quirk is not shown in the device. Any idea what I might need to do to get it applied?

Did you define the custom_quirks_path in your configuration.yaml as described f.e. here?

reef-actor commented 1 year ago

Yep, I even have enable_quirks out of desperation.

zha:
  enable_quirks: true
  custom_quirks_path: /config/zha_quirks/

In the logs for HA I do see "Loaded custom quirks". Are there normally further log messages about custom quirks?

MattWestb commented 1 year ago
logger:
  default: info
  logs:
    homeassistant.core: info
    homeassistant.components.zha: debug
    zigpy: debug
    zhaquirks: debug
    custom_zha_quirks: debug

Is working OK in my test systems and getting tuya DP commands in the log. Also the loading of the quirks is nice logged so can see if the custom quirk is loaded OK then ZHA is starting.

reef-actor commented 1 year ago

Thanks @MattWestb, I have at least got the quirk loaded now. In my logs I saw the following:

[zigpy.quirks.registry] Checking quirks for _TZE204_ac0fhfiq TS0601 (a4:c1:38:97:fc:b8:e7:4b)
[zigpy.quirks.registry] Considering <class 'ts0601_din_power.TuyaPowerMeter'>
[zigpy.quirks.registry] Fail because endpoint list mismatch: {1} {1, 242}

So I added the missing endpoint (242) to the #1973 quirk with

from zigpy.zcl.clusters.general import GreenPowerProxy

and this to the ENDPOINTS dictionary

            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },

Which gets me to this image

Does the need for this additional endpoint possibly suggest I have a different variant? Curious log messages:

[zigpy.appdb] Error handling '_unsupported_attribute_added' event with (a4:c1:38:97:fc:b8:e7:4b, 1, 1794, 772) params: FOREIGN KEY constraint failed
[zigpy.appdb] Error handling '_unsupported_attribute_added' event with (a4:c1:38:97:fc:b8:e7:4b, 1, 1794, 774) params: FOREIGN KEY constraint failed
[zigpy.appdb] Error handling '_unsupported_attribute_added' event with (a4:c1:38:97:fc:b8:e7:4b, 1, 1794, 769) params: FOREIGN KEY constraint failed
[zigpy.appdb] Error handling '_unsupported_attribute_added' event with (a4:c1:38:97:fc:b8:e7:4b, 1, 1794, 771) params: FOREIGN KEY constraint failed

There are about 30 of these, are they the problem?

rrozema commented 1 year ago

EDIT: As shown in later posts, the quirk file listed in this message (from #1973) does NOT work for the _TZE204_ac0fhfiq device. I do leave my original message as reference used when starting the discussion, but it's contents should NOT be used for this device by readers.

_Not 100% sure if they are related to this issue. However those messages do need to be addressed: your system is trying to insert some data, but it fails to do so because the data fails to match some criteria set in the database.

Some points of attention I took from the discussions in #1973:

  • What version of HA are you using?
  • Which quirk file did you take? I think mine is from this posting: cryptocelt commented on Jan 18
  • You need to have current flowing through the current transformer during adding of the device for the Current and Power entities to be added. (i.e. the current transformer must be around 1 (not both) wire of a device that is taking power from your power net. Do also note the direction of the power flow. Inside the current transformer is an arrow, make it point towards the power consuming/generating device to avoid sign problems).
  • You may have to restart HA one or more times before the entities get added.

An excerpt from my log when reloading the zigbee integration:

2023-06-07 07:33:15.879 DEBUG (MainThread) [zhaquirks] Loading custom quirks from PosixPath('/config/custom_zha_quirks')
2023-06-07 07:33:15.881 DEBUG (MainThread) [zhaquirks] Loading custom quirk module 'ts0601_din_power'
2023-06-07 07:33:15.893 WARNING (MainThread) [zhaquirks] Loaded custom quirks. Please contribute them to https://github.com/zigpy/zha-device-handlers
2023-06-07 07:33:15.910 DEBUG (MainThread) [homeassistant.components.zha] ZHA storage file does not exist or was already removed
2023-06-07 07:33:15.991 DEBUG (MainThread) [zigpy.appdb] Current database version is v11 (table version v11)

What number is shown for your database version?

The foreign key violation error messages must NOT be related to this issue, as I see them too in my log file, and for me the entities were added. Although also for me the Power factor is listed as Unknown. image DO note though that I have the device _TZE204_cjbofhxw from the #1973 issue, NOT the _TZE204ac0fhfiq from this issue. But it is my belief that both devices can be operated with the same quirk file, provided the proper identifiers are changed/added into it.

reef-actor commented 1 year ago
  • What version of HA are you using?

2023.5.4

My quirk is identical to that, except for the model identifier and the dp_type references which I understand was removed in March and prevents the quirk loading

  • You need to have current flowing through the current transformer during adding of the device for the Current and Power entities to be added. (i.e. the current transformer must be around 1 (not both) wire of a device that is taking power from your power net. Do also note the direction of the power flow. Inside the current transformer is an arrow, make it point towards the power consuming/generating device to avoid sign problems).

The clamp is attached to the main live line entering my house, I have also tried the neutral line.

  • You may have to restart HA one or more times before the entities get added.

I have restarted several times since adding the device, no new entities have appeared

What number is shown for your database version?

[zigpy.appdb] Current database version is v11 (table version v11)

for me the Power factor is listed as Unknown

I also have some zb tuya monitoring plugs that are working but also show the power factor as Unknown, so I guess that is a "feature"

In the logs did see a number No datapoint handler messages, which I guess means the datapoint ids are in fact different between this device and the _TZE204_cjbofhxw devices.

[zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x22DC), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=0, profile_id=260, cluster_id=61184, data=Serialized[b'\t\xea\x02\x00\x8fe\x02\x00\x04\x00\x00\x023'], tx_options=<TransmitOptions.NONE: 0>, radius=29, non_member_radius=0, lqi=24, rssi=None)
[zigpy.zcl] [0x22DC:1:0xef00] Received ZCL frame: b'\t\xea\x02\x00\x8fe\x02\x00\x04\x00\x00\x023'
[zigpy.zcl] [0x22DC:1:0xef00] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=0, direction=<Direction.Client_to_Server: 1>, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False), tsn=234, command_id=2, *direction=<Direction.Client_to_Server: 1>)
[zigpy.zcl] [0x22DC:1:0xef00] Decoded ZCL frame: TuyaManufClusterDinPower:set_data_response(data=TuyaCommand(status=0, tsn=143, datapoints=[TuyaDatapointData(dp=101, data=TuyaData(dp_type=<TuyaDPType.VALUE: 2>, function=0, raw=b'\x00\x00\x023', *payload=563))]))
[zigpy.zcl] [0x22DC:1:0xef00] Received command 0x02 (TSN 234): set_data_response(data=TuyaCommand(status=0, tsn=143, datapoints=[TuyaDatapointData(dp=101, data=TuyaData(dp_type=<TuyaDPType.VALUE: 2>, function=0, raw=b'\x00\x00\x023', *payload=563))]))
[zigpy.zcl] [0x22DC:1:0xef00] No datapoint handler for TuyaDatapointData(dp=101, data=TuyaData(dp_type=<TuyaDPType.VALUE: 2>, function=0, raw=b'\x00\x00\x023', *payload=563))

I made the following changes to the datapoints (converters removed for now), the No datapoint handler messages have gone away.

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

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "dp_1",
        ),
        2: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "dp_2",
        ),
        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "rms_current",
        ),
        101: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "active_power",
        ),
        102: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "dp_102",
        ),
        103: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "rms_voltage",
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        6: "_dp_2_attr_update",
        101: "_dp_2_attr_update",
        102: "_dp_2_attr_update",
        103: "_dp_2_attr_update",
    }

Some progress... image

The active power seems to be correct. Turning on an electric shower increased the value to ~10KW.

I have no experience with quirks or python so I have no idea what I am doing lol. @stombi, how did you identify the datapoints? Are they all showing the correct values for you?

How are these datapoints supposed to work?

stombi commented 1 year ago

@stombi, how did you identify the datapoints? Are they all showing the correct values for you?

I know nothing, I'm discovering about all this because my device didnt work :-) I saw that the datapoints 101 and 102 were missing in the logs of Zigbee2MQTT The only datapoints that work for me in Zigbee2MQTT and showed as entities in HA are 101 and 102 I'm only sure that 101 is the active power in W and I'm pretty sure 102 is the direction of the current (1 or 0) I had no success with ZHA quirks, when the quirk load, it kills my Sonoff Zigbee 3.0 USB Dongle Plus

@reef-actor can you share your quirk file to see if it works on my system?

reef-actor commented 1 year ago
"""Tuya Din Power Meter."""
from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl import foundation
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 zigpy.zcl.clusters.general import GreenPowerProxy

from zhaquirks import LocalDataCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import TuyaLocalCluster
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    EnchantedDevice,
    TuyaMCUCluster,
    TuyaOnOff,
)

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

    AC_CURRENT_MULTIPLIER = 0x0602
    AC_CURRENT_DIVISOR = 0x0603

    _CONSTANT_ATTRIBUTES = {AC_CURRENT_MULTIPLIER: 1, AC_CURRENT_DIVISOR: 1000}

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

    POWER_WATT = 0x0000

    _CONSTANT_ATTRIBUTES = {
        0x0300: POWER_WATT,  # unit_of_measure
        0x0302: 1000,  # divisor
    }

class DinPowerManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with din power datapoints."""

    class TuyaConnectionStatus(t.Struct):
        """Tuya request data."""

        tsn: t.uint8_t
        status: t.LVBytes

    client_commands = TuyaMCUCluster.client_commands.copy()
    client_commands.update(
        {
            0x25: foundation.ZCLCommandDef(
                "mcu_connection_status",
                {"payload": TuyaConnectionStatus},
                True,
                is_manufacturer_specific=True,
            ),
        }
    )

    server_commands = TuyaMCUCluster.server_commands.copy()
    server_commands.update(
        {
            0x25: foundation.ZCLCommandDef(
                "mcu_connection_status_rsp",
                {"payload": TuyaConnectionStatus},
                False,
                is_manufacturer_specific=True,
            ),
        }
    )

    def handle_mcu_connection_status(
        self, payload: TuyaConnectionStatus
    ) -> foundation.Status:
        """Handle gateway connection status requests (0x25)."""

        payload_rsp = DinPowerManufCluster.TuyaConnectionStatus()
        payload_rsp.tsn = payload.tsn
        payload_rsp.status = b"\x01"  # 0x00 not connected to internet | 0x01 connected to internet | 0x02 time out

        self.create_catching_task(
            super().command(0x25, payload_rsp, expect_reply=False)
        )

        return foundation.Status.SUCCESS

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        0x01: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_delivered",
        ),
        0x06: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current", "rms_voltage"),
            converter=lambda x: (x >> 16, (x & 0x0000FFFF) / 10),
        ),
        0x10: DPToAttributeMapping(
            TuyaOnOff.ep_attribute,
            "on_off",
        ),
        0x66: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_received",
        ),
        0x67: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "active_power",
        ),
        0x69: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "ac_frequency",
        ),
        0x6D: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "total_reactive_power",
        ),
        0x6E: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "reactive_power",
        ),
        0x6F: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "power_factor",
        ),
    }

    data_point_handlers = {
        0x01: "_dp_2_attr_update",
        0x06: "_dp_2_attr_update",
        0x10: "_dp_2_attr_update",
        0x66: "_dp_2_attr_update",
        0x67: "_dp_2_attr_update",
        0x69: "_dp_2_attr_update",
        0x6D: "_dp_2_attr_update",
        0x6E: "_dp_2_attr_update",
        0x6F: "_dp_2_attr_update",
    }

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

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "dp_1",
        ),
        2: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "dp_2",
        ),
        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "rms_current",
        ),
        101: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "active_power",
        ),
        102: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "dp_102",
        ),
        103: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "rms_voltage",
        ),
    }

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        6: "_dp_2_attr_update",
        101: "_dp_2_attr_update",
        102: "_dp_2_attr_update",
        103: "_dp_2_attr_update",
    }

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

    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: [
            ("_TZE204_ac0fhfiq", "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,
                    TuyaManufClusterDinPower.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.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,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

class HikingPowerMeter(CustomDevice):
    """Hiking Power Meter Device - DDS238-2."""

    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"),
            ("ffffffffffffffff", "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,
                    DinPowerManufCluster.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,
                    DinPowerManufCluster,
                    TuyaElectricalMeasurement,
                    TuyaPowerMeasurement,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            16: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                INPUT_CLUSTERS: [
                    TuyaOnOff,
                ],
                OUTPUT_CLUSTERS: [],
            },
        }
    }
stombi commented 1 year ago

Thank you!

Same as before for me it throws an error, unfortunatly I know almost nothing about python

Logger: homeassistant.config_entries
Source: custom_zha_quirks/TZE204_ac0fhfiq.py:100
First occurred: 22:24:07 (1 occurrences)
Last logged: 22:24:07

Error setting up entry Sonoff Zigbee 3.0 USB Dongle Plus - Sonoff Zigbee 3.0 USB Dongle Plus for zha
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 382, in async_setup
    result = await component.async_setup_entry(hass, self)
  File "/usr/src/homeassistant/homeassistant/components/zha/__init__.py", line 100, in async_setup_entry
    setup_quirks(config)
  File "/usr/local/lib/python3.10/site-packages/zhaquirks/__init__.py", line 409, in setup
    importer.find_module(modname).load_module(modname)
  File "<frozen importlib._bootstrap_external>", line 548, in _check_name_wrapper
  File "<frozen importlib._bootstrap_external>", line 1063, in load_module
  File "<frozen importlib._bootstrap_external>", line 888, in load_module
  File "<frozen importlib._bootstrap>", line 290, in _load_module_shim
  File "<frozen importlib._bootstrap>", line 719, in _load
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/config/custom_zha_quirks/TZE204_ac0fhfiq.py", line 51, in <module>
    class DinPowerManufCluster(TuyaMCUCluster):
  File "/config/custom_zha_quirks/TZE204_ac0fhfiq.py", line 100, in DinPowerManufCluster
    0x01: DPToAttributeMapping(
TypeError: DPToAttributeMapping.__init__() missing 1 required positional argument: 'dp_type'
reef-actor commented 1 year ago

Is is possible you are using an older version? dp_type was removed recently (and caused a lot of people using custom quirks to see errors like yours) . In my version, which I am using with the 2023.5.4 linuxserver.io docker container has the newer version which crashes if dp_type is present, my guess is you have an older version that crashes if it is not present 🤪

If you search in the quirk file in this comment and search for dp_type you will see where you need to add them. Bear in mind you will need to remove them again if you update...

Eucliwood090 commented 1 year ago

This code works for me. This code works for me, I can't change the name for current and voltage, which gives consumption and production

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;
const tuya = require('zigbee-herdsman-converters/lib/tuya');

const definition = {
    // Since a lot of TuYa devices use the same modelID, but use different datapoints
    // it's necessary to provide a fingerprint instead of a zigbeeModel
    fingerprint: [
        {
            // The model ID from: Device with modelID 'TS0601' is not supported
            // You may need to add \u0000 at the end of the name in some cases
            modelID: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE204_ac0fhfiq',
        },
    ],
    model: 'TS0601_TZE204_ac0fhfiq',
    vendor: 'TuYa',
    description: 'Bidirectional clamp meter',
    fromZigbee: [tuya.fz.datapoints],
    toZigbee: [tuya.tz.datapoints],
    onEvent: tuya.onEventSetTime, // Add this if you are getting no converter for 'commandMcuSyncTime'
    configure: tuya.configureMagicPacket,
    exposes: [
        // Here you should put all functionality that your device exposes
        e.current(),
        e.power(),
        e.voltage(),
        e.energy()
    ],
    meta: {
        // All datapoints go in here
        tuyaDatapoints: [
            [2, 'current', tuya.valueConverter.raw], // Production 
            [101, 'power', tuya.valueConverter.raw], // 101 - Instantaneous measured power
            [1, 'voltage', tuya.valueConverter.raw], // Consumption
            [102, 'energy', tuya.valueConverter.raw], // 102 - current direction in clamp 0 or 1
        ],
    },
};

module.exports = definition;``
reef-actor commented 1 year ago

I have found that changing datapoint 1 & 2 to the following I can also get the summation delivered (i.e. total energy consumption) (Line 156 of previously posted quirk)

        1: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_delivered",
            converter=lambda x: x * 10,
        ),
        2: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_received",
            converter=lambda x: x * 10,
        ),

[Updated with x10 scaling factors for correct values]

reef-actor commented 1 year ago

So far the Active Power and Summation Delivered/Received values seem to be good, but my logs were filled with the errors like these (filtered for clarity):

Exception in async_set_state when dispatching 'a4:c1:38:97:fc:b8:e7:4b:1:0x0b04_attribute_updated': (1291, 'active_power', 414)
ValueError: could not convert string to float: b'\t`\x00\t\x06\x00\x01\x9e'
Exception in async_set_state when dispatching 'a4:c1:38:97:fc:b8:e7:4b:1:0x0b04_attribute_updated': (1288, 'rms_current', b'\t_\x00\t\xd8\x00\x01\xd6')
ValueError: could not convert string to float: b'\t_\x00\t\xd8\x00\x01\xd6'
Exception in async_set_state when dispatching 'a4:c1:38:97:fc:b8:e7:4b:1:0x0b04_attribute_updated': (1291, 'active_power', 470)
ValueError: could not convert string to float: b'\t_\x00\t\xd8\x00\x01\xd6'
Exception in async_set_state when dispatching 'a4:c1:38:97:fc:b8:e7:4b:1:0x0b04_attribute_updated': (1288, 'rms_current', b'\te\x00\x08\xb6\x00\x01\x88')

I noticed in another tuya energy quirk they are using a definition like this

DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current", "rms_voltage"),
            converter=lambda x: (x >> 16, (x & 0x0000FFFF) / 10),
        ),

With a bit of playing around I have datapoint 6 to

        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current", "rms_voltage"),
            converter=lambda x: (int.from_bytes(x.serialize(), "little") >> 48, (int.from_bytes(x.serialize(), "little") & 0x0000FFFF) / 10),
        ),

Which results in image

The rms_current value is not quite right though (!). By my calculation in this instance it should be about 10.5A, I think the bitshift value in the converter and/or the values for AC_CURRENT_MULTIPLIER and AC_CURRENT_DIVISOR need adjusting, but I don't know how to work out what these values should be.

And I can't understand why active_power is creating errors as it seems to be reporting the correct values in HA. [Update: Oddly the active_power errors seem to have also disappeared after adding the converter for current and voltage 🤪]

fred-c1 commented 1 year ago

Just in case you don't have this: image

reef-actor commented 1 year ago

Thanks @fred-c1 Where did you find that data? I'm not 100% that is exactly accurate for the data I am receiving. While I was able to decode the examples correctly in a test script, the same code in the converter doesn't seem to get the right results. I have changed the datapoint to this

        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_voltage","rms_current"),
            converter=lambda x: (
                (int.from_bytes(x.serialize()[0:2], "big")) / 10,
                (int.from_bytes(x.serialize()[2:5], "big")) / 1000,
            ),
        ),

The result is this image If I use little endian instead I get this image

Neither seem quite right to me? Perhaps I misunderstand ac power calculations?

fred-c1 commented 1 year ago

@reef-actor : I also bought a unit here: https://www.aliexpress.com/item/1005005466228073.html Received about 2 weeks ago.

Debug info from zigbee2mqtt: Zigbee model 'TS0601' manufacturer name '_TZE204_ac0fhfiq'

The above table was provided by the seller last week.

(Active) power = RMS_voltage * RMS_current only works for pure sine waves with no delay

For active power, you should use dpID 101 and 102 Power Factor can be obtain from: power factor = P / Pa Pa = RMS_voltage * RMS_current (https://en.wikipedia.org/wiki/Power_factor)

reef-actor commented 1 year ago

So I think I have a working datapoint definition.

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_delivered",
            converter=lambda x: x * 10,
        ),
        2: DPToAttributeMapping(
            TuyaElectricalMeasurement.ep_attribute,
            "current_summ_received",
            converter=lambda x: x * 10,
        ),
        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current","rms_voltage"),
            converter=lambda x: (
                (int.from_bytes(x.serialize()[-6:-3], "big")),
                (int.from_bytes(x.serialize()[-8:-6], "big")) / 10,
            ),
        ),
        101: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            "active_power",
        ),
        102: DPToAttributeMapping(
            TuyaOnOff.ep_attribute,
            "on_off",
        ),
    }

image

The current value returned seems to not require /1000 as the document states (maybe HA is doing some conversion of its own?). I had no success in getting the active power value from dp 6 - the value was bouncing all over the place, possibly due to the \t values seen in my previous log errors. You can see I am using reverse array index notation to avoid the mess that sometimes occurs at the start of the array. But dp 101 seems to have a sensible and stable value.

So now I have:

I don't have any power generation, which I think is why I don't see Summation received or the power direction indication in HA.

If anyone has experience in tidying this up and getting it prepared for pushing upstream without stomping over the existing ts601_din_power.py, I think it is ready.

Full quirk: ts0601_din_power.py.txt

fred-c1 commented 1 year ago

1 small correction: image

fred-c1 commented 1 year ago

dpID 1 and 2 updated: image

SmartFoundations commented 1 year ago

Hi @reef-actor

I am trying to setup a different device but it is very similar to the device that you have been working with.

I have a 3-phase Tuya Power Meter. It is not bidirectional, not sure if this is important. Here is some information about my device: https://www.zigbee2mqtt.io/devices/TS0601_3_phase_clamp_meter.html

I used your quirk and after changing MODELS_INFO and manufacturer_code ZHA accepted this quirk for my device. But I only see one sensor: Power Factor and it is unknown. How can I get others sensor data with Active power, RMS etc? I should probably get them for all 3 phases so 3x the sensors that you have. I have only connected one phase but they should be there and show 0 anyway.

Do you have any ideas what I should do next to make it work? I am glad that I did not switched my network from ZHA to zigbee2mqtt because it looks like this device works fine there. Or created a Tuya gateway and connected it to HA, which is not cool and not secure solution anyway.

I had no experience in quirks or python before but I learn fast so hope to solve it instead of buying another power meter. I got this far anyway.

reef-actor commented 1 year ago

Enable debug logging as described here.

The device is probably using different endpoint IDs (data items like voltage, current, power are "endpoints"). With the logging enabled you might see some messages like "unhandled/undefined endpoint" (unsure of the exact wording) in the HA log.

Without a device datasheet it is a guessing game for which data point is which value and what scaling needs to be applied. In the case of the device this issue is originally about current and voltage are combined in one endpoint which required picking out specific bytes from the endpoint value which would have been close to impossible without the datasheet.

Try changing the endpoint IDs with the ones you see in your HA logs, delete the pycache dir and restart HA. Hopefully you will start to see some new entities and you can work out which values are which.

Good luck!

(Oh, and for power factor, I think it is added by default. I have never seen it provide any value for any of my power monitors so just disable the entity)

SmartFoundations commented 1 year ago

Enable debug logging as described here.

Thank you for all your suggestions!

I got it working! Used attribute 1 to get total consumed kW and attribute 6 to get rms_current and rms_voltage. So your formula for attribute 6 worked for me which is great, thank you very much.

Was not able to get data for all 3 phases but not really need it right now, can just use the total data since I got only one phase connected anyway. See some other values in logs - lots of zeroes - that are probable other phase data. Got value 50 - that is probably 50Hz.

Obviously got some results only 10 minutes after I ordered a 1-phase power meter because found a working quirk for it somewhere in another issue. Classic

I now understand a bit better how the world works and will be joining a club for python haters tomorrow.

P.S. Can't get active power without voodoo magic but can probably do this and it will work fine. Not sure about RMS and non-RMS though and will I get correct power out of it, need to google that later.

 6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current","rms_voltage","active_power"),
            converter=lambda x: (
                (int.from_bytes(x.serialize()[-6:-3], "big")),
                (int.from_bytes(x.serialize()[-8:-6], "big")) / 10,
                ((int.from_bytes(x.serialize()[-6:-3], "big")) * (int.from_bytes(x.serialize()[-8:-6], "big")) / 10) / 1000),
            ),
        ),

that looks horrible but not sure if this can be done any other way

P.P.S. It looks like to get any values (even if your quirk is correct) you need to add a device, make sure that it uses your quirk and then reboot HA. Others you will get stuck with only Power Factor sensor being unknown and no other sensors

P.P.P.S. Is there a way to calculate summation power together with some static value that can be changed in HA ? Like my real electric counter has 10000kW and I have just setup this power meter and it shows 0. So the current_summ_delivered should be x * 10 + 10000. And because at some point it will be out of sync - this variable should be changed quickly (and not by editing quirk, god forbid). Maybe there is some method for that somewhere already in HA, idk.

reef-actor commented 1 year ago

For calculating active power, I unfortunately think you're right about AC, it's not so straightforward. If I remember the formula involves the power factor. The power factor or the values needed to calculate it (phase angle?) aren't exposed, so if you want an accurate active power value it needs to come from the monitor.

I was also thinking about the possibility of syncing the sum delivered to the actual meter. It's possible that there may be a zb command to overwrite the stored value, but my current idea is a template sensor with an offset value.

SmartFoundations commented 1 year ago

For calculating active power, I unfortunately think you're right about AC, it's not so straightforward. If I remember the formula involves the power factor. The power factor or the values needed to calculate it (phase angle?) aren't exposed, so if you want an accurate active power value it needs to come from the monitor.

I was also thinking about the possibility of syncing the sum delivered to the actual meter. It's possible that there may be a zb command to overwrite the stored value, but my current idea is a template sensor with an offset value.

Actually, since we are getting RMS values it means that we can just do rms_current * rms_voltage and get the correct value. If it was not-RMS - then yea, it would be a problem. And this is just for one phase only (but I only have one phase in the house so it is ok). I have checked it with 5kW heater and it is very accurate. And since it is not something that we increment and the error can stack - I think I will use this for now

reef-actor commented 1 year ago

For syncing with the actual meter, HA has the utility meter helper which has a "calibrate" service to which you give the reading on your meter so they match up.

SmartFoundations commented 1 year ago

oh yea, thx for that hint. I created all types of energy meters that can reset daily hourly and monthly and even a day/night tarries. I did not know about helpers before and was adding counters in yaml manually - which is ok but sometimes some things can be added using helpers GUI and it is quick and awesome. you learn something new every day...

P.S. after setting up all the energy meters and stuff I found out the Power panel/dashboard and it does this automatically based on summation counter and shows it so much better then normal UI's that I created in the main panel

SmartFoundations commented 1 year ago

@reef-actor Any idea how to show 3 pairs of data for this device? Actually 4 - 3 phases and 1 total. Like "RMS Voltage A", "RMS Voltage B", etc I tried making names like rms_current1 rms_currentA rms_current_1 etc. but no luck. Also tried making classes named TuyaManufClusterDinPowerA and putting them into INPUT_CLUSTERS. But no, it does not work. I have no idea what I am doing and where to look for help about that.

I have an idea of how to find correct datapoint IDs for all the data : writing a script that will look at logs and show me some info and I will be able to see what is what based on that. But without the ability to create datapoints for different phases - that would be pointless.

AndreaCCIE commented 10 months ago

Full quirk: ts0601_din_power.py.txt

@reef-actor have you had to update the quirk? Using your one, my Active Power is off and the Summation is always 0

Screenshot 2023-08-25 at 07 28 15

2023-08-25 06:59:42.812 DEBUG (MainThread) [zigpy.zcl] [0x56F9:1:0xef00] No datapoint handler for TuyaDatapointData(dp=102, data=TuyaData(dp_type=<TuyaDPType.ENUM: 4>, function=0, raw=b'\x01', *payload=<enum8.undefined_0x01: 1>))

reef-actor commented 10 months ago

Still working for me. Looking at the log message, dp102 is for the power direction (which didn't show for me because I have no solar etc). Could be worth seeing what happens if you turn the clamp to face the other direction?

AndreaCCIE commented 10 months ago

Still working for me. Looking at the log message, dp102 is for the power direction (which didn't show for me because I have no solar etc). Could be worth seeing what happens if you turn the clamp to face the other direction?

I don't have solar ether. I turned it around and have the same issue. These devices are really weird

lesissou commented 9 months ago

Hi, Thank you for this quirk. It's working for me. I have a power meter bi-directionnal. The direction is given by the the third decimal of the current value (0 = production, 1 = consumption). So, the current have only 1 decimal digit. Is it possible to have more ? Thanks for your answer

reef-actor commented 9 months ago

The direction is given by the the third decimal of the current value (0 = production, 1 = consumption).

I think that is just the behaviour of the official app. Over zigbee the direction comes in as a seperate datapoint (DP102, line 177 in this quirk). However I have no energy production equipment to test against so have not seen this value received - it might need more work.

lesissou commented 9 months ago

no it isn't another endpoint : the DP102 is not provided by the device (in my case). You can see the direction to read directly the value with zigbee tool (rms_current). If you inverse the direction of the clam you could see the third digit change (1 to 0 or 0 to 1).

The summation delivered only increase if this third digit is 1.

bricamac commented 9 months ago

Hi, how a can report the value of current_summ_received in HA that could see into the zigbee tool ? Capture d’écran 2023-09-24 à 19 19 44 I could see only current_summ_delivered into HA.

I confirm the information of "lesissous" we could sse the direction of the current into the field "rms_current" Thanks advance.

Fienberber commented 9 months ago

Hi, I have solar and really wanted to have the bi-directionnal value (negative for production). The current setup gave me accurate measurement but I can't get the direction working. So I made some modification.

In the quick I made the rms current relative in order to create a helper in HA that will apply the sign to the power.

The last decimal of the current seem to be the only way to get the direction...

        6: DPToAttributeMapping(
            TuyaPowerMeasurement.ep_attribute,
            ("rms_current","rms_voltage"),
            converter=lambda x: (
                (int.from_bytes(x.serialize()[-6:-3], "big")) * (((int.from_bytes(x.serialize()[-6:-3], "big")) % 2) * 2 - 1),
                (int.from_bytes(x.serialize()[-8:-6], "big")) / 10,
            ),
        ),

In HA I created a helper with a template (I used the default device name)

don't use this version, a better one is available at the end of the comment :

{{(states('sensor.tze204_ac0fhfiq_ts0601_active_power') | float) * (states('sensor.tze204_ac0fhfiq_ts0601_rms_current') | float) / (states('sensor.tze204_ac0fhfiq_ts0601_rms_current') | float | abs)}}

It uses the rms current divided by its absolute value in order to get the sign. Then It's multiplied to the active power to get the relative power. image

I'm quite new to HA so there is certainly a more elegant solution.

PS: A friend rightfully pointed out an issue with my original template. When the current was zero, the division break (of course...). I realized the possibility to use IFs in templates. So I updated it. Please use this version instead.

{% set ap = (states('sensor.tze204_ac0fhfiq_ts0601_active_power') | float) %} 
{% set rc = (states('sensor.tze204_ac0fhfiq_ts0601_rms_current') | float) %}

{% if rc >= 0 %} {{ap}}
{% else %} {{ap * -1}}
{% endif %}
jovanyeo commented 4 months ago

Full quirk: ts0601_din_power.py.txt

@reef-actor have you had to update the quirk? Using your one, my Active Power is off and the Summation is always 0

Screenshot 2023-08-25 at 07 28 15

2023-08-25 06:59:42.812 DEBUG (MainThread) [zigpy.zcl] [0x56F9:1:0xef00] No datapoint handler for TuyaDatapointData(dp=102, data=TuyaData(dp_type=<TuyaDPType.ENUM: 4>, function=0, raw=b'\x01', *payload=<enum8.undefined_0x01: 1>))

Hi

May you share which quirk to use to have total energy Summation?

decrouxtom commented 3 months ago

Hello! I'm using the quirk which was working fine until the summation delivered reach a sort of saturation... Other values are still fine. Does someone have the same issue? IMG_20240331_232229 I've tried tu reset the device unsuccessfully. Cheers

reef-actor commented 3 months ago

I would be surprised if it was a quirk issue, certainly seems like something odd is going on with the device. Does it report the correct active power use?

I think current development support for these devices has moved to https://github.com/zigpy/zha-device-handlers/pull/2961, so probably best to get the new quirk from there and contribute to that discussion. Hopefully we will have native support for these soon!