HA GUI does not show temperature calibration and deadzone temperature on at the same time with custom quirk

Hello, HA community.
I need your help.
I bought a BHT-002-GCLZB thermostat [recognized as (“_TZE200_u9bfwha0”, “TS0601”) model].
It works with ZHA pretty well but ZHA doesn’t support two features that I need:

  1. Temperature offset (calibration)
  2. Deadzone temperate setting.

I found a few topics and I’m trying to add those features to my HA gui.

I found the standard handler for my thermostat
and I found this topic and code.

I combined both and I made my own zha_quirk:

"""Map from manufacturer to standard clusters for electric heating thermostats."""

from zigpy.profiles import zha
from zigpy.zcl import foundation
import zigpy.types as t
from zigpy.zcl.clusters.general import AnalogOutput, Basic, Groups, Ota, Scenes, Time

from zhaquirks import LocalDataCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.tuya import (
    TuyaManufClusterAttributes,
    TuyaThermostat,
    TuyaThermostatCluster,
    TuyaUserInterfaceCluster,
)

# info from https://github.com/zigpy/zha-device-handlers/pull/538#issuecomment-723334124
# https://github.com/Koenkk/zigbee-herdsman-converters/blob/24baf6800c109ad891899e580659d800ab4918ad/converters/fromZigbee.js#L239
# and https://github.com/Koenkk/zigbee-herdsman-converters/blob/24baf6800c109ad891899e580659d800ab4918ad/converters/common.js#L113
MOESBHT_TARGET_TEMP_ATTR = 0x0210  # [0,0,0,21] target room temp (degree)
MOESBHT_TEMPERATURE_ATTR = 0x0218  # [0,0,0,200] current room temp (decidegree)
MOESBHT_SCHEDULE_MODE_ATTR = 0x0403  # [1] false [0] true   /!\ inverted
MOESBHT_MANUAL_MODE_ATTR = 0x0402  # [1] false [0] true /!\ inverted
MOESBHT_ENABLED_ATTR = 0x0101  # [0] off [1] on
MOESBHT_RUNNING_MODE_ATTR = 0x0424  # [1] idle [0] heating /!\ inverted
MOESBHT_CHILD_LOCK_ATTR = 0x0128  # [0] unlocked [1] child-locked
MOESBHT_TEMPERATURE_CALIBRATION_ATTR = 0x021B  # temperature calibration (decidegree)
MOESBHT_DEADZONE_TEMPERATURE_ATTR = 0x0214 # deadzone value

class MoesBHTManufCluster(TuyaManufClusterAttributes):
    """Manufacturer Specific Cluster of some electric heating thermostats."""

    attributes = {
        MOESBHT_TARGET_TEMP_ATTR: ("target_temperature", t.uint32_t, True),
        MOESBHT_TEMPERATURE_ATTR: ("temperature", t.uint32_t, True),
        MOESBHT_SCHEDULE_MODE_ATTR: ("schedule_mode", t.uint8_t, True),
        MOESBHT_MANUAL_MODE_ATTR: ("manual_mode", t.uint8_t, True),
        MOESBHT_ENABLED_ATTR: ("enabled", t.uint8_t, True),
        MOESBHT_RUNNING_MODE_ATTR: ("running_mode", t.uint8_t, True),
        MOESBHT_CHILD_LOCK_ATTR: ("child_lock", t.uint8_t, True),
        MOESBHT_TEMPERATURE_CALIBRATION_ATTR: ("temperature_calibration", t.int32s, True),	
		MOESBHT_DEADZONE_TEMPERATURE_ATTR: ("deadzone_temperature", t.int32s, True),        
    }

    def _update_attribute(self, attrid, value):
        super()._update_attribute(attrid, value)
        if attrid == MOESBHT_TARGET_TEMP_ATTR:
            self.endpoint.device.thermostat_bus.listener_event(
                "temperature_change",
                "occupied_heating_setpoint",
                value * 100,  # degree to centidegree
            )
        elif attrid == MOESBHT_TEMPERATURE_ATTR:
            self.endpoint.device.thermostat_bus.listener_event(
                "temperature_change",
                "local_temperature",
                value * 10,  # decidegree to centidegree
            )
        elif attrid == MOESBHT_SCHEDULE_MODE_ATTR:
            if value == 0:  # value is inverted
                self.endpoint.device.thermostat_bus.listener_event(
                    "program_change", "scheduled"
                )
        elif attrid == MOESBHT_MANUAL_MODE_ATTR:
            if value == 0:  # value is inverted
                self.endpoint.device.thermostat_bus.listener_event(
                    "program_change", "manual"
                )
        elif attrid == MOESBHT_ENABLED_ATTR:
            self.endpoint.device.thermostat_bus.listener_event("enabled_change", value)
        elif attrid == MOESBHT_RUNNING_MODE_ATTR:
            # value is inverted
            self.endpoint.device.thermostat_bus.listener_event(
                "state_change", 1 - value
            )
        elif attrid == MOESBHT_CHILD_LOCK_ATTR:
            self.endpoint.device.ui_bus.listener_event("child_lock_change", value)


