Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Device Support Request] NOUS E6 TS0601 _TZE200_nnrfa68v #1702

Open
RhavoX opened this issue Aug 21, 2022 · 40 comments
Open

[Device Support Request] NOUS E6 TS0601 _TZE200_nnrfa68v #1702

RhavoX opened this issue Aug 21, 2022 · 40 comments
Labels
custom quirk available A custom quirk is available to solve the issue, but it's not merged in the repo yet stale Issue is inactivate and might get closed soon Tuya Request/PR regarding a Tuya device

Comments

@RhavoX
Copy link

RhavoX commented Aug 21, 2022

Is your feature request related to a problem? Please describe.
The NOUS E6 Thermometer and Humidity sensor does not provide any values, as well as the time on it is not synced.
I have modified the current sensor quirk a bit and made temperature, humidity and battery level work. Whatever I do though the time synchronization has no effect. In this issue I will provide the code I have now, maybe someone will have some ideas :)

I have also checked the tuya zigbee communication spec but I didn't find anything useful there.

Describe the solution you'd like
I would like for the device to be officially supported :). And maybe additionally to suport the time sync feature.

Device signature
{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0302",
      "in_clusters": [
        "0x0001",
        "0x0402",
        "0x0405",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZE200_nnrfa68v",
  "model": "TS0601",
  "class": "ts0601_sensor_cst.TuyaNousE6TempHumiditySensor"
}
Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant OS",
    "version": "2022.8.5",
    "dev": false,
    "hassio": true,
    "virtualenv": false,
    "python_version": "3.10.5",
    "docker": true,
    "arch": "x86_64",
    "timezone": "Europe/Warsaw",
    "os_name": "Linux",
    "os_version": "5.15.55",
    "supervisor": "2022.08.3",
    "host_os": "Home Assistant OS 8.4",
    "docker_version": "20.10.14",
    "chassis": "vm",
    "run_as_root": true
  },
  "custom_components": {
    "hacs": {
      "version": "1.26.2",
      "requirements": [
        "aiogithubapi>=22.2.4"
      ]
    },
    "ping_arp": {
      "version": "0.1.0",
      "requirements": []
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "config_flow": true,
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "requirements": [
      "bellows==0.32.0",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.78",
      "zigpy-deconz==0.18.0",
      "zigpy==0.49.1",
      "zigpy-xbee==0.15.0",
      "zigpy-zigate==0.9.1",
      "zigpy-znp==0.8.1"
    ],
    "usb": [
      {
        "vid": "10C4",
        "pid": "EA60",
        "description": "*2652*",
        "known_devices": [
          "slae.sh cc2652rb stick"
        ]
      },
      {
        "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*"
      }
    ],
    "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": 50565,
    "manufacturer": "_TZE200_nnrfa68v",
    "model": "TS0601",
    "name": "_TZE200_nnrfa68v TS0601",
    "quirk_applied": true,
    "quirk_class": "ts0601_sensor_cst.TuyaNousE6TempHumiditySensor",
    "manufacturer_code": 4417,
    "power_source": "Battery or Unknown",
    "lqi": 112,
    "rssi": null,
    "last_seen": "2022-08-21T22:13:32",
    "available": true,
    "device_type": "EndDevice",
    "signature": {
      "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)",
      "endpoints": {
        "1": {
          "profile_id": 260,
          "device_type": "0x0302",
          "in_clusters": [
            "0x0001",
            "0x0402",
            "0x0405",
            "0xef00"
          ],
          "out_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      }
    },
    "active_coordinator": false,
    "entities": [
      {
        "entity_id": "sensor.nous_e6_kuchnia_battery",
        "name": "_TZE200_nnrfa68v TS0601"
      },
      {
        "entity_id": "sensor.nous_e6_kuchnia_temperature",
        "name": "_TZE200_nnrfa68v TS0601"
      },
      {
        "entity_id": "sensor.nous_e6_kuchnia_humidity",
        "name": "_TZE200_nnrfa68v TS0601"
      }
    ],
    "neighbors": [],
    "endpoint_names": [
      {
        "name": "TEMPERATURE_SENSOR"
      }
    ],
    "user_given_name": "NOUS E6 Kuchnia",
    "device_reg_id": "44dcd0624ecf150b39fd626e1efd208c",
    "area_id": "kuchnia",
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "TEMPERATURE_SENSOR",
          "id": 770
        },
        "profile_id": 260,
        "in_clusters": {
          "0xef00": {
            "endpoint_attribute": "tuya_manufacturer",
            "attributes": {
              "0xef00": {
                "attribute_name": "mcu_version",
                "value": "1.0.0"
              }
            },
            "unsupported_attributes": {}
          },
          "0x0402": {
            "endpoint_attribute": "temperature",
            "attributes": {
              "0x0000": {
                "attribute_name": "measured_value",
                "value": 2370
              }
            },
            "unsupported_attributes": {}
          },
          "0x0405": {
            "endpoint_attribute": "humidity",
            "attributes": {
              "0x0000": {
                "attribute_name": "measured_value",
                "value": 7100
              }
            },
            "unsupported_attributes": {}
          },
          "0x0001": {
            "endpoint_attribute": "power",
            "attributes": {
              "0x0021": {
                "attribute_name": "battery_percentage_remaining",
                "value": 200
              }
            },
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x000a": {
            "endpoint_attribute": "time",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}
Additional logs
Here are the time_set logs that don't work

2022-08-21 23:56:06.587 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Received ZCL frame: b'\x09\x12\x24\x0D\x00'
2022-08-21 23:56:06.588 DEBUG (MainThread) [zigpy.zcl] [0xC585: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, *is_reply=True), tsn=18, command_id=36, *direction=<Direction.Client_to_Server: 1>, *is_reply=True)
2022-08-21 23:56:06.588 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Decoded ZCL frame: TemperatureHumidityManufCluster:set_time_request(data=[13, 0])
2022-08-21 23:56:06.588 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Received command 0x24 (TSN 18): set_time_request(data=[13, 0])
2022-08-21 23:56:06.589 WARNING (MainThread) [zigpy.zcl] [0xC585:1:0xef00] HANDLE SET TIME! NOT WORKING YET
2022-08-21 23:56:06.591 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=True, direction=<Direction.Server_to_Client: 0>, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False, *is_reply=False), manufacturer=4417, tsn=100, command_id=36, *direction=<Direction.Server_to_Client: 0>, *is_reply=False)
2022-08-21 23:56:06.592 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Sending request: set_time(time=[13, 0, 99, 2, 169, 246, 99, 2, 198, 22])
2022-08-21 23:56:06.592 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Sending reply header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=False, direction=<Direction.Client_to_Server: 1>, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True, *is_reply=True), tsn=18, command_id=<GeneralCommand.Default_Response: 11>, *direction=<Direction.Client_to_Server: 1>, *is_reply=True)
2022-08-21 23:56:06.593 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Sending reply: Default_Response(command_id=36, status=<Status.SUCCESS: 0>)
2022-08-21 23:56:06.593 DEBUG (MainThread) [zigpy.device] [0xc585] Extending timeout for 0x64 request
2022-08-21 23:56:06.595 DEBUG (MainThread) [zigpy_znp.api] Sending request: AF.DataRequestExt.Req(DstAddrModeAddress=AddrModeAddress(mode=<AddrMode.NWK: 2>, address=0xC585), DstEndpoint=1, DstPanId=0x0000, SrcEndpoint=1, ClusterId=61184, TSN=100, Options=<TransmitOptions.SUPPRESS_ROUTE_DISC_NETWORK|ACK_REQUEST: 48>, Radius=30, Data=b'\x05\x41\x11\x64\x24\x00\x0A\x0D\x00\x63\x02\xA9\xF6\x63\x02\xC6\x16')

