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] _TZ3210_weaqkhab TS110E (QS-Zigbee-D02-TRIAC-L whitout neutral) #2058

Closed
MaKiUA opened this issue Jan 2, 2023 · 9 comments
Labels
stale Issue is inactivate and might get closed soon Tuya Request/PR regarding a Tuya device

Comments

@MaKiUA
Copy link

MaKiUA commented Jan 2, 2023

Hi!
I have one gang Dimmer without neutral. Manufacturer _TZ3210_weaqkhab Model TS110E

Can someone convert to quirk from zigbee2mqtt format Koenkk/zigbee2mqtt#11853 (comment)

const fz = require("zigbee-herdsman-converters/converters/fromZigbee");
const tz = require("zigbee-herdsman-converters/converters/toZigbee");
const exposes = require("zigbee-herdsman-converters/lib/exposes");
const reporting = require("zigbee-herdsman-converters/lib/reporting");
const extend = require("zigbee-herdsman-converters/lib/extend");
const e = exposes.presets;
const ea = exposes.access;
const utils = require("zigbee-herdsman-converters/lib/utils");
const tuya = require("zigbee-herdsman-converters/lib/tuya");

const DataType = {
  uint8: 0x20,
  uint16: 0x21,
};

const fzLocal = {
  ts110e_brightness: {
    cluster: "genLevelCtrl",
    type: ["attributeReport", "readResponse"],
    convert: (model, msg, publish, options, meta) => {
      if (msg.data.hasOwnProperty("61440")) {
        let minBrightness = meta.state.hasOwnProperty("brightness_min")
          ? meta.state.brightness_min
          : 0;
        let maxBrightness = meta.state.hasOwnProperty("brightness_max")
          ? meta.state.brightness_max
          : 1000;
        let brightness = msg.data["61440"] > maxBrightness ? maxBrightness : msg.data["61440"];
        brightness = brightness < minBrightness ? minBrightness :  brightness;
        let level = utils.mapNumberRange(
          brightness,
          minBrightness,
          maxBrightness,
          0,
          255
        );
        meta.logger.debug(`TS110E: FZ-Brightness level changed to: ${level}`);
        return { brightness: level };
      }
    },
  },
  brightness_min: {
    cluster: "genLevelCtrl",
    type: ["attributeReport", "readResponse"],
    convert: (model, msg, publish, options, meta) => {
      if (msg.data.hasOwnProperty("64515")) {
        return { brightness_min: msg.data["64515"] };
      }
    },
  },
  brightness_max: {
    cluster: "genLevelCtrl",
    type: ["attributeReport", "readResponse"],
    convert: (model, msg, publish, options, meta) => {
      if (msg.data.hasOwnProperty("64516")) {
        return { brightness_max: msg.data["64516"] };
      }
    },
  },
};
const tzLocal = {

  light_onoff_brightness: {
    key: ["brightness"],
    options: [exposes.options.transition()],
    convertSet: async (entity, key, value, meta) => {
      const minBrightness = meta.state.hasOwnProperty("brightness_min")
        ? meta.state.brightness_min
        : 0;

      const maxBrightness = meta.state.hasOwnProperty("brightness_max")
        ? meta.state.brightness_max
        : 1000;

      const level = utils.mapNumberRange(value, 0, 255, minBrightness, maxBrightness);
      meta.logger.debug(`TS110E: TZ-Brightness level changed to: ${level}`);
      const switchState = value > 0 ? "ON" : "OFF";
      // await tz.on_off.convertSet(entity, "state", switchState, meta);
      await entity.command(
        "genOnOff",
        1,
        {},
        utils.getOptions(meta.mapped, entity)
      );
      await utils.sleep(1); // To-Think: why is this needed?
      await entity.command(
        "genLevelCtrl",
        "moveToLevelTuya",
        { level, transtime: 100 },
        utils.getOptions(meta.mapped, entity)
      );
    },
    convertGet: async (entity, key, meta) => {
      if (key === "state") {
        await tz.on_off.convertGet(entity, key, meta);
      }
    },
  },

  ts110e_brightness_min: {
    key: ["brightness_min"],
    convertSet: async (entity, key, value, meta) => {
      let payload = { 64515: { value: value, type: DataType.uint16 } };
      await entity.write(
        "genLevelCtrl",
        payload,
        utils.getOptions(meta.mapped, entity)
      );
    },
    convertGet: async (entity, key, meta) => {
      await entity.read("genLevelCtrl", [64515]);
    },
  },
  ts110e_brightness_max: {
    key: ["brightness_max"],
    convertSet: async (entity, key, value, meta) => {
      let payload = { 64516: { value: value, type: DataType.uint16 } };
      await entity.write(
        "genLevelCtrl",
        payload,
        utils.getOptions(meta.mapped, entity)
      );
    },
    convertGet: async (entity, key, meta) => {
      await entity.read("genLevelCtrl", [64516]);
    },
  },
};
const definition = {
  fingerprint: [
      {
          // The model ID from: Device with modelID 'TS0601' is not supported
          // You may need to add \u0000 at the end of the name in some cases
          modelID: 'TS110E',
          // The manufacturer name from: Device with modelID 'TS0601' is not supported.
          manufacturerName: '_TZ3210_weaqkhab'
      },
  ],
  model: 'QS-Zigbee-D02-TRIAC-L', // Vendor model number, look on the device for a model number
  vendor: 'RTX', // Vendor of the device (only used for documentation and startup logging)
  description: 'RTX One Channel Dimmer', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
  fromZigbee: [
    fz.on_off,
    fzLocal.ts110e_brightness,
    fzLocal.brightness_min,
    fzLocal.brightness_max,

  ],
  toZigbee: [
    tz.on_off,
    tzLocal.light_onoff_brightness,
    tzLocal.ts110e_brightness_min,
    tzLocal.ts110e_brightness_max,
  ],
  exposes: [
    e.light_brightness().setAccess('brightness', ea.STATE_SET),
    exposes.numeric("brightness_min", ea.ALL),
    exposes.numeric("brightness_max", ea.ALL),
  ],
  configure: async (device, coordinatorEndpoint, logger) => {
    const endpoint = device.getEndpoint(1);
    await extend
      .light_onoff_brightness()
      .configure(device, coordinatorEndpoint, logger);
    await reporting.bind(endpoint, coordinatorEndpoint, [
      "genOnOff",
      "genLevelCtrl",
    ]);
    await reporting.onOff(endpoint);
  },
};

