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] Immax Neo Smart Keypad "_TZE200_n9clpsht" #1852

Closed
PonyOny opened this issue Oct 21, 2022 · 13 comments
Closed

[Device Support Request] Immax Neo Smart Keypad "_TZE200_n9clpsht" #1852

PonyOny opened this issue Oct 21, 2022 · 13 comments
Labels
in progress Issue is being worked on, near fix stale Issue is inactivate and might get closed soon Tuya Request/PR regarding a Tuya device

Comments

@PonyOny
Copy link

PonyOny commented Oct 21, 2022

Immax Neo Keypad Zigbee 3.0 / MaxSmart MS-K1AZ, is discovered fine by ZHA, but unfortunately no entities show up.
It seems a Quirk is needed...

Describe the solution you'd like
Entities for armed/disarmed/armed-home, battery, SOS and others if possible to show up in HA

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=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=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": "0x0051",
      "in_clusters": [
        "0x0000",
        "0x0004",
        "0x0005",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZE200_n9clpsht",
  "model": "TS0601",
  "class": "zigpy.device.Device"
}
Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2022.10.5",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.10.5",
    "docker": true,
    "arch": "armv7l",
    "timezone": "Europe/Madrid",
    "os_name": "Linux",
    "os_version": "5.10.103-v7l+",
    "run_as_root": true
  },
  "custom_components": {
    "hacs": {
      "version": "1.28.2",
      "requirements": [
        "aiogithubapi>=22.2.4"
      ]
    },
    "zha_toolkit": {
      "version": "v0.8.20",
      "requirements": [
        "packaging>=20.8"
      ]
    },
    "imou_life": {
      "version": "1.0.2",
      "requirements": [
        "imouapi==1.0.2"
      ]
    },
    "alarmo": {
      "version": "v1.9.6",
      "requirements": []
    },
    "smartir": {
      "version": "1.17.6",
      "requirements": [
        "aiofiles==0.6.0"
      ]
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "config_flow": true,
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "requirements": [
      "bellows==0.34.2",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.83",
      "zigpy-deconz==0.19.0",
      "zigpy==0.51.3",
      "zigpy-xbee==0.16.2",
      "zigpy-zigate==0.10.2",
      "zigpy-znp==0.9.1"
    ],
    "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"
        ]
      }
    ],
    "codeowners": [
      "@dmulcahey",
      "@adminiuga",
      "@puddly"
    ],
    "zeroconf": [
      {
        "type": "_esphomelib._tcp.local.",
        "name": "tube*"
      },
      {
        "type": "_zigate-zigbee-gateway._tcp.local.",
        "name": "*zigate*"
      },
      {
        "type": "_zigstar_gw._tcp.local.",
        "name": "*zigstar*"
      }
    ],
    "dependencies": [
      "file_upload"
    ],
    "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": 25691,
    "manufacturer": "_TZE200_n9clpsht",
    "model": "TS0601",
    "name": "_TZE200_n9clpsht TS0601",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4098,
    "power_source": "Battery or Unknown",
    "lqi": 68,
    "rssi": -83,
    "last_seen": "2022-10-21T18:56:12",
    "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=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=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": "0x0051",
          "in_clusters": [
            "0x0000",
            "0x0004",
            "0x0005",
            "0xef00"
          ],
          "out_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      }
    },
    "active_coordinator": false,
    "entities": [],
    "neighbors": [],
    "endpoint_names": [
      {
        "name": "SMART_PLUG"
      }
    ],
    "user_given_name": "Alarm Keypad",
    "device_reg_id": "29d983dbf0e41066045e1ef60dcc2199",
    "area_id": null,
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "SMART_PLUG",
          "id": 81
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "_TZE200_n9clpsht"
              },
              "0x0005": {
                "attribute_name": "model",
                "value": "TS0601"
              }
            },
            "unsupported_attributes": {}
          },
          "0x0004": {
            "endpoint_attribute": "groups",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0005": {
            "endpoint_attribute": "scenes",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0xef00": {
            "endpoint_attribute": null,
            "attributes": {},
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x000a": {
            "endpoint_attribute": "time",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}