Additional context
Here is the page with the device info: https://noussmart.pl/product/e6.html
Here is the code I for the test quirk:

"""Tuya temp and humidity sensor with a screen."""

from typing import Dict

################## clean this up
import zigpy.types as t
from zigpy.zcl import foundation
from zhaquirks.tuya import TuyaTimePayload, TuyaCommand
import datetime
##################

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SKIP_CONFIGURATION,
)
from zhaquirks.tuya import TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaDPType, TuyaMCUCluster


TUYA_SET_TIME = 0x24

# NOTES:
# The data comes in as a string on cluster, if there is nothing set up you may see these lines in the logs:
# Unknown message (b'19830100a40102000400000118') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          28.0 degrees
# Unknown message (b'19840100a5020200040000022c') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          55.6% humid
# Unknown message (b'19850100a60402000400000064') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          100% battery


class TuyaTemperatureMeasurement(TemperatureMeasurement, TuyaLocalCluster):
    """Tuya local TemperatureMeasurement cluster."""


class TuyaRelativeHumidity(RelativeHumidity, TuyaLocalCluster):
    """Tuya local RelativeHumidity cluster."""


class TemperatureHumidityManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 10,  # decidegree to centidegree
        ),
        2: DPToAttributeMapping(
            TuyaRelativeHumidity.ep_attribute,
            "measured_value",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 100,  # 0.01 to 1.0
        ),
        4: DPToAttributeMapping(
            TuyaPowerConfigurationCluster2AAA.ep_attribute,
            "battery_percentage_remaining",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 2,  # reported percentage is doubled
        ),
    }
    
    set_time_offset = 1970

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
    }
    
    def handle_set_data(self, command: TuyaCommand) -> foundation.Status:
        return foundation.Status.SUCCESS 
    
    def handle_set_time_request(self, sequence_number: t.uint16_t) -> foundation.Status:
        self.warning("HANDLE SET TIME! NOT WORKING YET")
        payload = TuyaTimePayload()
        
        #utc_timestamp = int((datetime.datetime.utcnow()).timestamp())
        #local_timestamp = int(datetime.datetime.now().timestamp())
        
        utc_now = datetime.datetime.utcnow()
        now = datetime.datetime.now()
        offset_time = datetime.datetime(self.set_time_offset, 1, 1)
        
        utc_timestamp = int((utc_now - offset_time).total_seconds())
        local_timestamp = int((now - offset_time).total_seconds())
        
        payload.extend(sequence_number[0].to_bytes(1, "big", signed=False))
        payload.extend(sequence_number[1].to_bytes(1, "big", signed=False))
        payload.extend(utc_timestamp.to_bytes(4, "big", signed=False))
        payload.extend(local_timestamp.to_bytes(4, "big", signed=False))
        
        self.create_catching_task(
            self.command(TUYA_SET_TIME, payload, expect_reply=True, tsn = self.endpoint.device.application.get_sequence())
        )
        return foundation.Status.SUCCESS