module.exports = definition;
@javicalle
Copy link
Collaborator

Do you mind to add the device signature? There are a few TS110E quirks around:

@MaKiUA
Copy link
Author

MaKiUA commented Jan 3, 2023

Without any quirks works only on/off and no dimming.
I've tried several quirks but without success.
I see that in my device there is input cluster "0x0300",

for example response with this quirk #1840 (comment):

2023-01-03 17:56:23.449 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3210_weaqkhab TS110E (a4:c1:38:aa:c1:4a:20:16)
2023-01-03 17:56:23.449 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts110e_3.DimmerSwitchWithNeutral2Gang'>
2023-01-03 17:56:23.450 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint

This is signature of my device:

{
  "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=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=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",
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0008",
        "0x0300",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    },
    "2": {
      "profile_id": 260,
      "device_type": "0x0101",
      "in_clusters": [
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0008",
        "0x0300",
        "0xef00"
      ],
      "out_clusters": []
    },
    "242": {
      "profile_id": 41440,
      "device_type": "0x0061",
      "in_clusters": [],
      "out_clusters": [
        "0x0021"
      ]
    }
  },
  "manufacturer": "_TZ3210_weaqkhab",
  "model": "TS110E",
  "class": "zigpy.device.Device"
}2023-01-03 17:56:23.449 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3210_weaqkhab TS110E (a4:c1:38:aa:c1:4a:20:16)
2023-01-03 17:56:23.449 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts110e_3.DimmerSwitchWithNeutral2Gang'>
2023-01-03 17:56:23.450 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint

zha-_TZ3210_weaqkhab TS110E.json.txt

@MattWestb
Copy link
Contributor

Your device is not one QS-Zigbee-D02-TRIAC-L whitout neutral then it one one channel dimmer (one normal endpoint) and with 2 channel is one QS-Zigbee-D02-TRIAC-2C-L whitout neutral (with 2 normal endpoints).
If posting the wrong diagnostic updating it or updating the issue topic.

@MaKiUA
Copy link
Author

MaKiUA commented Jan 3, 2023

My two devices are QS-Zigbee-D02-TRIAC-L without neutral. At least this is printed on top of the box.
To electrical contacts can be connected only one switch and one bulb. I don't understand too why it showing 2 endpoints.
Maybe it flashed with wrong firmware.

photo_2023-01-03_20-41-58
photo_2023-01-03_20-41-53
photo_2023-01-03_20-42-01

@MattWestb
Copy link
Contributor

ITS TUYA !!!
The 2 gang without neutral:
IMG_20210704_162722
IMG_20210704_162748
IMG_20210705_122918
So they is using the same firmware configuration for 1 and 2 gang dimmers and the hardware is only having one.

Nice photos !!!

@javicalle
Copy link
Collaborator

I have a proposal for your device but probably will going to need adjusting:

