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 }