class TuyaNousE6TempHumiditySensor(CustomDevice):
    """Custom device representing tuya temp and humidity sensor with a screen (NOUS E6)."""

    signature = {
        # <SimpleDescriptor endpoint=1, profile=260, device_type=81
        # device_version=1
        # input_clusters=[4, 5, 61184, 0]
        # output_clusters=[25, 10]>
        MODELS_INFO: [("_TZE200_nnrfa68v", "TS0601")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG, # this is how the device reports itself
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    TemperatureHumidityManufCluster,  # Single bus for temp, humidity, and battery
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }
@javicalle
Copy link
Collaborator

I want to check this issue but I will be away from my computer until the end of August.

@colethegamer123
Copy link

Im also really interested. Thanks for your hard work.

@javicalle
Copy link
Collaborator

javicalle commented Sep 1, 2022

The first thing to say is that my knowledge of Python is quite basic, so I recommend interpreting my comments and instructions with caution.

In principle the functionality is implemented in another class:
https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/__init__.py#L377-L398

Also important, this comment:
https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/__init__.py#L283-L296

And:
https://developer.tuya.com/en/docs/iot/tuya-zigbee-module-uart-communication-protocol?id=K9ear5khsqoty#title-18-Sync%20clock%20time

The proposed code mostly looks correct to me, but there are a couple of differences that might be worth testing.

This code is not in the 'original' implementation:

        payload.extend(sequence_number[0].to_bytes(1, "big", signed=False))
        payload.extend(sequence_number[1].to_bytes(1, "big", signed=False))

I suppose that wants to put the sequence in the response payload, but there is also a sequence in the command:

self.command(TUYA_SET_TIME, payload, expect_reply=True, tsn = self.endpoint.device.application.get_sequence())

Maybe we could do without these parts (any of both).

Other diference with the original is the command call. The original is like this (the super() part):

        self.create_catching_task(
            super().command(TUYA_SET_TIME, payload, expect_reply=False)
        )

I would try with a more similar implementation to try to make it work (without the sequence_number[].to_bytes and the tsn = self.endpoint.device.application.get_sequence()) and then I would try to put the rest.

There are also the offsets (your code don't implement the set_time_local_offset one). Maybe to configure this values is relevant for this device. I'm not familiar with this part and I can't say which values could be needed here.
Looking at the code there are some values that I would try:

    set_time_offset = 2000
    set_time_local_offset = 1970

    set_time_offset = 1970

I suppose that the probe that code isn't working is that the device time is not synchronized. And that there is any info about it in the logs. Isn't it?
I also would keep the expect_reply=True to see which is the device response to command. I suppose that with your actual code there is no response from device.

@RhavoX
Copy link
Author

RhavoX commented Sep 6, 2022

Yeah the sequence_number was an addition by me based on some Tuya spec I've found bo it didn't work. I will check if I get the sensor to respond to my set time command. The reason why I am not using set_time_local_offset is because the base class for this request is not implementing any code to synchronize the time, so it won't make a difference, but I will try with various offsets.
Thanks, I will let you know if I have some progress.

@RhavoX
Copy link
Author

RhavoX commented Sep 6, 2022

@javicalle hhmm whatever I do I seem to get the following result:
2022-09-06 21:13:08.479 DEBUG (MainThread) [zigpy.zcl] [0xC585:1:0xef00] Decoded ZCL frame: TemperatureHumidityManufCluster:Default_Response(command_id=36, status=<Status.UNSUP_MANUF_CLUSTER_COMMAND: 131>)
It seems that either some sort of validation is failing on the MCU side or it just supports some different weird kind of setting time... Any ideas? :)

@javicalle
Copy link
Collaborator

The device is not liking the command we sent it. Could you include the full log? I want to see the device request and the response.

Also you can try with this code:

        self.create_catching_task(
            super().command(
                TUYA_SET_TIME,
                payload,
                manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID,
                expect_reply=False
            )
        )

@RhavoX
Copy link
Author

RhavoX commented Sep 6, 2022

Wow, it was the manufacturer parameter! Now it works flawlessly. How did you know? :D

Here is the current version of the quirk (which additionally is able to parse temperature_sensitivity etc, I am also trying to make setting of that param work):

"""Tuya temp and humidity sensor with screen."""

from typing import Dict

################## clean this up
import zigpy.types as t
from zigpy.zcl import foundation
from zhaquirks.tuya import TuyaTimePayload, TuyaCommand
import datetime
from typing import Tuple, Optional, Union
##################

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time, AnalogOutput
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SKIP_CONFIGURATION,
)
from zhaquirks.tuya import TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaDPType, TuyaMCUCluster