DimmerSwitchNoNeutral1Gang
class DimmerSwitchNoNeutral1Gang_var01(TuyaDimmerSwitch):
    """Tuya Dimmer Switch Module Without Neutral 1 Gang."""

    signature = {
        MODELS_INFO: [("_TZ3210_weaqkhab", "TS110E")],
        ENDPOINTS: {
            1: {
                # "profile_id": 260,
                # "device_type": "0x0101",
                # "in_clusters": ["0x0000","0x0004","0x0005","0x0006","0x0008","0x0300","0xef00"],
                # "out_clusters": ["0x000a","0x0019"]
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.DIMMABLE_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Color.cluster_id,
                    TuyaZBExternalSwitchTypeCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            2: {
                # "profile_id": 260,
                # "device_type": "0x0101",
                # "in_clusters": ["0x0004","0x0005","0x0006","0x0008","0x0300","0xef00"],
                # "out_clusters": []
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.DIMMABLE_LIGHT,
                INPUT_CLUSTERS: [
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    LevelControl.cluster_id,
                    Color.cluster_id,
                    TuyaZBExternalSwitchTypeCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [],
            },
            242: {
                # "profile_id": 41440,
                # "device_type": "0x0061",
                # "in_clusters": [],
                # "out_clusters": ["0x0021"]
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.DIMMABLE_LIGHT,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    F000LevelControlCluster,
                    TuyaZBExternalSwitchTypeCluster,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            },
            242: {
                PROFILE_ID: 41440,
                DEVICE_TYPE: 97,
                INPUT_CLUSTERS: [],
                OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
            },
        },
    }

You will need to import the Color cluster (from zigpy.zcl.clusters.general).
I have removed it in the replacement part because I think they don't make sense for this kind of device.

And because is a 1 gang device, I have also removed the endpoint 2.

All the other clusters are the same that already in use in the class, but not sure if they will be functional (F000LevelControlCluster and TuyaZBExternalSwitchTypeCluster)

@MaKiUA
Copy link
Author

MaKiUA commented Jan 3, 2023

There is no Color class in zigpy.zcl.clusters.general

ImportError: cannot import name 'Color' from 'zigpy.zcl.clusters.general' (/usr/local/lib/python3.10/site-packages/zigpy/zcl/clusters/general.py
I've used
from zigpy.zcl.clusters.lighting import Color

But without success:

2023-01-03 23:54:30.625 DEBUG (MainThread) [zigpy.quirks.registry] Checking quirks for _TZ3210_weaqkhab TS110E (a4:c1:38:aa:c1:4a:20:16)
2023-01-03 23:54:30.625 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts110e_4.DimmerSwitchNoNeutral1Gang_var01'>
2023-01-03 23:54:30.625 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint
2023-01-03 23:54:30.626 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts110e_4.DimmerSwitchNoNeutral1Gang_var01'>
2023-01-03 23:54:30.626 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint
2023-01-03 23:54:30.626 DEBUG (MainThread) [zigpy.quirks.registry] Considering <class 'ts110e_4.DimmerSwitchNoNeutral1Gang_var01'>
2023-01-03 23:54:30.626 DEBUG (MainThread) [zigpy.quirks.registry] Fail because input cluster mismatch on at least one endpoint

ts110e_4.py.txt

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

MaKiUA commented Jan 6, 2023

OK.
I have done it by my self.
Several remarks:

                [
                    self._match(device[eid].in_clusters, ep.get(SIG_EP_INPUT, []))
                    for eid, ep in sig.items()
                ]
            ):
                _LOGGER.debug(
                    "Fail because input cluster mismatch on at least one endpoint"
                )
                #MaK Additional debug
                for eid, ep in sig.items():
                _LOGGER.debug(
                    "MaK: %s -- %s -- %s -- %s",  eid, ep, device[eid].in_clusters, ep.get(SIG_EP_INPUT, [])
                )
                continue

and found that TuyaZBExternalSwitchTypeCluster cannot be used because of different cluster_id. Needed cluster_id is 0xef00.
But this cluster is empty

          "0xef00": {
            "endpoint_attribute": null,
            "attributes": {},
            "unsupported_attributes": {}
          }

I`ve created custom class (copypasted from code and changed cluster_id)

TUYA_CLUSTER_EF00_ID = 0xEF00

# Tuya Zigbee Cluster 0xEF00 Implementation
class TuyaZBEF00Cluster(CustomCluster):
    """Tuya manufacturer specific cluster 61184."""

    name = "Tuya Manufacturer Specific"
    cluster_id = TUYA_CLUSTER_EF00_ID
    ep_attribute = "tuya_is_pita_0"
  • another thing - number of replacement endpoints must be same as original endpoints. If not - there is exception.
  • in replacement endpoints I`ve removed Color.
    ts110e_5.py.txt

@github-actions
Copy link

github-actions bot commented Jul 5, 2023

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 Jul 5, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jul 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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

4 participants