TuyaLink 设备

更新时间:2023-03-09 08:04:35

TuyaLink 生态设备接入是面向物联网生态领域(自研模组/成品智能设备)全面开放的设备上云解决方案。

通过整合并全面升级涂鸦 IoT 开发平台技术底座 IoT Core,提供包含物模型、规则引擎、数据解析、设备管理、运维监控、告警管理、固件 OTA 升级和应用开发等全面的物联网开发套件,极大地降低了开发者接入门槛。

通过此方案可以快速加入涂鸦生态体系,实现跨领域设备间互连互通,并可使用平台丰富的 PaaS、SaaS 和 App 等应用开发能力,最大程度地降低物联网整体解决方案的落地实施成本,减少开发周期。

TuyaLink 设备

更多详情,请参考 生态设备接入

判断 TuyaLink 设备

接口说明

- (BOOL)isSupportThingModelDevice;

代码示例

Objc:

- (void)judgeSupportTuyaLink {
	// self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];
  
  BOOL isSupport =	[self.device.deviceModel isSupportThingModelDevice];
	NSLog(@"judgeSupportTuyaLink: %ld", isSupport);
}

Swift:

func judgeSupportTuyaLink() {
    let isSupport = device?.deviceModel?.isSupportThingModelDevice()
}

物模型

物模型是 涂鸦 IoT 开发平台 针对物理实体设备在云端建立的数据模型,主要用于描述产品的功能。

它主要通过定义一款产品设备所应具备的基本状态(属性)、可以被外部系统调用的复杂指令(动作),以及在运行过程中产生的各种事件状态(事件)。这三种功能类型的集合用来表示一个物理实体设备,在云端的数据展现。

功能类型 说明
属性 (Property) 属性主要用于定义设备具有持续性、可查询等特点的状态,它代表设备的某个或者某几个功能参数。属性可以通过设置读写操作来实现对设备功能参数的修改和查询。当设备本身的某项功能参数发生变更,设备也可以主动修改属性。如灯的亮度、开关状态等。
动作 (Action) 动作用于控制设备执行复杂的业务功能。 动作是不涉及设备属性变更的控制指令。 这种指令需要设备对其响应,从而对外提供一个可供调用的方法。如人脸识别、照片下发等。
事件 (Event) 事件是设备上报的瞬时通知消息,可以包含多个输出参数,需要被外部感知和处理。事件通常需要搭配数据订阅服务或者与规则引擎结合,对事件信息作出业务逻辑上的处理,以满足特定的功能。如水温报警、故障报警等。

目前,物模型信息是按需获取机制,请在合适的时机检查物模型本地缓存,并在适当的时候 获取并更新 物模型缓存。

接口说明

@interface TuyaSmartDeviceModel
// ....

// 获取设备的物模型本地缓存
@property (nonatomic, strong, nullable) TuyaSmartThingModel *thingModel;
@end

@interface TuyaSmartDevice
// ...
  
// 使用 当前设备信息 获取并更新物模型缓存
- (void)getThingModelWithSuccess:(nullable void(^)(TuyaSmartThingModel * _Nullable thingModel))success
                         failure:(nullable TYFailureError)failure;

// 使用 产品 ID + 产品 ID 版本获取并更新物模型
+ (void)getThingModelWithProductId:(NSString *)pid
                    productVersion:(NSString *)productVersion
                           success:(nullable void (^)(TuyaSmartThingModel * _Nullable thingModel))success
                           failure:(nullable TYFailureError)failure;
@end

TuyaSmartThingModel 数据模型

字段 类型 说明
modelId NSString 物模型 ID
productId NSString 产品 ID
productVersion NSString 产品 ID 版本
services NSArray<TuyaSmartThingServiceModel *> 服务模型
extensions NSDictionary 扩展信息

TuyaSmartThingServiceModel 数据模型

字段 类型 说明
properties TuyaSmartThingProperty 属性
actions TuyaSmartThingAction 动作
events TuyaSmartThingEvent 事件

TuyaSmartThingProperty 数据模型

字段 类型 说明
abilityId NSInteger 属性 ID
可与普通设备的 dps 的 dpId 对应
code NSString 属性 Code
accessMode NSString 访问模式:
rw: 可下发可上报
ro: 仅上报
wr: 仅下发
typeSpec NSDictionary 类型定义
defaultValue id 默认值

