zha-device-handlers: [Device Support Request] Please add so far unsupported attributes to Sonoff TRVZB

Problem description

The new Sonoff TRVZB (https://zigbee.blakadder.com/Sonoff_TRVZB.html) is working in general but missing some attributes that can be exposed and changed. For instance local_temperature_calibration can be adjusted sending a Zigbee SET command, but is not showing up in the list of available attributes.

Solution description

Like in zigbee2mqtt, it would be great if the missing attributes like local_temperature_calibration could be added to ZHA as well. See https://github.com/Koenkk/zigbee2mqtt/issues/19269 for details.

Screenshots/Video

Screenshots/Video

[Paste/upload your media here]

Device signature

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=4742, maximum_buffer_size=82, maximum_incoming_transfer_size=255, server_mask=11264, maximum_outgoing_transfer_size=255, 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": "0x0104",
      "device_type": "0x0301",
      "input_clusters": [
        "0x0000",
        "0x0001",
        "0x0003",
        "0x0006",
        "0x0020",
        "0x0201",
        "0xfc11",
        "0xfc57"
      ],
      "output_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "SONOFF",
  "model": "TRVZB",
  "class": "zigpy.device.Device"
}

Diagnostic information

Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2023.11.3",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.11.6",
    "docker": true,
    "arch": "aarch64",
    "timezone": "Europe/Berlin",
    "os_name": "Linux",
    "os_version": "6.1.21-v8+",
    "run_as_root": true
  },
  "custom_components": {},
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "after_dependencies": [
      "onboarding",
      "usb"
    ],
    "codeowners": [
      "@dmulcahey",
      "@adminiuga",
      "@puddly"
    ],
    "config_flow": true,
    "dependencies": [
      "file_upload"
    ],
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "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.36.8",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.106",
      "zigpy-deconz==0.21.1",
      "zigpy==0.59.0",
      "zigpy-xbee==0.19.0",
      "zigpy-zigate==0.11.0",
      "zigpy-znp==0.11.6",
      "universal-silabs-flasher==0.0.14",
      "pyserial-asyncio-fast==0.11"
    ],
    "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"
        ]
      }
    ],
    "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": 519,
    "manufacturer": "SONOFF",
    "model": "TRVZB",
    "name": "SONOFF TRVZB",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "quirk_id": null,
    "manufacturer_code": 4742,
    "power_source": "Battery or Unknown",
    "lqi": null,
    "rssi": null,
    "last_seen": "2023-11-26T09:20:02",
    "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=4742, maximum_buffer_size=82, maximum_incoming_transfer_size=255, server_mask=11264, maximum_outgoing_transfer_size=255, 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": "0x0104",
          "device_type": "0x0301",
          "input_clusters": [
            "0x0000",
            "0x0001",
            "0x0003",
            "0x0006",
            "0x0020",
            "0x0201",
            "0xfc11",
            "0xfc57"
          ],
          "output_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      },
      "manufacturer": "SONOFF",
      "model": "TRVZB"
    },
    "active_coordinator": false,
    "entities": [
      {
        "entity_id": "sensor.trv_battery",
        "name": "SONOFF TRVZB"
      },
      {
        "entity_id": "sensor.trv_hvac_action",
        "name": "SONOFF TRVZB"
      },
      {
        "entity_id": "climate.trv",
        "name": "SONOFF TRVZB"
      },
      {
        "entity_id": "button.trv_identify",
        "name": "SONOFF TRVZB"
      },
      {
        "entity_id": "switch.trv_switch",
        "name": "SONOFF TRVZB"
      }
    ],
    "neighbors": [],
    "routes": [],
    "endpoint_names": [
      {
        "name": "THERMOSTAT"
      }
    ],
    "user_given_name": "Thermostat",
    "device_reg_id": "9cf40a1e2e31ed20cf480c7240fa16eb",
    "area_id": "bad_oben",
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "THERMOSTAT",
          "id": 769
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "SONOFF"
              },
              "0x0005": {
                "attribute_name": "model",
                "value": "TRVZB"
              }
            },
            "unsupported_attributes": {}
          },
          "0x0001": {
            "endpoint_attribute": "power",
            "attributes": {
              "0x0021": {
                "attribute_name": "battery_percentage_remaining",
                "value": 142
              }
            },
            "unsupported_attributes": {
              "0x0020": {
                "attribute_name": "battery_voltage"
              },
              "0x0031": {
                "attribute_name": "battery_size"
              },
              "0x0033": {
                "attribute_name": "battery_quantity"
              }
            }
          },
          "0x0003": {
            "endpoint_attribute": "identify",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0006": {
            "endpoint_attribute": "on_off",
            "attributes": {
              "0x0000": {
                "attribute_name": "on_off",
                "value": 1
              }
            },
            "unsupported_attributes": {
              "0x4003": {
                "attribute_name": "start_up_on_off"
              }
            }
          },
          "0x0020": {
            "endpoint_attribute": "poll_control",
            "attributes": {
              "0x0000": {
                "attribute_name": "checkin_interval",
                "value": 13200
              }
            },
            "unsupported_attributes": {}
          },
          "0x0201": {
            "endpoint_attribute": "thermostat",
            "attributes": {
              "0x0004": {
                "attribute_name": "abs_max_heat_setpoint_limit",
                "value": 3500
              },
              "0x0003": {
                "attribute_name": "abs_min_heat_setpoint_limit",
                "value": 400
              },
              "0x001b": {
                "attribute_name": "ctrl_sequence_of_oper",
                "value": 2
              },
              "0x0000": {
                "attribute_name": "local_temperature",
                "value": 2420
              },
              "0x0010": {
                "attribute_name": "local_temperature_calibration",
                "value": 0
              },
              "0x0016": {
                "attribute_name": "max_heat_setpoint_limit",
                "value": 3500
              },
              "0x0015": {
                "attribute_name": "min_heat_setpoint_limit",
                "value": 400
              },
              "0x0002": {
                "attribute_name": "occupancy",
                "value": 1
              },
              "0x0012": {
                "attribute_name": "occupied_heating_setpoint",
                "value": 2500
              },
              "0x001e": {
                "attribute_name": "running_mode",
                "value": 0
              },
              "0x0029": {
                "attribute_name": "running_state",
                "value": 1
              },
              "0x001c": {
                "attribute_name": "system_mode",
                "value": 4
              }
            },
            "unsupported_attributes": {
              "0x0008": {
                "attribute_name": "pi_heating_demand"
              },
              "0x0006": {
                "attribute_name": "abs_max_cool_setpoint_limit"
              },
              "0x0005": {
                "attribute_name": "abs_min_cool_setpoint_limit"
              },
              "0x0013": {
                "attribute_name": "unoccupied_cooling_setpoint"
              },
              "0x0007": {
                "attribute_name": "pi_cooling_demand"
              },
              "0x0011": {
                "attribute_name": "occupied_cooling_setpoint"
              },
              "0x0014": {
                "attribute_name": "unoccupied_heating_setpoint"
              },
              "0x0017": {
                "attribute_name": "min_cool_setpoint_limit"
              },
              "0x0018": {
                "attribute_name": "max_cool_setpoint_limit"
              }
            }
          },
          "0xfc57": {
            "endpoint_attribute": "manufacturer_specific",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0xfc11": {
            "endpoint_attribute": "manufacturer_specific",
            "attributes": {},
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x000a": {
            "endpoint_attribute": "time",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}