TUYA_SET_TIME = 0x24

# NOTES:
# The data comes in as a string on cluster, if there is nothing set up you may see these lines in the logs:
# Unknown message (b'19830100a40102000400000118') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          28.0 degrees
# Unknown message (b'19840100a5020200040000022c') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          55.6% humid
# Unknown message (b'19850100a60402000400000064') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          100% battery

class TemperatureUnitConvert(t.enum8):
    """Tuya Temp unit convert enum."""

    Celsius = 0x00
    Fahrenheit = 0x01


class TuyaTemperatureMeasurement(TemperatureMeasurement, TuyaLocalCluster):
    """Tuya local TemperatureMeasurement cluster."""

    attributes = TemperatureMeasurement.attributes.copy()
    attributes.update(
        {
            0x8001: ("temp_unit_convert", t.enum8),
            0x8002: ("alarm_max_temperature", t.Single),
            0x8003: ("alarm_min_temperature", t.Single),
            0x8004: ("temperature_sensitivity", t.Single),
        }
    )


class TuyaRelativeHumidity(RelativeHumidity, TuyaLocalCluster):
    """Tuya local RelativeHumidity cluster."""


class TemperatureHumidityManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""
    
    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 10,  # decidegree to centidegree
        ),
        2: DPToAttributeMapping(
            TuyaRelativeHumidity.ep_attribute,
            "measured_value",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 100,  # 0.01 to 1.0
        ),
        4: DPToAttributeMapping(
            TuyaPowerConfigurationCluster2AAA.ep_attribute,
            "battery_percentage_remaining",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x * 2,  # reported percentage is doubled
        ),
        9: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "temp_unit_convert",
            dp_type=TuyaDPType.ENUM,
            converter=lambda x: TemperatureUnitConvert(x)
        ),
        10: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "alarm_max_temperature",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x / 10
        ),
        11: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "alarm_min_temperature",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x / 10
        ),
        19: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "temperature_sensitivity",
            dp_type=TuyaDPType.VALUE,
            converter=lambda x: x / 10
        )
    }
    
    set_time_offset = 1970
    set_time_local_offset = 1970

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        9: "_dp_2_attr_update",
        10: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        19: "_dp_2_attr_update",
    }

    def handle_set_time_request(self, sequence_number: t.uint16_t) -> foundation.Status:
        payload = TuyaTimePayload()

        utc_now = datetime.datetime.utcnow()
        now = datetime.datetime.now()

        offset_time = datetime.datetime(self.set_time_offset, 1, 1)
        offset_time_local = datetime.datetime(self.set_time_local_offset, 1, 1)
        
        utc_timestamp = int((utc_now - offset_time).total_seconds())
        local_timestamp = int((now - offset_time).total_seconds())
        
        payload.extend(utc_timestamp.to_bytes(4, "big", signed=False))
        payload.extend(local_timestamp.to_bytes(4, "big", signed=False))

        self.create_catching_task(
            self.command(TUYA_SET_TIME, payload, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID, expect_reply=False)
        )

        return foundation.Status.SUCCESS

class TuyaNousE6TempHumiditySensor(CustomDevice):
    """Custom device representing tuya temp and humidity sensor with a screen (NOUS E6)."""

    signature = {
        # <SimpleDescriptor endpoint=1, profile=260, device_type=81
        # device_version=1
        # input_clusters=[4, 5, 61184, 0]
        # output_clusters=[25, 10]>
        MODELS_INFO: [("_TZE200_nnrfa68v", "TS0601")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG, # this is how the device reports itself
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    TemperatureHumidityManufCluster,  # Single bus for temp, humidity, and battery
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

@javicalle
Copy link
Collaborator

Which param are you trying to set?

And where do these attributes come from?:

            0x8001: ("temp_unit_convert", t.enum8),
            0x8002: ("alarm_max_temperature", t.Single),
            0x8003: ("alarm_min_temperature", t.Single),
            0x8004: ("temperature_sensitivity", t.Single),

Any settings you want to modify must be done through the TemperatureHumidityManufCluster cluster. The rest of the clusters are fake clusters (hence TuyaLocalCluster)

PS: you can color your code if:

```python
here your code
```

@javicalle
Copy link
Collaborator

Ummmm, I think I see what you're trying to...

Add the attributes to the cluster TemperatureHumidityManufCluster and adapt the code.

class TemperatureHumidityManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            0x8001: ("temp_unit_convert", t.enum8),
            0x8002: ("alarm_max_temperature", t.Single),
            0x8003: ("alarm_min_temperature", t.Single),
            0x8004: ("temperature_sensitivity", t.Single),
        }
    )

    .../...

Device report logs with the datapoints may be helpful.

@wildlud
Copy link

wildlud commented Sep 17, 2022

Hi, also i'm a senior developper i started the zigbee and homeassistant adventure only yesterday, so i apologize if i'mbeing noob.