class MoesBHTThermostat(TuyaThermostatCluster):
    """Thermostat cluster for some electric heating controllers."""

    def map_attribute(self, attribute, value):
        """Map standardized attribute value to dict of manufacturer values."""

        if attribute == "occupied_heating_setpoint":
            # centidegree to degree
            return {MOESBHT_TARGET_TEMP_ATTR: round(value / 100)}
        if attribute == "system_mode":
            if value == self.SystemMode.Off:
                return {MOESBHT_ENABLED_ATTR: 0}
            if value == self.SystemMode.Heat:
                return {MOESBHT_ENABLED_ATTR: 1}
            self.error("Unsupported value for SystemMode")
        elif attribute == "programing_oper_mode":
            # values are inverted
            if value == self.ProgrammingOperationMode.Simple:
                return {MOESBHT_MANUAL_MODE_ATTR: 0, MOESBHT_SCHEDULE_MODE_ATTR: 1}
            if value == self.ProgrammingOperationMode.Schedule_programming_mode:
                return {MOESBHT_MANUAL_MODE_ATTR: 1, MOESBHT_SCHEDULE_MODE_ATTR: 0}
            self.error("Unsupported value for ProgrammingOperationMode")

        return super().map_attribute(attribute, value)

    def program_change(self, mode):
        """Programming mode change."""
        if mode == "manual":
            value = self.ProgrammingOperationMode.Simple
        else:
            value = self.ProgrammingOperationMode.Schedule_programming_mode

        self._update_attribute(
            self.attributes_by_name["programing_oper_mode"].id, value
        )

    def enabled_change(self, value):
        """System mode change."""
        if value == 0:
            mode = self.SystemMode.Off
        else:
            mode = self.SystemMode.Heat
        self._update_attribute(self.attributes_by_name["system_mode"].id, mode)


class MoesBHTUserInterface(TuyaUserInterfaceCluster):
    """HVAC User interface cluster for tuya electric heating thermostats."""

    _CHILD_LOCK_ATTR = MOESBHT_CHILD_LOCK_ATTR


class Thermostat_TZE200_u9bfwha0_TemperatureOffset(LocalDataCluster, AnalogOutput):
    """AnalogOutput cluster for setting temperature offset."""

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.thermostat_bus.add_listener(self)
        self._update_attribute(
            self.attributes_by_name["description"].id, "Temperature Offset"
        )
        self._update_attribute(self.attributes_by_name["max_present_value"].id, 6)
        self._update_attribute(self.attributes_by_name["min_present_value"].id, -6)
        self._update_attribute(self.attributes_by_name["resolution"].id, 1)
        self._update_attribute(self.attributes_by_name["application_type"].id, 0x0009)
        self._update_attribute(self.attributes_by_name["engineering_units"].id, 62)

    def set_value(self, value):
        """Set new temperature offset value."""
        self._update_attribute(self.attributes_by_name["present_value"].id, value)

    def get_value(self):
        """Get current temperature offset value."""
        return self._attr_cache.get(self.attributes_by_name["present_value"].id)

    async def write_attributes(self, attributes, manufacturer=None):
        """Modify value before passing it to the set_data tuya command."""
        for attrid, value in attributes.items():
            if isinstance(attrid, str):
                attrid = self.attributes_by_name[attrid].id
            if attrid not in self.attributes:
                self.error("%d is not a valid attribute id", attrid)
                continue
            self._update_attribute(attrid, value)

            await self.endpoint.tuya_manufacturer.write_attributes(
                {MOESBHT_TEMPERATURE_CALIBRATION_ATTR: value}, manufacturer=None
            )
        return ([foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)],)
        
