make-all / tuya-local

Local support for Tuya devices in Home Assistant
MIT License
1.07k stars 435 forks source link

Request support for NX-4964 Smart Lockbox #1851

Closed psuet closed 4 weeks ago

psuet commented 2 months ago

Log Message

2024-04-25 19:42:07.890 WARNING (MainThread) [custom_components.tuya_local.config_flow] Device matches None with quality of 0%. DPS: {"updated_at": 1714066925.192414, "8": 40, "9": "medium", "64": "1714062735"}

Information about DPS mappings

{
   "modelId":"000003xpvj",
   "services":[
      {
         "actions":[

         ],
         "code":"",
         "description":"",
         "events":[

         ],
         "name":"默认服务",
         "properties":[
            {
               "abilityId":1,
               "accessMode":"rw",
               "code":"unlock_method_create",
               "description":"【添加开锁方式】设备在线后,添加开锁方式。支持添加指密卡和人脸,以及支持管理员标记。",
               "extensions":{
                  "iconName":"icon-a_up",
                  "attribute":"1792"
               },
               "name":"添加开门方式",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":2,
               "accessMode":"rw",
               "code":"unlock_method_delete",
               "description":"【删除开锁方式】设备在线后,删除开锁方式。支持删除指密卡和人脸。",
               "extensions":{
                  "iconName":"icon-dp_rabbish",
                  "attribute":"1792"
               },
               "name":"删除开门方式",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":3,
               "accessMode":"rw",
               "code":"unlock_method_modify",
               "description":"【修改开锁方式】设备在线后,支持修改开锁方式的名称,以及密码的内容修改。",
               "extensions":{
                  "iconName":"icon-edit",
                  "attribute":"1792"
               },
               "name":"修改开门方式",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":8,
               "accessMode":"ro",
               "code":"residual_electricity",
               "description":"【设备状态】用于电量以百分比显示。范围-1~100,当设备上报数值-1时,不显示电量。(与DP9二选一)",
               "extensions":{
                  "iconName":"icon-dp_battery",
                  "attribute":"1280"
               },
               "name":"剩余电量",
               "typeSpec":{
                  "type":"value",
                  "max":100,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":9,
               "accessMode":"ro",
               "code":"battery_state",
               "description":"【设备状态】用于电量以状态等级显示。范围高中低。(与DP8二选一)",
               "extensions":{
                  "iconName":"icon-dp_battery",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"电池电量状态",
               "typeSpec":{
                  "type":"enum",
                  "range":[
                     "high",
                     "medium",
                     "low",
                     "poweroff"
                  ]
               }
            },
            {
               "abilityId":13,
               "accessMode":"ro",
               "code":"unlock_password",
               "description":"【上报开锁记录】用于设备上报普通密码开锁的记录。",
               "extensions":{
                  "iconName":"icon-dp_lock",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"普通密码解锁",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":14,
               "accessMode":"ro",
               "code":"unlock_dynamic",
               "description":"【上报开锁记录】用于设备上报动态密码开锁的记录。",
               "extensions":{
                  "iconName":"icon-shoushimima",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"动态密码解锁",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":19,
               "accessMode":"ro",
               "code":"unlock_ble",
               "description":"【上报开锁记录】用于设备上报手机蓝牙开锁时的记录。",
               "extensions":{
                  "iconName":"icon-dp_lock",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"蓝牙解锁记录",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":21,
               "accessMode":"ro",
               "code":"alarm_lock",
               "description":"【告警记录】本地发生告警时,用于上报设备的告警记录。对应值说明:0=指纹试错报警、1=密码试错报警、2=卡试错报警、3=人脸试错报警、4=假锁(锁舌卡住)、5=高温报警、6=超时未关门、7=电子锁舌未弹出、8=防撬报警 、9=钥匙插入、10=低电报警、11=电量耗尽报警、12=震动报警、13=布防报警,如设备只用到告警dp中的几项,多余的内容也不需要删除。如删除则mcu在上报时的序号需要调整。建议开发者不要改动告警中的枚举值。如遇特殊情况,请在pm的指导下,进行增、删,以及配置多语言等操作",
               "extensions":{
                  "iconName":"icon-dp_warming",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"告警",
               "typeSpec":{
                  "type":"enum",
                  "range":[
                     "wrong_finger",
                     "wrong_password",
                     "wrong_card",
                     "wrong_face",
                     "tongue_bad",
                     "too_hot",
                     "unclosed_time",
                     "tongue_not_out",
                     "pry",
                     "key_in",
                     "low_battery",
                     "power_off",
                     "shock",
                     "defense"
                  ]
               }
            },
            {
               "abilityId":51,
               "accessMode":"rw",
               "code":"temporary_password_creat",
               "description":"【在线临时密码】用于添加在线临时密码,支持设置时间段及循环重复的时效。",
               "extensions":{
                  "iconName":"icon-dp_add",
                  "attribute":"1280"
               },
               "name":"蓝牙锁临时密码添加",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":52,
               "accessMode":"rw",
               "code":"temporary_password_delete",
               "description":"【在线临时密码】用于删除在线临时密码。",
               "extensions":{
                  "iconName":"icon-dp_rabbish",
                  "attribute":"1280"
               },
               "name":"蓝牙锁临时密码删除",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":53,
               "accessMode":"rw",
               "code":"temporary_password_modify",
               "description":"【在线临时密码】用于修改在线临时密码的密码内容及对应的时效。",
               "extensions":{
                  "iconName":"icon-setting",
                  "attribute":"1280"
               },
               "name":"蓝牙锁临时密码修改",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":54,
               "accessMode":"rw",
               "code":"synch_method",
               "description":"【同步开锁方式】用于手机同步设备本地所有的开锁方式。",
               "extensions":{
                  "iconName":"icon-dp_upload",
                  "attribute":"1792"
               },
               "name":"同步开门方式(全量同步)",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":55,
               "accessMode":"ro",
               "code":"unlock_temporary",
               "description":"【上报开锁记录】用于设备上报在线临时密码开锁的记录。",
               "extensions":{
                  "iconName":"icon-shoushimima",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"临时密码解锁上报",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":61,
               "accessMode":"rw",
               "code":"remote_no_dp_key",
               "description":"【远程开锁】用于触发设备远程开锁操作。",
               "extensions":{
                  "iconName":"icon-dp_lock",
                  "attribute":"1280"
               },
               "name":"远程开门(带校验码)",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":62,
               "accessMode":"ro",
               "code":"unlock_phone_remote",
               "description":"【上报开锁记录】用于设备上报手机远程开锁的记录。",
               "extensions":{
                  "iconName":"icon-shouji",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"远程手机解锁",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":63,
               "accessMode":"ro",
               "code":"unlock_voice_remote",
               "description":"【上报开锁记录】用于设备上报语音语音开锁的记录。",
               "extensions":{
                  "iconName":"icon-dp_mic",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"远程语音解锁",
               "typeSpec":{
                  "type":"value",
                  "max":999,
                  "min":0,
                  "scale":0,
                  "step":1
               }
            },
            {
               "abilityId":64,
               "accessMode":"rw",
               "code":"password_offline_time",
               "description":"【离线密码】用于云端与设备的时间戳同步。若未选或未同步,离线密码将不可用。",
               "extensions":{
                  "iconName":"icon-dp_time2",
                  "attribute":"1280",
                  "trigger":"direct"
               },
               "name":"离线密码T0时间下发",
               "typeSpec":{
                  "type":"string",
                  "maxlen":255
               }
            },
            {
               "abilityId":65,
               "accessMode":"ro",
               "code":"unlock_offline_clear_single",
               "description":"【离线密码清空】当设备本地清空单条离线密码后,上报清空操作记录。",
               "extensions":{
                  "iconName":"icon-dp_upload",
                  "attribute":"1280"
               },
               "name":"单条离线密码清空上报",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":66,
               "accessMode":"ro",
               "code":"unlock_offline_clear",
               "description":"【离线密码清空】当设备本地清空所有离线密码后,上报清空操作记录。",
               "extensions":{
                  "iconName":"icon-dp_upload",
                  "attribute":"1152"
               },
               "name":"离线密码清空上报",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":67,
               "accessMode":"ro",
               "code":"unlock_offline_pd",
               "description":"【上报开锁记录】用于设备上报离线密码开锁的记录。",
               "extensions":{
                  "iconName":"icon-dp_upload",
                  "attribute":"1280"
               },
               "name":"离线密码解锁上报",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":69,
               "accessMode":"rw",
               "code":"record",
               "description":"【获取开锁记录】用于手机主动获取设备本地存储的开门记录。",
               "extensions":{
                  "iconName":"icon-dp_book",
                  "attribute":"1280"
               },
               "name":"获取门锁记录",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":70,
               "accessMode":"rw",
               "code":"check_code_set",
               "description":"【蓝牙开锁】用于配置设备执行蓝牙解锁时需要的校验信息。",
               "extensions":{
                  "iconName":"icon-dp_mode",
                  "attribute":"1280"
               },
               "name":"配置蓝牙解锁校验码",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":71,
               "accessMode":"rw",
               "code":"ble_unlock_check",
               "description":"【蓝牙开锁】用于触发设备执行蓝牙解锁。触发源可以是手机、配件或靠近解锁。",
               "extensions":{
                  "iconName":"icon-dp_lock",
                  "attribute":"1280"
               },
               "name":"蓝牙解锁(带校验码)",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":72,
               "accessMode":"ro",
               "code":"unlock_record_check",
               "description":"【上报开锁记录】用于设备上报配件开锁及开关锁的记录。需要包含设备端具体触发开关锁的方式。",
               "extensions":{
                  "iconName":"icon-tongji2",
                  "attribute":"1280"
               },
               "name":"(带校验码)开关锁记录",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            },
            {
               "abilityId":73,
               "accessMode":"rw",
               "code":"remote_pd_setkey_check",
               "description":"【远程开锁】用于配置远程开锁时需要的密钥。若设备需要进行远程开锁,该DP必选。",
               "extensions":{
                  "iconName":"icon-dp_lock",
                  "attribute":"1792"
               },
               "name":"配置远程解锁含校验",
               "typeSpec":{
                  "type":"raw",
                  "maxlen":128
               }
            }
         ]
      }
   ]
}

TinyTuya Output

{
        "name": "NX-4964",
        "id": "XXXXX",
        "key": "XXXXX",
        "mac": "DC:23:50:19:03:2E",
        "uuid": "XXXXX",
        "category": "jtmspro",
        "product_name": "NX-4964",
        "product_id": "qicggi0m",
        "biz_type": 0,
        "model": "SMKB2_ty_03_pro",
        "sub": true,
        "icon": "https://images.tuyaeu.com/smart/icon/ay1574387149429focnK/c9c7030303f490d5f5338414061c1876.jpg",
        "node_id": "XXXXXXX",
        "mapping": {
            "1": {
                "code": "unlock_method_create",
                "type": "Raw",
                "values": {}
            },
            "2": {
                "code": "unlock_method_delete",
                "type": "Raw",
                "values": {}
            },
            "3": {
                "code": "unlock_method_modify",
                "type": "Raw",
                "values": {}
            },
            "8": {
                "code": "residual_electricity",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 100,
                    "scale": 0,
                    "step": 1
                }
            },
            "9": {
                "code": "battery_state",
                "type": "Enum",
                "values": {
                    "range": [
                        "high",
                        "medium",
                        "low",
                        "poweroff"
                    ]
                }
            },
            "13": {
                "code": "unlock_password",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "14": {
                "code": "unlock_dynamic",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "19": {
                "code": "unlock_ble",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "21": {
                "code": "alarm_lock",
                "type": "Enum",
                "values": {
                    "range": [
                        "wrong_finger",
                        "wrong_password",
                        "wrong_card",
                        "wrong_face",
                        "tongue_bad",
                        "too_hot",
                        "unclosed_time",
                        "tongue_not_out",
                        "pry",
                        "key_in",
                        "low_battery",
                        "power_off",
                        "shock",
                        "defense"
                    ]
                }
            },
            "51": {
                "code": "temporary_password_creat",
                "type": "Raw",
                "values": {}
            },
            "52": {
                "code": "temporary_password_delete",
                "type": "Raw",
                "values": {}
            },
            "53": {
                "code": "temporary_password_modify",
                "type": "Raw",
                "values": {}
            },
            "54": {
                "code": "synch_method",
                "type": "Raw",
                "values": {}
            },
            "55": {
                "code": "unlock_temporary",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "61": {
                "code": "remote_no_dp_key",
                "type": "Raw",
                "values": {}
            },
            "62": {
                "code": "unlock_phone_remote",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "63": {
                "code": "unlock_voice_remote",
                "type": "Integer",
                "values": {
                    "min": 0,
                    "max": 999,
                    "scale": 0,
                    "step": 1
                }
            },
            "64": {
                "code": "password_offline_time",
                "type": "String",
                "values": "{}"
            },
            "65": {
                "code": "unlock_offline_clear_single",
                "type": "Raw",
                "values": {}
            },
            "66": {
                "code": "unlock_offline_clear",
                "type": "Raw",
                "values": {}
            },
            "67": {
                "code": "unlock_offline_pd",
                "type": "Raw",
                "values": {}
            },
            "69": {
                "code": "record",
                "type": "Raw",
                "values": {}
            },
            "70": {
                "code": "check_code_set",
                "type": "Raw",
                "values": {}
            },
            "71": {
                "code": "ble_unlock_check",
                "type": "Raw",
                "values": {}
            },
            "72": {
                "code": "unlock_record_check",
                "type": "Raw",
                "values": {}
            },
            "73": {
                "code": "remote_pd_setkey_check",
                "type": "Raw",
                "values": {}
            }
        },
        "parent": "",
        "ip": "",
        "version": ""
    },

Product ID

{
  "result": [
    {
      "active_time": 1714062735,
      "bind_space_id": "190439676",
      "category": "jtmspro",
      "create_time": 1714062735,
      "custom_name": "",
      "icon": "smart/icon/ay1574387149429focnK/c9c7030303f490d5f5338414061c1876.jpg",
      "id": "bf4bb44yiikykfld",
      "ip": "",
      "is_online": true,
      "lat": "50.1300",
      "local_key": "XXXXXXXXXXX",
      "lon": "8.6700",
      "model": "SMKB2_ty_03_pro",
      "name": "NX-4964",
      "product_id": "qicggi0m",
      "product_name": "NX-4964",
      "sub": true,
      "time_zone": "+02:00",
      "update_time": 1714062965,
      "uuid": "629498e1238981d8"
    }
  ],
  "success": true,
  "t": 1714067154179,
  "tid": "a9db08fc032b11efa53a0a297b5e2e3e"
}

Information about how the device functions

The Device in Question is a Tuya BLE Enabled Lock Box , that can be opened remotely and offers a multitude of code configuration options (time-based / one-time / interval).

By default the Lock used a 6 digit code

Manual (German / French): https://www.pearl.de/pdocs/NX4964_11_185461.pdf

In my example its connected through a ZX-5538 BLE Gateway.

psuet commented 2 months ago

I tried to build a config myself by somewhat copying and modifying from other BLE Locks implemented and experienced the following problem: The device in question does not implement the typical dp 46/47 or any other simple dp to unlock the box without some form of key (locking is not in-scope; its a snapping mechanism).

While trying to figure out how the app provides unlocking capabilities, i discovered that this lock uses dp 61 or dp 71 (https://developer.tuya.com/en/docs/iot/title?id=K9nmje3twsy7n#title-27-Locking%20and%20unlocking). As far as I can identify, this mechanism still needs to be implemented tuya-local. That was my logical next step.

Problem: I need a method to retrieve the required remote key programmatically. The (what looks like the) key can be easily retrieved by looking at the device logs in the developer portal (Base64 encoded). Example "No-Code Unlock (with Key)" Message from the Dev Portal: 01 EF BF BD 01 30 30 30 30 30 30 30 30 EF BF BD 01 (Hex, base64-decoded; Possible Key replaced by all zeros) I need clarification on how this message could be a valid DP 61 Message. Is that the right message? If someone can enlighten me, I would appreciate that.

Setting a new remote unlocking key seems to be possible be possible with DP 73 (instead of the far more simple DP 60)

I have not tried that yet since I somehow failed to grasp the concept of Central ID and peripheral ID and their influence on the Unlock Key. Not a Tuya Expert, yet.

Do you (@make-all) have any guidance on how you would like a remote function using DP 61 implemented in tuya-local? Does somebody know if there is a way to retrieve the key required?

make-all commented 2 months ago

If there is a standard way of unlocking by supplying a code, then I think the HA lock entity does provide an API for this. Because these dps are documented as "raw", I have not attempted to implement them yet, as I don't have sufficient info or a test device.

As for the method of finding the keycode, I think that can be left as a manual exercise, as it is supposed to be there for security, and in some cases the owner may want it entered by the user (on a pinpad etc), in others it can be passed as a parameter in an automation etc.