Additional context
image

@PonyOny
Copy link
Author

PonyOny commented Oct 22, 2022

There is a custom component for Zigbee2MQTT that is working for this device, could someone help out converting this to a ZHA quirk?

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 ota = require('zigbee-herdsman-converters/lib/ota');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const e = exposes.presets;
const ea = exposes.access;

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: 'TS0601',
            // The manufacturer name from: Device with modelID 'TS0601' is not supported.
            manufacturerName: '_TZE200_n9clpsht'
        },
    ],
    model: '07505L', // Vendor model number, look on the device for a model number
    vendor: 'Immax', // Vendor of the device (only used for documentation and startup logging)
    description: 'Immax Neo Smart Keypad', // Description of the device, copy from vendor site. (only used for documentation and startup logging)
    fromZigbee: [
		fz.ignore_basic_report,
		{
		  cluster: "manuSpecificTuya",
		  type: [
			"commandSetDataResponse",
			"commandGetData",
			"commandActiveStatusReport",
			"commandDataResponse"
		  ],
		  convert: (model, msg, publish, options, meta) => {
			const dp = msg.data.dpValues[0].dp;
			const armModeImmax = {
				26: 'disarm',
				27: 'arm_away',
				28: 'arm_home',
				29: 'sos',
			};

			return {action: armModeImmax[dp]};
//			return {data: msg.data};
		  },
		},
	],
    exposes: [e.action(['disarm', 'arm_home', 'arm_away', 'sos'])],
    toZigbee: [],
};

module.exports = definition;

@MrAdam
Copy link

MrAdam commented Oct 27, 2022

I've requested support for this in DeCONZ as well, and have purchased a Tuya Zigbee Gateway to map out all the DP ID's.
You can find the DP ID's here if it helps with the integration: dresden-elektronik/deconz-rest-plugin#6277 (comment)

@PonyOny
Copy link
Author

PonyOny commented Oct 27, 2022

I've requested support for this in DeCONZ as well, and have purchased a Tuya Zigbee Gateway to map out all the DP ID's. You can find the DP ID's here if it helps with the integration: dresden-elektronik/deconz-rest-plugin#6277 (comment)

Wow amazing work, I was going to buy a tuya hub just to be able to get those values, that is amazing, thanks for your hard work.

@MrAdam
Copy link

MrAdam commented Oct 28, 2022

I've requested support for this in DeCONZ as well, and have purchased a Tuya Zigbee Gateway to map out all the DP ID's. You can find the DP ID's here if it helps with the integration: dresden-elektronik/deconz-rest-plugin#6277 (comment)

Wow amazing work, I was going to buy a tuya hub just to be able to get those values, that is amazing, thanks for your hard work.

If I'm missing any values, please tell, and I'll try and get them :)

EDIT: I'll try migrating my setup from DeCONZ to ZHA and see if I can get a quirk working

@PonyOny
Copy link
Author

PonyOny commented Nov 1, 2022

That would be amazing, as now I have had to add Zigbee2MQTT just to use this keypad, it would be amazing if I could use it with ZHA, I have been trying with Quirks, but I don't know how to process the messages received from the device in ZHA, I can see them in the debug log but can't process them as in Z2M.

@MrAdam
Copy link

MrAdam commented Nov 1, 2022

That would be amazing, as now I have had to add Zigbee2MQTT just to use this keypad, it would be amazing if I could use it with ZHA, I have been trying with Quirks, but I don't know how to process the messages received from the device in ZHA, I can see them in the debug log but can't process them as in Z2M.

I'm running on ZHA instead of deCONZ now, so I'll at least give it a try when I get the time :)

@PonyOny
Copy link
Author

PonyOny commented Nov 1, 2022

Lets see if we can get something to work with ZHA, and get a working quirk so we can start using this keypad 😊

@MrAdam
Copy link

MrAdam commented Nov 6, 2022