附加说明

typeSpec 类型定义,目前支持 valuestringdateboolenumfault(bitmap)arraystruct 几种类型。

其中,arraystruct 是 TuyaLink 特有的。struct 中存在嵌套,在使用时请注意嵌套定义。

各种类型定义样例:

// type = value
{
  "unit" : "℃",
  "type" : "value",
  "min" : 0,
  "max" : 100,
  "typeDefaultValue" : 0,
  "step" : 1,
  "scale" : 1
}

// type = string
{
  "type" : "string",
  "typeDefaultValue" : "",
  "maxlen" : 50
}

// date
{
  "type" : "date",
  "typeDefaultValue" : 1658482441413
}

// bool
{
  "type" : "bool",
  "typeDefaultValue" : false
}

// enum
{
  "range" : [
    "e1",
    "e2",
    "e3"
  ],
  "typeDefaultValue" : "e1",
  "type" : "enum"
}

// fault (bitmap)
{
  "label" : [
    "f1",
    "f2"
  ],
  "typeDefaultValue" : 0,
  "type" : "bitmap"
}

// array
{
  "maxSize" : 4,
  "type" : "array",
  "elementTypeSpec" : {
    "type" : "value"
  }
}

// struct
{
  "type" : "struct",
  "properties" : {
    "struct_value" : {
      "name" : "test_value",
      "typeSpec" : {
        "type" : "value",
        "min" : 11,
        "typeDefaultValue" : 11,
        "max" : 22,
        "step" : 2,
        "scale" : 1
      }
    }
  }
}

TuyaSmartThingAction 数据模型

字段 类型 说明
abilityId NSInteger 动作 ID
code NSString 动作 Code
inputParams NSArray 下发参数列表
outputParams NSArray 上报参数列表

inputParamsoutputParams 实际上是 typeSpec 的数组,可为空数组,例如:

// input
{
  "code": "action_input_output_params",
  "abilityId" : 108,
  
  "inputParams" : [{
    "typeSpec" : {
      "unit" : "¥",
      "type" : "value",
      "min" : 0,
      "max" : 5,
      "typeDefaultValue" : 0,
      "step" : 1,
      "scale" : 0
    },
    "code" : "action_value"
  },
  {
    "typeSpec" : {
      "type" : "string",
      "typeDefaultValue" : "",
      "maxlen" : 100
    },
    "code" : "action_string"
  }],
  
  "outputParams" : [{
    "typeSpec" : {
      "unit" : "$",
      "type" : "value",
      "min" : 0,
      "max" : 100,
      "typeDefaultValue" : 0,
      "step" : 1,
      "scale" : 0
    },
    "code" : "out_action_value"
  }]
}

TuyaSmartThingEvent 数据模型

字段 类型 说明
abilityId NSInteger 事件 ID
code NSString 事件 Code
outputParams NSArray 上报参数列表

控制设备

下发控制

接口说明

- (void)publishThingMessageWithType:(TuyaSmartThingMessageType)thingMessageType
                            payload:(NSDictionary *)payload
                            success:(TYSuccessHandler)success
                            failure:(TYFailureError)failure

参数说明

参数 类型 说明
thingMessageType TuyaSmartThingMessageType 0: 属性,1: 动作,2: 事件
payload NSDictionary 下发内容
success TYSuccessHandler 成功回调
failure TYFailureError 失败回调
  • 由于下发时会校验内容合法性,下发时需要有物模型缓存,请在合适的时机判断和拉取物模型。

  • 由于 TuyaLink 设备的特性,目前仅通过 MQTT 通道进行控制。

  • TuyaSmartThingMessageType 中 事件 仅上报,不能下发。

  • Payload 格式如下:

下发属性时规则

// 可多个同时下发,code 是物模型中属性的 code,value 要符合 typeSpec 定义
{
	"code": value,
	"code": value
}

// 例如:
{
	"color":"green",
	"brightness": 50
}

下发动作时规则

// 同时只能下发一个动作
{
  "actionCode": "code",
  "inputParams": {
    "paramsCode": value,
    "paramsCode": value,
  }
}

// 例如:
{
	"actionCode": "action_input_output_params",
	"inputParams": {
		"action_value": 5,
		"action_string": "test_string"
	}
}

代码示例

Objc:

// 在合适的时机去拉取,比如:用户点击设备进入控制页的时候
- (void)fetchThingModel {
    // self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];

    if (!self.device.deviceModel.thingModel) {
      [self.device getThingModelWithSuccess:^(TuyaSmartThingModel * _Nullable thingModel) {
          NSLog(@"fetch thing model success: %@", thingModel);
      } failure:^(NSError *error) {
          NSLog(@"fetch thing model fail: %@", error);
      }];
    }
}

// 控制设备-下发属性
// 注意:在下发前需要 deviceModel.thingModel 是存在的
- (void)publishProperty {
    // self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];

  	// 下发属性
  	NSDictionary *propertyPayload = @@{
        @"color": @"green",
        @"brightness": @50
    };
		[self.device publishThingMessageWithType:TuyaSmartThingMessageTypeProperty payload:propertyPayload success:^{
        NSLog(@"success");
    } failure:^(NSError *error) {
        NSLog(@"failure: %@", error);
    }];
}

// 控制设备-下发动作
// 注意:在下发前需要 deviceModel.thingModel 是存在的
- (void)publishAction {
    // self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];

  	// 下发动作
		NSDictionary *actionPayload = @{
        @"actionCode": @"action_input_output_params",
        @"inputParams": @{
            @"action_value": @5,
            @"action_string": @"test_string"
        }
    };
  	[self.device publishThingMessageWithType:TuyaSmartThingMessageTypeAction payload:actionPayload success:^{
        NSLog(@"success");
    } failure:^(NSError *error) {
        NSLog(@"failure: %@", error);
    }];
}

Swift:

// 在合适的时机去拉取,比如:用户点击设备进入控制页的时候
func fetchThingModel() {
  device?.getThingModel(success: { thingModel in
      print("success \(thingModel)")
  }, failure: { error in
      if let err = error {
          print("failure \(err)")
      }
  })
}

// 控制设备-下发属性
// 注意:在下发前需要 deviceModel.thingModel 是存在的
func publishProperty() {
  let porpetyPayload = [
    "color": "green",
    "brightness": 50
  ]
  device?.publishThingMessage(with: .property, payload: porpetyPayload, success: {
    print("publish property success")
  }, failure: { error in
    if let err = error {
      print("publish property fail.(%@)", err)
    }
  })
}

func publishAction() {
  let actionPayload = [
    "actionCode": "action_input_output_params",
    "inputParams": [
      "action_value": 5,
      "action_string": "test_string"
    ]
  ]
  device?.publishThingMessage(with: .action, payload: actionPayload, success: {
    print("publish action success")
  }, failure: { error in
    if let err = error {
      print("publish action fail.(%@)", err)
    }
  })
}

监听回调

通过 TuyaSmartDevice 设置 delegate 进行监听回调。

接口说明

- (void)device:(TuyaSmartDevice *)device didReceiveThingMessageWithType:(TuyaSmartThingMessageType)thingMessageType payload:(NSDictionary *)payload;

参数说明

参数 类型 说明
device TuyaSmartDevice 设备操作对象
TuyaSmartThingMessageType TuyaSmartThingMessageType 0:属性,1:动作,2:事件
payload NSDictionary 收到的上报内容,type不同格式不同,具体见 下列注意事项 中的样例
  • 由于上报时会校验内容合法性,上报时需要有物模型缓存,请在合适的时机判断和拉取物模型
  • 仅对于属性消息,还会转换成 dps 格式,也会通过 -device:dpsUpdate: 回调
  • payload 在不同消息类型下的格式,如下样例所示:

上报属性时

// 格式规则
{
  "code": {
    "value": value,
    "time": 1234567890
  },
  "code": {
    "value": value,
    "time": 1234567890
  }
}

// 例如:
{
	"color": {
		"value": "green",
		"time": 1234567890
	},
	"brightness": {
		"value": 50,
		"time": 1234567890
	}
}

上报动作时

// 格式规则
{
	"actionCode": code,
	"outputParams": {
		"outputParam1": value,
		"outputParam2": value
	}
}

// 例如
{
	"actionCode": "open_door_action",
	"outputParams": {
		"status": "open",
		"angle": 30
	}
}

上报事件时

// 格式规则
{
	"eventCode": code,
	"outputParams": {
		"outputParam1": value,
		"outputParam2": value
	}
}

