1 /*
2 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3 */
4
5 #include "amp_platform.h"
6 #include "aos_system.h"
7 #include "amp_defines.h"
8 #include "amp_task.h"
9 #include "aos/kv.h"
10 #include "aiot_sysdep_api.h"
11 #include "aiot_mqtt_api.h"
12 #include "aiot_dm_api.h"
13 #include "aiot_state_api.h"
14 #include "quickjs.h"
15 #include "ota_agent.h"
16 #include "module_aiot.h"
17 #include "app_upgrade.h"
18 #include "quickjs_addon_common.h"
19
20 #define MOD_STR "AIOT"
21
22 #define OTA_MODE_NAME "system"
23
24 static char g_iot_close_flag = 0;
25 static char g_iot_conn_flag = 0;
26 static char g_iot_clean_flag = 0;
27 static aos_sem_t g_iot_close_sem = NULL;
28 JSClassID js_aiot_device_class_id;
29
30 ota_service_t g_aiot_device_appota_service;
31 static ota_store_module_info_t module_info[3];
32
33 // extern const char *aos_userjs_version_get(void);
34
35 typedef struct {
36 iot_device_handle_t *iot_device_handle;
37 char *topic;
38 char *payload;
39 char *service_id;
40 char *params;
41 JSValue js_cb_ref;
42 int ret_code;
43 int topic_len;
44 int payload_len;
45 int params_len;
46 int dm_recv;
47 uint64_t msg_id;
48 aiot_mqtt_option_t option;
49 aiot_mqtt_event_type_t event_type;
50 aiot_mqtt_recv_type_t recv_type;
51 aiot_dm_recv_type_t dm_recv_type;
52 aiot_dev_jscallback_type_t cb_type;
53 } device_notify_param_t;
54
__amp_strdup(char * src)55 static char *__amp_strdup(char *src)
56 {
57 char *dst;
58 size_t len = 0;
59
60 if (src == NULL) {
61 return NULL;
62 }
63
64 len = strlen(src);
65
66 dst = amp_malloc(len + 1);
67 if (dst == NULL) {
68 return NULL;
69 }
70
71 memcpy(dst, src, len);
72 dst[len] = '\0';
73 return dst;
74 }
75
aiot_device_notify(void * pdata)76 static void aiot_device_notify(void *pdata)
77 {
78 device_notify_param_t *param = (device_notify_param_t *)pdata;
79
80 JSContext *ctx = js_get_context();
81 JSValue obj = JS_NewObject(ctx);
82
83 if (param->dm_recv) {
84 switch (param->dm_recv_type) {
85 case AIOT_DMRECV_PROPERTY_SET:
86 amp_error(MOD_STR, "AIOT_DMRECV_PROPERTY_SET CASE");
87 JS_SetPropertyStr(ctx, obj, "msg_id", JS_NewInt32(ctx, param->msg_id));
88 JS_SetPropertyStr(ctx, obj, "params_len", JS_NewInt32(ctx, param->params_len));
89 JS_SetPropertyStr(ctx, obj, "params", JS_NewStringLen(ctx, param->params,param->params_len));
90 amp_free(param->params);
91 break;
92 case AIOT_DMRECV_ASYNC_SERVICE_INVOKE:
93 amp_error(MOD_STR, "AIOT_DMRECV_ASYNC_SERVICE_INVOKE CASE");
94 JS_SetPropertyStr(ctx, obj, "msg_id", JS_NewInt32(ctx, param->msg_id));
95 JS_SetPropertyStr(ctx, obj, "service_id", JS_NewString(ctx, param->service_id));
96 JS_SetPropertyStr(ctx, obj, "params_len", JS_NewInt32(ctx, param->params_len));
97 JS_SetPropertyStr(ctx, obj, "params", JS_NewStringLen(ctx, param->params,param->params_len));
98 amp_free(param->service_id);
99 amp_free(param->params);
100 break;
101 default:
102 amp_free(param);
103 return;
104 }
105 } else if (param->option == AIOT_MQTTOPT_EVENT_HANDLER) {
106 switch (param->event_type) {
107 case AIOT_MQTTEVT_CONNECT:
108 case AIOT_MQTTEVT_RECONNECT:
109 case AIOT_MQTTEVT_DISCONNECT:
110 JS_SetPropertyStr(ctx, obj, "code", JS_NewInt32(ctx, param->ret_code));
111 JSValue aiot = JS_NewObjectClass(ctx, js_aiot_device_class_id);
112 aos_printf("iot linsdk handle %p\n", param->iot_device_handle);
113 JS_SetOpaque(aiot, param->iot_device_handle);
114 JS_SetPropertyStr(ctx, obj, "handle", aiot);
115 break;
116 default:
117 amp_free(param);
118 return;
119 }
120 } else {
121 switch (param->recv_type) {
122 case AIOT_MQTTRECV_PUB:
123 JS_SetPropertyStr(ctx, obj, "code", JS_NewInt32(ctx, param->ret_code));
124 JS_SetPropertyStr(ctx, obj, "handle", JS_NewInt32(ctx, param->iot_device_handle));
125 JS_SetPropertyStr(ctx, obj, "topic", JS_NewStringLen(ctx,param->topic, param->topic_len));
126 JS_SetPropertyStr(ctx, obj, "payload", JS_NewStringLen(ctx, param->payload,param->payload_len));
127 amp_free(param->topic);
128 amp_free(param->payload);
129 break;
130 default:
131 amp_free(param);
132 return;
133 }
134 }
135
136 JSValue val = JS_Call(ctx, param->js_cb_ref, JS_UNDEFINED, 1, &obj);
137 JS_FreeValue(ctx, val);
138 JS_FreeValue(ctx, obj);
139
140 amp_free(param);
141 }
142
143 /* 用户数据接收处理回调函数 */
aiot_app_dm_recv_handler(void * dm_handle,const aiot_dm_recv_t * recv,void * userdata)144 static void aiot_app_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
145 {
146 iot_device_handle_t *iot_device_handle = (iot_device_handle_t *)userdata;
147 device_notify_param_t *param;
148 JSContext *ctx = js_get_context();
149
150 param = amp_malloc(sizeof(device_notify_param_t));
151 if (!param) {
152 amp_error(MOD_STR, "alloc device_notify_param_t fail");
153 return;
154 }
155 memset(param, 0, sizeof(device_notify_param_t));
156 param->dm_recv = 1;
157 param->dm_recv_type = recv->type;
158 param->iot_device_handle = iot_device_handle;
159
160 switch (recv->type) {
161
162 /* 属性上报, 事件上报, 获取期望属性值或者删除期望属性值的应答 */
163 case AIOT_DMRECV_GENERIC_REPLY: {
164 // printf("msg_id = %d, code = %d, data = %.*s, message = %.*s\r\n",
165 // recv->data.generic_reply.msg_id,
166 // recv->data.generic_reply.code,
167 // recv->data.generic_reply.data_len,
168 // recv->data.generic_reply.data,
169 // recv->data.generic_reply.message_len,
170 // recv->data.generic_reply.message);
171 goto exit;
172 }
173 break;
174
175 /* 属性设置 */
176 case AIOT_DMRECV_PROPERTY_SET: {
177 amp_debug(MOD_STR, "msg_id = %ld, params = %.*s",
178 (unsigned long)recv->data.property_set.msg_id,
179 recv->data.property_set.params_len,
180 recv->data.property_set.params);
181
182 param->js_cb_ref = iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_ONPROPS_REF];
183 param->msg_id = recv->data.property_set.msg_id;
184 param->params_len = recv->data.property_set.params_len;
185 param->params = __amp_strdup(recv->data.property_set.params);
186 param->cb_type = AIOT_DEV_JSCALLBACK_ONPROPS_REF;
187
188 /* TODO: 以下代码演示如何对来自云平台的属性设置指令进行应答, 用户可取消注释查看演示效果 */
189 /*
190 {
191 aiot_dm_msg_t msg;
192
193 memset(&msg, 0, sizeof(aiot_dm_msg_t));
194 msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY;
195 msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id;
196 msg.data.property_set_reply.code = 200;
197 msg.data.property_set_reply.data = "{}";
198 int32_t res = aiot_dm_send(dm_handle, &msg);
199 if (res < 0) {
200 printf("aiot_dm_send failed\r\n");
201 }
202 }
203 */
204 }
205 break;
206
207 /* 异步服务调用 */
208 case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
209 amp_debug(MOD_STR, "msg_id = %ld, service_id = %s, params = %.*s",
210 (unsigned long)recv->data.async_service_invoke.msg_id,
211 recv->data.async_service_invoke.service_id,
212 recv->data.async_service_invoke.params_len,
213 recv->data.async_service_invoke.params);
214
215 param->js_cb_ref = iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_ONSERVICE_REF];
216 param->msg_id = recv->data.async_service_invoke.msg_id;
217 param->params_len = recv->data.async_service_invoke.params_len;
218 param->service_id = __amp_strdup(recv->data.async_service_invoke.service_id);
219 param->params = __amp_strdup(recv->data.async_service_invoke.params);
220 param->cb_type = AIOT_DEV_JSCALLBACK_ONSERVICE_REF;
221
222 /* TODO: 以下代码演示如何对来自云平台的异步服务调用进行应答, 用户可取消注释查看演示效果
223 *
224 * 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
225 */
226
227 /*
228 {
229 aiot_dm_msg_t msg;
230
231 memset(&msg, 0, sizeof(aiot_dm_msg_t));
232 msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY;
233 msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id;
234 msg.data.async_service_reply.code = 200;
235 msg.data.async_service_reply.service_id = "ToggleLightSwitch";
236 msg.data.async_service_reply.data = "{\"dataA\": 20}";
237 int32_t res = aiot_dm_send(dm_handle, &msg);
238 if (res < 0) {
239 printf("aiot_dm_send failed\r\n");
240 }
241 }
242 */
243 }
244 break;
245
246 /* 同步服务调用 */
247 case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
248 amp_debug(MOD_STR, "msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s",
249 (unsigned long)recv->data.sync_service_invoke.msg_id,
250 recv->data.sync_service_invoke.rrpc_id,
251 recv->data.sync_service_invoke.service_id,
252 recv->data.sync_service_invoke.params_len,
253 recv->data.sync_service_invoke.params);
254
255 /* TODO: 以下代码演示如何对来自云平台的同步服务调用进行应答, 用户可取消注释查看演示效果
256 *
257 * 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id和rrpc_id字符串, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
258 */
259
260 /*
261 {
262 aiot_dm_msg_t msg;
263
264 memset(&msg, 0, sizeof(aiot_dm_msg_t));
265 msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY;
266 msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id;
267 msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id;
268 msg.data.sync_service_reply.code = 200;
269 msg.data.sync_service_reply.service_id = "SetLightSwitchTimer";
270 msg.data.sync_service_reply.data = "{}";
271 int32_t res = aiot_dm_send(dm_handle, &msg);
272 if (res < 0) {
273 printf("aiot_dm_send failed\r\n");
274 }
275 }
276 */
277 goto exit;
278 }
279 break;
280
281 /* 下行二进制数据 */
282 case AIOT_DMRECV_RAW_DATA: {
283 amp_debug(MOD_STR, "raw data len = %d", recv->data.raw_data.data_len);
284 /* TODO: 以下代码演示如何发送二进制格式数据, 若使用需要有相应的数据透传脚本部署在云端 */
285 /*
286 {
287 aiot_dm_msg_t msg;
288 uint8_t raw_data[] = {0x01, 0x02};
289
290 memset(&msg, 0, sizeof(aiot_dm_msg_t));
291 msg.type = AIOT_DMMSG_RAW_DATA;
292 msg.data.raw_data.data = raw_data;
293 msg.data.raw_data.data_len = sizeof(raw_data);
294 aiot_dm_send(dm_handle, &msg);
295 }
296 */
297 goto exit;
298 }
299 break;
300
301 /* 二进制格式的同步服务调用, 比单纯的二进制数据消息多了个rrpc_id */
302 case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: {
303 amp_debug(MOD_STR, "raw sync service rrpc_id = %s, data_len = %d",
304 recv->data.raw_service_invoke.rrpc_id,
305 recv->data.raw_service_invoke.data_len);
306 goto exit;
307 }
308 break;
309
310 default:
311 goto exit;
312 break;
313 }
314
315 amp_task_schedule_call(aiot_device_notify, param);
316 return;
317 exit:
318 amp_free(param);
319 }
320
321
aiot_connect_event(iot_device_handle_t * iot_device_handle)322 static void aiot_connect_event(iot_device_handle_t *iot_device_handle)
323 {
324 int res = 0;
325
326 if (iot_device_handle->dm_handle != NULL) {
327 amp_warn(MOD_STR, "dm_handle is already initialized");
328 return;
329 }
330
331 /* device model service */
332 iot_device_handle->dm_handle = aiot_dm_init();
333 if (iot_device_handle->dm_handle == NULL) {
334 amp_debug(MOD_STR, "aiot_dm_init failed");
335 return;
336 }
337
338 /* 配置MQTT实例句柄 */
339 aiot_dm_setopt(iot_device_handle->dm_handle, AIOT_DMOPT_MQTT_HANDLE, iot_device_handle->mqtt_handle);
340 /* 配置消息接收处理回调函数 */
341 aiot_dm_setopt(iot_device_handle->dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)aiot_app_dm_recv_handler);
342 /* 配置回调函数参数 */
343 aiot_dm_setopt(iot_device_handle->dm_handle, AIOT_DMOPT_USERDATA, iot_device_handle);
344
345 /* app device active info report */
346 res = amp_app_devinfo_report(iot_device_handle->mqtt_handle);
347 if (res < STATE_SUCCESS) {
348 amp_debug(MOD_STR, "device active info report failed");
349 }
350 }
351
aiot_mqtt_message_cb(iot_mqtt_message_t * message,void * userdata)352 static void aiot_mqtt_message_cb(iot_mqtt_message_t *message, void *userdata)
353 {
354 iot_mqtt_userdata_t *udata = (iot_mqtt_userdata_t *)userdata;
355 device_notify_param_t *param;
356
357 if (!message || !udata)
358 return;
359
360 amp_error(MOD_STR, "aiot_mqtt_message_cb IS CALLED");
361
362 param = amp_malloc(sizeof(device_notify_param_t));
363 if (!param) {
364 amp_error(MOD_STR, "alloc device notify param fail");
365 return;
366 }
367 memset(param, 0, sizeof(device_notify_param_t));
368 param->iot_device_handle = (iot_device_handle_t *)udata->handle;
369 param->option = message->option;
370 param->js_cb_ref = param->iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_CREATE_DEV_REF];
371 param->cb_type = AIOT_DEV_JSCALLBACK_CREATE_DEV_REF;
372
373 if (message->option == AIOT_MQTTOPT_EVENT_HANDLER) {
374 switch (message->event.type) {
375 case AIOT_MQTTEVT_CONNECT:
376 case AIOT_MQTTEVT_RECONNECT:
377 aiot_connect_event(param->iot_device_handle);
378 case AIOT_MQTTEVT_DISCONNECT:
379 param->ret_code = message->event.code;
380 param->event_type = message->event.type;
381 break;
382 default:
383 amp_free(param);
384 return;
385 }
386 } else if (message->option == AIOT_MQTTOPT_RECV_HANDLER) {
387 switch (message->recv.type) {
388 case AIOT_MQTTRECV_PUB:
389 param->ret_code = message->recv.code;
390 param->topic_len = message->recv.topic_len;
391 param->payload_len = message->recv.payload_len;
392 param->topic = __amp_strdup(message->recv.topic);
393 param->payload = __amp_strdup(message->recv.payload);
394 param->recv_type = message->recv.type;
395 break;
396 default:
397 amp_free(param);
398 return;
399 }
400 } else {
401 amp_free(param);
402 return;
403 }
404
405 amp_task_schedule_call(aiot_device_notify, param);
406 }
407
aiot_device_connect(void * pdata)408 static void aiot_device_connect(void *pdata)
409 {
410 int res = -1;
411 char current_amp_ver[64];
412 ota_service_t *ota_svc = &g_aiot_device_appota_service;
413 iot_device_handle_t *iot_device_handle = (iot_device_handle_t *)pdata;
414 iot_mqtt_userdata_t *userdata;
415 JSContext *ctx = js_get_context();
416
417 uint16_t keepaliveSec = 0;
418
419 if (!iot_device_handle) {
420 amp_error(MOD_STR, "mqtt client hadnle null");
421 return;
422 }
423
424 keepaliveSec = iot_device_handle->keepaliveSec;
425
426 userdata = amp_malloc(sizeof(iot_mqtt_userdata_t));
427 if (!userdata) {
428 amp_error(MOD_STR, "alloc mqtt userdata fail");
429 return;
430 }
431 memset(userdata, 0, sizeof(iot_mqtt_userdata_t));
432
433 userdata->callback = aiot_mqtt_message_cb;
434 userdata->handle = iot_device_handle;
435
436 res = aiot_mqtt_client_start(&iot_device_handle->mqtt_handle, keepaliveSec, userdata);
437 if (res < STATE_SUCCESS) {
438 amp_error(MOD_STR, "mqtt client create failed");
439 amp_free(userdata);
440 amp_free(iot_device_handle);
441 return;
442 }
443
444 g_iot_conn_flag = 1;
445 iot_device_handle->res = res;
446
447 while (!g_iot_close_flag){
448 aos_msleep(1000);
449 }
450
451 aiot_mqtt_client_stop(&iot_device_handle->mqtt_handle);
452
453 for (int i = 0; i < AIOT_DEV_JSCALLBACK_INVALID_CODE; i++) {
454 if (iot_device_handle->js_cb_ref[i] != JS_UNDEFINED) {
455 JS_FreeValue(ctx, iot_device_handle->js_cb_ref[i]);
456 }
457 }
458
459 amp_free(userdata);
460 amp_free(iot_device_handle);
461 g_iot_conn_flag = 0;
462 aos_sem_signal(&g_iot_close_sem);
463 aos_task_exit(0);
464 return;
465 }
466
467 /*************************************************************************************
468 * Function: native_aiot_create_device
469 * Description: js native addon for UDP.createSocket();
470 * Called by: js api
471 * Input: none
472 * Output: return socket fd when create socket success,
473 * return error number when create socket fail
474 **************************************************************************************/
native_aiot_create_device(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)475 static JSValue native_aiot_create_device(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
476 {
477 int res = -1;
478 void *mqtt_handle = NULL;
479 const char *productKey;
480 const char *productSecret;
481 const char *deviceName;
482 const char *deviceSecret;
483 uint32_t keepaliveSec = 0;
484 JSValue js_cb_ref;
485 aos_task_t iot_device_task;
486 iot_device_handle_t *iot_device_handle = NULL;
487 ota_service_t *ota_svc = &g_aiot_device_appota_service;
488
489 /* check paramters */
490 JSValue options = argv[0];
491 JSValue cb = argv[1];
492 if (!JS_IsObject(options) || !JS_IsFunction(ctx, cb)) {
493 amp_warn(MOD_STR, "parameter must be object and function\n");
494 res = -1;
495 goto out;
496 }
497
498 if (g_iot_clean_flag) {
499 amp_warn(MOD_STR, "module source clean, ignore");
500 goto out;
501 }
502
503 /* get device certificate */
504
505 JSValue j_productKey = JS_GetPropertyStr(ctx, argv[0], "productKey");
506 JSValue j_deviceName = JS_GetPropertyStr(ctx, argv[0], "deviceName");
507 JSValue j_deviceSecret = JS_GetPropertyStr(ctx, argv[0], "deviceSecret");
508 JSValue j_keepaliveSec = JS_GetPropertyStr(ctx, argv[0], "keepaliveSec");
509
510 if(!JS_IsString(j_productKey) || !JS_IsString(j_deviceName) ||
511 !JS_IsString(j_deviceSecret) || !JS_IsNumber(j_keepaliveSec)){
512 amp_warn(MOD_STR, "Parameter invalid");
513 res = -2;
514 goto out;
515 }
516
517 productKey = JS_ToCString(ctx, j_productKey);
518 deviceName = JS_ToCString(ctx, j_deviceName);
519 deviceSecret = JS_ToCString(ctx,j_deviceSecret);
520 JS_ToUint32(ctx, &keepaliveSec, j_keepaliveSec);
521 amp_info(MOD_STR, "productKey=%s deviceName=%s deviceSecret=%s keepaliveSec=%d\n",productKey,deviceName,deviceSecret,keepaliveSec);
522
523 memset(ota_svc->pk, 0, sizeof(ota_svc->pk));
524 memset(ota_svc->dn, 0, sizeof(ota_svc->dn));
525 memset(ota_svc->ds, 0, sizeof(ota_svc->ds));
526 memcpy(ota_svc->pk, productKey, strlen(productKey));
527 memcpy(ota_svc->dn, deviceName, strlen(deviceName));
528 memcpy(ota_svc->ds, deviceSecret, strlen(deviceSecret));
529
530 aos_kv_set(AMP_CUSTOMER_PRODUCTKEY, productKey, IOTX_PRODUCT_KEY_LEN, 1);
531 aos_kv_set(AMP_CUSTOMER_DEVICENAME, deviceName, IOTX_DEVICE_NAME_LEN, 1);
532 aos_kv_set(AMP_CUSTOMER_DEVICESECRET, deviceSecret, IOTX_DEVICE_SECRET_LEN, 1);
533
534 js_cb_ref = JS_DupValue(ctx, cb);
535
536 iot_device_handle = (iot_device_handle_t *)amp_malloc(sizeof(iot_device_handle_t));
537 if (!iot_device_handle) {
538 amp_error(MOD_STR, "allocate memory failed\n");
539 goto out;
540 }
541 memset(iot_device_handle, 0, sizeof(iot_device_handle_t));
542
543 for (int i = 0; i < AIOT_DEV_JSCALLBACK_INVALID_CODE; i++) {
544 iot_device_handle->js_cb_ref[i] = JS_UNDEFINED;
545 }
546
547 iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_CREATE_DEV_REF] = js_cb_ref;
548 iot_device_handle->keepaliveSec = keepaliveSec;
549
550 res = aos_task_new_ext(&iot_device_task, "amp aiot device task", aiot_device_connect, iot_device_handle, 1024 * 10, AOS_DEFAULT_APP_PRI);
551 if (res != STATE_SUCCESS) {
552 amp_warn(MOD_STR, "iot create task failed");
553 JS_FreeValue(ctx, js_cb_ref);
554 amp_free(iot_device_handle);
555 goto out;
556 }
557
558 out:
559 if(productKey != NULL){
560 JS_FreeCString(ctx, productKey);
561 }
562 if(deviceName != NULL){
563 JS_FreeCString(ctx, deviceName);
564 }
565 if(deviceSecret != NULL){
566 JS_FreeCString(ctx, deviceSecret);
567 }
568
569 if (iot_device_handle != NULL) {
570 JSValue obj;
571 obj = JS_NewObjectClass(ctx, js_aiot_device_class_id);
572 JS_SetOpaque(obj, (void *)iot_device_handle);
573 return obj;
574 }
575
576 return JS_NewInt32(ctx, res);
577 }
578
native_aiot_get_ntp_time(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)579 static JSValue native_aiot_get_ntp_time(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
580 {
581 int res = -1;
582 iot_device_handle_t *iot_device_handle = NULL;
583 amp_debug(MOD_STR, "native_aiot_get_ntp_time called");
584 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
585 if (!iot_device_handle) {
586 amp_warn(MOD_STR, "parameter must be handle");
587 goto out;
588 }
589 JSValue js_cb = JS_DupValue(ctx, argv[0]);
590 res = aiot_amp_ntp_service(iot_device_handle->mqtt_handle, js_cb);
591 if (res < STATE_SUCCESS) {
592 amp_error(MOD_STR, "device dynmic register failed");
593 }
594 out:
595 return JS_NewInt32(ctx, res);
596 }
597
598 /* dynmic register */
native_aiot_dynreg(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)599 static JSValue native_aiot_dynreg(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
600 {
601 int res = -1;
602
603 char *productKey = NULL;
604 char *deviceName = NULL;
605 char *productSecret = NULL;
606
607 /* check paramters */
608 JSValue options = argv[0];
609 JSValue cb = argv[1];
610 if (!JS_IsObject(options) || !JS_IsFunction(ctx, cb)) {
611 amp_warn(MOD_STR, "parameter must be object and function\n");
612 res = -1;
613 goto out;
614 }
615
616 if (g_iot_clean_flag) {
617 amp_warn(MOD_STR, "module source clean, ignore");
618 goto out;
619 }
620
621 /* get device certificate */
622 JSValue j_productKey = JS_GetPropertyStr(ctx, argv[0], "productKey");
623 JSValue j_deviceName = JS_GetPropertyStr(ctx, argv[0], "deviceName");
624 JSValue j_productSecret = JS_GetPropertyStr(ctx, argv[0], "productSecret");
625
626 if (!JS_IsString(j_productKey) || !JS_IsString(j_deviceName) ||
627 !JS_IsString(j_productSecret)) {
628 amp_warn(MOD_STR, "Parameter invalid");
629 res = -2;
630 goto out;
631 }
632
633 productKey = JS_ToCString(ctx, j_productKey);
634 deviceName = JS_ToCString(ctx, j_deviceName);
635 productSecret = JS_ToCString(ctx, j_productSecret);
636
637 aos_kv_set(AMP_CUSTOMER_PRODUCTKEY, productKey, IOTX_PRODUCT_KEY_LEN, 1);
638 aos_kv_set(AMP_CUSTOMER_DEVICENAME, deviceName, IOTX_DEVICE_NAME_LEN, 1);
639 aos_kv_set(AMP_CUSTOMER_PRODUCTSECRET, productSecret, IOTX_PRODUCT_SECRET_LEN, 1);
640
641 JSValue js_cb_ref = JS_DupValue(ctx, argv[1]);
642
643 res = aiot_dynreg_http(&js_cb_ref);
644 aos_printf("native_aiot_dynreg res is %d\n", res);
645 if (res < STATE_SUCCESS) {
646 amp_debug(MOD_STR, "device dynmic register failed");
647 }
648
649 out:
650 JS_FreeValue(ctx, j_productKey);
651 JS_FreeValue(ctx, j_deviceName);
652 JS_FreeValue(ctx, j_productSecret);
653 JS_FreeCString(ctx, productKey);
654 JS_FreeCString(ctx, deviceName);
655 JS_FreeCString(ctx, productSecret);
656 aos_printf("res is %d\n", res);
657 return JS_NewInt32(ctx, res);
658 }
659
660 /* post event */
native_aiot_postEvent(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)661 static JSValue native_aiot_postEvent(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
662 {
663 int res = -1;
664 iot_device_handle_t *iot_device_handle = NULL;
665 char *event_id = NULL;
666 char *event_payload = NULL;
667
668 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
669 if (!iot_device_handle) {
670 amp_warn(MOD_STR, "parameter must be handle");
671 goto out;
672 }
673 JSValue id = JS_GetPropertyStr(ctx, argv[0], "id");
674 if (JS_IsString(id)) {
675 event_id = JS_ToCString(ctx, id);
676 }
677
678 JSValue params = JS_GetPropertyStr(ctx, argv[0], "params");
679 JSValue jjson = JS_UNDEFINED;
680 if (!JS_IsUndefined(params)) {
681 if (JS_VALUE_GET_TAG(params) != JS_TAG_STRING) {
682 jjson = JS_JSONStringify(ctx, params, JS_NULL, JS_NULL);
683 event_payload = JS_ToCString(ctx, jjson);
684 } else {
685 event_payload = JS_ToCString(ctx, params);
686 }
687 }
688
689 JS_FreeValue(ctx, params);
690 amp_debug(MOD_STR, "native_aiot_postEvent event_id=%s event_payload=%s\n",event_id,event_payload);
691
692 /* callback */
693 // iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_POST_EVENT_REF] = JS_DupValue(ctx, argv[1]);
694
695 res = aiot_app_send_event_post(iot_device_handle->dm_handle, event_id, event_payload);
696 if (res < STATE_SUCCESS) {
697 amp_warn(MOD_STR, "post event failed!");
698 }
699
700 if (event_id != NULL) {
701 JS_FreeCString(ctx, event_id);
702 }
703 if (event_payload != NULL) {
704 JS_FreeCString(ctx, event_payload);
705 }
706 if (!JS_IsUndefined(jjson)) {
707 JS_FreeValue(ctx, jjson);
708 }
709 JS_FreeValue(ctx, id);
710
711 out:
712 return JS_NewInt32(ctx, res);
713 }
714
715 /* post props */
native_aiot_postProps(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)716 static JSValue native_aiot_postProps(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
717 {
718 int res = -1;
719 iot_device_handle_t *iot_device_handle = NULL;
720 char *payload_str= NULL;
721
722 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
723 if (!iot_device_handle) {
724 amp_warn(MOD_STR, "parameter must be handle");
725 goto out;
726 }
727 JSValue payload = argv[0];
728
729 JSValue jjson = JS_UNDEFINED;
730 if (!JS_IsUndefined(payload)) {
731 if (JS_VALUE_GET_TAG(payload) != JS_TAG_STRING) {
732 jjson = JS_JSONStringify(ctx, payload, JS_NULL, JS_NULL);
733 payload_str = JS_ToCString(ctx, jjson);
734 } else {
735 payload_str = JS_ToCString(ctx, payload);
736 }
737 }
738
739 // iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_POST_PROPS_REF] = JS_DupValue(ctx, argv[1]);
740
741 res = aiot_app_send_property_post(iot_device_handle->dm_handle, payload_str);
742 if (res < STATE_SUCCESS) {
743 amp_warn(MOD_STR, "property post failed, res:%d", res);
744 }
745
746 JS_FreeCString(ctx, payload_str);
747 if (!JS_IsUndefined(jjson)) {
748 JS_FreeValue(ctx, jjson);
749 }
750
751 out:
752 return JS_NewInt32(ctx, res);
753 }
754
755 /* onprops */
native_aiot_onProps(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)756 static JSValue native_aiot_onProps(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
757 {
758 int res = STATE_SUCCESS;
759 iot_device_handle_t *iot_device_handle = NULL;
760
761 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
762
763 if (!iot_device_handle) {
764 amp_warn(MOD_STR, "parameter must be handle");
765 goto out;
766 }
767
768 JSValue js_cb_ref = JS_DupValue(ctx, argv[0]);
769
770 iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_ONPROPS_REF] = js_cb_ref;
771
772 out:
773 return JS_NewInt32(ctx, res);
774 }
775
776 /* onService */
native_aiot_onService(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)777 static JSValue native_aiot_onService(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
778 {
779 int res = STATE_SUCCESS;
780 iot_device_handle_t *iot_device_handle = NULL;
781
782 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
783 if (!iot_device_handle) {
784 amp_warn(MOD_STR, "parameter must be handle");
785 goto out;
786 }
787
788 iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_ONSERVICE_REF] = JS_DupValue(ctx, argv[0]);
789
790 out:
791 return JS_NewInt32(ctx, res);
792 }
793
794 /* publish */
native_aiot_publish(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)795 static JSValue native_aiot_publish(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
796 {
797 int res = -1;
798 iot_device_handle_t *iot_device_handle = NULL;
799 char *topic;
800 char *payload;
801 uint16_t payload_len = 0;
802 uint8_t qos = 0;
803
804 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
805 if (!iot_device_handle) {
806 amp_warn(MOD_STR, "mqtt handle is null");
807 goto out;
808 }
809
810 JSValue val = JS_GetPropertyStr(ctx, argv[0], "topic");
811 topic = JS_ToCString(ctx,val);
812 JS_FreeValue(ctx,val);
813
814 val = JS_GetPropertyStr(ctx, argv[0], "payload");
815 payload = JS_ToCString(ctx,val);
816 JS_FreeValue(ctx,val);
817 payload_len = strlen(payload);
818
819 val = JS_GetPropertyStr(ctx, argv[0], "qos");
820 JS_ToInt32(ctx,&qos, val);
821 JS_FreeValue(ctx,val);
822
823 // iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_PUBLISH_REF] = JS_DupValue(ctx, argv[1]);
824
825 amp_debug(MOD_STR, "publish topic: %s, payload: %s, qos is: %d", topic, payload, qos);
826
827 res = aiot_mqtt_pub(iot_device_handle->mqtt_handle, topic, payload, payload_len, qos);
828 if (res < STATE_SUCCESS) {
829 amp_error(MOD_STR, "aiot app mqtt publish failed\n");
830 goto out;
831 }
832
833 out:
834 if(topic != NULL){
835 JS_FreeCString(ctx, topic);
836 }
837 if(payload != NULL){
838 JS_FreeCString(ctx, payload);
839 }
840 return JS_NewInt32(ctx, res);
841 }
842
843 /* unsubscribe */
native_aiot_unsubscribe(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)844 static JSValue native_aiot_unsubscribe(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
845 {
846 int res = -1;
847 iot_device_handle_t *iot_device_handle = NULL;
848 char *topic;
849 uint8_t qos = 0;
850
851 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
852 if (!iot_device_handle) {
853 amp_warn(MOD_STR, "mqtt handle is null");
854 goto out;
855 }
856
857 topic = JS_ToCString(ctx, argv[0]);
858
859 iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_UNSUBSCRIBE_REF] = JS_DupValue(ctx, argv[1]);
860
861 amp_debug(MOD_STR, "unsubscribe topic: %s", topic);
862
863 res = aiot_mqtt_unsub(iot_device_handle->mqtt_handle, topic);
864 if (res < STATE_SUCCESS) {
865 amp_error(MOD_STR, "aiot app mqtt unsubscribe failed, ret %d", res);
866 goto out;
867 }
868
869 out:
870 if(topic != NULL) {
871 JS_FreeCString(ctx, topic);
872 }
873 return JS_NewInt32(ctx,res);
874 }
875
876 /* subscribe */
native_aiot_subscribe(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)877 static JSValue native_aiot_subscribe(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
878 {
879 int res = -1;
880 iot_device_handle_t *iot_device_handle = NULL;
881 char *topic = NULL;
882 uint32_t qos = 0;
883
884 iot_device_handle = JS_GetOpaque2(ctx, this_val, js_aiot_device_class_id);
885 if (!iot_device_handle) {
886 amp_warn(MOD_STR, "mqtt handle is null");
887 goto out;
888 }
889
890 JSValue val = JS_GetPropertyStr(ctx, argv[0], "topic");
891 topic = JS_ToCString(ctx, val);
892 JS_FreeValue(ctx, val);
893 val = JS_GetPropertyStr(ctx, argv[0], "qos");
894 JS_ToInt32(ctx,&qos, val);
895 JS_FreeValue(ctx, val);
896
897 iot_device_handle->js_cb_ref[AIOT_DEV_JSCALLBACK_SUBSCRIBE_REF] = JS_DupValue(ctx, argv[1]);
898
899 amp_debug(MOD_STR, "subscribe topic: %s, qos is: %d", topic, qos);
900
901 res = aiot_mqtt_sub(iot_device_handle->mqtt_handle, topic, NULL, (uint8_t)qos, NULL);
902 if (res < STATE_SUCCESS) {
903 amp_error(MOD_STR, "aiot app mqtt subscribe failed\n");
904 goto out;
905 }
906
907 out:
908 if(topic != NULL){
909 JS_FreeCString(ctx, topic);
910 }
911 return JS_NewInt32(ctx,res);
912 }
913
module_iot_source_clean(void)914 static void module_iot_source_clean(void)
915 {
916 JSContext *ctx = js_get_context();
917
918 if (g_iot_conn_flag) {
919 g_iot_close_flag = 1;
920 aos_sem_wait(&g_iot_close_sem, 8000);
921 g_iot_close_flag = 0;
922 aos_msleep(10);
923 }
924
925 g_iot_clean_flag = 1;
926 }
native_aiot_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)927 static JSValue native_aiot_close(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
928 {
929 module_iot_source_clean();
930 return JS_NewInt32(ctx,0);
931 }
932
933 static JSClassDef js_aiot_device_class = {
934 "AIOT_DEVICE",
935 };
936
937 static const JSCFunctionListEntry js_aiot_device_funcs[] = {
938 JS_CFUNC_DEF("device", 1, native_aiot_create_device ),
939 JS_CFUNC_DEF("subscribe", 3, native_aiot_subscribe),
940 JS_CFUNC_DEF("unsubscribe", 3, native_aiot_unsubscribe),
941 JS_CFUNC_DEF("publish", 3, native_aiot_publish),
942 JS_CFUNC_DEF("postProps", 3, native_aiot_postProps),
943 JS_CFUNC_DEF("onProps", 2, native_aiot_onProps),
944 JS_CFUNC_DEF("postEvent", 3, native_aiot_postEvent),
945 JS_CFUNC_DEF("getNtpTime", 2, native_aiot_get_ntp_time),
946 JS_CFUNC_DEF("register", 2, native_aiot_dynreg),
947 JS_CFUNC_DEF("onService", 2, native_aiot_onService),
948 JS_CFUNC_DEF("close", 2, native_aiot_close),
949 };
950
js_aiot_device_init(JSContext * ctx,JSModuleDef * m)951 static int js_aiot_device_init(JSContext *ctx, JSModuleDef *m)
952 {
953 JSValue proto;
954
955 JS_NewClassID(&js_aiot_device_class_id);
956 JS_NewClass(JS_GetRuntime(ctx), js_aiot_device_class_id, &js_aiot_device_class);
957 proto = JS_NewObject(ctx);
958 JS_SetPropertyFunctionList(ctx, proto, js_aiot_device_funcs, countof(js_aiot_device_funcs));
959 JS_SetClassProto(ctx, js_aiot_device_class_id, proto);
960
961 return JS_SetModuleExportList(ctx, m, js_aiot_device_funcs, countof(js_aiot_device_funcs));
962 }
963
js_init_module_aiot_device(JSContext * ctx,const char * module_name)964 JSModuleDef *js_init_module_aiot_device(JSContext *ctx, const char *module_name)
965 {
966 JSModuleDef *m;
967 m = JS_NewCModule(ctx, module_name, js_aiot_device_init);
968 if (!m)
969 return NULL;
970 JS_AddModuleExportList(ctx, m, js_aiot_device_funcs, countof(js_aiot_device_funcs));
971 return m;
972 }
973
module_aiot_device_register(void)974 void module_aiot_device_register(void)
975 {
976 amp_debug(MOD_STR, "module_aiot_device_register");
977 JSContext *ctx = js_get_context();
978
979 if (!g_iot_close_sem) {
980 if (aos_sem_new(&g_iot_close_sem, 0) != 0) {
981 amp_error(MOD_STR, "create iot sem fail");
982 return;
983 }
984 }
985 g_iot_clean_flag = 0;
986 aos_printf("module iot register\n");
987 amp_module_free_register(module_iot_source_clean);
988
989 js_init_module_aiot_device(ctx, "AIOT_DEVICE");
990 }