I installed the above quirk in haos 8.5 and it works thank you very much. Without you i should have returned my device.

However i was wondering why did i have to go to the device and make the pairing again so it began to work. before that, the humidy and temperature where marked as unavailable. is it specific to haos ? When we edit a quirk, beside reloading haos must i always redo the pairing ?

thank you again.

@MattWestb
Copy link
Contributor

If the device never have reported one attribute ZHA is not knowing it.
The Humidity is not sent as Zigbee attribute its tuya DP commands and need the quirk for converting it to Zigbee attribute.
Then preparing the device the tuya MCU is sending all DPs its using and the quirk is updating the attribute, very likely the same is happening if waiting some time and the system is getting it from the device.

Normal is not needed repairing device but repower can being good for most devices with Exceptions.

With python you shall always deleting the generated cash folder then editing the file before restarting HA.

@mfeledyn
Copy link

mfeledyn commented Nov 29, 2022

Thanks a lot for the quirk, I see now correct sensor values from the NOUS E6 thermostat.
I just copied the provided quirk.

Sadly I only get few updates... The temperature on the device has to get at least 0.6 °C lower or higher for device to notify HA with an updated value. Same for humidity sensor, few updates. Is there a way to configure the device regarding sensitivity ?

@848422
Copy link

848422 commented Dec 4, 2022

Thanks a lot for the quirk, I see now correct sensor values from the NOUS E6 thermostat. I just copied the provided quirk.

Sadly I only get few updates... The temperature on the device has to get at least 0.6 °C lower or higher for device to notify HA with an updated value. Same for humidity sensor, few updates. Is there a way to configure the device regarding sensitivity ?

Hello mfeledyn,
I have the same issue leading to something not really usable to handle my heater. I'm using the above quirk (thanks for that RhavoX!)
Screenshot_20221204_225952_Home Assistant

In TuyaTemperatureMeasurement I found the temperature_sensitivity attribut
which was set to 0.6. I modified it to 0.1 but without success, the temperature stills updated only when it is modified from 0.6°C at least.

Has anybody an idea to improve the frequency of the update?

@mfeledyn
Copy link

mfeledyn commented Dec 5, 2022

Trying to configure temperature sensitivity in device management, I select "TuyaTemperatureMeasurement (Endpoint id: 1, Id: 0x0402, Type: in)" cluster, then "temperature_sensitivity (id: 0x8004)" in attributes list, set value to 0.1 but I miss the manufacturer replacing code on the next input.
May be it is not updated because I leave it blank or set wrong code. I have searched that code with no luck so far. Anyone would have this NOUS E6 manufacturer code ? May be specific to that input ?...
Thx!

@spandi
Copy link

spandi commented Dec 11, 2022

Has anybody an idea to improve the frequency of the update?

I have just tried to update the sensitivity (using the quirk from @RhavoX - great stuff, thanks!) and it works more or less fine for me. It seems not to always update after a 0.1 degree change, but much more often as can be seen in the screenshots temp_reconfigured_0_1
configure_sensitivity

I have now set it to 0.2 and will let let it run for a few days.

@javicalle
Copy link
Collaborator

Which value returns the cluster if you try to read the attribute again? It returns the same you set?

@spandi
Copy link

spandi commented Dec 11, 2022

Which value returns the cluster if you try to read the attribute again? It returns the same you set?

Yes, I tried this for both devices. It always returns the value I have set. E.g. first I tried READ ATTRIBUTE which got the initial value 0.5, then I tried WRITE ATTRIBUTE for the values 0.1 and READ ATTRIBUTE which gave again 0.1. The same I tried for 0.2. So it seems the value is correctly set.

@markuskonojacki
Copy link

I've tried that and at least for me it does not work. It reports 0.2 in the read value but it's still at the default 0.6 if I look at the logs.

grafik
grafik
grafik

Tried this as well: https://community.home-assistant.io/t/report-configuration-of-a-sleepy-device-with-zha-toolkit/384617
Configuring the reporting interval via zha_toolkit. Maybe this helps someone to find a solution.

My service call looks like this:

service: zha_toolkit.conf_report
data:
  ieee: sensor.thermometer_01_temperature
  endpoint: 1
  cluster: 0x0402
  attribute: 0
  min_interval: 5
  max_interval: 300
  reportable_change: 10
  tries: 100
  event_done: zha_done

@jrogov
Copy link

jrogov commented Dec 13, 2022

Thanks @RhavoX for complete current version of the quirk above, works flawlessly.


Thanks @spandi for the sensitivity tweak above.
It looks like it kinda worked (for test change of "0.2 C" it immediately showed in the dashboard), but sometimes temperature data just isn't being reported or is reported with a delay.

