iotcloud.lua 40 KB

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