class MoesBHT(TuyaThermostat):
    """Tuya thermostat for devices like the Moes BHT-002GCLZB valve and BHT-003GBLZB Electric floor heating."""

    signature = {
        #  endpoint=1 profile=260 device_type=81 device_version=1 input_clusters=[0, 4, 5, 61184],
        #  output_clusters=[10, 25]
        MODELS_INFO: [
            ("_TZE200_aoclfnxz", "TS0601"),
            ("_TZE200_2ekuz3dz", "TS0601"),
            ("_TZE200_ye5jkfsb", "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.THERMOSTAT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    MoesBHTManufCluster,
                    MoesBHTThermostat,
                    MoesBHTUserInterface,
                    Thermostat_TZE200_u9bfwha0_TemperatureOffset,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

I added a new class Thermostat_TZE200_u9bfwha0_TemperatureOffset and this class is used for replace endpoints.

Now I set the temperature offset from HA gui and use both:
Controls:
Pic no. 1


and in manage zigbee device:
Pic no. 2

A. First problem that I have is that: If I change the value via the control panel then the value is changed everywhere (control panel, and both clusters (MoesBHTManufCluster, Thermostat_TZE200_u9bfwha0_TemperatureOffset) in manage zigbee device.
But If I will change in Thermostat_TZE200_u9bfwha0_TemperatureOffset value for “present_value” then change is not applied.

B. I’m trying also add deadzone temperature settings to control panel for this device.
I extended my quirk and added new class “Thermostat_TZE200_u9bfwha0_TemperatureDeadZone” and used this class in replacement:

The attribute id for deadzone temperature is ok because it works in “manage zigbee device”

#<code snipped>
class Thermostat_TZE200_u9bfwha0_TemperatureDeadZone(LocalDataCluster, AnalogOutput):
    """AnalogOutput cluster for setting temperature offset."""

    def __init__(self, *args, **kwargs):
        """Init."""
        super().__init__(*args, **kwargs)
        self.endpoint.device.thermostat_bus.add_listener(self)
        self._update_attribute(
            self.attributes_by_name["description"].id, "Temperature DeadZone"
        )
        self._update_attribute(self.attributes_by_name["max_present_value"].id, 5)
        self._update_attribute(self.attributes_by_name["min_present_value"].id, 1)
        self._update_attribute(self.attributes_by_name["resolution"].id, 1)
        self._update_attribute(self.attributes_by_name["application_type"].id, 0x0009)
        self._update_attribute(self.attributes_by_name["engineering_units"].id, 62)

    def set_value(self, value):
        """Set new temperature offset value."""
        self._update_attribute(self.attributes_by_name["present_value"].id, value)

    def get_value(self):
        """Get current temperature offset value."""
        return self._attr_cache.get(self.attributes_by_name["present_value"].id)

    async def write_attributes(self, attributes, manufacturer=None):
        """Modify value before passing it to the set_data tuya command."""
        for attrid, value in attributes.items():
            if isinstance(attrid, str):
                attrid = self.attributes_by_name[attrid].id
            if attrid not in self.attributes:
                self.error("%d is not a valid attribute id", attrid)
                continue
            self._update_attribute(attrid, value)

            await self.endpoint.tuya_manufacturer.write_attributes(
                {MOESBHT_DEADZONE_TEMPERATURE_ATTR: value}, manufacturer=None
            )
        return ([foundation.WriteAttributesStatusRecord(foundation.Status.SUCCESS)],)
        
        
class MoesBHT(TuyaThermostat):
    """Tuya thermostat for devices like the Moes BHT-002GCLZB valve and BHT-003GBLZB Electric floor heating."""

    signature = {
        #  endpoint=1 profile=260 device_type=81 device_version=1 input_clusters=[0, 4, 5, 61184],
        #  output_clusters=[10, 25]
        MODELS_INFO: [
            ("_TZE200_aoclfnxz", "TS0601"),
            ("_TZE200_2ekuz3dz", "TS0601"),
            ("_TZE200_ye5jkfsb", "TS0601"),
            ("_TZE200_u9bfwha0", "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.THERMOSTAT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    MoesBHTManufCluster,
                    MoesBHTThermostat,
                    MoesBHTUserInterface,
                    Thermostat_TZE200_u9bfwha0_TemperatureOffset,
                    Thermostat_TZE200_u9bfwha0_TemperatureDeadZone,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

In “manage zigbee device” panel in “MoesBHTManufCluster” I can see and use both attributes:
Pic no. 4
but in the controls panel, I can see only one control - for deadzone temperature
Pic no. 3

I suppose this is because both classes have the same cluster id (from AnalogOutput).

What I should do if I want to see and use both attributes in the control panel for this device?

Thank you in advance.