本地定时相对云端定时而言,是指当设备离线时,也能自动执行定时任务。本文档介绍自有App中的定时功能的开发实践。
概述
生活物联网平台提供两种App定时功能的开发方式。
集成定时插件
- 创建一个自有App,并完成SDK下载。详细操作请参见创建自有App。
- 在自有品牌App的App界面中,单击本地定时对应的生成代码。
- 单击复制,将代码粘贴到您的App代码中,调用定时插件。
示例代码片段如下。
- iOS
NSString *url = @"link://router/localTimer";
[[IMSRouterService sharedService] openURL:url options:@{@"iotId":iotId}];
- Android
Intent intent = new Intent();
intent.putExtra("iotId",iotId);
Router.getInstance().toUrl(context, "link://router/localTimer");
Native开发定时功能(iOS)
- 创建一个自有App,并完成SDK下载。详细操作请参见创建自有App。
- 获取本地定时的属性字段。
本地定时是属于TSL的一个属性,定时就是操作TSL的LocalTimer。根据设备模型SDK获取对应设备的TSL,并从中获取本地定时的个数、Targets、Timezoneoffset属性字段。
// 获取本地定时的个数,是否支持 Targets、Timezoneoffset
NSUInteger size = 0;
IMSThing *thing = [[IMSThingManager sharedManager] buildThing:iotId];
IMSThingProfile *profile = [thing getThingProfile];
NSArray<IMSThingTslProperty *> *proList = [profile allPropertiesOfModel];
__block BOOL hasTargets = NO;
__block BOOL hasTimezoneOffset = NO;
for (NSUInteger i = 0; i<proList.count; i++) {
IMSThingTslProperty * _Nonnull obj = proList[i];
NSLog(@"%lul", (unsigned long)i);
if ([obj.identifier isEqualToString:@"LocalTimer"]) {
NSString *sizeTmp = obj.dataType[@"specs"][@"size"];
size = sizeTmp.integerValue;
NSArray *itemList = obj.dataType[@"specs"][@"item"][@"specs"];
[itemList enumerateObjectsUsingBlock:^(id _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
if ([item[@"identifier"] isEqualToString:@"Targets"]) {
hasTargets = YES;
} else if ([item[@"identifier"] isEqualToString:@"TimezoneOffset"]){
hasTimezoneOffset = YES;
}
}];
}
}
- 获取设备端的定时器列表数据。
// 本地定时model数据结构
@interface IMSLocalTimerModel : NSObject
@property (nonatomic, copy) NSString *timer;
@property (nonatomic, assign) BOOL enable;
@property (nonatomic, assign) BOOL isValid;
@property (nonatomic, strong) NSMutableDictionary *propertyValueDic;
@property (nonatomic, strong) NSString *iotId;
@property (nonatomic, strong, nullable) NSString *targets;
@property (nonatomic, assign) NSInteger timezoneOffset;
@end
// 获取本地定时列表数据
id<IMSThingActions> thingObj = [thing getThingActions];
NSMutableArray<IMSLocalTimerModel *> *list = [NSMutableArray array];
[thingObj getPropertiesFull:^(IMSThingActionsResponse * _Nullable response) {
// 回调不在主线程,请注意
NSDictionary *dic = (NSDictionary *)response.dataObject;
NSDictionary *data = dic[@"data"];
for (NSString *key in data){
if ([key isEqualToString:@"LocalTimer"]){
NSDictionary *localTimer = data[key];
NSArray *value = localTimer[@"value"];
for (NSDictionary *item in value) {
IMSLocalTimerModel *timer = [IMSLocalTimerModel new];
timer.isValid = NO;
timer.propertyValueDic = [NSMutableDictionary dictionary];
for (NSString *a in item) {
if ([a isEqualToString:@"IsValid"]) {
NSNumber *isValid = item[a];
timer.isValid = isValid.boolValue;
} else if ([a isEqualToString:@"Enable"]) {
NSNumber *enable = item[a];
timer.enable = enable.boolValue;
} else if ([a isEqualToString:@"Timer"]) {
timer.timer = item[a];
} else if ([a isEqualToString:@"Targets"]){
timer.targets = item[a];
} else if ([a isEqualToString:@"TimezoneOffset"]){
NSNumber *timezoneOffset = item[a];
timer.timezoneOffset = timezoneOffset.integerValue;
} else {
timer.propertyValueDic[a] = item[a];
}
}
if (!timer.isValid) {
timer.propertyValueDic = [NSMutableDictionary dictionary];
timer.timer = @"";
timer.targets = @"";
timer.timezoneOffset = 0;
}
timer.hasTargets = hasTargets;
timer.hasTimezoneOffset = hasTimezoneOffset;
[list addObject:timer];
}
break;
}
}
// 如果get到的属性值的timer个数没有达到tsl要求的定时器数,需要补齐,因为set的时候,tsl要求返回的个数和tsl要求的一样
for (NSInteger i = list.count; i < size; i++) {
IMSLocalTimerModel *timer = [IMSLocalTimerModel new];
timer.isValid = NO;
timer.timer = @"";
timer.iotId = iotId;
timer.hasTargets = hasTargets;
timer.hasTimezoneOffset = hasTimezoneOffset;
timer.propertyValueDic = [NSMutableDictionary dictionary];
timer.modelList = list;
[list addObject:timer];
}
}
- 设置(编辑/创建/删除等)一个定时器。
@implementation IMSLocalTimerModel
// 单个定时转换为json接口,供参考
- (NSMutableDictionary *)toJson{
NSMutableDictionary *json = [NSMutableDictionary dictionary];
[json addEntriesFromDictionary:self.propertyValueDic];
[json addEntriesFromDictionary:@{@"Enable":@(self.enable?1:0),@"IsValid":@(self.isValid?1:0), @"Timer":self.timer?:@""}];
if (self.hasTargets) {
[json addEntriesFromDictionary:@{@"Targets":self.targets?:@""}];
}
if (self.hasTimezoneOffset) {
[json addEntriesFromDictionary:@{@"TimezoneOffset":@(self.timezoneOffset)}];
}
return json;
}
/* 如果 TimezoneOffset 字段存在 这个字段是为了让设备端能获取到app端设置定时时所处时区*/
propertyValues[@"TimezoneOffset"] = @([NSTimeZone localTimeZone].secondsFromGMT);
/*
假设当前设备有3个属性可以通过本地定时进行控制,这3个属性的identifier分别为id1、id2、id3
如果本地定时存在Targets字段,则允许1个定时只控制3个属性中部分属性,假设这个定时是控制属性id1和属性id2,
则 Targets = @"id1, id2"
如果本地定时不存在Targets字段,则要求改定时创建或者编辑的时候,必须对3个属性都设置属性值(定时时间到的是属性需要生效的值)
*/
// list 是属性的 identifier 的列表
- (NSString *)setTargetList:(NSArray<NSString *> *)list{
if (list.count == 0) {
return @"";
}
NSString *targets = list.firstObject;
for (NSUInteger i = 1; i < list.count; i++) {
targets = [targets stringByAppendingString:@","];
targets = [targets stringByAppendingString:list[i]];
}
return targets;
}
propertyValues[@"TimezoneOffset"] = [obj setTargetList:@[id1, id2]];
/* 假设tsl中获取到设备可以设置3个本地定时,则timerList=@[[timer1 toJson], [timer2 toJson], [timer3 toJson]] :
每个定时器的json生成参考 [IMSLocalTimerModel toJson]
[
1. 同时控制属性1和属性2的定时
{"id1":1,"id2":2,"id3":1,"Timer":"0 8 * * *","Enable":1,"IsValid":1, "Targets":"id1, id2", "TimezoneOffset":""},
2. 只控制属性1的定时
{"id1":1,"id2":2,"id3":1,"Timer":"2 6 * * 3","Enable":1,"IsValid":1, "Targets":"id1", "TimezoneOffset":""},
3. 设置一个定时的IsValid为0,则就是删除该定时器,但是这里建议把IsValid 也设置为0,因为设备端的实现可能只认IsValid字段
{"id1":1,"id2":2,"id3":1,"Timer":"2 6 * * 3","Enable":0,"IsValid":0, "Targets":"", "TimezoneOffset":""}
]
*/
[thingActions setProperties:@{@"LocalTimer":timerList} responseHandler:^(IMSThingActionsResponse * _Nullable response) {
}
- 订阅本地定时属性值。
以监听定时的Enable状态为例,设定定时的有效性是一次,当定时超时后,App端会收到定时的Enable属性变为0的通知,并进行界面刷新等相关操作。相应示例代码如下。
// 代码供参考
a.注册订阅
IMSThing *thing = [[IMSThingManager sharedManager] buildThing:controller.iotId];
[thing registerThingObserver:(id<IMSThingObserver>)obj];
b.订阅通知处理
- (void)onPropertyChange:(NSString *)iotId params:(NSDictionary *)params{
IMSDeviceLogDebug(@"onPropertyChange %@ %@", iotId, params);
NSArray *list = params[@"items"][@"LocalTimer"][@"value"];
for (NSInteger i = 0; i < list.count; i++) {
NSDictionary *timer = list[i];
if (i >= self.list.count) {
[self.list addObject:[IMSLocalTimerModel new]];
}
IMSLocalTimerModel *model = self.list[i];
model.timezoneOffset = NO;
model.hasTargets = NO;
model.propertyValueDic = [NSMutableDictionary dictionary];
[timer enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, NSNumber* _Nonnull obj, BOOL * _Nonnull stop) {
if ([key isEqualToString:@"Enable"]) {
model.enable = obj.boolValue;
} else if ([key isEqualToString:@"IsValid"]){
model.isValid = obj.boolValue;
} else if ([key isEqualToString:@"Targets"]){
model.targets = (NSString *)obj;
model.hasTargets = YES;
} else if ([key isEqualToString:@"Timer"]){
model.timer = (NSString *)obj;
} else if ([key isEqualToString:@"TimezoneOffset"]){
model.timezoneOffset = obj.integerValue;
model.hasTimezoneOffset = YES;
} else {
model.propertyValueDic[key] = obj;
}
}];
}
// 刷新界面等
}
c.注销订阅
IMSThing *thing = [[IMSThingManager sharedManager] buildThing:self.iotId];
[thing unregisterThingObserver:self];
Native开发定时功能(Android)
- 创建一个自有App,并完成SDK下载。详细操作请参见创建自有App。
- 获取设备TSL模型和定时属性。
- 创建
com.aliyun.alink.linksdk.tmp.device.panel.PanelDevice
对象。
PanelDevice panelDevice = new PanelDevice(iotId);
panelDevice.init(null, new IPanelCallback() {
@Override
public void onComplete(boolean succeed, Object json) {
// 根据自己的业务逻辑实现相关代码
}
})
- 获取TSL模型。
本地定时是属于TSL的一个属性,更多TSL模型的介绍请参见设备模型SDK。
panelDevice.getTslByCache(new IPanelCallback() {
@Override
public void onComplete(boolean succeed, Object json) {
// 根据自己的业务逻辑实现相关代码
}
});
- 获取设备属性。
panelDevice.getPropertiesByCache(new IPanelCallback() {
@Override
public void onComplete(boolean succeed, @Nullable Object json) {
// 根据自己的业务逻辑实现相关代码
}
}, null);
返回数据中的JSON格式示例如下。
{
"items":{
"LocalTimer": // 以下为定时数据的示例
[{
"LightSwitch": 1,
"ColorTemperature": 2000,
"Timer": "5 4 1,2,3",
"TimezoneOffset": 43200,
"Brightness": 0,
"Enable": 1,
"Targets": "LightSwitch",
"WorkMode": 0,
"IsValid": 1
}]
}
}
- 解析定时数据。
根据TSL模型解析定时数据的方法如下,更详细的数据结构请参见
物模型SDK。
static private void parseLocalTimer(JSONObject dataObj, LocalTimerData localTimerData) {
for (String key : dataObj.keySet()) {
if (key.equals("LocalTimer")) {
JSONArray value = dataObj.getJSONObject(key).getJSONArray("value");
for (int i = 0; i < value.size(); i++) {
JSONObject valueItem = value.getJSONObject(i);
LocalTimer localTimer = new LocalTimer();
for (String valueItemkey : valueItem.keySet()) {
if (valueItemkey.equals("Timer")) {
localTimer.timer = (String) valueItem.get(valueItemkey);
} else if (valueItemkey.equals("Enable")) {
localTimer.enable = (int) valueItem.get(valueItemkey);
} else if (valueItemkey.equals("IsValid")) {
localTimer.valid = (int) valueItem.get(valueItemkey);
} else if (valueItemkey.equals("Targets")) {
localTimer.targets = (String) valueItem.get(valueItemkey);
} else if (valueItemkey.equals("TimezoneOffset")) {
localTimer.timezoneOffset = (int) valueItem.get(valueItemkey);
} else {
localTimer.property.put(valueItemkey, valueItem.get(valueItemkey));
}
}
localTimerData.items.add(localTimer); }
}
}
- 设置一个定时器。
- 单设备
使用PanelDevice#setProperties()
方法更新设备属性。
panelDevice.setProperties(json, new IPanelCallback() {
@Override
public void onComplete(final boolean succeed, final Object json) {
}
});
入参JSON格式示例如下。
{
"iotId":"",
"items":{
"LocalTimer":[ // 以下为定时数据的示例
{
"LightSwitch":1,
"ColorTemperature":2000,
"Timer":"5 4 1,2,3",
"TimezoneOffset":43200,
"Brightness":0,
"Enable":1,
"Targets":"LightSwitch",
"WorkMode":0,
"IsValid":1
}
]
}
}
- 组控设备
使用PanelGroup#setGroupProperties()
方法更新设备属性。
PanelGroup的初始化方法与panelDevice的初始化类似,区别是要传入组控的groupId。
panelGroup.setGroupProperties(json, new IPanelCallback() {
@Override
public void onComplete(final boolean succeed, final Object json) {
}
});
入参JSON格式示例如下。
{
"controlGroupId":"",
"items":{
"LocalTimer":[ // 以下为定时数据的示例
{
"LightSwitch":1,
"ColorTemperature":2000,
"Timer":"5 4 1,2,3",
"TimezoneOffset":43200,
"Brightness":0,
"Enable":1,
"Targets":"LightSwitch",
"WorkMode":0,
"IsValid":1
}
]
}
}
- 订阅本地定时属性值。
实时更新常用来处理多端同时设置设备属性的情况。
// 接口:/app/down/thing/properties 设备端上报属性
panelDevice.subAllEvents(new IPanelEventCallback() {
@Override
public void onNotify(String id, String path, Object json) {
if (!id.equals(iotId)) {
return;
}
if (!"/app/down/thing/properties".equals(path)) {
return;
}
}
}, new IPanelCallback() {
@Override
public void onComplete(boolean b, Object o) {
}
});
返回数据中的JSON格式示例如下。
{
"items":{
"LocalTimer": // 以下为定时数据的示例
[{
"LightSwitch": 1,
"ColorTemperature": 2000,
"Timer": "5 4 1,2,3",
"TimezoneOffset": 43200,
"Brightness": 0,
"Enable": 1,
"Targets": "LightSwitch",
"WorkMode": 0,
"IsValid": 1
}]
}
}
通用说明
- corn 表达式说明(即LocalTimer结构中的Timer字段 )
定时属性中的 CronTrigger 配置完整格式为: [分] [小时] [日] [月] [周]
*
表示所有值。在分钟里表示每一分钟触发。如在小时、日期、月份里面表示每一小时、每一日、每一月。
-
表示区间。小时设置为10-12表示10、11、12点均会触发。
,
表示多个值。 周设置成 2、3、4、5、6 表示在周一至周五工作日会触发。
/
表示递增触发。 5/15表示从第5秒开始,每隔15秒触发。
L
表示最后的意思。 日上表示最后一天。星期上表示星期六或7。 L前加数据,表示该数据的最后一个。 星期上设置6L表示最后一个星期五(6表示星期五)。
W
表示离指定日期最近的工作日触发。15W离该月15号最近的工作日触发。表示每月的第几个周几(6#3表示该月的第三个周五)。
- 时区说明(即LocalTimer结构中的TimeOffset字段 )
由于Android中自带的Calendar对于Daylight Saving Time (DST)
的处理有问题。在需要处理冬令时和夏令时的地区,请使用Java 8提供的Instant类或者其他方法来计算时差。代码示例如下。
private int timezoneOffset() {
try {
Instant instant = Instant.now();
Calendar calendar = new GregorianCalendar();
TimeZone timezone = calendar.getTimeZone();
ZoneId zone = ZoneId.of(timezone.getID());
ZonedDateTime z = instant.atZone(zone);
int offset = z.getOffset().getTotalSeconds();
ALog.d(TAG, "timezoneOffset(): ZoneId:" + timezone.getID() + ", getTotalSeconds: " + offset);
return offset;
} catch (Exception ignored) {
return 0;
}
}
- Targets字段说明
如果在LocalTimer里添加了多个动作, 则必须在Target字段里面添加您本次修改的字段。否则,您必须完整设置所有的动作,本地定时才能正常保存。
{
"LightSwitch":0,
"Timer":"45 12 * * *",
"Enable":0,
"Targets":"LightSwitch",
"IsValid":1
}
在文档使用中是否遇到以下问题
更多建议
匿名提交