如何开发一个新驱动 - dipakparmar/tuya-homebridge GitHub Wiki

TESTTEST

当前涂鸦仅支持 部分 PBT 类别 产品接入 Homebridge。 你可以基于自身的业务需求,结合涂鸦提供的标准指令集文档,通过以下的流程,开发 Tuya Homebridge 插件的单品硬件驱动,实现通过 Homebridge 控制更多的 PTB 设备。

前提条件

第一步:获取设备信息

即使部分 PBT 品类的设备未接入 Homebridge,但是我们还是可以通过运行 Tuya Homebridge 插件的方式,在日志中获取该设备状态信息,例如设备在涂鸦产品体系中的分类,即品类 catagory

  1. 开启调试模式,查看调试日志。

    说明: 调试模式下查看日志的方法参见 如何获取日志

  2. 在日志中通过 您的设备名称 获取设备信息的日志。

    说明: 以 "active_time" 开始, 以 "uuid" 结束。例如,从日志中搜 "Living room switch",能获取到该设备的信息,示例代码如下。

    image.png

    {
          "active_time": 1623229189,
          "biz_type": 18,
          "category": "cz",
          "create_time": 1560491945,
          "icon": "smart/product_icon/cz.png",
          "id": "xxxxxxxxxxxxxxx",
          "ip": "xxxxxxxxxxxxxxx",
          "lat": "30.30286857361191",
          "local_key": "xxxxxxxx",
          "lon": "120.0639743842656",
          "model": "",
          "name": "Living room switch",
          "online": false,
          "owner_id": "34794909",
          "product_id": "yfemiswbgjhddhcf",
          "product_name": "Switch Product",
          "status": [
            {
              "code": "switch",
              "value": false
            },
            {
              "code": "countdown_1",
              "value": 0
            },
            {
              "code": "cur_current",
              "value": 0
            },
            {
              "code": "cur_power",
              "value": 0
            },
            {
              "code": "cur_voltage",
              "value": 2343
            }
          ],
          "sub": false,
          "time_zone": "+08:00",
          "uid": "ay1622097934070h5Mpi",
          "update_time": 1625101929,
          "uuid": "65015854bcddc21a9073"
    }
    

第二步:查找品类

获取了品类信息后,你可以在涂鸦开发者平台的文档中心,通过 category (例如,cz)获取标准指令集和标准状态集的参数说明,为后续驱动开发做准备。

  1. 获取品类的指令集和状态集参数说明。

    1. 从日志中的 "category": "cz" 获知,本示例中的设备 "Living room switch" 在涂鸦定义的品类为 cz

    2. 涂鸦开发者文档中心 的左侧目录搜索栏,搜索品类值(例如 cz )对应云端指令集和状态集。

  2. 获取品类的 Homebirdge 服务模板。 在 HomeBridge API 中查找相对接近的 Service 模型,方便后续的驱动开发。本示例中, 插座比较接近 Outlet,因此选择 Outlet Serviceimage.png

第三步:开发驱动

  1. 根据选择的 Outlet Service,在 tuya-homebridge 项目中新建 outlet_accessory.js 文件。 image.png

  2. 添加品类。 需要支持的设备为 cz,所以在 index.js 文件中的 addAccessory() 方法中添加 cz,代码如下。

    image.png

    case 'cz':
        deviceAccessory = new OutletAccessory(this, homebridgeAccessory, device);
        this.accessories.set(uuid, deviceAccessory.homebridgeAccessory);
        this.deviceAccessories.set(uuid, deviceAccessory);
        break;
    
  3. 开发插件。

    outlet_accessory.js 文件中开发插件,代码示例参见 outlet_accessory.js

    1. 设置 Characteristics,并且设置该属性的 Get 和 Set 回调。

      说明: Characteristics 是 HomeBridge API 中 Outlet 类型对应 Service 的属性,Outlet 的 Service 的 Characteristics 参数为 on。

      • get:将设备的状态返回 HomeBridge,用于 Home App 上的展示。
      • set:在 Home App 触发开、关动作时,通过该回调下发对应的指令给设备。
       if (this.isRefresh) {
          service
            .getCharacteristic(Characteristic.On)
            .updateValue(value);
        } else {
          this.getAccessoryCharacteristic(service, Characteristic.On);
        }
      
      getAccessoryCharacteristic(service, name) {
          //set  Accessory service Characteristic
          service.getCharacteristic(name)
            .on('get', callback => {
              if (this.hasValidCache()) {
                callback(null, this.getCachedState(service.displayName));
              }
            })
            .on('set', (value, callback) => {
              var param = this.getSendParam(service.displayName, value)
              this.platform.tuyaOpenApi.sendCommand(this.deviceId, param).then(() => {
                this.setCachedState(service.displayName, value);
                callback();
              }).catch((error) => {
                this.log.error('[SET][%s] Characteristic.Brightness Error: %s', this.homebridgeAccessory.displayName, error);
                this.invalidateCache();
                callback(error);
              });
            });
        }
      
    2. 在 get 回调中将 DP Code ("switch") 对应的值("false")回调给 Homebridge Service,在 set 回调中将 "switch" 作为 dp code 与对应的 value 下发至设备。

      "status": [
              {
                "code": "switch",
                "value": false
              },
              {
                "code": "countdown_1",
                "value": 0
              },
            ]
      
      

      image.png

      codevalue 的对应关系你可以在标准指令集和标准状态集说明中获取。 image.png

      以下为开启灯光的指令。

      {
         "code": "switch",
         "value": true
      }
      