I got some time to work on the quirk this weekend, but am far from finished.
So far, I am able to control the configuration (enabling/disabling settings, and setting arm delay) of the device using ZHA, and I am also able to reset the device from ZHA.
I still need to figure out how to map the state events, and if it is possible to set the armed/unarmed more using ZHA.

from typing import Dict

from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
import zigpy.types as t
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time
from zigpy.zcl.clusters.security import IasAce
from zigpy.zcl.clusters.general import PowerConfiguration


from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    SKIP_CONFIGURATION,
)

from zhaquirks.tuya import TuyaLocalCluster, TUYA_CLUSTER_ID
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    TuyaDPType,
    TuyaMCUCluster,
)

TUYA_DP_ID_BATTERY_LEVEL_PERCENTAGE = 3  # Percentage
TUYA_DP_ID_SOS_ALARM = 23
TUYA_DP_ID_ANTI_REMOVE_ALARM = 24
TUYA_DP_ID_BATTERY_LEVEL_ENUM = 25  # "Low", "Middle", "High"
TUYA_DP_ID_DISARMED = 26
TUYA_DP_ID_ARMED = 27
TUYA_DP_ID_ARMED_HOME = 28
TUYA_DP_ID_SOS = 29
TUYA_DP_ID_ARM_DELAY_TIME = 103  # Seconds
TUYA_DP_ID_KEYPAD_BEEPS = 104  # Boolean
TUYA_DP_ID_QUICK_SOS = 105  # Boolean
TUYA_DP_ID_QUICK_DISARM = 106  # Boolean
TUYA_DP_ID_QUICK_ARM = 107  # Boolean
TUYA_DP_ID_ADMIN_CODE = 108
TUYA_DP_ID_USER_CODE = 109
TUYA_DP_ID_RESET = 110
TUYA_DP_ID_ARM_DELAY_BEEPS = 111  # Boolean
TUYA_DP_ID_UNKNOWN = 112


class TuyaPowerConfigurationCluster3AAA(PowerConfiguration, TuyaLocalCluster):
    """PowerConfiguration cluster for devices with 3 AAA."""

    BATTERY_SIZE = 0x0031
    BATTERY_QUANTITY = 0x0033
    BATTERY_RATED_VOLTAGE = 0x0034

    _CONSTANT_ATTRIBUTES = {
        BATTERY_SIZE: PowerConfiguration.BatterySize.AAA,
        BATTERY_QUANTITY: 3,
        BATTERY_RATED_VOLTAGE: 15,
    }