FWIW, it looks like humidity metrics reporting is dragging a bit too (75% -> 74% -> 72% is yet to be reported as I'm writing this).

Also I don't see any automatic changes to sensitivity parameter as reported above (at least as seen from HASS). Probably different device revisions somehow?

@spandi, per your comment with tests, am I missing something and there's humidity sensitivity parameter, or do you assume that temperature_sensitivity controls bove humidity and temperature?

@mfeledyn
Copy link

@markuskonojacki I have the same behaviour. I think "writing" the 0.2 value in temperature_sensitivity is just doing it locally in HA memory state for the device. When using tre "out" clusters I can see HA issuing some commands to the device, but temperature is an "in" cluster and does not send any command/attribute update ?...

@LouisAtGH
Copy link

Hello, I am brand new to Home Assistant. I just manage to get things working which do work ^out of the box^. However sadly enough, this device does not :(

The device seems to be developed for tuya, what is as I see it now is a bad thing (I do not like to have my home controlled from china). I have a clean install of Home Assistant without any connection which a cloud service and I would like to keep it that way.

I have a ^clean^ Home Assistant system with an USB-zigbee-stick. The device is recognized on zigbee level, but does not work.

So what to do:

  • where can I find the latest code version? (here in this treat, or as a file in git?)
  • which steps should I take to integrate the code (and how)

Could some one give me a hand / explain?

@mfeledyn
Copy link

Hello @LouisAtGH ,
I think the latest version of the code is here in that thread. I have not heard of a git or something else.
In order to install the quirk manually you need to create the "quirks" sub-directory in your HomeConfig directory. Then in "quirks" dir create a file, say "nous_e6.py", and paste the quirk. Save the file. Restart HA.
You should then see correct values from the NOUS E6 temperature sensor.

@LouisAtGH
Copy link

Thanks, but probably very stupid, but I really have no idea how to do that from the GUI :(

@jrogov
Copy link

jrogov commented Dec 30, 2022

@LouisAtGH
AFAIK it's impossible to do from GUI: you have to access your server files directly.

Here's a guide on how to do this: https://github.com/home-assistant/home-assistant.io/pull/23884/files#diff-f6bd4d42fd2565238968333a466474a25384fb4eecddc0235902e5ff5e798c8dR422-R435

@LouisAtGH
Copy link

LouisAtGH commented Dec 30, 2022

I was afraid of that. In general ..... the Home Assistant GUI could use ^some^ improvements.

So I need SSH. I already did a first trail to se up SSH. But I am afraid that I have to do that from the command line as well. Including generating a key etc.

After searching the internet, I will probably manage that, but oh oh oh oh ..... it could and should be so much simpler ...

@jrogov
Copy link

jrogov commented Dec 30, 2022

Well, it's not an issue of Home Assistant per se (apart from lack of docs may be)

The thing is that the device in question (NOUS E6) is quite new and, on top of that, doesn't follow the standard way for communication. And obviously HA (or zha-device-handlers for that matter) can't handle every single edge case.

Hence, HA clearly (at least in from my perspective) separates between "supported" and "do-it-yourself" kind of integrations, with former being supported in GUI quite well, and the latter requering some direct tinkering with underlying configuration files.

It shouldn't be hard to do by yourself, so give it a shot. The instructions above and complete quirk code from here should be enough to do that.

(Sorry for a bit of off-topic everyone)

@LouisAtGH
Copy link

Thanks after taking a lot of steps, I managed to get this device working. I will describe those steps in the forum as reaction on my help request today. https://community.home-assistant.io/t/how-to-define-an-unsupported-device-starting-with-a-fresh-home-assistant-install/510150.

However one thing is still unclear to me that is every thing written here about "Manage Zigbee Device and clusters. I searched in the gui and on the internet .... can not find how to reach those settings ....

@TheJulianJES TheJulianJES added the Tuya Request/PR regarding a Tuya device label Jan 4, 2023
@Byter09
Copy link

Byter09 commented Mar 2, 2023

So far I used #1702 (comment) but the most recent Home Assistant update (2023.3.0) seems to have broken it. Thanks to the error I got my whole ZigBee integration stopped working so I removed the quirk for now.

This was the error I got:
ImportError: cannot import name 'TuyaDPType' from 'zhaquirks.tuya.mcu' (/usr/lib/python3.10/site-packages/zhaquirks/tuya/mcu/__init__.py)

Does anyone know whats up with that? I'm using the linuxserver/homeassistant:latest image.

If this is the wrong project/issue to report this in, please let me know and I'll go where-ever else is appropriate. It's just that I originally found the quirk here and it worked perfectly for so long :D

@RhavoX
Copy link
Author

RhavoX commented Mar 2, 2023

Hi, yep got hit by that too :)
ZHA did some small changes here and there so the quirk had to be adjusted. I think I have to finally try to push this to the mainline and add other features later if necessary. For now the following updated quirk can be used (2023.03 compatible)

"""Tuya temp and humidity sensor with screen."""

from typing import Dict

################## clean this up
import zigpy.types as t
from zigpy.zcl import foundation
from zhaquirks.tuya import TuyaTimePayload, TuyaCommand, TuyaDPType
import datetime
from typing import Tuple, Optional, Union
##################

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time, AnalogOutput
from zigpy.zcl.clusters.measurement import RelativeHumidity, TemperatureMeasurement

from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SKIP_CONFIGURATION,
)
from zhaquirks.tuya import TuyaLocalCluster, TuyaPowerConfigurationCluster2AAA
from zhaquirks.tuya.mcu import DPToAttributeMapping, TuyaMCUCluster

TUYA_SET_TIME = 0x24

# NOTES:
# The data comes in as a string on cluster, if there is nothing set up you may see these lines in the logs:
# Unknown message (b'19830100a40102000400000118') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          28.0 degrees
# Unknown message (b'19840100a5020200040000022c') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          55.6% humid
# Unknown message (b'19850100a60402000400000064') on cluster 61184: unknown endpoint or cluster id: 'No cluster ID 0xef00 on (a4:c1:38:d0:18:8b:64:aa, 1)'
#                                          100% battery

class TemperatureUnitConvert(t.enum8):
    """Tuya Temp unit convert enum."""

    Celsius = 0x00
    Fahrenheit = 0x01


class TuyaTemperatureMeasurement(TemperatureMeasurement, TuyaLocalCluster):
    """Tuya local TemperatureMeasurement cluster."""

    attributes = TemperatureMeasurement.attributes.copy()
    attributes.update(
        {
            0x8001: ("temp_unit_convert", t.enum8),
            0x8002: ("alarm_max_temperature", t.Single),
            0x8003: ("alarm_min_temperature", t.Single),
            0x8004: ("temperature_sensitivity", t.Single),
        }
    )


class TuyaRelativeHumidity(RelativeHumidity, TuyaLocalCluster):
    """Tuya local RelativeHumidity cluster."""


class TemperatureHumidityManufCluster(TuyaMCUCluster):
    """Tuya Manufacturer Cluster with Temperature and Humidity data points."""
    
    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        1: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "measured_value",
            converter=lambda x: x * 10,  # decidegree to centidegree
        ),
        2: DPToAttributeMapping(
            TuyaRelativeHumidity.ep_attribute,
            "measured_value",
            converter=lambda x: x * 100,  # 0.01 to 1.0
        ),
        4: DPToAttributeMapping(
            TuyaPowerConfigurationCluster2AAA.ep_attribute,
            "battery_percentage_remaining",
            converter=lambda x: x * 2,  # reported percentage is doubled
        ),
        9: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "temp_unit_convert",
            converter=lambda x: TemperatureUnitConvert(x)
        ),
        10: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "alarm_max_temperature",
            converter=lambda x: x / 10
        ),
        11: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "alarm_min_temperature",
            converter=lambda x: x / 10
        ),
        19: DPToAttributeMapping(
            TuyaTemperatureMeasurement.ep_attribute,
            "temperature_sensitivity",
            converter=lambda x: x / 10
        )
    }
    
    set_time_offset = 1970
    set_time_local_offset = 1970

    data_point_handlers = {
        1: "_dp_2_attr_update",
        2: "_dp_2_attr_update",
        4: "_dp_2_attr_update",
        9: "_dp_2_attr_update",
        10: "_dp_2_attr_update",
        11: "_dp_2_attr_update",
        19: "_dp_2_attr_update",
    }

    def handle_set_time_request(self, sequence_number: t.uint16_t) -> foundation.Status:
        payload = TuyaTimePayload()

        utc_now = datetime.datetime.utcnow()
        now = datetime.datetime.now()

        offset_time = datetime.datetime(self.set_time_offset, 1, 1)
        offset_time_local = datetime.datetime(self.set_time_local_offset, 1, 1)
        
        utc_timestamp = int((utc_now - offset_time).total_seconds())
        local_timestamp = int((now - offset_time).total_seconds())
        
        payload.extend(utc_timestamp.to_bytes(4, "big", signed=False))
        payload.extend(local_timestamp.to_bytes(4, "big", signed=False))

        self.create_catching_task(
            self.command(TUYA_SET_TIME, payload, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID, expect_reply=False)
        )

        return foundation.Status.SUCCESS

