iotcloud.lua 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. --[[
  2. @module iotcloud
  3. @summary iotcloud 云平台库 (已支持: 腾讯云 阿里云 onenet 华为云 涂鸦云 百度云 Tlink云 其他也会支持,有用到的提issue会加速支持)
  4. @version 1.0
  5. @date 2023.06.19
  6. @author Dozingfiretruck
  7. @usage
  8. --注意:因使用了sys.wait()所有api需要在协程中使用
  9. -- 腾讯云
  10. -- 动态注册
  11. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx" ,product_secret = "xxx"})
  12. -- 密钥校验
  13. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "123456789",key = "xxx=="})
  14. -- 证书校验
  15. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "123456789"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  16. -- 阿里云
  17. -- 动态注册(免预注册)(一型一密)
  18. -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{produt_id = "xxx",product_secret = "xxx"})
  19. -- 密钥校验 (预注册)(一机一密)
  20. -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{produt_id = "xxx",device_name = "xxx",key = "xxx"})
  21. -- 证书校验(预注册)
  22. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "xxx"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  23. -- ONENET云
  24. -- 动态注册
  25. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",userid = "xxx",userkey = "xxx"})
  26. -- 一型一密
  27. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",product_secret = "xxx"})
  28. -- 一机一密
  29. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})
  30. -- 华为云
  31. -- 动态注册(免预注册)
  32. -- iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "xxx",project_id = "xxx",endpoint = "xxx",
  33. -- iam_username="xxx",iam_password="xxx",iam_domain="xxx"})
  34. -- 密钥校验 (预注册)
  35. -- iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "xxx",endpoint = "xxx",device_name = "xxx",device_secret = "xxx"})
  36. -- -- 涂鸦云
  37. -- iotcloudc = iotcloud.new(iotcloud.TUYA,{device_name = "xxx",device_secret = "xxx"})
  38. -- 百度云
  39. -- iotcloudc = iotcloud.new(iotcloud.BAIDU,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})
  40. -- Tlink云
  41. -- iotcloudc = iotcloud.new(iotcloud.TLINK,{produt_id = "xxx",product_secret = "xxx",device_name = "xxx"})
  42. -- iotcloudc = iotcloud.new(iotcloud.TLINK,{produt_id = "xxx",product_secret = "xxx",device_name = "xxx"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  43. ]]
  44. local iotcloud = {}
  45. --云平台
  46. --//@const TENCENT string 腾讯云
  47. iotcloud.TENCENT = "tencent" -- 腾讯云
  48. --//@const ALIYUN string 阿里云
  49. iotcloud.ALIYUN = "aliyun" -- 阿里云
  50. --//@const ONENET string ONENET云
  51. iotcloud.ONENET = "onenet" -- ONENET云
  52. --//@const HUAWEI string 华为云
  53. iotcloud.HUAWEI = "huawei" -- 华为云
  54. --//@const TUYA string 涂鸦云
  55. iotcloud.TUYA = "tuya" -- 涂鸦云
  56. --//@const BAIDU string 百度云
  57. iotcloud.BAIDU = "baidu" -- 百度云
  58. --//@const TLINK string Tlink云
  59. iotcloud.TLINK = "tlink" -- Tlink云
  60. --认证方式
  61. local iotcloud_certificate = "certificate" -- 秘钥认证
  62. local iotcloud_key = "key" -- 证书认证
  63. -- event
  64. --//@const CONNECT string 连接上服务器
  65. iotcloud.CONNECT = "connect" -- 连接上服务器
  66. --//@const SEND string 发送消息
  67. iotcloud.SEND = "SEND" -- 发送消息
  68. --//@const RECEIVE string 接收到消息
  69. iotcloud.RECEIVE = "receive" -- 接收到消息
  70. --//@const DISCONNECT string 服务器连接断开
  71. iotcloud.DISCONNECT = "disconnect" -- 服务器连接断开
  72. --//@const OTA string ota消息
  73. iotcloud.OTA = "ota" -- ota消息
  74. local cloudc_table = {} -- iotcloudc 对象表
  75. local cloudc = {}
  76. cloudc.__index = cloudc
  77. -- 云平台连接成功处理函数,此处可订阅一些主题或者上报版本等默认操作
  78. local function iotcloud_connect(iotcloudc)
  79. -- mqtt_client:subscribe({[topic1]=1,[topic2]=1,[topic3]=1})--多主题订阅
  80. if iotcloudc.cloud == iotcloud.TENCENT then -- 腾讯云
  81. iotcloudc:subscribe("$ota/update/"..iotcloudc.product_id.."/"..iotcloudc.device_name) -- 订阅ota主题
  82. iotcloudc:publish("$ota/report/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"type\":\"report_version\",\"report\":{\"version\": \"".._G.VERSION.."\"}}") -- 上报ota版本信息
  83. elseif iotcloudc.cloud == iotcloud.ALIYUN then -- 阿里云
  84. iotcloudc:subscribe("/ota/device/upgrade/"..iotcloudc.product_id.."/"..iotcloudc.device_name) -- 订阅ota主题
  85. iotcloudc:publish("/ota/device/inform/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"id\":1,\"params\":{\"version\":\"".._G.VERSION.."\"}}") -- 上报ota版本信息
  86. elseif iotcloudc.cloud == iotcloud.ONENET then -- 中国移动云
  87. elseif iotcloudc.cloud == iotcloud.HUAWEI then -- 华为云
  88. iotcloudc:subscribe("$oc/devices/"..iotcloudc.device_id.."/sys/events/down") -- 订阅ota主题
  89. iotcloudc:publish("$oc/devices/"..iotcloudc.device_id.."/sys/events/up","{\"services\":[{\"service_id\":\"$ota\",\"event_type\":\"version_report\",\"paras\":{\"fw_version\":\"".._G.VERSION.."\"}}]}") -- 上报ota版本信息
  90. elseif iotcloudc.cloud == iotcloud.TUYA then -- 涂鸦云
  91. elseif iotcloudc.cloud == iotcloud.TLINK then -- Tlink云
  92. iotcloudc:subscribe(iotcloudc.device_name.."/+") -- 订阅主题
  93. end
  94. end
  95. local function http_downloald_callback(content_len,body_len,iotcloudc)
  96. -- print("http_downloald_callback-------------------",content_len,body_len)
  97. if iotcloudc.cloud == iotcloud.TENCENT then
  98. if body_len == 0 then
  99. -- 开始升级 type:消息类型 state:状态为烧制中
  100. iotcloudc:publish("$ota/report/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\": \"burning\",\"result_code\": \"0\",\"result_msg\": \"\"}},\"version\": \""..iotcloudc.ota_version.."\"}")
  101. else
  102. -- 下载进度 type:消息类型 state:状态为正在下载中 percent:当前下载进度,百分比
  103. iotcloudc:publish("$ota/report/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\": \"downloading\",\"percent\": \""..body_len*100//content_len.."\",\"result_code\": \"0\",\"result_msg\": \"\"}},\"version\": \""..iotcloudc.ota_version.."\"}")
  104. end
  105. elseif iotcloudc.cloud == iotcloud.HUAWEI then
  106. iotcloudc:publish("$oc/devices/"..iotcloudc.device_id.."/sys/events/up","{\"services\":[{\"service_id\":\"$ota\",\"event_type\":\"upgrade_progress_report\",\"paras\":{\"result_code\":\"0\",\"version\":\"".._G.VERSION.."\",\"progress\":\""..(body_len*100//content_len - 1).."\"}}]}") -- 上报ota版本信息
  107. end
  108. end
  109. local function iotcloud_ota_download(iotcloudc,ota_payload,config)
  110. local ota_url = nil
  111. local ota_headers = nil
  112. local body_ota = nil
  113. config.callback = http_downloald_callback
  114. config.userdata = iotcloudc
  115. if iotcloudc.cloud == iotcloud.TENCENT then
  116. ota_url = ota_payload.url
  117. elseif iotcloudc.cloud == iotcloud.ALIYUN then
  118. ota_url = ota_payload.data.url
  119. elseif iotcloudc.cloud == iotcloud.HUAWEI then
  120. ota_url = ota_payload.services[1].paras.url
  121. ota_headers = {["Content-Type"]="application/json;charset=UTF-8",["Authorization"]="Bearer "..ota_payload.services[1].paras.access_token}
  122. end
  123. local code, headers, body = http.request("GET", ota_url, ota_headers, body_ota, config).wait()
  124. -- log.info("ota download", code, headers, body) -- 只返回code和headers
  125. if code == 200 or code == 206 then
  126. if iotcloudc.cloud == iotcloud.TENCENT then -- 此为腾讯云
  127. -- 升级成功 type:消息类型 state:状态为已完成
  128. iotcloudc:publish("$ota/report/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\": \"done\",\"result_code\": \"0\",\"result_msg\": \"\"}},\"version\": \""..iotcloudc.ota_version.."\"}")
  129. elseif iotcloudc.cloud == iotcloud.ALIYUN then -- 此为阿里云
  130. elseif iotcloudc.cloud == iotcloud.HUAWEI then
  131. iotcloudc:publish("$oc/devices/"..iotcloudc.device_id.."/sys/events/up","{\"services\":[{\"service_id\":\"$ota\",\"event_type\":\"upgrade_progress_report\",\"paras\":{\"result_code\":\"0\",\"version\":\""..iotcloudc.ota_version.."\",\"progress\":\"100\"}}]}") -- 上报ota版本信息
  132. end
  133. else
  134. if iotcloudc.cloud == iotcloud.TENCENT then -- 此为腾讯云
  135. -- 升级失败 type:消息类型 state:状态为失败 result_code:错误码,-1:下载超时;-2:文件不存在;-3:签名过期;-4:MD5不匹配;-5:更新固件失败 result_msg:错误消息
  136. iotcloudc:publish("$ota/report/"..iotcloudc.product_id.."/"..iotcloudc.device_name,"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\": \"fail\",\"result_code\": \"-5\",\"result_msg\": \"ota_fail\"}},\"version\": \""..iotcloudc.ota_version.."\"}")
  137. elseif iotcloudc.cloud == iotcloud.ALIYUN then -- 此为阿里云
  138. elseif iotcloudc.cloud == iotcloud.HUAWEI then
  139. iotcloudc:publish("$oc/devices/"..iotcloudc.device_id.."/sys/events/up","{\"services\":[{\"service_id\":\"$ota\",\"event_type\":\"upgrade_progress_report\",\"paras\":{\"result_code\":\"255\"}}]}") -- 上报ota版本信息
  140. end
  141. end
  142. sys.publish("iotcloud", iotcloudc, iotcloud.OTA,code == 200 or code == 206)
  143. end
  144. -- iotcloud mqtt回调函数
  145. local function iotcloud_mqtt_callback(mqtt_client, event, data, payload)
  146. local iotcloudc = nil
  147. -- 遍历出 iotcloudc
  148. for k, v in pairs(cloudc_table) do
  149. if v.mqttc == mqtt_client then
  150. iotcloudc = v
  151. end
  152. end
  153. local isfota,otadst
  154. if fota then isfota = true else otadst = "/update.bin" end
  155. -- otadst = "/update.bin"--test
  156. -- print("iotcloud_mqtt_callback",mqtt_client, event, data, payload)
  157. -- 用户自定义代码
  158. if event == "conack" then -- 连接上服务器
  159. iotcloud_connect(iotcloudc)
  160. sys.publish("iotcloud",iotcloudc,iotcloud.CONNECT, data, payload)
  161. elseif event == "recv" then -- 接收到消息
  162. if iotcloudc.cloud == iotcloud.TENCENT and data == "$ota/update/"..iotcloudc.product_id.."/"..iotcloudc.device_name then -- 腾讯云ota
  163. local ota_payload = json.decode(payload)
  164. if ota_payload.type == "update_firmware" then
  165. iotcloudc.ota_version = ota_payload.version
  166. sys.taskInit(iotcloud_ota_download,iotcloudc,ota_payload,{fota=isfota,dst=otadst,timeout = 120000})
  167. end
  168. elseif iotcloudc.cloud == iotcloud.ALIYUN and data == "/ota/device/upgrade/"..iotcloudc.product_id.."/"..iotcloudc.device_name then -- 阿里云ota
  169. local ota_payload = json.decode(payload)
  170. if ota_payload.message == "success" then
  171. iotcloudc.ota_version = ota_payload.version
  172. sys.taskInit(iotcloud_ota_download,iotcloudc,ota_payload,{fota=isfota,dst=otadst,timeout = 120000})
  173. end
  174. elseif iotcloudc.cloud == iotcloud.HUAWEI and data == "$oc/devices/"..iotcloudc.device_id.."/sys/events/down" then -- 华为云ota
  175. local ota_payload = json.decode(payload)
  176. if ota_payload.services[1].event_type == "version_query" then
  177. iotcloudc:publish("$oc/devices/"..iotcloudc.device_id.."/sys/events/up","{\"services\":[{\"service_id\":\"$ota\",\"event_type\":\"version_report\",\"paras\":{\"fw_version\":\"".._G.VERSION.."\"}}]}") -- 上报ota版本信息
  178. elseif ota_payload.services[1].event_type == "firmware_upgrade" then
  179. iotcloudc.ota_version = ota_payload.services[1].paras.version
  180. sys.taskInit(iotcloud_ota_download,iotcloudc,ota_payload,{fota=isfota,dst=otadst,timeout = 120000})
  181. end
  182. else
  183. sys.publish("iotcloud", iotcloudc, iotcloud.RECEIVE,data,payload)
  184. end
  185. elseif event == "sent" then -- 发送消息
  186. sys.publish("iotcloud", iotcloudc, iotcloud.SEND,data,payload)
  187. elseif event == "disconnect" then -- 服务器连接断开
  188. sys.publish("iotcloud", iotcloudc, iotcloud.DISCONNECT)
  189. end
  190. end
  191. -- 腾讯云自动注册
  192. local function iotcloud_tencent_autoenrol(iotcloudc)
  193. local deviceName = iotcloudc.device_name
  194. local nonce = math.random(1,100)
  195. local timestamp = os.time()
  196. local data = "deviceName="..deviceName.."&nonce="..nonce.."&productId="..iotcloudc.product_id.."&timestamp="..timestamp
  197. local hmac_sha1_data = crypto.hmac_sha1(data,iotcloudc.product_secret):lower()
  198. local signature = crypto.base64_encode(hmac_sha1_data)
  199. local cloud_body = {
  200. deviceName=deviceName,
  201. nonce=nonce,
  202. productId=iotcloudc.product_id,
  203. timestamp=timestamp,
  204. signature=signature,
  205. }
  206. local cloud_body_json = json.encode(cloud_body)
  207. local code, headers, body = http.request("POST","https://ap-guangzhou.gateway.tencentdevices.com/register/dev",
  208. {["Content-Type"]="application/json;charset=UTF-8"},
  209. cloud_body_json
  210. ).wait()
  211. if code == 200 then
  212. local dat, result, errinfo = json.decode(body)
  213. if result then
  214. if dat.code==0 then
  215. local payload = crypto.cipher_decrypt("AES-128-CBC","ZERO",crypto.base64_decode(dat.payload),string.sub(iotcloudc.product_secret,1,16),"0000000000000000")
  216. local payload = json.decode(payload)
  217. fskv.set("iotcloud_tencent", payload)
  218. if payload.encryptionType == 1 then -- 证书认证
  219. iotcloudc.authentication = iotcloud_certificate
  220. elseif payload.encryptionType == 2 then -- 密钥认证
  221. iotcloudc.authentication = iotcloud_key
  222. end
  223. return true
  224. else
  225. log.info("http.post", code, headers, body)
  226. return false
  227. end
  228. end
  229. else
  230. log.info("http.post", code, headers, body)
  231. return false
  232. end
  233. end
  234. local function iotcloud_aliyun_callback(mqtt_client, event, data, payload)
  235. -- log.info("mqtt", "event", event, mqtt_client, data, payload)
  236. if data == "/ext/regnwl" then sys.publish("aliyun_autoenrol", payload) end
  237. if event == "disconnect" then mqtt_client:close() end
  238. end
  239. -- 阿里云自动注册
  240. local function iotcloud_aliyun_autoenrol(iotcloudc)
  241. local random = math.random(1,999)
  242. local data = "deviceName"..iotcloudc.device_name.."productKey"..iotcloudc.product_id.."random"..random
  243. local mqttClientId = iotcloudc.device_name.."|securemode=-2,authType=regnwl,random="..random..",signmethod=hmacsha1|"
  244. local mqttUserName = iotcloudc.device_name.."&"..iotcloudc.product_id
  245. local mqttPassword = crypto.hmac_sha1(data,iotcloudc.product_secret):lower()
  246. -- print("iotcloud_aliyun_autoenrol",mqttClientId,mqttUserName,mqttPassword)
  247. aliyun_mqttc = mqtt.create(nil, iotcloudc.product_id..".iot-as-mqtt.cn-shanghai.aliyuncs.com", 443,true)
  248. aliyun_mqttc:auth(mqttClientId,mqttUserName,mqttPassword)
  249. aliyun_mqttc:on(iotcloud_aliyun_callback)
  250. aliyun_mqttc:connect()
  251. local result, payload = sys.waitUntil("aliyun_autoenrol", 30000)
  252. -- print("aliyun_autoenrol",result, payload)
  253. if result then
  254. local payload = json.decode(payload)
  255. fskv.set("iotcloud_aliyun", payload)
  256. -- print("aliyun_autoenrol payload",payload.clientId, payload.deviceToken)
  257. return true
  258. else
  259. return false
  260. end
  261. end
  262. -- 腾讯云参数配置逻辑
  263. local function iotcloud_tencent_config(iotcloudc,iot_config,connect_config)
  264. iotcloudc.cloud = iotcloud.TENCENT
  265. iotcloudc.product_id = iot_config.product_id
  266. if iot_config.product_secret then -- 有product_secret说明是动态注册
  267. iotcloudc.product_secret = iot_config.product_secret
  268. if not fskv.get("iotcloud_tencent") then
  269. if not iotcloud_tencent_autoenrol(iotcloudc) then return false end
  270. end
  271. local data = fskv.get("iotcloud_tencent")
  272. -- print("payload",data.encryptionType,data.psk,data.clientCert,data.clientKey)
  273. if data.encryptionType == 1 then -- 证书认证
  274. iotcloudc.ip = 8883
  275. iotcloudc.isssl = true
  276. iotcloudc.ca_file = {client_cert = data.clientCert,client_key = data.clientKey}
  277. iotcloudc.client_id,iotcloudc.user_name = iotauth.qcloud(iotcloudc.product_id,iotcloudc.device_name,"")
  278. elseif data.encryptionType == 2 then -- 密钥认证
  279. iotcloudc.ip = 1883
  280. iot_config.key = data.psk
  281. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.qcloud(iotcloudc.product_id,iotcloudc.device_name,iot_config.key,iot_config.method)
  282. end
  283. else -- 否则为非动态注册
  284. if iot_config.key then -- 密钥认证
  285. iotcloudc.ip = 1883
  286. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.qcloud(iotcloudc.product_id,iotcloudc.device_name,iot_config.key,iot_config.method)
  287. elseif connect_config.tls then -- 证书认证
  288. iotcloudc.ip = 8883
  289. iotcloudc.isssl = true
  290. iotcloudc.ca_file = {client_cert = connect_config.tls.client_cert}
  291. iotcloudc.client_id,iotcloudc.user_name = iotauth.qcloud(iotcloudc.product_id,iotcloudc.device_name,"")
  292. else -- 密钥证书都没有
  293. return false
  294. end
  295. end
  296. if connect_config then
  297. iotcloudc.host = connect_config.host or iotcloudc.product_id..".iotcloud.tencentdevices.com"
  298. if connect_config.ip then iotcloudc.ip = connect_config.ip end
  299. else
  300. iotcloudc.host = iotcloudc.product_id..".iotcloud.tencentdevices.com"
  301. end
  302. return true
  303. end
  304. -- 阿里云参数配置逻辑
  305. local function iotcloud_aliyun_config(iotcloudc,iot_config,connect_config)
  306. iotcloudc.cloud = iotcloud.ALIYUN
  307. iotcloudc.product_id = iot_config.product_id
  308. if iot_config.product_secret then -- 有product_secret说明是动态注册
  309. iotcloudc.product_secret = iot_config.product_secret
  310. if not fskv.get("iotcloud_aliyun") then
  311. if not iotcloud_aliyun_autoenrol(iotcloudc) then return false end
  312. end
  313. local data = fskv.get("iotcloud_aliyun")
  314. -- print("aliyun_autoenrol payload",data.clientId, data.deviceToken)
  315. iotcloudc.client_id = data.clientId.."|securemode=-2,authType=connwl|"
  316. iotcloudc.user_name = iotcloudc.device_name.."&"..iotcloudc.product_id
  317. iotcloudc.password = data.deviceToken
  318. iotcloudc.ip = 1883
  319. else -- 否则为非动态注册
  320. if iot_config.key then -- 密钥认证
  321. iotcloudc.ip = 1883
  322. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.aliyun(iotcloudc.product_id,iotcloudc.device_name,iot_config.key,iot_config.method)
  323. -- elseif connect_config.tls then -- 证书认证
  324. -- iotcloudc.ip = 443
  325. -- iotcloudc.isssl = true
  326. -- iotcloudc.ca_file = {client_cert = connect_config.tls.client_cert}
  327. -- iotcloudc.client_id,iotcloudc.user_name = iotauth.aliyun(iotcloudc.product_id,iotcloudc.device_name,"",iot_config.method,nil,true)
  328. else -- 密钥证书都没有
  329. return false
  330. end
  331. end
  332. if connect_config then
  333. iotcloudc.host = connect_config.host or iotcloudc.product_id..".iot-as-mqtt.cn-shanghai.aliyuncs.com"
  334. if connect_config.ip then iotcloudc.ip = connect_config.ip end
  335. else
  336. iotcloudc.host = iotcloudc.product_id..".iot-as-mqtt.cn-shanghai.aliyuncs.com"
  337. end
  338. return true
  339. end
  340. -- 中国移动云自动注册
  341. local function iotcloud_onenet_autoenrol(iotcloudc)
  342. local version = '2022-05-01'
  343. local res = "userid/"..iotcloudc.userid
  344. local et = '32472115200'
  345. local method = 'SHA256'
  346. local key = crypto.base64_decode(iotcloudc.userkey)
  347. local StringForSignature = et .. '\n' .. method .. '\n' .. res ..'\n' .. version
  348. local sign1 = crypto.hmac_sha256(StringForSignature,key)
  349. local sign2 = sign1:fromHex()
  350. local sign = crypto.base64_encode(sign2)
  351. sign = string.urlEncode(sign)
  352. res = string.urlEncode(res)
  353. local token = string.format('version=%s&res=%s&et=%s&method=%s&sign=%s',version, res, et, method, sign)
  354. local code, headers, body = http.request("POST","https://iot-api.heclouds.com/device/create",
  355. {["Content-Type"]="application/json;charset=UTF-8",["authorization"]=token},
  356. "{\"product_id\":\""..iotcloudc.product_id.."\",\"device_name\":\""..iotcloudc.device_name.."\"}"
  357. ).wait()
  358. if code == 200 then
  359. local dat, result, errinfo = json.decode(body)
  360. if result then
  361. if dat.code==0 then
  362. fskv.set("iotcloud_onenet", dat.data.sec_key)
  363. return true
  364. else
  365. log.info("http.post", code, headers, body)
  366. return false
  367. end
  368. end
  369. else
  370. log.info("http.post", code, headers, body)
  371. return false
  372. end
  373. end
  374. -- 中国移动云参数配置逻辑
  375. local function iotcloud_onenet_config(iotcloudc,iot_config,connect_config)
  376. iotcloudc.cloud = iotcloud.ONENET
  377. iotcloudc.product_id = iot_config.product_id
  378. iotcloudc.host = "mqtts.heclouds.com"
  379. iotcloudc.ip = 1883
  380. if iot_config.product_secret then -- 一型一密
  381. iotcloudc.product_secret = iot_config.product_secret
  382. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.onenet(iotcloudc.product_id, iotcloudc.device_name, iot_config.product_secret, nil, nil, nil, "products/" .. iotcloudc.product_id)
  383. elseif iot_config.device_secret then -- 一机一密
  384. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.onenet(iotcloudc.product_id,iotcloudc.device_name,iot_config.device_secret)
  385. elseif iot_config.userid and iot_config.userkey then -- 动态注册
  386. iotcloudc.userid = iot_config.userid
  387. iotcloudc.userkey = iot_config.userkey
  388. if not fskv.get("iotcloud_onenet") then
  389. if not iotcloud_onenet_autoenrol(iotcloudc) then return false end
  390. end
  391. local data = fskv.get("iotcloud_onenet")
  392. -- print("fskv.get data",data)
  393. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.onenet(iotcloudc.product_id,iotcloudc.device_name,data)
  394. end
  395. return true
  396. end
  397. -- 华为云自动注册
  398. local function iotcloud_huawei_autoenrol(iotcloudc)
  399. local token_code, token_headers, token_body = http.request("POST","https://iam."..iotcloudc.region..".myhuaweicloud.com/v3/auth/tokens",
  400. {["Content-Type"]="application/json;charset=UTF-8"},
  401. "{\"auth\":{\"identity\":{\"methods\":[\"password\"],\"password\":{\"user\":{\"domain\":{\"name\":\""..iotcloudc.iam_domain.."\"},\"name\":\""..iotcloudc.iam_username.."\",\"password\":\""..iotcloudc.iam_password.."\"}}},\"scope\":{\"project\":{\"name\":\""..iotcloudc.region.."\"}}}}"
  402. ).wait()
  403. if token_code ~= 201 then
  404. log.error("iotcloud_huawei_autoenrol",token_body)
  405. return false
  406. end
  407. local http_url = "https://"..iotcloudc.endpoint..".iotda."..iotcloudc.region..".myhuaweicloud.com/v5/iot/"..iotcloudc.project_id.."/devices"
  408. local code, headers, body = http.request("POST",http_url,
  409. {["Content-Type"]="application/json;charset=UTF-8",["X-Auth-Token"]=token_headers["X-Subject-Token"]},
  410. "{\"node_id\": \""..iotcloudc.device_name.."\",\"product_id\": \""..iotcloudc.product_id.."\"}"
  411. ).wait()
  412. -- print("iotcloud_huawei_autoenrol", code, headers, body)
  413. if code == 201 then
  414. local dat, result, errinfo = json.decode(body)
  415. if result then
  416. fskv.set("iotcloud_huawei", dat.auth_info.secret)
  417. return true
  418. end
  419. else
  420. log.error("iotcloud_huawei_autoenrol", code, headers, body)
  421. return false
  422. end
  423. end
  424. local function iotcloud_huawei_config(iotcloudc,iot_config,connect_config)
  425. iotcloudc.cloud = iotcloud.HUAWEI
  426. iotcloudc.region = iot_config.region or "cn-north-4"
  427. iotcloudc.endpoint = iot_config.endpoint
  428. iotcloudc.product_id = iot_config.product_id
  429. iotcloudc.project_id = iot_config.project_id
  430. iotcloudc.iam_username = iot_config.iam_username
  431. iotcloudc.iam_password = iot_config.iam_password
  432. iotcloudc.iam_domain = iot_config.iam_domain
  433. iotcloudc.device_id = iotcloudc.product_id.."_"..iotcloudc.device_name
  434. iotcloudc.device_secret = iot_config.device_secret
  435. iotcloudc.ip = 1883
  436. if iotcloudc.endpoint then
  437. iotcloudc.host = iotcloudc.endpoint..".iot-mqtts."..iotcloudc.region..".myhuaweicloud.com"
  438. else
  439. log.error("iotcloud","huawei","endpoint is nil")
  440. return false
  441. end
  442. -- 一型一密(自动注册) 最终会获取设备秘钥
  443. if iotcloudc.product_id and iotcloudc.project_id and iotcloudc.iam_username and iotcloudc.iam_password and iotcloudc.iam_domain then
  444. if not fskv.get("iotcloud_huawei") then
  445. if not iotcloud_huawei_autoenrol(iotcloudc) then return false end
  446. end
  447. iotcloudc.device_secret = fskv.get("iotcloud_huawei")
  448. end
  449. if iotcloudc.device_secret then -- 一机一密
  450. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.iotda(iotcloudc.device_id,iotcloudc.device_secret)
  451. else
  452. return false
  453. end
  454. return true
  455. end
  456. -- 涂鸦云参数配置逻辑
  457. local function iotcloud_tuya_config(iotcloudc,iot_config,connect_config)
  458. iotcloudc.cloud = iotcloud.TUYA
  459. iotcloudc.host = "m1.tuyacn.com"
  460. iotcloudc.ip = 8883
  461. iotcloudc.isssl = true
  462. iotcloudc.device_secret = iot_config.device_secret
  463. if iotcloudc.device_secret then
  464. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.tuya(iotcloudc.device_name,iotcloudc.device_secret)
  465. else
  466. return false
  467. end
  468. return true
  469. end
  470. -- 百度云参数配置逻辑
  471. local function iotcloud_baidu_config(iotcloudc,iot_config,connect_config)
  472. iotcloudc.cloud = iotcloud.BAIDU
  473. iotcloudc.product_id = iot_config.product_id
  474. iotcloudc.region = iot_config.region or "gz"
  475. iotcloudc.host = iotcloudc.product_id..".iot."..iotcloudc.region..".baidubce.com"
  476. iotcloudc.ip = 1883
  477. -- iotcloudc.isssl = true
  478. iotcloudc.device_secret = iot_config.device_secret
  479. if iotcloudc.device_secret then
  480. iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password = iotauth.baidu(iotcloudc.product_id,iotcloudc.device_name,iotcloudc.device_secret)
  481. else
  482. return false
  483. end
  484. return true
  485. end
  486. -- TLINK云参数配置逻辑
  487. local function iotcloud_tlink_config(iotcloudc,iot_config,connect_config)
  488. iotcloudc.cloud = iotcloud.TLINK
  489. iotcloudc.host = "mq.tlink.io"
  490. iotcloudc.ip = 1883
  491. iotcloudc.client_id = iotcloudc.device_name
  492. iotcloudc.user_name = iot_config.product_id
  493. iotcloudc.password = iot_config.product_secret
  494. if connect_config.tls then -- 证书认证
  495. iotcloudc.ip = 8883
  496. iotcloudc.isssl = true
  497. iotcloudc.ca_file = {client_cert = connect_config.tls.client_cert}
  498. end
  499. return true
  500. end
  501. --[[
  502. 创建云平台对象
  503. @api iotcloud.new(cloud,iot_config,connect_config)
  504. @string 云平台 iotcloud.TENCENT:腾讯云 iotcloud.ALIYUN:阿里云 iotcloud.ONENET:中国移动云 iotcloud.HUAWEI:华为云 iotcloud.TUYA:涂鸦云
  505. @table iot云平台配置, device_name:可选,默认为imei否则为unique_id iot_config.product_id:产品id(阿里云则为产品key) iot_config.product_secret:产品密钥,有此项则为动态注册 iot_config.key:设备秘钥,有此项则为秘钥连接 userid:用户ID,onenet专用,动态注册使用 userkey:用户Accesskey,onenet专用,动态注册使用
  506. @table mqtt配置, host:可选,默认为平台默认host ip:可选,默认为平台默认ip tls:加密,若有此项一般为产品认证 keepalive:心跳时间,单位s 可选,默认240
  507. @return table 云平台对象
  508. @usage
  509. -- 腾讯云
  510. -- 动态注册
  511. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx" ,product_secret = "xxx"})
  512. -- 密钥校验
  513. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "123456789",key = "xxx=="})
  514. -- 证书校验
  515. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "123456789"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  516. -- 阿里云
  517. -- 动态注册(免预注册)(一型一密)
  518. -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{produt_id = "xxx",product_secret = "xxx"})
  519. -- 密钥校验 (预注册)(一机一密)
  520. -- iotcloudc = iotcloud.new(iotcloud.ALIYUN,{produt_id = "xxx",device_name = "xxx",key = "xxx"})
  521. -- 证书校验(预注册)
  522. -- iotcloudc = iotcloud.new(iotcloud.TENCENT,{produt_id = "xxx",device_name = "xxx"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  523. -- ONENET云
  524. -- 动态注册
  525. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",userid = "xxx",userkey = "xxx"})
  526. -- 一型一密
  527. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",product_secret = "xxx"})
  528. -- 一机一密
  529. -- iotcloudc = iotcloud.new(iotcloud.ONENET,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})
  530. -- 华为云
  531. -- 动态注册(免预注册)
  532. -- iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "xxx",project_id = "xxx",endpoint = "xxx",
  533. -- iam_username="xxx",iam_password="xxx",iam_domain="xxx"})
  534. -- 密钥校验 (预注册)
  535. -- iotcloudc = iotcloud.new(iotcloud.HUAWEI,{produt_id = "xxx",endpoint = "xxx",device_name = "xxx",device_secret = "xxx"})
  536. -- -- 涂鸦云
  537. -- iotcloudc = iotcloud.new(iotcloud.TUYA,{device_name = "xxx",device_secret = "xxx"})
  538. -- 百度云
  539. -- iotcloudc = iotcloud.new(iotcloud.BAIDU,{produt_id = "xxx",device_name = "xxx",device_secret = "xxx"})
  540. -- Tlink云
  541. -- iotcloudc = iotcloud.new(iotcloud.TLINK,{produt_id = "xxx",product_secret = "xxx",device_name = "xxx"})
  542. -- iotcloudc = iotcloud.new(iotcloud.TLINK,{produt_id = "xxx",product_secret = "xxx",device_name = "xxx"},{tls={client_cert=io.readFile("/luadb/client_cert.crt")}})
  543. ]]
  544. function iotcloud.new(cloud,iot_config,connect_config)
  545. if not connect_config then connect_config = {} end
  546. local mqtt_ssl = nil
  547. local iotcloudc = setmetatable({
  548. cloud = nil, -- 云平台
  549. host = nil, -- host
  550. ip = nil, -- ip
  551. mqttc = nil, -- mqtt对象
  552. device_name = nil, -- 设备名(一般为设备id)
  553. product_id = nil, -- 产品id
  554. product_secret = nil, -- 产品秘钥
  555. device_id = nil, -- 设备id(一般为设备名)
  556. device_secret = nil, -- 设备秘钥
  557. region = nil, -- 云区域
  558. client_id = nil, -- mqtt客户端id
  559. user_name = nil, -- mqtt用户名
  560. password = nil, -- mqtt密码
  561. authentication = nil, -- 认证方式:密钥认证/证书认证
  562. isssl = nil, -- 是否加密
  563. ca_file = nil, -- 证书
  564. ota_version = nil, -- ota时目标版本
  565. userid = nil, -- onenet API专用
  566. userkey = nil, -- onenet API专用
  567. iam_username = nil, -- 华为云 API专用 IAM用户名
  568. iam_password = nil, -- 华为云 API专用 华为云密码
  569. iam_domain = nil, -- 华为云 API专用 账号名
  570. endpoint = nil, -- 华为云 API专用
  571. project_id = nil, -- 华为云 API专用
  572. }, cloudc)
  573. if fskv then fskv.init() else return false end
  574. if iot_config.produt_id then
  575. iot_config.product_id = iot_config.produt_id
  576. end
  577. if iot_config.device_name then -- 设定了就使用指定的device_name
  578. iotcloudc.device_name = iot_config.device_name
  579. elseif mobile then -- 未设定优先使用imei
  580. iotcloudc.device_name = mobile.imei()
  581. else -- 无imei使用unique_id
  582. iotcloudc.device_name = mcu.unique_id():toHex()
  583. end
  584. if cloud == iotcloud.TENCENT or cloud == "qcloud" then -- 此为腾讯云
  585. if not iotcloud_tencent_config(iotcloudc,iot_config,connect_config) then return false end
  586. elseif cloud == iotcloud.ALIYUN then
  587. if not iotcloud_aliyun_config(iotcloudc,iot_config,connect_config) then return false end
  588. elseif cloud == iotcloud.ONENET then
  589. if not iotcloud_onenet_config(iotcloudc,iot_config,connect_config) then return false end
  590. elseif cloud == iotcloud.HUAWEI then
  591. if not iotcloud_huawei_config(iotcloudc,iot_config,connect_config) then return false end
  592. elseif cloud == iotcloud.TUYA then
  593. if not iotcloud_tuya_config(iotcloudc,iot_config,connect_config) then return false end
  594. elseif cloud == iotcloud.BAIDU then
  595. if not iotcloud_baidu_config(iotcloudc,iot_config,connect_config) then return false end
  596. elseif cloud == iotcloud.TLINK then
  597. if not iotcloud_tlink_config(iotcloudc,iot_config,connect_config) then return false end
  598. else
  599. log.error("iotcloud","cloud not support",cloud)
  600. return false
  601. end
  602. -- print("iotauth.mqtt",iotcloudc.host,iotcloudc.ip,iotcloudc.isssl)
  603. -- print("iotauth.auth",iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password)
  604. if iotcloudc.ca_file then
  605. iotcloudc.ca_file.verify = 1
  606. mqtt_ssl = iotcloudc.ca_file
  607. elseif iotcloudc.isssl then
  608. mqtt_ssl = iotcloudc.isssl
  609. end
  610. iotcloudc.mqttc = mqtt.create(nil, iotcloudc.host, iotcloudc.ip, mqtt_ssl)
  611. -- iotcloudc.mqttc:debug(true)
  612. iotcloudc.mqttc:auth(iotcloudc.client_id,iotcloudc.user_name,iotcloudc.password)
  613. iotcloudc.mqttc:keepalive(connect_config.keepalive or 240)
  614. iotcloudc.mqttc:autoreconn(true, 3000) -- 自动重连机制
  615. iotcloudc.mqttc:on(iotcloud_mqtt_callback) -- mqtt回调
  616. table.insert(cloudc_table,iotcloudc) -- 添加到表里记录
  617. return iotcloudc,error_code -- 错误返回待处理
  618. end
  619. --[[
  620. 云平台连接
  621. @api cloudc:connect()
  622. @usage
  623. iotcloudc:connect()
  624. ]]
  625. function cloudc:connect()
  626. self.mqttc:connect()
  627. end
  628. --[[
  629. 云平台断开
  630. @api cloudc:disconnect()
  631. @usage
  632. iotcloudc:disconnect()
  633. ]]
  634. function cloudc:disconnect()
  635. self.mqttc:disconnect()
  636. end
  637. --[[
  638. 云平台订阅
  639. @api cloudc:subscribe(topic, qos)
  640. @string/table 主题
  641. @number topic为string时生效 0/1/2 默认0
  642. ]]
  643. function cloudc:subscribe(topic, qos)
  644. self.mqttc:subscribe(topic, qos)
  645. end
  646. --[[
  647. 云平台取消订阅
  648. @api cloudc:unsubscribe(topic)
  649. @string/table 主题
  650. ]]
  651. function cloudc:unsubscribe(topic)
  652. self.mqttc:unsubscribe(topic)
  653. end
  654. --[[
  655. 云平台发布
  656. @api cloudc:publish(topic,data,qos,retain)
  657. @string/table 主题
  658. @string 消息,必填,但长度可以是0
  659. @number 消息级别 0/1 默认0
  660. @number 是否存档, 0/1,默认0
  661. ]]
  662. function cloudc:publish(topic,data,qos,retain)
  663. self.mqttc:publish(topic,data,qos,retain)
  664. end
  665. --[[
  666. 云平台关闭
  667. @api cloudc:close()
  668. @usage
  669. iotcloudc:close()
  670. ]]
  671. function cloudc:close()
  672. self.mqttc:close()
  673. for k, v in pairs(cloudc_table) do
  674. if v.mqttc == self.mqttc then
  675. table.remove(cloudc_table,k)
  676. end
  677. end
  678. end
  679. return iotcloud