// 例如
{
	"eventCode": "people_move_event",
	"outputParams": {
		"count": 2,
		"time": "2022-07-22 18:30:00"
	}
}

代码示例

Objc:

// 在合适的时机去监听
- (void)addThingDeviceListner {
  	// self.device = [TuyaSmartDevice deviceWithDeviceId:@"your_device_id"];
	  device.delegate = self;
}

// 注意:在接收上报前需要 deviceModel.thingModel 是存在的
- (void)device:(TuyaSmartDevice *)device didReceiveThingMessageWithType:(TuyaSmartThingMessageType)thingMessageType payload:(NSDictionary *)payload {
    
    NSLog(@"---did receive thing msg type: %ld, msg: %@", thingMessageType, payload);
    // 不同 type 的消息 payload 格式不同,详见参数说明
    if (thingMessageType == TuyaSmartThingMessageTypeProperty) {
        //.. do something
    } else if (thingMessageType == TuyaSmartThingMessageTypeAction) {
        //.. do something
    } else if (thingMessageType == TuyaSmartThingMessageTypeEvent) {
        //.. do something
    }
}

Swift:

// 在合适的时机去监听
func addThingDeviceListner {
  device?.delegate = self;
}

// 注意:在接收上报前需要 deviceModel.thingModel 是存在的
func device(_ device: TuyaSmartDevice, didReceiveThingMessageWith thingMessageType: TuyaSmartThingMessageType, payload: [AnyHashable : Any]) {
  print("---did receive thing msg type: \(thingMessageType), payload: \(payload)")
  switch thingMessageType {
  case .property:
    //.. do something
  case .action:
    //.. do something
  case .event:
    //.. do something
  }
}

属性、动作、事件的 payload 缓存

在物模型介绍中,提到: 属性 表示设备的持续性、可查询的状态,而 动作事件 是设备实时的响应。

所以,对应消息的 payload 的缓存机制如下所述:

  • 属性 有缓存,可以通过 TuyaSmartDeviceModel::dps 来获取缓存
  • 动作 无缓存
  • 事件 无缓存

属性 实际与原本普通设备的 DP 含义一致,所以通过 TuyaSmartDeviceModel::dps 来缓存,格式定义为:dpId = value

在使用时,请通过 属性模型TuyaSmartThingProperty::abilityId 来进行映射对应来展示。

属性 payload 转换为 DP

TuyaLink 设备的属性实际上和原本涂鸦普通设备的 DP 点概念一致。只是相比 DP 点,增加 arraystruct 两种新的类型定义。

当收到属性消息的上报时,在通过 -device:didReceiveThingMessageWithType:payload: 回调的同时,也会同步转换成 dps,通过 -device:dpsUpdate: 回调并缓存在 deviceModel.dpsdeviceModel.dpsTime 中。

您也可以通过 TuyaSmartThingModel 开放的方法 -dpsFromProperties: 将 属性 转换为 DP。

接口说明

@interface TuyaSmartThingModel : NSObject
//...
  
// 将属性 转换为 DP 格式
- (NSDictionary *)dpsFromProperties:(NSDictionary *)properties;
@end

参数说明

参数 类型 说明
properties NSDictionary 属性 payload,格式与上报属性时一致,格式如下

属性 payload 格式:

{
  "code": {
    "value": value,
    "time": time
  },
  "code": {
    "value": value,
    "time": time
  },
}

代码示例

Objc:

- (void)transferPropertiesToDp {
	  TuyaSmartThingModel *thing = self.device.deviceModel.thingModel;
  
  	NSDictionary *propertiesPayload = @{
        @"color": @"green",
        @"brightness": @50
    }
    NSDictionary *dps = [thing dpsFromProperties:propertiesPayload];
    NSLog(@"---- dps: %@", dps);
  
  	/**
  		输出结果是,101、105实际就是 "color" 和 "brightness" 的 TuyaSmartThingProperty::abilityId
  		@{
  			@"101": @"green",
  			@"105": @50
  		}
  	*/
}

Swift:

func trnasferPropertiesToDp {
  if let thing = device?.deviceModel.thingModel {
    let propertiesPayload = [
      "color": "green",
      "brightness": 50
    ]
    let dps = thing?.dps(fromProperties: propertiesPayload)
    print("--- dps: \(dps)")
  }
}