本地倒计时功能开发实践
更新时间:2018-09-21 15:40:15
1. 概述
本文提供了一个插座本地倒计时功能的开发案例,开发者可以参考本文,实现任意设备的本地倒计时功能。
“本地倒计时”是指,由App页面向设备端下发“开始倒计时”的任务后,设备端开始根据本地时钟,执行倒计时任务。
要实现本地倒计时的效果,设备端和APP端需要按照本文推荐的方式进行实现。
设备端:按照平台标准数据格式,实现倒计时任务的增删改。
APP端:在公版App中,或平台给自定义App提供的设备界面中,如“灯”“开关”,可以直接使用倒计时功能,如下图:
点击倒计时:
2. 准备工作
控制台中注册您的产品,详细参考 产品注册,本文以插座为例。在 功能定义 中,需要选择标准功能“倒计时”(CountDown),倒计时属性的数据结构:JSON对象
例子:
CountDown: {
IsRunning: 0(bool,0:没有倒计时,1:正在倒计时,),
TimeLeft: 200(int,秒数,最大值为23h59m59s对应的秒数),
PowerSwitch: 0(bool,代表倒计时时间到了后,开关要执行的动作,0为关闭,1为打开)
Timestamp: 12312312321(date,调用设置接口时当时的时间戳,用于同步时间)
}
功能名称:本地倒计时
标识符:CountDown
类型:JSON
参数名称 | 参数标识 | 数据类型 | 值 |
---|---|---|---|
执行状态 | IsRunning | bool | 0 - 已停止1 - 执行中 |
剩余时间 | TimeLeft | int | min: 1max: 86399step: 1unit: 秒/s |
开关动作 | PowerSwitch | bool | 0 - 关闭1 - 打开 |
当前时间戳 | Timestamp | date | string类型的UTC时间戳 |
3. 设计原理
如果实现本地倒计时:
APP上设定时间,点击开始倒计时,会将上面所述的CountDown通过云端下发到设备端;
设备端收到CountDown后,根据TimeLeft和PowerSwitch的内容,开始执行倒计时,并且在倒计时时间到了之后,执行PowerSwitch中的开关动作;
倒计时的动作执行后,将下发时的CountDown中的IsRunning设置为0,再将CountDown上报到云端(不用修改CountDown中除IsRunning外的其他字段);
若设备中途断电,在上电后,需要将CountDown中的IsRunning设置为0,然后上报CountDown到云端。
如何取消本地倒计时功能:
将之前收到的CountDown中的IsRunning设置为0下行到设备端
设备端会回复CountDown(IsRunning=0)至云端
一些说明:
APP设置了一个200s后关闭电源的动作,set时候传的数据应为:
CountDown: {
IsRunning: 1(bool,0:没有倒计时,1:正在倒计时,),
TimeLeft: 200(int,秒数,最大值为23h59m59s对应的秒数),
PowerSwitch: 0(bool,代表倒计时时间到了后,开关要执行的动作,0为关闭,1为打开)
Timestamp: 1537496418274(date,调用设置接口时当时的本地时间戳,用于同步时间)
}APP收到set成功的返回响应后,跳转到开始倒计时的页面,开始正常倒计时
此时若退出APP,50s后再打开APP进入设备控制界面或本地倒计时插件,那么此时显示的剩余时间应为:
200 - (当前本地时间戳 - set时候传递的本地时间戳)= 200 - 50 = 150s
4. 设备端开发
以下时间案例中,设备每次收到云端下行的指令会立即回复CountDown做出响应,同时创建一个绑定至定时器服务函数app_timer_expired_handle()的定时器,等到本地倒计时时间耗尽时,该定时器服务函数会自动调用,CountDown属性中的IsRunning修改成0上行至云端,同时上报设备PowerSwitch属性,即宣告本地倒计时完成。
/*
* the handler of property changed
* alink method: thing.service.property.set
*/
static int thing_prop_changed(const void *thing_id, const char *property, void *ctx)
{
int ret = -1;
app_context_t *app_ctx = (app_context_t *)ctx;
APP_TRACE("property \"%s\" changed", property);
if (strstr(property, app_ctx->prop_countdown) != 0) {
/* post property
* result is response_id; if response_id = -1, it is fail, else it is success.
* response_id by be compare in post_property_cb.
*/
int powerSwitch[1] = {0};
int timeLeft[1] = {0};
int isRunning[1] = {0};
/* Get property value of "CountDown" */
linkkit_get_value(linkkit_method_get_property_value, thing_id, app_ctx->prop_countdown_pwrsw, powerSwitch, NULL);
APP_TRACE("app get property value, PowerSwitch = %d", powerSwitch[0]);
linkkit_get_value(linkkit_method_get_property_value, thing_id, app_ctx->prop_countdown_timelf, timeLeft, NULL);
APP_TRACE("app get property value, TimeLeft = %d", timeLeft[0]);
linkkit_get_value(linkkit_method_get_property_value, thing_id, app_ctx->prop_countdown_isrun, isRunning, NULL);
APP_TRACE("app get property value, IsRunning = %d", isRunning[0]);
/* Start or stop timer according to "IsRunning" */
if (isRunning[0] == 1) {
app_timer_start(app_ctx->timerHandle, timeLeft[0]);
/* temp powerswitch value to app context */
app_ctx->powerSwitch_Target = powerSwitch[0];
} else if (isRunning[0] == 0) {
app_timer_stop(app_ctx->timerHandle);
}
/* Post property "CountDown" to response request from cloud*/
ret = linkkit_post_property(thing_id, property, post_property_cb);
if (ret != SUCCESS_RETURN) {
APP_TRACE("app post property \"%s\" failed", property);
return ret;
}
APP_TRACE("app post property \"%s\" succeed", property);
/* Set property "PowerSwitch" then post */
ret = linkkit_set_value(linkkit_method_set_property_value, app_ctx->thing, app_ctx->prop_powerswitch,
&app_ctx->powerSwitch_Actual, NULL);
if (ret != SUCCESS_RETURN) {
APP_TRACE("app set property \"%s\" failed", app_ctx->prop_powerswitch);
return ret;
}
ret = linkkit_post_property(app_ctx->thing, app_ctx->prop_powerswitch, post_property_cb);
if (ret != SUCCESS_RETURN) {
APP_TRACE("app post property \"%s\" failed", app_ctx->prop_powerswitch);
return ret;
}
APP_TRACE("app post property \"%s\" succeed", app_ctx->prop_powerswitch);
} else if (strstr(property, app_ctx->prop_powerswitch) != 0) {
int powerSwitch[1] = {0};
/* Get property value of "PowerSwitch" */
linkkit_get_value(linkkit_method_get_property_value, thing_id, app_ctx->prop_powerswitch, powerSwitch, NULL);
APP_TRACE("app get property value, PowerSwitch = %d", powerSwitch[0]);
/* Trigger powerswitch action, just set value here */
app_ctx->powerSwitch_Actual = powerSwitch[0];
/* Set property "PowerSwitch" then post */
ret = linkkit_set_value(linkkit_method_set_property_value, app_ctx->thing, app_ctx->prop_powerswitch,
&app_ctx->powerSwitch_Actual, NULL);
if (ret != SUCCESS_RETURN) {
APP_TRACE("app set property \"%s\" failed", app_ctx->prop_powerswitch);
return ret;
}
ret = linkkit_post_property(app_ctx->thing, app_ctx->prop_powerswitch, post_property_cb);
if (ret != SUCCESS_RETURN) {
APP_TRACE("app post property \"%s\" failed", app_ctx->prop_powerswitch);
return ret;
}
APP_TRACE("app post property \"%s\" succeed", app_ctx->prop_powerswitch);
}
return 0;
}