class WirelessZigbeeKeypadManufCluster(TuyaMCUCluster):
    """Tuya manufacturer cluster."""

    attributes = TuyaMCUCluster.attributes.copy()
    attributes.update(
        {
            TUYA_CLUSTER_ID + TUYA_DP_ID_ARM_DELAY_TIME: ("arm_delay_time", t.uint32_t),
            TUYA_CLUSTER_ID + TUYA_DP_ID_KEYPAD_BEEPS: ("keypad_beeps", t.Bool),
            TUYA_CLUSTER_ID + TUYA_DP_ID_QUICK_SOS: ("quick_sos", t.Bool),
            TUYA_CLUSTER_ID + TUYA_DP_ID_QUICK_DISARM: ("quick_disarm", t.Bool),
            TUYA_CLUSTER_ID + TUYA_DP_ID_QUICK_ARM: ("quick_arm", t.Bool),
            TUYA_CLUSTER_ID + TUYA_DP_ID_ARM_DELAY_BEEPS: ("arm_delay_beeps", t.Bool),
            TUYA_CLUSTER_ID + TUYA_DP_ID_RESET: ("reset", t.Bool),
        }  # type: ignore
    )

    dp_to_attribute: Dict[int, DPToAttributeMapping] = {
        TUYA_DP_ID_BATTERY_LEVEL_PERCENTAGE: DPToAttributeMapping(
            TuyaPowerConfigurationCluster3AAA.ep_attribute,
            "battery_percentage_remaining",
            TuyaDPType.VALUE,
            converter=lambda x: x * 2,
        ),
        TUYA_DP_ID_ARM_DELAY_TIME: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "arm_delay_time",
            TuyaDPType.VALUE,
        ),
        TUYA_DP_ID_KEYPAD_BEEPS: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "keypad_beeps",
            TuyaDPType.BOOL,
        ),
        TUYA_DP_ID_QUICK_SOS: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "quick_sos",
            TuyaDPType.BOOL,
        ),
        TUYA_DP_ID_QUICK_DISARM: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "quick_disarm",
            TuyaDPType.BOOL,
        ),
        TUYA_DP_ID_QUICK_ARM: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "quick_arm",
            TuyaDPType.BOOL,
        ),
        TUYA_DP_ID_USER_CODE: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "user_code",
            TuyaDPType.VALUE,
        ),
        TUYA_DP_ID_ARM_DELAY_BEEPS: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "arm_delay_beeps",
            TuyaDPType.BOOL,
        ),
        TUYA_DP_ID_RESET: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "reset",
            TuyaDPType.BOOL,
        ),
    }

    data_point_handlers = {
        TUYA_DP_ID_BATTERY_LEVEL_PERCENTAGE: "_dp_2_attr_update",
        TUYA_DP_ID_SOS_ALARM: "_dp_2_attr_update",
        TUYA_DP_ID_ANTI_REMOVE_ALARM: "_dp_2_attr_update",
        TUYA_DP_ID_BATTERY_LEVEL_ENUM: "_dp_2_attr_update",
        TUYA_DP_ID_DISARMED: "_dp_2_attr_update",
        TUYA_DP_ID_ARMED: "_dp_2_attr_update",
        TUYA_DP_ID_ARMED_HOME: "_dp_2_attr_update",
        TUYA_DP_ID_SOS: "_dp_2_attr_update",
        TUYA_DP_ID_ARM_DELAY_TIME: "_dp_2_attr_update",
        TUYA_DP_ID_KEYPAD_BEEPS: "_dp_2_attr_update",
        TUYA_DP_ID_QUICK_SOS: "_dp_2_attr_update",
        TUYA_DP_ID_QUICK_DISARM: "_dp_2_attr_update",
        TUYA_DP_ID_QUICK_ARM: "_dp_2_attr_update",
        TUYA_DP_ID_ADMIN_CODE: "_dp_2_attr_update",
        TUYA_DP_ID_USER_CODE: "_dp_2_attr_update",
        TUYA_DP_ID_RESET: "_dp_2_attr_update",
        TUYA_DP_ID_ARM_DELAY_BEEPS: "_dp_2_attr_update",
        TUYA_DP_ID_UNKNOWN: "_dp_2_attr_update",
    }


class WirelessZigbeeKeypad(CustomDevice):
    """MS-K1AZ Wireless Zigbee Keypad."""

    signature = {
        MODELS_INFO: [("_TZE200_n9clpsht", "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,
                    WirelessZigbeeKeypadManufCluster.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.IAS_ANCILLARY_CONTROL,
                INPUT_CLUSTERS: [
                    WirelessZigbeeKeypadManufCluster,
                    TuyaPowerConfigurationCluster3AAA,
                ],
                OUTPUT_CLUSTERS: [
                    Ota.cluster_id,
                    Time.cluster_id,
                ],
            }
        },
    }

@dmulcahey
Copy link
Collaborator

Have these conversations in PR’s just mark them draft until you’re ready

@PonyOny
Copy link
Author

PonyOny commented Nov 6, 2022

@MrAdam Amazing work 👌🏻 I will give it a try.

@pimseb
Copy link

pimseb commented Jun 17, 2023

I've moved from zigbee2mqtt to ZHA and I own this keypad.
I'm not able to make it work with ZHA. Is there any update on this ?
Thank you

@MrAdam
Copy link

MrAdam commented Jun 19, 2023

I stopped working on this and moved to zigbee2mqtt, as I had to make changes in both home assistant repository, and this repository, but weren't able to get any feedback on which direction to go with the code.
See comment here: #1908 (comment)

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 Dec 16, 2023
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Dec 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in progress Issue is being worked on, near fix stale Issue is inactivate and might get closed soon Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants