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
743 stars 679 forks source link

[Device Support Request] Tuya 0-10V dimmer TS110F #1149

Closed PlusPlus-ua closed 2 years ago

PlusPlus-ua commented 2 years ago

Is your feature request related to a problem? Please describe. Just have received a dimmer https://www.aliexpress.com/item/1005003150502224.html. It works without a quirk, all functionality is available except one setting.

In Tuya smart application is possible to set minimal level of dimming: image

Describe the solution you'd like I'd like to add this setting to zigpy.

Device signature - this can be acquired by removing the device from ZHA and pairing it again from the add devices screen. Be sure to add the entire content of the log panel after pairing the device to a code block below this line.

  "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.AllocateAddress|RxOnWhenIdle|MainsPowered|FullFunctionDevice: 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)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0101",
      "in_clusters": [
        "0x0000",
        "0x0003",
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0008"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZ3000_snlc8jgn",
  "model": "TS110F",
  "class": "zigpy.device.Device"
}

Additional context As you can see, there are no custom clusters. So, I think the setting is an attribute in one of the standard clusters. All I need to prepare custom quirk is the attribute ID.

MattWestb commented 2 years ago

THAT I HAVE MISSED !!

I have one dual with no line dimmer and its works great but also not having any extra clusters. https://github.com/MattWestb/tuya-Switches-Dimmer

The problem no one have "finding" the commands / attributes for doing those settings.

Then you is having one tuya ZBGW its good but do you have somthing for sniffing the zigbee traffic like CC-2531 or one EFR32MG module ? If yes its easy sniffing only need little practice for getting it done done.

MattWestb commented 2 years ago

By the way is you living in the AI warehouse then you is getting all the nice devices ?

Some was giving it 5 starts then using it in ZHA !!

PlusPlus-ua commented 2 years ago

Then you is having one tuya ZBGW its good but do you have somthing for sniffing the zigbee traffic like CC-2531 or one EFR32MG module ? If yes its easy sniffing only need little practice for getting it done done.

Yes, I've CC2531. Will try to sniff zigbee traffic later.

By the way is you living in the AI warehouse then you is getting all the nice devices ? Some was giving it 5 starts then using it in ZHA !!

No, I'm just trying to make my new house a bit smarter. I've started with two smart plugs, but appetite comes with eating :)

MattWestb commented 2 years ago

I have the same experience but have not so large aria for making smarter but i like having the light for my flowers going with the sun and how much light is outside and also the night light is doing all its self as long me Pi is online.

I was adding your dimmer in my list then its from the same "family".

Interesting what you can finding from sniffing the dimmer !!

PlusPlus-ua commented 2 years ago

For now, my most annoying device is this BLE fingerbot https://www.kickstarter.com/projects/adaprox/fingerbot-control-all-devices-remotely-through-voice-or-app

It is not supported by any of Tuya integrations. I've ever sniffed BLE traffic between my phone and the device. It communicates in very not standard way and all traffic is encrypted. It looks like only one way to integrate it is via cloud, despite I don't like such way.

PlusPlus-ua commented 2 years ago

So, I've sniffed it.

When I set minimal level on 1%, next packet was sent to device:

Frame 1847: 53 bytes on wire (424 bits), 51 bytes captured (408 bits) on interface \\.\pipe\zboss_sniffer_COM4, id 0
IEEE 802.15.4 Data, Dst: 0xa149, Src: 0x0000
ZigBee Network Layer Data, Dst: 0xa149, Src: 0x0000
ZigBee Application Support Layer Data, Dst Endpt: 1, Src Endpt: 1
    Frame Control Field: Data (0x00)
    Destination Endpoint: 1
    Cluster: Level Control (0x0008)
    Profile: Home Automation (0x0104)
    Source Endpoint: 1
    Counter: 242
ZigBee Cluster Library Frame, Command: Write Attributes, Seq: 98
    Frame Control Field: Profile-wide (0x10)
    Sequence Number: 98
    Command: Write Attributes (0x02)
    Attribute Field, Uint16: 1023
        Attribute: Unknown (0xfc00)
        Data Type: 16-Bit Unsigned Integer (0x21)
        Uint16: 1023 (0x03ff)

For 10% all the same except the value Uint16: 6911 (0x1aff) 20% Uint16: 13055 (0x32ff) 99% Uint16: 64767 (0xfcff)

It's clear that the first byte of the 16-bit word is minimal dimming level. I suppose that second byte might be maximal dimming level.