class TuyaNousE6TempHumiditySensor(CustomDevice):
    """Custom device representing tuya temp and humidity sensor with a screen (NOUS E6)."""

    signature = {
        # <SimpleDescriptor endpoint=1, profile=260, device_type=81
        # device_version=1
        # input_clusters=[4, 5, 61184, 0]
        # output_clusters=[25, 10]>
        MODELS_INFO: [("_TZE200_nnrfa68v", "TS0601")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG, # this is how the device reports itself
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    TemperatureHumidityManufCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

    replacement = {
        SKIP_CONFIGURATION: True,
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.TEMPERATURE_SENSOR,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    TemperatureHumidityManufCluster,  # Single bus for temp, humidity, and battery
                    TuyaTemperatureMeasurement,
                    TuyaRelativeHumidity,
                    TuyaPowerConfigurationCluster2AAA,
                ],
                OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
            }
        },
    }

@Byter09
Copy link

Byter09 commented Mar 2, 2023

Wow, that was fast. It works too! Thank you 😄

@TheJulianJES TheJulianJES added the custom quirk available A custom quirk is available to solve the issue, but it's not merged in the repo yet label Mar 2, 2023
@Th0tti
Copy link

Th0tti commented Apr 28, 2023

I am using the code from 3/2/2023. Thanks very much. Everything is working fine so far. I would like to add the following information: It takes a moment for the time to appear on the display.

