mqtt-devices-parser 1.0.10 → 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,25 +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
35
  parseMessage : async (client,topic,payload,retain)=>{
55
36
 
56
37
  let topic_bck = topic;
@@ -60,23 +41,36 @@ var self = module.exports = {
60
41
  let index = topic.indexOf("/");
61
42
  if(index == -1)
62
43
  return;
44
+
63
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;
64
52
 
65
53
  // --- uid ---
66
54
  topic = topic.substring(index+1);
67
55
  index = topic.indexOf("/");
68
56
  if(index == -1)
69
57
  return;
70
- let uid = topic.substring(0,index);
71
58
 
59
+ let uid = topic.substring(0,index);
72
60
  topic = topic.substring(index+1);
73
61
 
74
62
  // check if topic corresponds to a device
75
- if(uid.startsWith(MACRO_UID_PREFIX)){
63
+ if(uid.startsWith(project.uidPrefix) && uid.length == project?.uidLength){
64
+
76
65
  let device = await $.db_device.get(uid);
77
66
 
67
+ // Insert device if not exists on db
78
68
  if(device == null){
79
- $.db_device.insert(uid)
69
+ let obj = {
70
+ uid : uid,
71
+ accept_release : "prod",
72
+ }
73
+ $.db_device.insert(obj)
80
74
  .then(async()=>{
81
75
  device = await $.db_device.get(uid);
82
76
  })
@@ -84,71 +78,54 @@ var self = module.exports = {
84
78
  }
85
79
 
86
80
  // update project id if needed
87
- let res = await $.db_project.getByName(project_name);
88
- let project_id = res?.id;
89
81
  if(project_id != null && project_id != device?.project_id)
90
- $.db_device.updateProject(uid,project_id);
91
-
92
- if(topic.endsWith("status")){
93
- if(payload == "online" || payload == "offline")
94
- await $.db_device.updateStatus(uid,payload);
95
- console.log(project_name,uid,payload)
96
- }else if(topic.endsWith("model")){
97
- let res = await $.db_model.getByName(payload);
98
- let model_id = res?.id;
99
-
100
- if(model_id != null){
101
- let res = await $.db_device.updateModel(uid,model_id);
102
- }
103
- }else if(topic.endsWith("tech")){
104
- await $.db_device.updateTech(uid,payload);
105
- }else if(topic.endsWith("fw_version")){
106
- let fw_version = await $.db.getFieldFromDeviceId(project_name,device.id,"fw_version");
107
- if(fw_version != payload)
108
- await $.db_data.update(project_name,device.id,"fota_tries",1); // ! 0 is not working..
109
- }else if(topic.endsWith("app_version")){
110
- let app_version = await $.db.getFieldFromDeviceId(project_name,device.id,"app_version");
111
- if(app_version != payload)
112
- await $.db_data.update(project_name,device.id,"fota_tries",1); // ! 0 is not working..
113
- }else if(topic.endsWith("uptime")){ // !! maybe this topic should be change for something ending in fota
114
- // check fw version
115
- // get current fw and app version
116
- let release = await $.db.getFieldFromDeviceId(project_name,device.id,"fw_release");
117
- if(release == null || release == "critical"){
118
- // don't update
119
- return;
120
- }
121
- let fota_tries = await $.db.getFieldFromDeviceId(project_name,device.id,"fota_tries");
122
- if(fota_tries > 3)
123
- return;
124
-
125
- let fw_version = await $.db.getFieldFromDeviceId(project_name,device.id,"fw_version");
126
- let app_version = await $.db.getFieldFromDeviceId(project_name,device.id,"app_version");
127
- let new_firmware = await $.db_firmware.getLatestFWVersion(device.model_id,release);
128
- let new_app = await $.db_firmware.getLatestAppVersion(device.model_id,release);
129
- if(new_firmware == null || new_app == null)
130
- return;
131
-
132
- let mqtt_prefix = `${project_name}/${uid}`;
133
-
134
- if(new_app != null && semver.lt(app_version, new_app.app_version)) {
135
- console.log(new_app)
136
- console.log(`updating firmware of ${uid} to minor version ${new_app.app_version}`);
137
- let link = `${$.config.web.protocol}${$.config.web.domain}${$.config.web.fw_path}${new_app.filename}/download?token=${new_app.token}`;
138
- client.publish(mqtt_prefix+"/fw/fota/update/set",`{"url":"${link}"}`,{qos:2,retain:false});
139
- await $.db_data.update(project_name,device.id,"fota_tries",++fota_tries);
140
- }else{
141
- if(new_firmware != null && semver.lt(fw_version, new_firmware.fw_version)) {
142
- console.log(`updating firmware of ${uid} to major version ${new_firmware.fw_version}`);
143
- let link = `${$.config.web.protocol}${$.config.web.domain}${$.config.web.fw_path}${new_firmware.filename}/download?token=${new_firmware.token}`;
144
- client.publish(mqtt_prefix+"/fw/fota/update/set",`{"url":"${link}"}`,{qos:2,retain:false});
145
- await $.db_data.update(project_name,device.id,"fota_tries",++fota_tries);
82
+ $.db_device.update(device.id,"project_id",project_id);
83
+
84
+ switch (topic) {
85
+ case "status":
86
+ if (payload === "online" || payload === "offline") {
87
+ await $.db_device.update(device.id, "status", payload);
146
88
  }
147
- }
148
-
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;
149
119
  }
150
120
 
151
- 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);
152
129
  let res = await $.db_sensor.getByRef(topic)
153
130
  if(res != null){
154
131
  await $.db_sensor.insert(logs_table,device.id,res.id,payload);
@@ -157,60 +134,224 @@ var self = module.exports = {
157
134
 
158
135
  // store remote device settings on project table - twin model
159
136
  if(topic.endsWith("/set") && payload != "" ){
137
+ self.updateSettings(project_name,device,topic,payload);
138
+ }
160
139
 
161
- let route = topic.split("/");
140
+ if(project[project_name]){
141
+ project[project_name].module.parseMessage(client,project_name,device,topic,payload,retain,()=>{});
142
+ }
143
+ }
162
144
 
163
- if(route == null){
164
- console.log("topic invalid:",topic);
165
- return;
166
- }
167
- // get settings
168
- let settings = await $.db_project.getSettings(project_name,device.id);
169
- let obj = settings;
145
+ },
170
146
 
171
- if(obj == null){
172
- console.log("no settings available for deviceId:",device.id)
173
- obj = {};
174
- }
175
- // Traverse through all route parts except the last one
176
- route.slice(0, -1).forEach(property => {
177
- if (!obj.hasOwnProperty(property) ||
178
- ( obj.hasOwnProperty(property) && typeof obj[property] !== 'object'))
179
- {
180
- obj[property] = {}; // create nested object if missing or not an object
147
+ deleteLogs : async()=>{
148
+
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;
181
233
  }
182
- obj = obj[property]; // go deeper
183
- })
184
-
185
- let data = {};
186
- try{
187
- data = JSON.parse(payload)
188
- }catch(error){}
189
-
190
- if (true || (data && typeof payload === 'object' && !Array.isArray(payload) ) ) {
191
- // Assuming payload is an object with properties to process
192
- for (const [key, value] of Object.entries(data)) {
193
- obj[key] = value; // go deeper
194
- };
195
- }else{
196
- obj = payload
197
- }
198
234
 
199
- // update on device fw settings
200
- try{
201
- await $.db_project.updateSettings(project_name,JSON.stringify(settings),device.id);
202
- }catch(error){
203
- 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
+ }
204
267
  }
268
+ }catch(error){
269
+ console.log(error);
270
+ continue;
205
271
  }
272
+ }
273
+ },
206
274
 
207
- if(project[project_name]){
208
- 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));
209
333
  }
210
334
  }
335
+ },
211
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);
212
347
  },
213
348
 
349
+ handleFotaError : async (deviceId, error) => {
350
+ let object = {
351
+ error : error,
352
+ }
353
+ await $.db_fota.updateLog(deviceId,obj);
354
+ }
214
355
  }
215
356
 
216
357