mqtt-devices-parser 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,83 +32,6 @@ var self = module.exports = {
32
32
 
33
33
  },
34
34
 
35
- deleteLogs : async()=>{
36
-
37
- let tables = await $.db.getTables();
38
- // Define one week in milliseconds
39
- // Loop through each table
40
- for (const tableObj of tables) {
41
- const tableName = tableObj['Tables_in_mqtt-aedes']
42
- const oneWeekAgo = moment().utc().subtract(7, 'days').format('YYYY-MM-DD HH:mm:ss');
43
- if(tableName.startsWith("logs_")){
44
- const uptimeSeconds = Math.floor(process.uptime());
45
- console.log(`deleting logs of table ${tableName} older than ${oneWeekAgo}"`);
46
- await $.db.deleteOldEntries(tableName, { createdAt: oneWeekAgo });
47
- time = Math.floor(process.uptime()) - uptimeSeconds;
48
- console.log(`Logs of table ${tableName} deleted in ${time}s`);
49
- }
50
- }
51
-
52
- },
53
-
54
- checkFota : async ()=>{
55
-
56
- const devices = await $.db_device.listOnline();
57
-
58
- const batchSize = 10;
59
- for (let i = 0; i < devices.length; i += batchSize) {
60
- const batch = devices.slice(i, i + batchSize);
61
- await Promise.all(batch.map(async (device) => {
62
-
63
- if (device.release == null || device.release === "critical")
64
- {
65
- // don't update
66
- return;
67
- }
68
-
69
- if (device.fota_tries > 3)
70
- {
71
- return;
72
- }
73
-
74
- let new_firmware = await $.db_firmware.getLatestFWVersion(device.model_id,device.release);
75
- let new_app = await $.db_firmware.getLatestAppVersion(device.model_id,device.release);
76
- if(new_firmware == null || new_app == null)
77
- return;
78
-
79
- let project_name = "";
80
- let res = await $.db_project.getById(device.project_id);
81
- if(res != null)
82
- project_name = res.name;
83
- else
84
- return;
85
-
86
- let mqtt_prefix = `${project_name}/${device.uid}`;
87
- if(new_app != null && semver.lt(device.app_version, new_app.app_version))
88
- {
89
- console.log(`updating firmware of ${device.uid} to minor version ${new_app.app_version}`);
90
- let link = `${$.config.web.protocol}${$.config.web.domain}${$.config.web.fw_path}${new_app.filename}/download?token=${new_app.token}`;
91
- client.publish(mqtt_prefix+"/fw/fota/update/set",`{"url":"${link}"}`,{qos:2,retain:false});
92
- await $.db_data.update(project_name,device.id,"fota_tries",++device.fota_tries);
93
- }else{
94
- if(new_firmware != null && semver.lt(device.version, new_firmware.fw_version))
95
- {
96
- console.log(`updating firmware of ${device.uid} to major version ${new_firmware.fw_version}`);
97
- let link = `${$.config.web.protocol}${$.config.web.domain}${$.config.web.fw_path}${new_firmware.filename}/download?token=${new_firmware.token}`;
98
- $.mqtt_client.publish(mqtt_prefix+"/fw/fota/update/set",`{"url":"${link}"}`,{qos:2,retain:false});
99
- await $.db_data.update(project_name,device.id,"fota_tries",++device.fota_tries);
100
- }
101
- }
102
- }));
103
-
104
- // Wait for 1 minute after processing each batch
105
- if (i + batchSize < devices.length) {
106
- await new Promise(resolve => setTimeout(resolve, 60000));
107
- }
108
- }
109
-
110
- },
111
-
112
35
  parseMessage : async (client,topic,payload,retain)=>{
113
36
 
114
37
  let topic_bck = topic;
@@ -118,23 +41,36 @@ var self = module.exports = {
118
41
  let index = topic.indexOf("/");
119
42
  if(index == -1)
120
43
  return;
44
+
121
45
  let project_name = topic.substring(0,index);
46
+ let project = await $.db_project.getByName(project_name);
47
+
48
+ if(project == null)
49
+ return;
50
+
51
+ let project_id = project?.id;
122
52
 
123
53
  // --- uid ---
124
54
  topic = topic.substring(index+1);
125
55
  index = topic.indexOf("/");
126
56
  if(index == -1)
127
57
  return;
128
- let uid = topic.substring(0,index);
129
58
 
59
+ let uid = topic.substring(0,index);
130
60
  topic = topic.substring(index+1);
131
61
 
132
62
  // check if topic corresponds to a device
133
- if(uid.startsWith(MACRO_UID_PREFIX)){
63
+ if(uid.startsWith(project.uidPrefix) && uid.length == project?.uidLength){
64
+
134
65
  let device = await $.db_device.get(uid);
135
66
 
67
+ // Insert device if not exists on db
136
68
  if(device == null){
137
- $.db_device.insert(uid)
69
+ let obj = {
70
+ uid : uid,
71
+ accept_release : "prod",
72
+ }
73
+ $.db_device.insert(obj)
138
74
  .then(async()=>{
139
75
  device = await $.db_device.get(uid);
140
76
  })
@@ -142,37 +78,54 @@ var self = module.exports = {
142
78
  }
143
79
 
144
80
  // update project id if needed
145
- let res = await $.db_project.getByName(project_name);
146
- let project_id = res?.id;
147
81
  if(project_id != null && project_id != device?.project_id)
148
82
  $.db_device.update(device.id,"project_id",project_id);
149
83
 
150
- if(topic === "status"){
151
- if(payload == "online" || payload == "offline")
152
- await $.db_device.update(device.id,"status",payload);
153
- }else if(topic === "model"){
154
- let res = await $.db_model.getByName(payload);
155
- let model_id = res?.id;
156
-
157
- if(model_id != null){
158
- let res = await $.db_device.update(device.id,"model_id",model_id);
159
- }
160
- }else if(topic === "tech"){
161
- await $.db_device.update(device.id,"tech",payload);
162
- }else if(topic === "version"){
163
- // Update version and app_version on device table..
164
- if(payload != device.version){
165
- await $.db_device.update(device.id,"version",payload);
166
- await $.db_device.update(device.id,"fota_tries",1); // ! value 0 doesn't work..
167
- }
168
- }else if(topic === "app_version"){
169
- if(payload != device.app_version){
170
- await $.db_device.update(device.id,"app_version",payload);
171
- await $.db_device.update(device.id,"fota_tries",1); // ! value 0 doesn't work..
172
- }
84
+ switch (topic) {
85
+ case "status":
86
+ if (payload === "online" || payload === "offline") {
87
+ await $.db_device.update(device.id, "status", payload);
88
+ }
89
+ break;
90
+ case "model":
91
+ let res = await $.db_model.getByName(payload);
92
+ let model_id = res?.id;
93
+ if (model_id != null) {
94
+ await $.db_device.update(device.id, "model_id", model_id);
95
+ }
96
+ break;
97
+ case "tech":
98
+ await $.db_device.update(device.id, "tech", payload);
99
+ break;
100
+ case "version":
101
+ if (device?.version && payload != device.version) {
102
+ await $.db_device.update(device.id, "version", payload);
103
+ await self.handleFotaSuccess(device.id);
104
+ }
105
+ break;
106
+ case "app_version":
107
+ if (device?.app_version && payload != device.app_version) {
108
+ await $.db_device.update(device.id, "app_version", payload);
109
+ await self.handleFotaSuccess(device.id);
110
+ }
111
+ break;
112
+ case "fw/fota/update/status":
113
+ await self.handleFotaError(device.id, payload);
114
+ break;
115
+ // Optional: default case if needed
116
+ default:
117
+ // handle other topics or do nothing
118
+ break;
173
119
  }
174
120
 
175
- if(device.project_id != null && device.model_id != null){
121
+ if(topic.startsWith("sensor/")){
122
+
123
+ updateSensor(device,topic,paylaod)
124
+ index = topic.indexOf("/");
125
+ if(index == -1)
126
+ return;
127
+
128
+ const name = topic.substring(index+1);
176
129
  let res = await $.db_sensor.getByRef(topic)
177
130
  if(res != null){
178
131
  await $.db_sensor.insert(logs_table,device.id,res.id,payload);
@@ -181,60 +134,224 @@ var self = module.exports = {
181
134
 
182
135
  // store remote device settings on project table - twin model
183
136
  if(topic.endsWith("/set") && payload != "" ){
137
+ self.updateSettings(project_name,device,topic,payload);
138
+ }
139
+
140
+ if(project[project_name]){
141
+ project[project_name].module.parseMessage(client,project_name,device,topic,payload,retain,()=>{});
142
+ }
143
+ }
184
144
 
185
- let route = topic.split("/");
145
+ },
186
146
 
187
- if(route == null){
188
- console.log("topic invalid:",topic);
189
- return;
190
- }
191
- // get settings
192
- let settings = await $.db_project.getSettings(project_name,device.id);
193
- let obj = settings;
147
+ deleteLogs : async()=>{
194
148
 
195
- if(obj == null){
196
- console.log("no settings available for deviceId:",device.id)
197
- obj = {};
198
- }
199
- // Traverse through all route parts except the last one
200
- route.slice(0, -1).forEach(property => {
201
- if (!obj.hasOwnProperty(property) ||
202
- ( obj.hasOwnProperty(property) && typeof obj[property] !== 'object'))
203
- {
204
- obj[property] = {}; // create nested object if missing or not an object
149
+ let tables = await $.db.getTables();
150
+ // Define one week in milliseconds
151
+ // Loop through each table
152
+ for (const tableObj of tables) {
153
+ const tableName = tableObj['Tables_in_mqtt-aedes']
154
+ const oneWeekAgo = moment().utc().subtract(7, 'days').format('YYYY-MM-DD HH:mm:ss');
155
+ if(tableName.startsWith("logs_")){
156
+ const uptimeSeconds = Math.floor(process.uptime());
157
+ console.log(`deleting logs of table ${tableName} older than ${oneWeekAgo}"`);
158
+ await $.db.deleteOldEntries(tableName, { createdAt: oneWeekAgo });
159
+ time = Math.floor(process.uptime()) - uptimeSeconds;
160
+ console.log(`Logs of table ${tableName} deleted in ${time}s`);
161
+ }
162
+ }
163
+ },
164
+
165
+ updateSettings : async (project_name,device,topic,payload)=>{
166
+ let route = topic.split("/");
167
+
168
+ if(route == null){
169
+ console.log("topic invalid:",topic);
170
+ return;
171
+ }
172
+ // get settings
173
+ let settings = await $.db_project.getSettings(project_name,device.id);
174
+ let obj = settings;
175
+
176
+ if(obj == null){
177
+ obj = {};
178
+ }
179
+ // Traverse through all route parts except the last one
180
+ route.slice(0, -1).forEach(property => {
181
+ if (!obj.hasOwnProperty(property) ||
182
+ ( obj.hasOwnProperty(property) && typeof obj[property] !== 'object'))
183
+ {
184
+ obj[property] = {}; // create nested object if missing or not an object
185
+ }
186
+ obj = obj[property]; // go deeper
187
+ })
188
+
189
+ let data = {};
190
+ try{
191
+ data = JSON.parse(payload)
192
+ }catch(error){}
193
+
194
+ if ((data && typeof payload === 'object' && !Array.isArray(payload) ) ) {
195
+ // Assuming payload is an object with properties to process
196
+ for (const [key, value] of Object.entries(data)) {
197
+ obj[key] = value; // go deeper
198
+ };
199
+ }else{
200
+ obj = payload
201
+ }
202
+
203
+ // update on device fw settings
204
+ try{
205
+ await $.db_project.updateSettings(project_name,JSON.stringify(settings),device.id);
206
+ }catch(error){
207
+ console.log(error);
208
+ }
209
+ },
210
+
211
+ checkFota : async (release = "dev")=>{
212
+
213
+ const models = await $.db_model.getAll();
214
+
215
+ for (const model of models) {
216
+
217
+ if(!model?.id)
218
+ continue;
219
+
220
+ try{
221
+ const latestVersion = await $.db_firmware.getLatestVersion(model.id,release);
222
+ const latestAppVersion = await $.db_firmware.getLatestAppVersion(model.id,release);
223
+
224
+ const devices = await $.db_device.listByModel(model.id);
225
+
226
+ if(devices == null)
227
+ continue;
228
+
229
+ for (const device of devices) {
230
+
231
+ if(device?.accept_release != release){
232
+ continue;
205
233
  }
206
- obj = obj[property]; // go deeper
207
- })
208
-
209
- let data = {};
210
- try{
211
- data = JSON.parse(payload)
212
- }catch(error){}
213
-
214
- if (true || (data && typeof payload === 'object' && !Array.isArray(payload) ) ) {
215
- // Assuming payload is an object with properties to process
216
- for (const [key, value] of Object.entries(data)) {
217
- obj[key] = value; // go deeper
218
- };
219
- }else{
220
- obj = payload
221
- }
222
234
 
223
- // update on device fw settings
224
- try{
225
- await $.db_project.updateSettings(project_name,JSON.stringify(settings),device.id);
226
- }catch(error){
227
- console.log(error);
235
+ let obj = null;
236
+ // insert filename on fota table for this device or update.
237
+ if(device?.app_version != latestAppVersion.app_version){
238
+ obj = {
239
+ model_id : device.model_id,
240
+ target_version : latestVersion.version,
241
+ target_app_version : latestAppVersion.app_version,
242
+ target_release : device.accept_release,
243
+ firmware_id : latestAppVersion.id,
244
+ nAttempts : 0,
245
+ fUpdate : true,
246
+ };
247
+ }else if(latestVersion?.version && (device?.version != latestVersion.version)){
248
+ obj = {
249
+ model_id : device.model_id,
250
+ target_version : latestVersion.version,
251
+ target_app_version : latestAppVersion.app_version,
252
+ target_release : device.accept_release,
253
+ firmware_id : latestVersion.id,
254
+ nAttempts : 0,
255
+ fUpdate : true,
256
+ };
257
+ }
258
+ if(obj != null){
259
+ try{
260
+ let res = await $.db_fota.getEntry(device.id,obj);
261
+ if(res == null)
262
+ $.db_fota.update(device.id,obj);
263
+ }catch(error){
264
+ console.log(error)
265
+ }
266
+ }
228
267
  }
268
+ }catch(error){
269
+ console.log(error);
270
+ continue;
229
271
  }
272
+ }
273
+ },
230
274
 
231
- if(project[project_name]){
232
- project[project_name].module.parseMessage(client,project_name,device,topic,payload,retain,()=>{});
275
+
276
+ triggerFota : async (release = "dev")=>{
277
+
278
+ const devices = await $.db_fota.getUpdatable(release);
279
+
280
+ const batchSize = 10;
281
+ for (let i = 0; i < devices.length; i += batchSize) {
282
+ const batch = devices.slice(i, i + batchSize);
283
+ await Promise.all(batch.map(async (device) => {
284
+
285
+ const firmware = await $.db_firmware.getById(device?.firmware_id)
286
+ if(firmware != null){
287
+ const project = await $.db_project.getById(device.project_id);
288
+ const project_name = project?.name;
289
+ const model = await $.db_model.getById(firmware.model_id);
290
+ const model_name = model?.name;
291
+ let topic = "";
292
+ if(model_name == "sniffer"){
293
+ // get sniffer info associated to device
294
+ const sniffer = await $.db_data.getGwAssociatedToDevice("sniffer",device.uid);
295
+ if(sniffer == null) return;
296
+ // get device_uid from devices table
297
+ const gw = await $.db_device.getById(sniffer?.id)
298
+ if(gw == null) return;
299
+ let mqtt_prefix = `${project_name}/${gw.uid}`;
300
+ topic = mqtt_prefix+"/app/sniffer/fota/update/set";
301
+ }
302
+ else{
303
+ let mqtt_prefix = `${project_name}/${device.uid}`;
304
+ topic = mqtt_prefix+"/fw/fota/update/set";
305
+ }
306
+ let link = `${$.config.web.protocol}${$.config.web.domain}${$.config.web.fw_path}${firmware?.filename}/download?token=${firmware?.token}`;
307
+ console.log(`Requesting firmware update of ${device.uid} to ${firmware?.filename}`);
308
+ $.mqtt_client.publish(topic,`{"url":"${link}"}`,{qos:1,retain:false});
309
+
310
+ let obj = {
311
+ nAttempts : ++device.nAttempts
312
+ }
313
+
314
+ await $.db_fota.update(device.id,obj);
315
+
316
+ obj = {
317
+ device_id : device.id,
318
+ local_version : device.version,
319
+ local_app_version : device.app_version,
320
+ target_version : firmware.version,
321
+ target_app_version : firmware.app_version,
322
+ target_file : firmware.filename,
323
+ nAttempt : device.nAttempts
324
+ }
325
+ await $.db_fota.newLog(device.id,obj);
326
+ }
327
+
328
+ }));
329
+
330
+ // Wait for 1 minute after processing each batch
331
+ if (i + batchSize < devices.length) {
332
+ await new Promise(resolve => setTimeout(resolve, 60000));
233
333
  }
234
334
  }
335
+ },
235
336
 
337
+ handleFotaSuccess : async (deviceId) => {
338
+ let object = {
339
+ "nAttempts" : 0,
340
+ "fUpdate" : 0,
341
+ }
342
+ await $.db_fota.update(deviceId,object);
343
+ object = {
344
+ success : 1,
345
+ }
346
+ await $.db_fota.updateLog(deviceId,obj);
236
347
  },
237
348
 
349
+ handleFotaError : async (deviceId, error) => {
350
+ let object = {
351
+ error : error,
352
+ }
353
+ await $.db_fota.updateLog(deviceId,obj);
354
+ }
238
355
  }
239
356
 
240
357