Open kneschi2 opened 3 months ago
This worked for me ts0601_din_power.py.txt
ts0601_din_power.zip Corrected version
Correcting @stast1 code, dividing ZEMISMART_VOLTAGE_ATTR by 10 since it was reporting the decimal point as unit.
"""Tuya Din Power Meter."""
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import TuyaManufClusterAttributes
from zhaquirks.tuya.ts0601_din_power import TuyaElectricalMeasurement, TuyaPowerMeasurement
"""Zemismart Power Meter Attributes"""
ZEMISMART_TOTAL_ENERGY_ATTR = 0x0201
ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR = 0x0202
ZEMISMART_POWER_FACTOR_ATTR = 0x020F
ZEMISMART_FREQUENCY_ATTR = 0x0265
ZEMISMART_VOLTAGE_ATTR = 0x0266
ZEMISMART_CURRENT_ATTR = 0x0267
ZEMISMART_POWER_ATTR = 0x0268
class ZemismartManufCluster(TuyaManufClusterAttributes):
"""Manufacturer Specific Cluster of the Zemismart SPM01 Power Meter device."""
attributes = {
ZEMISMART_TOTAL_ENERGY_ATTR: ("energy", t.uint32_t, True),
ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR: ("reverse_energy", t.uint32_t, True),
ZEMISMART_CURRENT_ATTR: ("current", t.int16s, True),
ZEMISMART_POWER_ATTR: ("power", t.uint16_t, True),
ZEMISMART_VOLTAGE_ATTR: ("voltage", t.uint16_t, True),
ZEMISMART_FREQUENCY_ATTR: ("frequency", t.uint16_t, True),
ZEMISMART_POWER_FACTOR_ATTR: ("power_factor", t.uint16_t, True),
}
def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
if attrid == ZEMISMART_TOTAL_ENERGY_ATTR:
self.endpoint.smartenergy_metering.energy_deliver_reported(value * 10)
elif attrid == ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR:
self.endpoint.smartenergy_metering.energy_receive_reported(value * 10)
elif attrid == ZEMISMART_CURRENT_ATTR:
self.endpoint.electrical_measurement.current_reported(value)
elif attrid == ZEMISMART_POWER_ATTR:
self.endpoint.electrical_measurement.power_reported(value)
elif attrid == ZEMISMART_VOLTAGE_ATTR:
self.endpoint.electrical_measurement.voltage_reported(value / 10)
elif attrid == ZEMISMART_FREQUENCY_ATTR:
self.endpoint.electrical_measurement.frequency_reported(value)
elif attrid == ZEMISMART_POWER_FACTOR_ATTR:
self.endpoint.electrical_measurement.power_factor_reported(value)
class TuyaZemismartPowerMeter(CustomDevice):
"""Tuya power meter device."""
signature = {
MODELS_INFO: [
("_TZE200_qhlxve78", "TS0601"),
],
ENDPOINTS: {
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,
TuyaElectricalMeasurement,
TuyaPowerMeasurement,
ZemismartManufCluster,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
}
}
}
HI, the file above worked for me. However as I use in Energy Panel in HA, it was not working on it. So I used configuration from other similar device and now is working both as device and as energy meter. (similar to ("_TZE200_bcusnqt8", "TS0601") ) I used as below:
"""Tuya Din Power Meter."""
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.homeautomation import ElectricalMeasurement
from zhaquirks import LocalDataCluster
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import TuyaManufClusterAttributes
from zhaquirks.tuya.ts0601_din_power import TuyaElectricalMeasurement
"""Zemismart Power Meter Attributes"""
ZEMISMART_TOTAL_ENERGY_ATTR = 0x0201
ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR = 0x0202
ZEMISMART_VCP_ATTR = 0x0006
class ZemismartPowerMeasurement(LocalDataCluster, ElectricalMeasurement):
"""Custom class for power, voltage and current measurement."""
cluster_id = ElectricalMeasurement.cluster_id
POWER_ID = 0x050B
VOLTAGE_ID = 0x0505
CURRENT_ID = 0x0508
AC_VOLTAGE_MULTIPLIER = 0x0600
AC_VOLTAGE_DIVISOR = 0x0601
AC_CURRENT_MULTIPLIER = 0x0602
AC_CURRENT_DIVISOR = 0x0603
_CONSTANT_ATTRIBUTES = {
AC_VOLTAGE_MULTIPLIER: 1,
AC_VOLTAGE_DIVISOR: 10,
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)
class ZemismartElectricalMeasurement(TuyaElectricalMeasurement):
"""Custom class for total energy measurement."""
POWER_UNIT = 0x300
POWER_DIVISOR = 0x302
POWER_WATT = 0x0000 # Actually this does not work. Data is interpreted as kWh.
"""Setting unit of measurement."""
_CONSTANT_ATTRIBUTES = {0x0300: POWER_WATT, POWER_DIVISOR: 1000}
class ZemismartManufCluster(TuyaManufClusterAttributes):
"""Manufacturer Specific Cluster of the Zemismart SPM01 Power Meter device."""
attributes = {
ZEMISMART_TOTAL_ENERGY_ATTR: ("energy", t.uint32_t, True),
ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR: ("reverse_energy", t.uint32_t, True),
ZEMISMART_VCP_ATTR: ("vcp_raw", t.data64, True),
}
def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
if attrid == ZEMISMART_TOTAL_ENERGY_ATTR:
self.endpoint.smartenergy_metering.energy_deliver_reported(value)
elif attrid == ZEMISMART_TOTAL_REVERSE_ENERGY_ATTR:
self.endpoint.smartenergy_metering.energy_receive_reported(value)
elif attrid == ZEMISMART_VCP_ATTR:
self.endpoint.electrical_measurement.voltage_reported(
(value[7] * 256) + value[6]
)
self.endpoint.electrical_measurement.current_reported(
(value[5] * 256 * 256) + (value[4] * 256) + value[3]
)
self.endpoint.electrical_measurement.power_reported(
(value[2] * 256 * 256) + (value[1] * 256) + value[0]
)
class TuyaZemismartPowerMeter(CustomDevice):
"""Tuya power meter device."""
signature = {
# "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0,
# user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>,
# mac_capability_flags=<MACCapabilityFlags.FullFunctionDevice|MainsPowered|RxOnWhenIdle|AllocateAddress: 142>,
# manufacturer_code=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264,
# maximum_outgoing_transfer_size=82, descriptor_capability_field=<DescriptorCapability.NONE: 0>,
# *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)",
# device_version=1
# input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
# output_clusters=[0x000a, 0x0019]
MODELS_INFO: [
("_TZE200_qhlxve78" , "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,
ZemismartManufCluster,
ZemismartElectricalMeasurement,
ZemismartPowerMeasurement,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
}
}
}
Problem description
Paring went smoothly but I don't see any sensors or entities
Solution description
Sensors and entities to display power in W and energy in kWh
Screenshots/Video
Device signature
Device signature
```json { "node_descriptor": "NodeDescriptor(logical_type=Diagnostic information
Diagnostic information
```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2024.3.1", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.12.2", "docker": true, "arch": "x86_64", "timezone": "Europe/Berlin", "os_name": "Linux", "os_version": "6.6.20-haos", "supervisor": "2024.03.0", "host_os": "Home Assistant OS 12.1", "docker_version": "24.0.7", "chassis": "embedded", "run_as_root": true }, "custom_components": { "alarmo": { "version": "v1.9.15", "requirements": [] }, "hoymiles_dtu": { "version": "0.6.1", "requirements": [ "plum-py>=0.8.6" ] }, "adaptive_lighting": { "version": "1.20.0", "requirements": [ "ulid-transform" ] }, "hacs": { "version": "1.34.0", "requirements": [ "aiogithubapi>=22.10.1" ] }, "frigate": { "version": "5.0.1", "requirements": [ "pytz==2022.7" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly", "@TheJulianJES" ], "config_flow": true, "dependencies": [ "file_upload" ], "documentation": "https://www.home-assistant.io/integrations/zha", "import_executor": true, "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp", "universal_silabs_flasher" ], "requirements": [ "bellows==0.38.1", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.112", "zigpy-deconz==0.23.1", "zigpy==0.63.4", "zigpy-xbee==0.20.1", "zigpy-zigate==0.12.0", "zigpy-znp==0.12.1", "universal-silabs-flasher==0.0.18", "pyserial-asyncio-fast==0.11" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "10C4", "pid": "EA60", "description": "*slzb-07*", "known_devices": [ "smlight slzb-07" ] }, { "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": "0403", "pid": "6015", "description": "*conbee*", "known_devices": [ "Conbee III" ] }, { "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" ] } ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_uzg-01._tcp.local.", "name": "uzg-01*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 20669, "manufacturer": "_TZE200_qhlxve78", "model": "TS0601", "name": "_TZE200_qhlxve78 TS0601", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "quirk_id": null, "manufacturer_code": 4098, "power_source": "Mains", "lqi": 61, "rssi": null, "last_seen": "2024-03-16T18:36:54", "available": true, "device_type": "Router", "signature": { "node_descriptor": "NodeDescriptor(logical_type=Logs
Logs
```python [Paste the logs here] ```Custom quirk
Custom quirk
```python [Paste your custom quirk here] ```Additional information
No response