开发 Homebridge 硬件驱动

更新时间:2023-10-18 02:47:49下载pdf

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

前提条件

第一步:获取设备信息

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

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

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

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

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

    开发 Homebridge 硬件驱动

    {
          "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 )对应云端指令集和状态集。

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

第三步:开发驱动

  1. 根据选择的 Outlet Service,在 tuya-homebridge 项目中新建 outlet_accessory.js 文件。
    开发 Homebridge 硬件驱动

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

    开发 Homebridge 硬件驱动

    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
              },
            ]
      
      

      开发 Homebridge 硬件驱动

      codevalue 的对应关系你可以在标准指令集和标准状态集说明中获取。
      开发 Homebridge 硬件驱动

      以下为开启灯光的指令。

      {
         "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);
        }
      }
    }