The temperature values ​​and humidity values ​​are sent in Home Assistant. Only the status of the battery is still unknown. This value will hopefully be achievable in the near future. I'll wait and have a coffee first. Great work

@Any-berg
Copy link

Any-berg commented Jun 3, 2023

I, too, have my own quirk for this issue - and it addresses also #1286.

I have compressed all essential changes to a single standalone script that can be used for testing: https://github.com/Any-berg/zha-device-handlers/blob/standalone/zhaquirks/nous/ts0601.py

My actual fork relies on some changes and additions to Tuya scripts: https://github.com/Any-berg/zha-device-handlers

Based on my own testing with a different Nous sensor, there are no remaining problems to fix. Time synchronization and device configuration through attributes works well after TuyaMCUCluster is made to work with NoManufacturerCluster, and all datapoints including the alarms are accessible. Even battery status always shows up after some waiting.

@mfeledyn
Copy link

mfeledyn commented Sep 6, 2023

Using zigbee2mqtt I was able to change the parameter temperature_sensitivity to 0.1. Now I get updates for every little temperature changes :-)
I switched the whole ZHA integration to zigbee2mqtt, all devices are now better supported.

Tip: if you want to switch to zigbee2mqtt, have a look at "network_key" in "advanced" section of the configuration, this might save you some time, as the default key is not secured and changing the key requires re-pairing all devices.
You might also want to change "pan_id" and "ext_pan_id" parameters.

@TheMonsterIT
Copy link

Hello,
i just modified the previous quirk to work with new sensor _TZE200_vvmbj46n
hope will help someone ..
_TZE200_vvmbj46n.zip

@dadoef
Copy link

dadoef commented Oct 6, 2023

Hello, i just modified the previous quirk to work with new sensor _TZE200_vvmbj46n hope will help someone .. _TZE200_vvmbj46n.zip

Thanks, certainly helped me to get my T&H sensor added in ZHA. Working like a charm now.

@harrlih
Copy link

harrlih commented Oct 6, 2023

Hello, i just modified the previous quirk to work with new sensor _TZE200_vvmbj46n hope will help someone .. _TZE200_vvmbj46n.zip

Thank you so much!! works great!!
Just noticed one tiny thing... the device has 3xAAA batteries but in homeassistant/zigbee when I click on the battery symbol is shows 2 attributes: battery size = AAA and quantity = 2. I tried to replace TuyaPowerConfigurationCluster2AAA with TuyaPowerConfigurationCluster3AAA - but that didn't work. Do you know where that could be adjusted?

@dadoef
Copy link

dadoef commented Oct 9, 2023

Hello, i just modified the previous quirk to work with new sensor _TZE200_vvmbj46n hope will help someone .. _TZE200_vvmbj46n.zip

Thank you so much!! works great!!
Just noticed one tiny thing... the device has 3xAAA batteries but in homeassistant/zigbee when I click on the battery symbol is shows 2 attributes: battery size = AAA and quantity = 2. I tried to replace TuyaPowerConfigurationCluster2AAA with TuyaPowerConfigurationCluster3AAA - but that didn't work. Do you know where that could be adjusted?

Same here. I also tried the same but it seems there is no library for TuyaPowerConfigurationCluster3AAA.
The battery is not 100% showing up correctly and shows 141% while it is at 100%. I tried to compensate this by iso multiplying by 2, multiplying by 1.5 but still doesn't end up at 100%. Looks like somewhere it is dividing the result by square root of 2 or something.

@MattWestb
Copy link
Contributor

All tuya Power cluster si made for the MCU TRVs and adding the battery types to them.
By Zigbee standard the battery shall being reported 200% so have 0.5°C of preposition.
One manufacture was doing it wrong from the beginning but is doing it right now but you can look on the code for fixing it and you can doping the same by diverting instead of multiplying the value.

class DoublingPowerConfigurationCluster(CustomCluster, PowerConfiguration):
"""PowerConfiguration cluster implementation.
This implementation doubles battery pct remaining for non standard devices
that don't follow the reporting spec.
"""
cluster_id = PowerConfiguration.cluster_id
BATTERY_PERCENTAGE_REMAINING = 0x0021
def _update_attribute(self, attrid, value):
if attrid == self.BATTERY_PERCENTAGE_REMAINING:
value = value * 2
super()._update_attribute(attrid, value)

@morgantic
Copy link

morgantic commented Oct 23, 2023

Hi, I got one issue with the quirk. Everything works great, apart from the time. I'm UTC+1, but the device is being set to UTC-1. Could there be a sign issue? Thanks in advance!

EDIT: we had winter time shift now. Turns out it's just displaying UTC, not the local time...

Copy link

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale Issue is inactivate and might get closed soon label Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
custom quirk available A custom quirk is available to solve the issue, but it's not merged in the repo yet stale Issue is inactivate and might get closed soon Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

No branches or pull requests