第四步:控制设备

新增品类后,你可以再次运行 Tuya Homebridge 插件,成功连接 Homekit 后,在 HomeKit 上控制 PBT 设备。

设备注册逻辑

当你启动插件的时候,入口文件 index.js 会去调用 TuyaSHOpenAPI().getDevices() 获取当前关联 App 账号下的设备,获取到设备后,会遍历调用 this.addAccessory(device); 来将对应的 PBT 设备创建对应的 Accessory 实例然后注册到 PlatformAccessories中。

Accessory、Service 的创建实现在 base_accessory.js中,不需要开发者独立开发。

//Accessory
    if (this.homebridgeAccessory) {
      this.homebridgeAccessory.controller = this;
      if (!this.homebridgeAccessory.context.deviceId) {
        this.homebridgeAccessory.context.deviceId = this.deviceConfig.id;
      }
      
      this.log.log(`Existing Accessory found ${homebridgeAccessory.displayName}  ${homebridgeAccessory.context.deviceId} ${homebridgeAccessory.UUID}`);
      this.homebridgeAccessory.displayName = this.deviceConfig.name;
    }
    else {
      // Create new Accessory
      this.log.log(`Creating New Accessory ${this.deviceConfig.id}`);
      this.homebridgeAccessory = new PlatformAccessory(
        this.deviceConfig.name,
        UUIDGen.generate(this.deviceConfig.id),
        categoryType);
      this.homebridgeAccessory.context.deviceId = this.deviceConfig.id;
      this.homebridgeAccessory.controller = this;
      this.platform.registerPlatformAccessory(this.homebridgeAccessory);
    }
	
    // Service
    if (this.subServices.length == 0) {
      // Service
      this.service = this.homebridgeAccessory.getService(this.serviceType);
      if (this.service) {
        this.service.setCharacteristic(Characteristic.Name, this.deviceConfig.name);
      }
      else {
        // add new service
        this.service = this.homebridgeAccessory.addService(this.serviceType, this.deviceConfig.name);
      }
    } else {
      // SubService
      for (const subService of this.subServices) {
        var service = this.homebridgeAccessory.getService(subService);
        if (service) {
          service.setCharacteristic(Characteristic.Name, subService);
        } else {
          // add new subService
          this.homebridgeAccessory.addService(this.serviceType, subService, subService);
        }
      }
    }

插件实现流程

重点 JSON 文件

  • index.js:添加你需要的品类至 addAccessory() function, 然后创建对应的 ****_accessy.js 文件。

  • ****_accessory.js:遍历设备信息的 Code ,创建对应的 Characteristic

  • tuyaopenapi.js:包含设备相关的 APIs

  • tuyamqttapi.js:包含 MQTT 服务相关配置

Common issues

更多关于 Homebridge 安装的信息参见Homebridge Common Issues

Tuya Open API

  • login(username, password): 登录涂鸦 IoT 平台
  • getDeviceList(): 获取资产下所有设备的信息
  • get_assets(): 获取可用资产
  • getDeviceIDList(assetID): 获取指定资产下设备 ID
  • getDeviceFunctions(deviceID): 获取设备支持的指令集
  • getDeviceInfo(deviceID): 获取单个设备详细信息
  • getDeviceListInfo(devIds = []): 获取多个设备的详细信息
  • getDeviceStatus(deviceID): 获取单个设备的状态
  • getDeviceListStatus(devIds = []): 获取多个设备的状态
  • sendCommand(deviceID, params): 对设备发送指令

更多信息参见 Tuya Open API docs

MQTT

  • start(): 启动 MQTT
  • stop(): 中断 MQTT
  • addMessageListener(listener): 添加回调
  • removeMessageListener(listener): 删除回调