edilkamin 1.10.1 → 1.11.0
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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/bluetooth-protocol.d.ts +178 -0
- package/dist/cjs/src/bluetooth-protocol.js +423 -0
- package/dist/cjs/src/bluetooth-protocol.test.d.ts +1 -0
- package/dist/cjs/src/bluetooth-protocol.test.js +389 -0
- package/dist/cjs/src/bluetooth.d.ts +2 -0
- package/dist/cjs/src/bluetooth.js +17 -1
- package/dist/cjs/src/index.d.ts +1 -1
- package/dist/cjs/src/index.js +2 -1
- package/dist/cjs/src/library.d.ts +16 -0
- package/dist/cjs/src/library.js +59 -40
- package/dist/cjs/src/library.test.js +133 -0
- package/dist/esm/package.json +1 -1
- package/dist/esm/src/bluetooth-protocol.d.ts +178 -0
- package/dist/esm/src/bluetooth-protocol.js +415 -0
- package/dist/esm/src/bluetooth-protocol.test.d.ts +1 -0
- package/dist/esm/src/bluetooth-protocol.test.js +387 -0
- package/dist/esm/src/bluetooth.d.ts +2 -0
- package/dist/esm/src/bluetooth.js +8 -0
- package/dist/esm/src/index.d.ts +1 -1
- package/dist/esm/src/index.js +1 -1
- package/dist/esm/src/library.d.ts +16 -0
- package/dist/esm/src/library.js +57 -39
- package/dist/esm/src/library.test.js +134 -1
- package/package.json +1 -1
- package/src/bluetooth-protocol.test.ts +497 -0
- package/src/bluetooth-protocol.ts +524 -0
- package/src/bluetooth.ts +21 -0
- package/src/index.ts +1 -1
- package/src/library.test.ts +173 -1
- package/src/library.ts +69 -48
package/src/library.test.ts
CHANGED
|
@@ -3,7 +3,11 @@ import * as amplifyAuth from "aws-amplify/auth";
|
|
|
3
3
|
import pako from "pako";
|
|
4
4
|
import sinon from "sinon";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
configure,
|
|
8
|
+
createAuthService,
|
|
9
|
+
deriveUsageAnalytics,
|
|
10
|
+
} from "../src/library";
|
|
7
11
|
import { API_URL } from "./constants";
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -1307,6 +1311,174 @@ describe("library", () => {
|
|
|
1307
1311
|
});
|
|
1308
1312
|
});
|
|
1309
1313
|
|
|
1314
|
+
describe("deriveUsageAnalytics", () => {
|
|
1315
|
+
const mockDeviceInfoForDerive = {
|
|
1316
|
+
status: {
|
|
1317
|
+
commands: {
|
|
1318
|
+
power: false,
|
|
1319
|
+
},
|
|
1320
|
+
temperatures: {
|
|
1321
|
+
enviroment: 20,
|
|
1322
|
+
set_air: 21,
|
|
1323
|
+
get_air: 20,
|
|
1324
|
+
set_water: 40,
|
|
1325
|
+
get_water: 35,
|
|
1326
|
+
},
|
|
1327
|
+
counters: {
|
|
1328
|
+
service_time: 1108,
|
|
1329
|
+
},
|
|
1330
|
+
flags: {
|
|
1331
|
+
is_pellet_in_reserve: false,
|
|
1332
|
+
},
|
|
1333
|
+
pellet: {
|
|
1334
|
+
autonomy_time: 180,
|
|
1335
|
+
},
|
|
1336
|
+
},
|
|
1337
|
+
nvm: {
|
|
1338
|
+
user_parameters: {
|
|
1339
|
+
language: 1,
|
|
1340
|
+
is_auto: false,
|
|
1341
|
+
is_fahrenheit: false,
|
|
1342
|
+
is_sound_active: false,
|
|
1343
|
+
enviroment_1_temperature: 19,
|
|
1344
|
+
enviroment_2_temperature: 20,
|
|
1345
|
+
enviroment_3_temperature: 20,
|
|
1346
|
+
manual_power: 1,
|
|
1347
|
+
fan_1_ventilation: 3,
|
|
1348
|
+
fan_2_ventilation: 0,
|
|
1349
|
+
fan_3_ventilation: 0,
|
|
1350
|
+
is_standby_active: false,
|
|
1351
|
+
standby_waiting_time: 60,
|
|
1352
|
+
},
|
|
1353
|
+
total_counters: {
|
|
1354
|
+
power_ons: 278,
|
|
1355
|
+
p1_working_time: 833,
|
|
1356
|
+
p2_working_time: 15,
|
|
1357
|
+
p3_working_time: 19,
|
|
1358
|
+
p4_working_time: 8,
|
|
1359
|
+
p5_working_time: 17,
|
|
1360
|
+
},
|
|
1361
|
+
service_counters: {
|
|
1362
|
+
p1_working_time: 100,
|
|
1363
|
+
p2_working_time: 10,
|
|
1364
|
+
p3_working_time: 5,
|
|
1365
|
+
p4_working_time: 2,
|
|
1366
|
+
p5_working_time: 1,
|
|
1367
|
+
},
|
|
1368
|
+
regeneration: {
|
|
1369
|
+
time: 0,
|
|
1370
|
+
last_intervention: 1577836800,
|
|
1371
|
+
daylight_time_flag: 0,
|
|
1372
|
+
blackout_counter: 43,
|
|
1373
|
+
airkare_working_hours_counter: 0,
|
|
1374
|
+
},
|
|
1375
|
+
alarms_log: {
|
|
1376
|
+
number: 6,
|
|
1377
|
+
index: 6,
|
|
1378
|
+
alarms: [],
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
it("should derive analytics from device info without API call", () => {
|
|
1384
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1385
|
+
const analytics = deriveUsageAnalytics(mockDeviceInfoForDerive as any);
|
|
1386
|
+
|
|
1387
|
+
assert.equal(analytics.totalPowerOns, 278);
|
|
1388
|
+
assert.equal(analytics.totalOperatingHours, 892); // 833+15+19+8+17
|
|
1389
|
+
assert.equal(analytics.blackoutCount, 43);
|
|
1390
|
+
assert.equal(analytics.alarmCount, 6);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
it("should calculate power distribution correctly", () => {
|
|
1394
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1395
|
+
const analytics = deriveUsageAnalytics(mockDeviceInfoForDerive as any);
|
|
1396
|
+
|
|
1397
|
+
// P1: 833/892 ≈ 93.4%
|
|
1398
|
+
assert.ok(analytics.powerDistribution.p1 > 93);
|
|
1399
|
+
assert.ok(analytics.powerDistribution.p1 < 94);
|
|
1400
|
+
|
|
1401
|
+
// Sum should be 100%
|
|
1402
|
+
const sum = Object.values(analytics.powerDistribution).reduce(
|
|
1403
|
+
(a, b) => a + b,
|
|
1404
|
+
0,
|
|
1405
|
+
);
|
|
1406
|
+
assert.ok(Math.abs(sum - 100) < 0.001);
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
it("should handle zero operating hours", () => {
|
|
1410
|
+
const zeroHoursInfo = {
|
|
1411
|
+
...mockDeviceInfoForDerive,
|
|
1412
|
+
nvm: {
|
|
1413
|
+
...mockDeviceInfoForDerive.nvm,
|
|
1414
|
+
total_counters: {
|
|
1415
|
+
power_ons: 0,
|
|
1416
|
+
p1_working_time: 0,
|
|
1417
|
+
p2_working_time: 0,
|
|
1418
|
+
p3_working_time: 0,
|
|
1419
|
+
p4_working_time: 0,
|
|
1420
|
+
p5_working_time: 0,
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
};
|
|
1424
|
+
|
|
1425
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1426
|
+
const analytics = deriveUsageAnalytics(zeroHoursInfo as any);
|
|
1427
|
+
assert.deepEqual(analytics.powerDistribution, {
|
|
1428
|
+
p1: 0,
|
|
1429
|
+
p2: 0,
|
|
1430
|
+
p3: 0,
|
|
1431
|
+
p4: 0,
|
|
1432
|
+
p5: 0,
|
|
1433
|
+
});
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
it("should respect custom service threshold", () => {
|
|
1437
|
+
const analytics = deriveUsageAnalytics(
|
|
1438
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1439
|
+
mockDeviceInfoForDerive as any,
|
|
1440
|
+
100,
|
|
1441
|
+
);
|
|
1442
|
+
|
|
1443
|
+
// 118 hours since service >= 100 threshold
|
|
1444
|
+
assert.equal(analytics.serviceStatus.isServiceDue, true);
|
|
1445
|
+
assert.equal(analytics.serviceStatus.serviceThresholdHours, 100);
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
it("should use default threshold of 2000 hours", () => {
|
|
1449
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1450
|
+
const analytics = deriveUsageAnalytics(mockDeviceInfoForDerive as any);
|
|
1451
|
+
|
|
1452
|
+
assert.equal(analytics.serviceStatus.serviceThresholdHours, 2000);
|
|
1453
|
+
assert.equal(analytics.serviceStatus.isServiceDue, false); // 118 < 2000
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
it("should convert last_intervention timestamp to Date", () => {
|
|
1457
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1458
|
+
const analytics = deriveUsageAnalytics(mockDeviceInfoForDerive as any);
|
|
1459
|
+
|
|
1460
|
+
assert.ok(analytics.lastMaintenanceDate instanceof Date);
|
|
1461
|
+
assert.equal(analytics.lastMaintenanceDate?.getTime(), 1577836800 * 1000);
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
it("should return null for lastMaintenanceDate when timestamp is 0", () => {
|
|
1465
|
+
const noMaintenanceInfo = {
|
|
1466
|
+
...mockDeviceInfoForDerive,
|
|
1467
|
+
nvm: {
|
|
1468
|
+
...mockDeviceInfoForDerive.nvm,
|
|
1469
|
+
regeneration: {
|
|
1470
|
+
...mockDeviceInfoForDerive.nvm.regeneration,
|
|
1471
|
+
last_intervention: 0,
|
|
1472
|
+
},
|
|
1473
|
+
},
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1477
|
+
const analytics = deriveUsageAnalytics(noMaintenanceInfo as any);
|
|
1478
|
+
assert.equal(analytics.lastMaintenanceDate, null);
|
|
1479
|
+
});
|
|
1480
|
+
});
|
|
1481
|
+
|
|
1310
1482
|
describe("Error Handling", () => {
|
|
1311
1483
|
const errorTests = [
|
|
1312
1484
|
{ status: 400, statusText: "Bad Request" },
|
package/src/library.ts
CHANGED
|
@@ -718,6 +718,74 @@ const getServiceTime =
|
|
|
718
718
|
*/
|
|
719
719
|
const DEFAULT_SERVICE_THRESHOLD = 2000;
|
|
720
720
|
|
|
721
|
+
/**
|
|
722
|
+
* Derives usage analytics from an existing DeviceInfo response.
|
|
723
|
+
* This is a pure function that performs client-side calculations without API calls.
|
|
724
|
+
*
|
|
725
|
+
* Use this when you already have a DeviceInfo object (e.g., from a previous deviceInfo() call)
|
|
726
|
+
* to avoid making an additional API request.
|
|
727
|
+
*
|
|
728
|
+
* @param {DeviceInfoType} deviceInfo - The device info response object.
|
|
729
|
+
* @param {number} [serviceThreshold=2000] - Service threshold in hours.
|
|
730
|
+
* @returns {UsageAnalyticsType} - Comprehensive usage analytics.
|
|
731
|
+
*
|
|
732
|
+
* @example
|
|
733
|
+
* const info = await api.deviceInfo(token, mac);
|
|
734
|
+
* const analytics = deriveUsageAnalytics(info);
|
|
735
|
+
*/
|
|
736
|
+
export const deriveUsageAnalytics = (
|
|
737
|
+
deviceInfo: DeviceInfoType,
|
|
738
|
+
serviceThreshold: number = DEFAULT_SERVICE_THRESHOLD,
|
|
739
|
+
): UsageAnalyticsType => {
|
|
740
|
+
const totalCounters = deviceInfo.nvm.total_counters;
|
|
741
|
+
const serviceCounters = deviceInfo.nvm.service_counters;
|
|
742
|
+
const regeneration = deviceInfo.nvm.regeneration;
|
|
743
|
+
const alarmsLog = deviceInfo.nvm.alarms_log;
|
|
744
|
+
|
|
745
|
+
const totalOperatingHours =
|
|
746
|
+
totalCounters.p1_working_time +
|
|
747
|
+
totalCounters.p2_working_time +
|
|
748
|
+
totalCounters.p3_working_time +
|
|
749
|
+
totalCounters.p4_working_time +
|
|
750
|
+
totalCounters.p5_working_time;
|
|
751
|
+
|
|
752
|
+
const hoursSinceService =
|
|
753
|
+
serviceCounters.p1_working_time +
|
|
754
|
+
serviceCounters.p2_working_time +
|
|
755
|
+
serviceCounters.p3_working_time +
|
|
756
|
+
serviceCounters.p4_working_time +
|
|
757
|
+
serviceCounters.p5_working_time;
|
|
758
|
+
|
|
759
|
+
const powerDistribution: PowerDistributionType =
|
|
760
|
+
totalOperatingHours === 0
|
|
761
|
+
? { p1: 0, p2: 0, p3: 0, p4: 0, p5: 0 }
|
|
762
|
+
: {
|
|
763
|
+
p1: (totalCounters.p1_working_time / totalOperatingHours) * 100,
|
|
764
|
+
p2: (totalCounters.p2_working_time / totalOperatingHours) * 100,
|
|
765
|
+
p3: (totalCounters.p3_working_time / totalOperatingHours) * 100,
|
|
766
|
+
p4: (totalCounters.p4_working_time / totalOperatingHours) * 100,
|
|
767
|
+
p5: (totalCounters.p5_working_time / totalOperatingHours) * 100,
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
return {
|
|
771
|
+
totalPowerOns: totalCounters.power_ons,
|
|
772
|
+
totalOperatingHours,
|
|
773
|
+
powerDistribution,
|
|
774
|
+
serviceStatus: {
|
|
775
|
+
totalServiceHours: deviceInfo.status.counters.service_time,
|
|
776
|
+
hoursSinceService,
|
|
777
|
+
serviceThresholdHours: serviceThreshold,
|
|
778
|
+
isServiceDue: hoursSinceService >= serviceThreshold,
|
|
779
|
+
},
|
|
780
|
+
blackoutCount: regeneration.blackout_counter,
|
|
781
|
+
lastMaintenanceDate:
|
|
782
|
+
regeneration.last_intervention > 0
|
|
783
|
+
? new Date(regeneration.last_intervention * 1000)
|
|
784
|
+
: null,
|
|
785
|
+
alarmCount: alarmsLog.number,
|
|
786
|
+
};
|
|
787
|
+
};
|
|
788
|
+
|
|
721
789
|
const getTotalOperatingHours =
|
|
722
790
|
(baseURL: string) =>
|
|
723
791
|
/**
|
|
@@ -821,54 +889,7 @@ const getUsageAnalytics =
|
|
|
821
889
|
serviceThreshold: number = DEFAULT_SERVICE_THRESHOLD,
|
|
822
890
|
): Promise<UsageAnalyticsType> => {
|
|
823
891
|
const info = await deviceInfo(baseURL)(jwtToken, macAddress);
|
|
824
|
-
|
|
825
|
-
const totalCounters = info.nvm.total_counters;
|
|
826
|
-
const serviceCounters = info.nvm.service_counters;
|
|
827
|
-
const regeneration = info.nvm.regeneration;
|
|
828
|
-
const alarmsLog = info.nvm.alarms_log;
|
|
829
|
-
|
|
830
|
-
const totalOperatingHours =
|
|
831
|
-
totalCounters.p1_working_time +
|
|
832
|
-
totalCounters.p2_working_time +
|
|
833
|
-
totalCounters.p3_working_time +
|
|
834
|
-
totalCounters.p4_working_time +
|
|
835
|
-
totalCounters.p5_working_time;
|
|
836
|
-
|
|
837
|
-
const hoursSinceService =
|
|
838
|
-
serviceCounters.p1_working_time +
|
|
839
|
-
serviceCounters.p2_working_time +
|
|
840
|
-
serviceCounters.p3_working_time +
|
|
841
|
-
serviceCounters.p4_working_time +
|
|
842
|
-
serviceCounters.p5_working_time;
|
|
843
|
-
|
|
844
|
-
const powerDistribution: PowerDistributionType =
|
|
845
|
-
totalOperatingHours === 0
|
|
846
|
-
? { p1: 0, p2: 0, p3: 0, p4: 0, p5: 0 }
|
|
847
|
-
: {
|
|
848
|
-
p1: (totalCounters.p1_working_time / totalOperatingHours) * 100,
|
|
849
|
-
p2: (totalCounters.p2_working_time / totalOperatingHours) * 100,
|
|
850
|
-
p3: (totalCounters.p3_working_time / totalOperatingHours) * 100,
|
|
851
|
-
p4: (totalCounters.p4_working_time / totalOperatingHours) * 100,
|
|
852
|
-
p5: (totalCounters.p5_working_time / totalOperatingHours) * 100,
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
return {
|
|
856
|
-
totalPowerOns: totalCounters.power_ons,
|
|
857
|
-
totalOperatingHours,
|
|
858
|
-
powerDistribution,
|
|
859
|
-
serviceStatus: {
|
|
860
|
-
totalServiceHours: info.status.counters.service_time,
|
|
861
|
-
hoursSinceService,
|
|
862
|
-
serviceThresholdHours: serviceThreshold,
|
|
863
|
-
isServiceDue: hoursSinceService >= serviceThreshold,
|
|
864
|
-
},
|
|
865
|
-
blackoutCount: regeneration.blackout_counter,
|
|
866
|
-
lastMaintenanceDate:
|
|
867
|
-
regeneration.last_intervention > 0
|
|
868
|
-
? new Date(regeneration.last_intervention * 1000)
|
|
869
|
-
: null,
|
|
870
|
-
alarmCount: alarmsLog.number,
|
|
871
|
-
};
|
|
892
|
+
return deriveUsageAnalytics(info, serviceThreshold);
|
|
872
893
|
};
|
|
873
894
|
|
|
874
895
|
const registerDevice =
|