How it's better to implement this in quirk? Just expose it as one 16-bit value or create two fields structure?

MattWestb commented 2 years ago

Nice done ! :-))) I was thinking making one slider like we have doing for some tuya TRV for setting analogue values like temperature correction and countdowns.

If making one new cluster in tuya INIT you is getting one slide for dim min and one for dim max limit only need using "tuya lightlevel_dim".

I need the absolute min and may values and also the stepping for both dim min and max. Have you trying if you can setting the max level and if its working in real.

The small problem is splitting the values and making 2 functions of it.

You can looking how hi have doing it for me TRV for software calibrating of local temperature (its having one bug after test in in the night but the slider is working).

If you like i asking our tuya TRV dreamcoder if hi can helping doing the new cluster with slides.

MattWestb commented 2 years ago

Sorry i was forgetting the link to the TRV implementation https://github.com/zigpy/zha-device-handlers/discussions/1084#discussioncomment-1675754

jacekk015 commented 2 years ago

You have "class": "zigpy.device.Device" so you're not using any quirk at now. In simple words: what do you want to achieve??? Min level correction??

MattWestb commented 2 years ago

Its one "normal" zigbee device but have extra attribute on the level cluster that setting the min and max level with one Uint16 for both the min and max level the device shall using.

MattWestb commented 2 years ago

Some found and used extra attribute used of tuya "normal" Zigbee devices. https://github.com/zigpy/zigpy/discussions/823#discussioncomment-1539469

PlusPlus-ua commented 2 years ago

I'll prepare quirk in a couple of days and check second byte function. Personally for me, it will be enough. I have no plans to control this dimmer from UI, only from automatization and from external momentary switch. But controlling the dimmer from UI requires some changes if min and max levels will be applied.

PlusPlus-ua commented 2 years ago

@MattWestb could you please point me to latest ZigBee Cluster Library specification. In some documents I see attributes MIN_LEVEL (0x0002) and MAX_LEVEL (0x0003) in Level Control Cluster, but it's not clear to which version of ZCL they belong.

MattWestb commented 2 years ago

The ZCL ver 7 is the normal but new devices can using ZCL R8 that i was "finding" for one week ago. Both is in the liked posting https://github.com/zigpy/zigpy/discussions/595#discussioncomment-287276.

On the level cluster its need being one new made for tuya dimmers and i think you dont have any large problems getting the values right but for normal user its no so easy so i think making it with 2 slides is the best method but it can being done in the quirk but but is need 2 end points then its using the same cluster and one end point can only using one cluster of the same type.

If some is good programming i ZHA it have being easy only adding the cluster and ZHA is making the magic but for the moment we dont have one that can doing the ZHA part so best way is making sliders on 2 endpoints so user can easily using the function.

jacekk015 commented 2 years ago

For normal work your device uses standard LevelControl cluster 0x0008 https://github.com/zigpy/zigpy/blob/dev/zigpy/zcl/clusters/general.py#L433 But this is Tuya - they used 0xfc00 attribute to control min/max

So even you have some good looking standard attributes:

        0x0011: ("on_level", t.uint8_t),
        0x4000: ("start_up_current_level", StartUpCurrentLevel),
      1. 2.14 St art UpCur r ent Level Attr ibut e 5382 The StartUpCurrentLevel attribute SHALL define the desired startup level for a device when it is supplied 5383 with power and this level SHALL be reflected in the CurrentLevel attribute. The values of the StartUpCur-5384 rentLevel attribute are listed below: 5385 Table 3-58. Values of the StartUpCurrentLevel attribute

Probably none of them won't work since Tuya is using custom attribute 0xfc00 In my opinion you need to build quirk and extend there that class below with manufacturer attribute 0xfc00

class LevelControl(Cluster):
PlusPlus-ua commented 2 years ago

I've created a quirk and checked behavior of 0xfc00 attribute.

1) It works, first byte represents min dimming level, second byte - max dimming level. These values remain after power outage. 2) These min and max values restrict only external momentary switch. It's possible to set any value via ZigBee.

I've checked it with t.uint16_t as type of attribute.

I'm trying to change the type in next way:

class MinMaxLevel(t.Struct):
    """Tuya min and max dimming level attribute."""

    max_level: t.uint8_t
    min_level: t.uint8_t