Logs

Logs
[Paste the logs here]

Custom quirk

Custom quirk
[Paste your custom quirk here]

Additional information

No response

About this issue

  • Original URL
  • State: open
  • Created 7 months ago
  • Reactions: 31
  • Comments: 60 (1 by maintainers)

Most upvoted comments

Which Zigbee TRVs can work better as a normal mechanical TRV with HA?

I think, we should not discuss this here, as this issue is about the Sonoff TRVZB. There are many threads about this on home-assistant. IMHO, the perfect does not exist or not all features are implemented, yet.

I support this request

I also cannot recommend those thermostats. They only know two states, open and close. This means it is hard to maintain a constant temperature in a room. I used them for three days in my workspace and it did not feel comfortable. Here is an example for one working day:

image The blue line is the target temp, the thermostat was open on the yellow areas.

I switched back to the Danfoss Ally, as they learn how to heat up the room over time and maintain the temperature quite well.

If your room is well isolated, this might not be relevant for you and the Sonoff may work well. Every room in my house has two exterior walls which are not well isolated. Therefore the room and radiator temperatures decrease quite quickly, when the Sonoff closes the valve.

This is just my experience with the Sonoff TRVZB.

Am I right that the Sonoff is technically/mechanically capable of incrementally opening and closing the valve instead of hard-flipping between 0% and 100% all the time? Is such already working via Zig2MQTT?

Just asking because the overheating that comes with that is really annoying. I hope that can be fixed with a better ZHA integration soon. I also asked Sonoff the other day if there are opportunities to fix such behaviour via firmware in the future. Might be totally up to ZHA, though.

Will give the Danfoss a try until then. Thanks guys!

This is my workspace with the Sonoff TRV. Have only ± 0,5°C with Better Thermostate. image

I support this request. Got a couple of them too.

I support this request. Got a couple of them too.

I support this request. I bought 5 devices!

+1 Support for this request!

Thanks to all Dev’s that know how to do that!

Regards,

I have started with a quirk to implement the local temperature calibration. Now I am stuck because I don’t know how to set it as a float input value. Maybe someone can help over here? Furthermore, I found out that “local_temperature_calibration” is defined in the zigbee cluster library. Why is it than not supported out of the box?

"""Quirk for SONOFF TRVZB."""
# import typing

from zigpy.profiles import zha 
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import (
    Basic,
    Identify,
    OnOff,
    Ota,
    PollControl,
    PowerConfiguration,
    Time,
)
from zigpy.zcl.clusters.hvac import (
    Thermostat,
)
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)

# Thermostat_Cluster_ID: typing.Final = 0x0201

class Thermostat(CustomCluster):
    "Cluster for Thomostatic values"

    cluster_id = Thermostat.cluster_id
    TempCalibration = 0x0010


    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.voltage_bus.add_listener(self)
        self.endpoint.device.consumption_bus.add_listener(self)
        self.endpoint.device.power_bus.add_listener(self)

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



class SonoffTrvzb(CustomDevice):
    """Sonoff TRVZB custom device implementation."""

    signature = {
        MODELS_INFO: [("SONOFF", "TRVZB")],
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.THERMOSTAT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    OnOff.cluster_id,
                    PollControl.cluster_id,
                    Thermostat.cluster_id,
                    0xfc11,
                    0xfc57,
                ],
                OUTPUT_CLUSTERS: [
                    Time.cluster_id,
                    Ota.cluster_id,
                ],
            },
        },
    }

    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.THERMOSTAT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    OnOff.cluster_id,
                    PollControl.cluster_id,
                    Thermostat.cluster_id,
                    0xfc11,
                    0xfc57,
                ],
                OUTPUT_CLUSTERS: [
                    Time.cluster_id,
                    Ota.cluster_id,
                ],
            },
        },
    }

I just tried to change the year from 1900 to 2100 in the YAML file

Which YAML file are you referring to?

Screenshot 2023-12-04 at 12 02 16 I was referring state in yaml format, not the yaml file itself, just mistyped