class TuyaZBLevelControl(CustomCluster, LevelControl):
    """Tuya Zigbee Level Control cluster with extra attributes."""

    attributes = LevelControl.attributes.copy()
    attributes.update({0xFC00: ("min_max_level", MinMaxLevel)})

but getting value of attribute still returns 16-bit value. What is the right way to use Struct?

MattWestb commented 2 years ago

I think we need writing the min and max to the level cluster attribute so ZHA is knowing them. From ZCL V7: Level01 If its working like with TRVs and min and max temperature ZHA is using the stored value for limiting the GUI so not trying sending out of rage values to the device. The question is ZHA using this attribute or is it not implanted ?

Example from my TRV is making the GUI having 5°C as lowest value instead or 7°C that is standard in ZHA. Its statick then the TRV is not sending this value but its the real limit of the device and cant being updated with the simple code.

    _CONSTANT_ATTRIBUTES = {
        0x0015: 500,  # Min heating SP
        0x0016: 3000,  # Max heating SP
PlusPlus-ua commented 2 years ago

For now, ever zigpy doesn't know about MinLevel and MaxLevel attributes

    cluster_id = 0x0008
    name = "Level control"
    ep_attribute = "level"
    attributes = {
        0x0000: ("current_level", t.uint8_t),
        0x0001: ("remaining_time", t.uint16_t),
        0x0010: ("on_off_transition_time", t.uint16_t),
        0x0011: ("on_level", t.uint8_t),

I think it will be the best solution to map Tuya specific attribute to standard ones.

MattWestb commented 2 years ago

Looks like min and max is for general OnOff but is not needed for light profile devices :(( But its moderate for" 3.20Pulse Width Modulation" devices so im not sure its working on normal lights.

Mapping tuya attribute to standard ones is great if its possible. I was thinking that the right way for the power on state for getting better support for the function.

MattWestb commented 2 years ago

I was also not finding the attribute in ZHA. Also the "0x001c Derived Pulse Width Modulation (3.20)" looks not being implanted.

MattWestb commented 2 years ago

Do your device have support for different load types like this ? https://github.com/Koenkk/zigbee2mqtt/issues/9878 Its looks like they is having one attribute for min and one for max so perhaps tuya have making more versions :-(((

PlusPlus-ua commented 2 years ago

@MattWestb no, mine only has 0-10V output.

MattWestb commented 2 years ago

And the Z2M is not having that :-))

javicalle commented 2 years ago

I'm not sure if this is exactly what you need, but I think it would be possible to map the values of the Tuya cluster to the standard attributes with something like the following:


FC00_MIN_MAX_LEVEL_ATTRIBUTE = 0xFC00

class MinMaxLevel(t.Struct):
    """Tuya min and max dimming level attribute."""

    min_level: t.uint8_t
    max_level: t.uint8_t

class FC00MinMaxLevelControl(LevelControl):
    """0xFC00 cluster with min_max_level attribute."""

    manufacturer_attributes = {
        FC00_MIN_MAX_LEVEL_ATTRIBUTE: ("manufacturer_min_max_level", MinMaxLevel),  # 0xFC00
    }

    # 0xFC00 reports min&max dim level
    def _update_attribute(self, attrid, value):
        if attrid == FC00_MIN_MAX_LEVEL_ATTRIBUTE:
            self.debug("Getting MIN_MAX %s", value)
            super()._update_attribute(0x0002, value.min_level)
            super()._update_attribute(0x0003, value.max_level)
        else:
            super()._update_attribute(attrid, value)

To modify the range, it should be done through the cluster attribute manufacturer_min_max_level assigning the hexadecimal value (for example: 0x00FF). Not very friendly.

To make it more user friendly (and follow the zigbee standard) the 0x0002 and 0x0003 attributes of the cluster should be used. This would require overriding the write_attributes method. It would be necessary to read the manufacturer_min_max_level value and modify the part of the limit to set before write the value.

I don't have much time available, but if someone wants to explore that path I can try to help as much as I can.

PlusPlus-ua commented 2 years ago

@javicalle thank you for suggested code, I'll try it as I can. I'm from Ukraine and here is a war. I'm a refugee today. When I return, I can find my house (which I try to make smart) damaged, or completely ruined. I have no illusion.

javicalle commented 2 years ago

That's not the kind of trouble you expect to find around here 💔 I'm really very, very sorry. I can't even imagine what you must be going through. Just to say that I hope that you and your relatives are safe and that you may have a good end to your situation.

github-actions[bot] commented 2 years ago

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.