edilkamin 1.10.0 → 1.10.1
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/cli.js +105 -0
- package/dist/cjs/src/index.d.ts +2 -1
- package/dist/cjs/src/index.js +4 -1
- package/dist/cjs/src/library.d.ts +10 -1
- package/dist/cjs/src/library.js +199 -0
- package/dist/cjs/src/library.test.js +223 -0
- package/dist/cjs/src/types.d.ts +127 -1
- package/dist/cjs/src/types.js +64 -0
- package/dist/esm/package.json +1 -1
- package/dist/esm/src/cli.js +105 -0
- package/dist/esm/src/index.d.ts +2 -1
- package/dist/esm/src/index.js +1 -0
- package/dist/esm/src/library.d.ts +10 -1
- package/dist/esm/src/library.js +199 -0
- package/dist/esm/src/library.test.js +223 -0
- package/dist/esm/src/types.d.ts +127 -1
- package/dist/esm/src/types.js +63 -1
- package/package.json +1 -1
- package/src/cli.ts +156 -0
- package/src/index.ts +10 -0
- package/src/library.test.ts +290 -0
- package/src/library.ts +258 -0
- package/src/types.ts +180 -0
|
@@ -215,6 +215,17 @@ describe("library", () => {
|
|
|
215
215
|
"getLanguage",
|
|
216
216
|
"getPelletInReserve",
|
|
217
217
|
"getPelletAutonomyTime",
|
|
218
|
+
// Statistics getters
|
|
219
|
+
"getTotalCounters",
|
|
220
|
+
"getServiceCounters",
|
|
221
|
+
"getAlarmHistory",
|
|
222
|
+
"getRegenerationData",
|
|
223
|
+
"getServiceTime",
|
|
224
|
+
// Analytics functions
|
|
225
|
+
"getTotalOperatingHours",
|
|
226
|
+
"getPowerDistribution",
|
|
227
|
+
"getServiceStatus",
|
|
228
|
+
"getUsageAnalytics",
|
|
218
229
|
];
|
|
219
230
|
it("should create API methods with the correct baseURL", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
231
|
const baseURL = "https://example.com/api/";
|
|
@@ -786,6 +797,218 @@ describe("library", () => {
|
|
|
786
797
|
assert.equal(result, 240);
|
|
787
798
|
}));
|
|
788
799
|
});
|
|
800
|
+
describe("statistics getters", () => {
|
|
801
|
+
const mockDeviceInfoWithStats = {
|
|
802
|
+
status: {
|
|
803
|
+
commands: { power: true },
|
|
804
|
+
temperatures: { board: 25, enviroment: 20 },
|
|
805
|
+
flags: { is_pellet_in_reserve: false },
|
|
806
|
+
pellet: { autonomy_time: 900 },
|
|
807
|
+
counters: { service_time: 1108 },
|
|
808
|
+
},
|
|
809
|
+
nvm: {
|
|
810
|
+
user_parameters: {
|
|
811
|
+
language: 1,
|
|
812
|
+
is_auto: false,
|
|
813
|
+
is_fahrenheit: false,
|
|
814
|
+
is_sound_active: false,
|
|
815
|
+
enviroment_1_temperature: 19,
|
|
816
|
+
enviroment_2_temperature: 20,
|
|
817
|
+
enviroment_3_temperature: 20,
|
|
818
|
+
manual_power: 1,
|
|
819
|
+
fan_1_ventilation: 3,
|
|
820
|
+
fan_2_ventilation: 0,
|
|
821
|
+
fan_3_ventilation: 0,
|
|
822
|
+
is_standby_active: false,
|
|
823
|
+
standby_waiting_time: 60,
|
|
824
|
+
},
|
|
825
|
+
total_counters: {
|
|
826
|
+
power_ons: 278,
|
|
827
|
+
p1_working_time: 833,
|
|
828
|
+
p2_working_time: 15,
|
|
829
|
+
p3_working_time: 19,
|
|
830
|
+
p4_working_time: 8,
|
|
831
|
+
p5_working_time: 17,
|
|
832
|
+
},
|
|
833
|
+
service_counters: {
|
|
834
|
+
p1_working_time: 100,
|
|
835
|
+
p2_working_time: 10,
|
|
836
|
+
p3_working_time: 5,
|
|
837
|
+
p4_working_time: 2,
|
|
838
|
+
p5_working_time: 1,
|
|
839
|
+
},
|
|
840
|
+
alarms_log: {
|
|
841
|
+
number: 2,
|
|
842
|
+
index: 2,
|
|
843
|
+
alarms: [
|
|
844
|
+
{ type: 3, timestamp: 1700000000 },
|
|
845
|
+
{ type: 21, timestamp: 1700001000 },
|
|
846
|
+
],
|
|
847
|
+
},
|
|
848
|
+
regeneration: {
|
|
849
|
+
time: 0,
|
|
850
|
+
last_intervention: 1577836800,
|
|
851
|
+
daylight_time_flag: 0,
|
|
852
|
+
blackout_counter: 43,
|
|
853
|
+
airkare_working_hours_counter: 0,
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
it("should get total counters", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
858
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
859
|
+
const api = configure(API_URL);
|
|
860
|
+
const result = yield api.getTotalCounters(expectedToken, "00:11:22:33:44:55");
|
|
861
|
+
assert.deepEqual(result, mockDeviceInfoWithStats.nvm.total_counters);
|
|
862
|
+
}));
|
|
863
|
+
it("should get service counters", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
864
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
865
|
+
const api = configure(API_URL);
|
|
866
|
+
const result = yield api.getServiceCounters(expectedToken, "00:11:22:33:44:55");
|
|
867
|
+
assert.deepEqual(result, mockDeviceInfoWithStats.nvm.service_counters);
|
|
868
|
+
}));
|
|
869
|
+
it("should get alarm history", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
870
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
871
|
+
const api = configure(API_URL);
|
|
872
|
+
const result = yield api.getAlarmHistory(expectedToken, "00:11:22:33:44:55");
|
|
873
|
+
assert.deepEqual(result, mockDeviceInfoWithStats.nvm.alarms_log);
|
|
874
|
+
}));
|
|
875
|
+
it("should get regeneration data", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
876
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
877
|
+
const api = configure(API_URL);
|
|
878
|
+
const result = yield api.getRegenerationData(expectedToken, "00:11:22:33:44:55");
|
|
879
|
+
assert.deepEqual(result, mockDeviceInfoWithStats.nvm.regeneration);
|
|
880
|
+
}));
|
|
881
|
+
it("should get service time", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
882
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
883
|
+
const api = configure(API_URL);
|
|
884
|
+
const result = yield api.getServiceTime(expectedToken, "00:11:22:33:44:55");
|
|
885
|
+
assert.equal(result, 1108);
|
|
886
|
+
}));
|
|
887
|
+
});
|
|
888
|
+
describe("analytics functions", () => {
|
|
889
|
+
const mockDeviceInfoWithStats = {
|
|
890
|
+
status: {
|
|
891
|
+
commands: { power: true },
|
|
892
|
+
temperatures: { board: 25, enviroment: 20 },
|
|
893
|
+
flags: { is_pellet_in_reserve: false },
|
|
894
|
+
pellet: { autonomy_time: 900 },
|
|
895
|
+
counters: { service_time: 1108 },
|
|
896
|
+
},
|
|
897
|
+
nvm: {
|
|
898
|
+
user_parameters: {
|
|
899
|
+
language: 1,
|
|
900
|
+
is_auto: false,
|
|
901
|
+
is_fahrenheit: false,
|
|
902
|
+
is_sound_active: false,
|
|
903
|
+
enviroment_1_temperature: 19,
|
|
904
|
+
enviroment_2_temperature: 20,
|
|
905
|
+
enviroment_3_temperature: 20,
|
|
906
|
+
manual_power: 1,
|
|
907
|
+
fan_1_ventilation: 3,
|
|
908
|
+
fan_2_ventilation: 0,
|
|
909
|
+
fan_3_ventilation: 0,
|
|
910
|
+
is_standby_active: false,
|
|
911
|
+
standby_waiting_time: 60,
|
|
912
|
+
},
|
|
913
|
+
total_counters: {
|
|
914
|
+
power_ons: 278,
|
|
915
|
+
p1_working_time: 833,
|
|
916
|
+
p2_working_time: 15,
|
|
917
|
+
p3_working_time: 19,
|
|
918
|
+
p4_working_time: 8,
|
|
919
|
+
p5_working_time: 17,
|
|
920
|
+
},
|
|
921
|
+
service_counters: {
|
|
922
|
+
p1_working_time: 100,
|
|
923
|
+
p2_working_time: 10,
|
|
924
|
+
p3_working_time: 5,
|
|
925
|
+
p4_working_time: 2,
|
|
926
|
+
p5_working_time: 1,
|
|
927
|
+
},
|
|
928
|
+
alarms_log: {
|
|
929
|
+
number: 2,
|
|
930
|
+
index: 2,
|
|
931
|
+
alarms: [
|
|
932
|
+
{ type: 3, timestamp: 1700000000 },
|
|
933
|
+
{ type: 21, timestamp: 1700001000 },
|
|
934
|
+
],
|
|
935
|
+
},
|
|
936
|
+
regeneration: {
|
|
937
|
+
time: 0,
|
|
938
|
+
last_intervention: 1577836800,
|
|
939
|
+
daylight_time_flag: 0,
|
|
940
|
+
blackout_counter: 43,
|
|
941
|
+
airkare_working_hours_counter: 0,
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
it("should calculate total operating hours", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
946
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
947
|
+
const api = configure(API_URL);
|
|
948
|
+
const result = yield api.getTotalOperatingHours(expectedToken, "00:11:22:33:44:55");
|
|
949
|
+
// 833 + 15 + 19 + 8 + 17 = 892
|
|
950
|
+
assert.equal(result, 892);
|
|
951
|
+
}));
|
|
952
|
+
it("should calculate power distribution percentages", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
953
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
954
|
+
const api = configure(API_URL);
|
|
955
|
+
const result = yield api.getPowerDistribution(expectedToken, "00:11:22:33:44:55");
|
|
956
|
+
// Total: 892 hours
|
|
957
|
+
assert.ok(result.p1 > 90); // 833/892 = 93.4%
|
|
958
|
+
assert.ok(result.p2 < 5); // 15/892 = 1.7%
|
|
959
|
+
// Sum should be ~100%
|
|
960
|
+
const sum = result.p1 + result.p2 + result.p3 + result.p4 + result.p5;
|
|
961
|
+
assert.ok(Math.abs(sum - 100) < 0.1);
|
|
962
|
+
}));
|
|
963
|
+
it("should handle zero operating hours in power distribution", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
964
|
+
const zeroHoursInfo = Object.assign(Object.assign({}, mockDeviceInfoWithStats), { nvm: Object.assign(Object.assign({}, mockDeviceInfoWithStats.nvm), { total_counters: {
|
|
965
|
+
power_ons: 0,
|
|
966
|
+
p1_working_time: 0,
|
|
967
|
+
p2_working_time: 0,
|
|
968
|
+
p3_working_time: 0,
|
|
969
|
+
p4_working_time: 0,
|
|
970
|
+
p5_working_time: 0,
|
|
971
|
+
} }) });
|
|
972
|
+
fetchStub.resolves(mockResponse(zeroHoursInfo));
|
|
973
|
+
const api = configure(API_URL);
|
|
974
|
+
const result = yield api.getPowerDistribution(expectedToken, "00:11:22:33:44:55");
|
|
975
|
+
assert.deepEqual(result, { p1: 0, p2: 0, p3: 0, p4: 0, p5: 0 });
|
|
976
|
+
}));
|
|
977
|
+
it("should calculate service status", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
978
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
979
|
+
const api = configure(API_URL);
|
|
980
|
+
const result = yield api.getServiceStatus(expectedToken, "00:11:22:33:44:55");
|
|
981
|
+
assert.equal(result.totalServiceHours, 1108);
|
|
982
|
+
// 100 + 10 + 5 + 2 + 1 = 118 hours since service
|
|
983
|
+
assert.equal(result.hoursSinceService, 118);
|
|
984
|
+
assert.equal(result.isServiceDue, false); // 118 < 2000
|
|
985
|
+
}));
|
|
986
|
+
it("should indicate service is due when threshold exceeded", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
987
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
988
|
+
const api = configure(API_URL);
|
|
989
|
+
// Use threshold of 100 hours
|
|
990
|
+
const result = yield api.getServiceStatus(expectedToken, "00:11:22:33:44:55", 100);
|
|
991
|
+
assert.equal(result.isServiceDue, true); // 118 >= 100
|
|
992
|
+
}));
|
|
993
|
+
it("should get comprehensive usage analytics", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
994
|
+
fetchStub.resolves(mockResponse(mockDeviceInfoWithStats));
|
|
995
|
+
const api = configure(API_URL);
|
|
996
|
+
const result = yield api.getUsageAnalytics(expectedToken, "00:11:22:33:44:55");
|
|
997
|
+
assert.equal(result.totalPowerOns, 278);
|
|
998
|
+
assert.equal(result.totalOperatingHours, 892);
|
|
999
|
+
assert.equal(result.blackoutCount, 43);
|
|
1000
|
+
assert.equal(result.alarmCount, 2);
|
|
1001
|
+
assert.ok(result.lastMaintenanceDate instanceof Date);
|
|
1002
|
+
assert.equal(result.serviceStatus.isServiceDue, false);
|
|
1003
|
+
}));
|
|
1004
|
+
it("should handle null lastMaintenanceDate when timestamp is 0", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
1005
|
+
const noMaintenanceInfo = Object.assign(Object.assign({}, mockDeviceInfoWithStats), { nvm: Object.assign(Object.assign({}, mockDeviceInfoWithStats.nvm), { regeneration: Object.assign(Object.assign({}, mockDeviceInfoWithStats.nvm.regeneration), { last_intervention: 0 }) }) });
|
|
1006
|
+
fetchStub.resolves(mockResponse(noMaintenanceInfo));
|
|
1007
|
+
const api = configure(API_URL);
|
|
1008
|
+
const result = yield api.getUsageAnalytics(expectedToken, "00:11:22:33:44:55");
|
|
1009
|
+
assert.equal(result.lastMaintenanceDate, null);
|
|
1010
|
+
}));
|
|
1011
|
+
});
|
|
789
1012
|
describe("Error Handling", () => {
|
|
790
1013
|
const errorTests = [
|
|
791
1014
|
{ status: 400, statusText: "Bad Request" },
|
package/dist/esm/src/types.d.ts
CHANGED
|
@@ -19,11 +19,18 @@ interface GeneralFlagsType {
|
|
|
19
19
|
interface PelletAutonomyType {
|
|
20
20
|
autonomy_time: number;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Status counters including service time.
|
|
24
|
+
*/
|
|
25
|
+
interface StatusCountersType {
|
|
26
|
+
service_time: number;
|
|
27
|
+
}
|
|
22
28
|
interface StatusType {
|
|
23
29
|
commands: CommandsType;
|
|
24
30
|
temperatures: TemperaturesType;
|
|
25
31
|
flags: GeneralFlagsType;
|
|
26
32
|
pellet: PelletAutonomyType;
|
|
33
|
+
counters: StatusCountersType;
|
|
27
34
|
}
|
|
28
35
|
interface UserParametersType {
|
|
29
36
|
enviroment_1_temperature: number;
|
|
@@ -40,10 +47,128 @@ interface UserParametersType {
|
|
|
40
47
|
is_fahrenheit: boolean;
|
|
41
48
|
language: number;
|
|
42
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Lifetime operating counters - never reset.
|
|
52
|
+
* Tracks total power-on cycles and runtime hours per power level.
|
|
53
|
+
*/
|
|
54
|
+
interface TotalCountersType {
|
|
55
|
+
power_ons: number;
|
|
56
|
+
p1_working_time: number;
|
|
57
|
+
p2_working_time: number;
|
|
58
|
+
p3_working_time: number;
|
|
59
|
+
p4_working_time: number;
|
|
60
|
+
p5_working_time: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Service counters - reset after maintenance.
|
|
64
|
+
* Tracks runtime hours per power level since last service.
|
|
65
|
+
*/
|
|
66
|
+
interface ServiceCountersType {
|
|
67
|
+
p1_working_time: number;
|
|
68
|
+
p2_working_time: number;
|
|
69
|
+
p3_working_time: number;
|
|
70
|
+
p4_working_time: number;
|
|
71
|
+
p5_working_time: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Individual alarm entry from the alarm history log.
|
|
75
|
+
*/
|
|
76
|
+
interface AlarmEntryType {
|
|
77
|
+
type: number;
|
|
78
|
+
timestamp: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Alarm history log - circular buffer of recent alarms.
|
|
82
|
+
*/
|
|
83
|
+
interface AlarmsLogType {
|
|
84
|
+
number: number;
|
|
85
|
+
index: number;
|
|
86
|
+
alarms: AlarmEntryType[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Regeneration and maintenance tracking data.
|
|
90
|
+
*/
|
|
91
|
+
interface RegenerationDataType {
|
|
92
|
+
time: number;
|
|
93
|
+
last_intervention: number;
|
|
94
|
+
daylight_time_flag: number;
|
|
95
|
+
blackout_counter: number;
|
|
96
|
+
airkare_working_hours_counter: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Power level distribution as percentages.
|
|
100
|
+
*/
|
|
101
|
+
interface PowerDistributionType {
|
|
102
|
+
p1: number;
|
|
103
|
+
p2: number;
|
|
104
|
+
p3: number;
|
|
105
|
+
p4: number;
|
|
106
|
+
p5: number;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Service status with computed fields.
|
|
110
|
+
*/
|
|
111
|
+
interface ServiceStatusType {
|
|
112
|
+
totalServiceHours: number;
|
|
113
|
+
hoursSinceService: number;
|
|
114
|
+
serviceThresholdHours: number;
|
|
115
|
+
isServiceDue: boolean;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Comprehensive usage analytics.
|
|
119
|
+
*/
|
|
120
|
+
interface UsageAnalyticsType {
|
|
121
|
+
totalPowerOns: number;
|
|
122
|
+
totalOperatingHours: number;
|
|
123
|
+
powerDistribution: PowerDistributionType;
|
|
124
|
+
serviceStatus: ServiceStatusType;
|
|
125
|
+
blackoutCount: number;
|
|
126
|
+
lastMaintenanceDate: Date | null;
|
|
127
|
+
alarmCount: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Alarm type codes from Edilkamin devices.
|
|
131
|
+
* Based on AlarmsEnum.java from Android app (version 1.3.1-RC2 released 2025/12/10).
|
|
132
|
+
*/
|
|
133
|
+
declare enum AlarmCode {
|
|
134
|
+
NONE = 0,
|
|
135
|
+
FLUE_BLOCK = 1,
|
|
136
|
+
SMOKE_EXTRACTOR_FAILURE = 2,
|
|
137
|
+
END_PELLET = 3,
|
|
138
|
+
MISSING_LIGHTING = 4,
|
|
139
|
+
NO_NAME_ALARM = 5,
|
|
140
|
+
SMOKE_PROBE_FAILURE = 6,
|
|
141
|
+
FUMES_OVERTEMPERATURE = 7,
|
|
142
|
+
PELLET_TANK_SAFETY_THERMOSTAT = 8,
|
|
143
|
+
GEAR_MOTOR_FAILURE = 9,
|
|
144
|
+
BOARD_OVER_TEMPERATURE = 10,
|
|
145
|
+
SAFETY_PRESSURE_SWITCH_INTERVENTION = 11,
|
|
146
|
+
ROOM_PROBE_FAILURE = 12,
|
|
147
|
+
ROOM_PROBE_2_OR_BOILER_PROBE_FAILURE = 13,
|
|
148
|
+
ROOM_PROBE_3_OR_BOILER_PROBE_FAILURE = 14,
|
|
149
|
+
WATER_EARTHQUAKE_SAFETY_THERMOSTAT_INTERVENTION = 15,
|
|
150
|
+
WATER_PRESSURE_TRANSDUCER_FAILURE = 16,
|
|
151
|
+
OUTSIDE_PROBE_OR_LOW_STORAGE_PROBE_FAILURE = 17,
|
|
152
|
+
PUFFER_PROBE_FAILURE = 18,
|
|
153
|
+
CRUCIBLE_CLEANING_FAILURE = 19,
|
|
154
|
+
TRIAC_FAILURE = 20,
|
|
155
|
+
BLACKOUT = 21,
|
|
156
|
+
OPEN_DOOR = 22,
|
|
157
|
+
OVERTEMPERATURE_PANEL = 23,
|
|
158
|
+
BOARD_FUSE = 24
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Human-readable descriptions for alarm codes.
|
|
162
|
+
*/
|
|
163
|
+
declare const AlarmDescriptions: Record<AlarmCode, string>;
|
|
43
164
|
interface DeviceInfoType {
|
|
44
165
|
status: StatusType;
|
|
45
166
|
nvm: {
|
|
46
167
|
user_parameters: UserParametersType;
|
|
168
|
+
total_counters: TotalCountersType;
|
|
169
|
+
service_counters: ServiceCountersType;
|
|
170
|
+
alarms_log: AlarmsLogType;
|
|
171
|
+
regeneration: RegenerationDataType;
|
|
47
172
|
};
|
|
48
173
|
}
|
|
49
174
|
/**
|
|
@@ -99,4 +224,5 @@ interface DiscoveredDevice {
|
|
|
99
224
|
/** Signal strength in dBm (optional, not all platforms provide this) */
|
|
100
225
|
rssi?: number;
|
|
101
226
|
}
|
|
102
|
-
export type { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, GeneralFlagsType, PelletAutonomyType, StatusType, TemperaturesType, UserParametersType, };
|
|
227
|
+
export type { AlarmEntryType, AlarmsLogType, BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, GeneralFlagsType, PelletAutonomyType, PowerDistributionType, RegenerationDataType, ServiceCountersType, ServiceStatusType, StatusCountersType, StatusType, TemperaturesType, TotalCountersType, UsageAnalyticsType, UserParametersType, };
|
|
228
|
+
export { AlarmCode, AlarmDescriptions };
|
package/dist/esm/src/types.js
CHANGED
|
@@ -1 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Alarm type codes from Edilkamin devices.
|
|
3
|
+
* Based on AlarmsEnum.java from Android app (version 1.3.1-RC2 released 2025/12/10).
|
|
4
|
+
*/
|
|
5
|
+
var AlarmCode;
|
|
6
|
+
(function (AlarmCode) {
|
|
7
|
+
AlarmCode[AlarmCode["NONE"] = 0] = "NONE";
|
|
8
|
+
AlarmCode[AlarmCode["FLUE_BLOCK"] = 1] = "FLUE_BLOCK";
|
|
9
|
+
AlarmCode[AlarmCode["SMOKE_EXTRACTOR_FAILURE"] = 2] = "SMOKE_EXTRACTOR_FAILURE";
|
|
10
|
+
AlarmCode[AlarmCode["END_PELLET"] = 3] = "END_PELLET";
|
|
11
|
+
AlarmCode[AlarmCode["MISSING_LIGHTING"] = 4] = "MISSING_LIGHTING";
|
|
12
|
+
AlarmCode[AlarmCode["NO_NAME_ALARM"] = 5] = "NO_NAME_ALARM";
|
|
13
|
+
AlarmCode[AlarmCode["SMOKE_PROBE_FAILURE"] = 6] = "SMOKE_PROBE_FAILURE";
|
|
14
|
+
AlarmCode[AlarmCode["FUMES_OVERTEMPERATURE"] = 7] = "FUMES_OVERTEMPERATURE";
|
|
15
|
+
AlarmCode[AlarmCode["PELLET_TANK_SAFETY_THERMOSTAT"] = 8] = "PELLET_TANK_SAFETY_THERMOSTAT";
|
|
16
|
+
AlarmCode[AlarmCode["GEAR_MOTOR_FAILURE"] = 9] = "GEAR_MOTOR_FAILURE";
|
|
17
|
+
AlarmCode[AlarmCode["BOARD_OVER_TEMPERATURE"] = 10] = "BOARD_OVER_TEMPERATURE";
|
|
18
|
+
AlarmCode[AlarmCode["SAFETY_PRESSURE_SWITCH_INTERVENTION"] = 11] = "SAFETY_PRESSURE_SWITCH_INTERVENTION";
|
|
19
|
+
AlarmCode[AlarmCode["ROOM_PROBE_FAILURE"] = 12] = "ROOM_PROBE_FAILURE";
|
|
20
|
+
AlarmCode[AlarmCode["ROOM_PROBE_2_OR_BOILER_PROBE_FAILURE"] = 13] = "ROOM_PROBE_2_OR_BOILER_PROBE_FAILURE";
|
|
21
|
+
AlarmCode[AlarmCode["ROOM_PROBE_3_OR_BOILER_PROBE_FAILURE"] = 14] = "ROOM_PROBE_3_OR_BOILER_PROBE_FAILURE";
|
|
22
|
+
AlarmCode[AlarmCode["WATER_EARTHQUAKE_SAFETY_THERMOSTAT_INTERVENTION"] = 15] = "WATER_EARTHQUAKE_SAFETY_THERMOSTAT_INTERVENTION";
|
|
23
|
+
AlarmCode[AlarmCode["WATER_PRESSURE_TRANSDUCER_FAILURE"] = 16] = "WATER_PRESSURE_TRANSDUCER_FAILURE";
|
|
24
|
+
AlarmCode[AlarmCode["OUTSIDE_PROBE_OR_LOW_STORAGE_PROBE_FAILURE"] = 17] = "OUTSIDE_PROBE_OR_LOW_STORAGE_PROBE_FAILURE";
|
|
25
|
+
AlarmCode[AlarmCode["PUFFER_PROBE_FAILURE"] = 18] = "PUFFER_PROBE_FAILURE";
|
|
26
|
+
AlarmCode[AlarmCode["CRUCIBLE_CLEANING_FAILURE"] = 19] = "CRUCIBLE_CLEANING_FAILURE";
|
|
27
|
+
AlarmCode[AlarmCode["TRIAC_FAILURE"] = 20] = "TRIAC_FAILURE";
|
|
28
|
+
AlarmCode[AlarmCode["BLACKOUT"] = 21] = "BLACKOUT";
|
|
29
|
+
AlarmCode[AlarmCode["OPEN_DOOR"] = 22] = "OPEN_DOOR";
|
|
30
|
+
AlarmCode[AlarmCode["OVERTEMPERATURE_PANEL"] = 23] = "OVERTEMPERATURE_PANEL";
|
|
31
|
+
AlarmCode[AlarmCode["BOARD_FUSE"] = 24] = "BOARD_FUSE";
|
|
32
|
+
})(AlarmCode || (AlarmCode = {}));
|
|
33
|
+
/**
|
|
34
|
+
* Human-readable descriptions for alarm codes.
|
|
35
|
+
*/
|
|
36
|
+
const AlarmDescriptions = {
|
|
37
|
+
[AlarmCode.NONE]: "No alarm",
|
|
38
|
+
[AlarmCode.FLUE_BLOCK]: "Flue/chimney blockage",
|
|
39
|
+
[AlarmCode.SMOKE_EXTRACTOR_FAILURE]: "Smoke extractor motor failure",
|
|
40
|
+
[AlarmCode.END_PELLET]: "Pellet tank empty",
|
|
41
|
+
[AlarmCode.MISSING_LIGHTING]: "Ignition failure",
|
|
42
|
+
[AlarmCode.NO_NAME_ALARM]: "Unnamed alarm",
|
|
43
|
+
[AlarmCode.SMOKE_PROBE_FAILURE]: "Smoke temperature probe failure",
|
|
44
|
+
[AlarmCode.FUMES_OVERTEMPERATURE]: "Exhaust overtemperature",
|
|
45
|
+
[AlarmCode.PELLET_TANK_SAFETY_THERMOSTAT]: "Pellet tank safety thermostat triggered",
|
|
46
|
+
[AlarmCode.GEAR_MOTOR_FAILURE]: "Gear motor/auger failure",
|
|
47
|
+
[AlarmCode.BOARD_OVER_TEMPERATURE]: "Control board overtemperature",
|
|
48
|
+
[AlarmCode.SAFETY_PRESSURE_SWITCH_INTERVENTION]: "Safety pressure switch activated",
|
|
49
|
+
[AlarmCode.ROOM_PROBE_FAILURE]: "Room temperature probe failure",
|
|
50
|
+
[AlarmCode.ROOM_PROBE_2_OR_BOILER_PROBE_FAILURE]: "Room probe 2 or boiler probe failure",
|
|
51
|
+
[AlarmCode.ROOM_PROBE_3_OR_BOILER_PROBE_FAILURE]: "Room probe 3 or boiler probe failure",
|
|
52
|
+
[AlarmCode.WATER_EARTHQUAKE_SAFETY_THERMOSTAT_INTERVENTION]: "Water/earthquake safety thermostat",
|
|
53
|
+
[AlarmCode.WATER_PRESSURE_TRANSDUCER_FAILURE]: "Water pressure transducer failure",
|
|
54
|
+
[AlarmCode.OUTSIDE_PROBE_OR_LOW_STORAGE_PROBE_FAILURE]: "Outside probe or low storage probe failure",
|
|
55
|
+
[AlarmCode.PUFFER_PROBE_FAILURE]: "Buffer tank probe failure",
|
|
56
|
+
[AlarmCode.CRUCIBLE_CLEANING_FAILURE]: "Crucible cleaning failure",
|
|
57
|
+
[AlarmCode.TRIAC_FAILURE]: "Power control (TRIAC) failure",
|
|
58
|
+
[AlarmCode.BLACKOUT]: "Power outage detected",
|
|
59
|
+
[AlarmCode.OPEN_DOOR]: "Door open alarm",
|
|
60
|
+
[AlarmCode.OVERTEMPERATURE_PANEL]: "Panel overtemperature",
|
|
61
|
+
[AlarmCode.BOARD_FUSE]: "Control board fuse issue",
|
|
62
|
+
};
|
|
63
|
+
export { AlarmCode, AlarmDescriptions };
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { version } from "../package.json";
|
|
|
6
6
|
import { NEW_API_URL, OLD_API_URL } from "./constants";
|
|
7
7
|
import { configure, configureAmplify, getSession, signIn } from "./library";
|
|
8
8
|
import { clearSession, createFileStorage } from "./token-storage";
|
|
9
|
+
import { AlarmCode, AlarmDescriptions } from "./types";
|
|
9
10
|
|
|
10
11
|
const promptPassword = (): Promise<string> => {
|
|
11
12
|
const rl = readline.createInterface({
|
|
@@ -336,6 +337,117 @@ const createProgram = (): Command => {
|
|
|
336
337
|
mac: string,
|
|
337
338
|
) => api.getPelletAutonomyTime(jwtToken, mac),
|
|
338
339
|
},
|
|
340
|
+
// Statistics getters
|
|
341
|
+
{
|
|
342
|
+
commandName: "getTotalCounters",
|
|
343
|
+
description:
|
|
344
|
+
"Get lifetime operating counters (power-ons, runtime by power level)",
|
|
345
|
+
getter: (
|
|
346
|
+
api: ReturnType<typeof configure>,
|
|
347
|
+
jwtToken: string,
|
|
348
|
+
mac: string,
|
|
349
|
+
) => api.getTotalCounters(jwtToken, mac),
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
commandName: "getServiceCounters",
|
|
353
|
+
description: "Get service counters (runtime since last maintenance)",
|
|
354
|
+
getter: (
|
|
355
|
+
api: ReturnType<typeof configure>,
|
|
356
|
+
jwtToken: string,
|
|
357
|
+
mac: string,
|
|
358
|
+
) => api.getServiceCounters(jwtToken, mac),
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
commandName: "getRegenerationData",
|
|
362
|
+
description: "Get regeneration and maintenance data",
|
|
363
|
+
getter: (
|
|
364
|
+
api: ReturnType<typeof configure>,
|
|
365
|
+
jwtToken: string,
|
|
366
|
+
mac: string,
|
|
367
|
+
) => api.getRegenerationData(jwtToken, mac),
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
commandName: "getServiceTime",
|
|
371
|
+
description: "Get total service time in hours",
|
|
372
|
+
getter: (
|
|
373
|
+
api: ReturnType<typeof configure>,
|
|
374
|
+
jwtToken: string,
|
|
375
|
+
mac: string,
|
|
376
|
+
) => api.getServiceTime(jwtToken, mac),
|
|
377
|
+
},
|
|
378
|
+
// Analytics getters
|
|
379
|
+
{
|
|
380
|
+
commandName: "getTotalOperatingHours",
|
|
381
|
+
description: "Get total operating hours across all power levels",
|
|
382
|
+
getter: (
|
|
383
|
+
api: ReturnType<typeof configure>,
|
|
384
|
+
jwtToken: string,
|
|
385
|
+
mac: string,
|
|
386
|
+
) => api.getTotalOperatingHours(jwtToken, mac),
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
commandName: "getPowerDistribution",
|
|
390
|
+
description: "Get power level usage distribution as percentages",
|
|
391
|
+
getter: async (
|
|
392
|
+
api: ReturnType<typeof configure>,
|
|
393
|
+
jwtToken: string,
|
|
394
|
+
mac: string,
|
|
395
|
+
) => {
|
|
396
|
+
const result = await api.getPowerDistribution(jwtToken, mac);
|
|
397
|
+
return {
|
|
398
|
+
p1: `${result.p1.toFixed(1)}%`,
|
|
399
|
+
p2: `${result.p2.toFixed(1)}%`,
|
|
400
|
+
p3: `${result.p3.toFixed(1)}%`,
|
|
401
|
+
p4: `${result.p4.toFixed(1)}%`,
|
|
402
|
+
p5: `${result.p5.toFixed(1)}%`,
|
|
403
|
+
};
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
commandName: "getServiceStatus",
|
|
408
|
+
description: "Get service status including whether maintenance is due",
|
|
409
|
+
getter: (
|
|
410
|
+
api: ReturnType<typeof configure>,
|
|
411
|
+
jwtToken: string,
|
|
412
|
+
mac: string,
|
|
413
|
+
) => api.getServiceStatus(jwtToken, mac),
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
commandName: "getUsageAnalytics",
|
|
417
|
+
description: "Get comprehensive usage analytics in single response",
|
|
418
|
+
getter: async (
|
|
419
|
+
api: ReturnType<typeof configure>,
|
|
420
|
+
jwtToken: string,
|
|
421
|
+
mac: string,
|
|
422
|
+
) => {
|
|
423
|
+
const analytics = await api.getUsageAnalytics(jwtToken, mac);
|
|
424
|
+
return {
|
|
425
|
+
lifetime: {
|
|
426
|
+
powerOnCount: analytics.totalPowerOns,
|
|
427
|
+
totalOperatingHours: analytics.totalOperatingHours,
|
|
428
|
+
blackoutCount: analytics.blackoutCount,
|
|
429
|
+
},
|
|
430
|
+
powerDistribution: {
|
|
431
|
+
p1: `${analytics.powerDistribution.p1.toFixed(1)}%`,
|
|
432
|
+
p2: `${analytics.powerDistribution.p2.toFixed(1)}%`,
|
|
433
|
+
p3: `${analytics.powerDistribution.p3.toFixed(1)}%`,
|
|
434
|
+
p4: `${analytics.powerDistribution.p4.toFixed(1)}%`,
|
|
435
|
+
p5: `${analytics.powerDistribution.p5.toFixed(1)}%`,
|
|
436
|
+
},
|
|
437
|
+
service: {
|
|
438
|
+
totalServiceHours: analytics.serviceStatus.totalServiceHours,
|
|
439
|
+
hoursSinceLastService: analytics.serviceStatus.hoursSinceService,
|
|
440
|
+
thresholdHours: analytics.serviceStatus.serviceThresholdHours,
|
|
441
|
+
isServiceDue: analytics.serviceStatus.isServiceDue,
|
|
442
|
+
lastMaintenanceDate:
|
|
443
|
+
analytics.lastMaintenanceDate?.toISOString() || "Never",
|
|
444
|
+
},
|
|
445
|
+
alarms: {
|
|
446
|
+
totalCount: analytics.alarmCount,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
},
|
|
450
|
+
},
|
|
339
451
|
].forEach(({ commandName, description, getter }) => {
|
|
340
452
|
addLegacyOption(
|
|
341
453
|
addMacOption(
|
|
@@ -676,6 +788,50 @@ const createProgram = (): Command => {
|
|
|
676
788
|
console.log(JSON.stringify(result, null, 2));
|
|
677
789
|
});
|
|
678
790
|
|
|
791
|
+
// Alarm history command with human-readable descriptions
|
|
792
|
+
addLegacyOption(
|
|
793
|
+
addMacOption(
|
|
794
|
+
addAuthOptions(
|
|
795
|
+
program
|
|
796
|
+
.command("getAlarmHistory")
|
|
797
|
+
.description(
|
|
798
|
+
"Get alarm history log with human-readable descriptions",
|
|
799
|
+
),
|
|
800
|
+
),
|
|
801
|
+
),
|
|
802
|
+
).action(async (options) => {
|
|
803
|
+
const { username, password, mac, legacy = false } = options;
|
|
804
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
805
|
+
const storage = createFileStorage();
|
|
806
|
+
configureAmplify(storage);
|
|
807
|
+
let jwtToken: string;
|
|
808
|
+
try {
|
|
809
|
+
jwtToken = await getSession(false, legacy);
|
|
810
|
+
} catch {
|
|
811
|
+
if (!username) {
|
|
812
|
+
throw new Error(
|
|
813
|
+
"No session found. Please provide --username to sign in.",
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
const pwd = password || (await promptPassword());
|
|
817
|
+
jwtToken = await signIn(username, pwd, legacy);
|
|
818
|
+
}
|
|
819
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
820
|
+
const api = configure(apiUrl);
|
|
821
|
+
const result = await api.getAlarmHistory(jwtToken, normalizedMac);
|
|
822
|
+
// Format alarms with human-readable descriptions
|
|
823
|
+
const formattedAlarms = result.alarms.map((alarm) => ({
|
|
824
|
+
...alarm,
|
|
825
|
+
typeName: AlarmCode[alarm.type] || "UNKNOWN",
|
|
826
|
+
description:
|
|
827
|
+
AlarmDescriptions[alarm.type as AlarmCode] || "Unknown alarm",
|
|
828
|
+
date: new Date(alarm.timestamp * 1000).toISOString(),
|
|
829
|
+
}));
|
|
830
|
+
console.log(
|
|
831
|
+
JSON.stringify({ ...result, alarms: formattedAlarms }, null, 2),
|
|
832
|
+
);
|
|
833
|
+
});
|
|
834
|
+
|
|
679
835
|
// Command: register
|
|
680
836
|
addLegacyOption(
|
|
681
837
|
addAuthOptions(
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ export {
|
|
|
10
10
|
serialNumberToHex,
|
|
11
11
|
} from "./serial-utils";
|
|
12
12
|
export {
|
|
13
|
+
AlarmEntryType,
|
|
14
|
+
AlarmsLogType,
|
|
13
15
|
BufferEncodedType,
|
|
14
16
|
CommandsType,
|
|
15
17
|
DeviceAssociationBody,
|
|
@@ -18,10 +20,18 @@ export {
|
|
|
18
20
|
DeviceInfoType,
|
|
19
21
|
DiscoveredDevice,
|
|
20
22
|
EditDeviceAssociationBody,
|
|
23
|
+
PowerDistributionType,
|
|
24
|
+
RegenerationDataType,
|
|
25
|
+
ServiceCountersType,
|
|
26
|
+
ServiceStatusType,
|
|
27
|
+
StatusCountersType,
|
|
21
28
|
StatusType,
|
|
22
29
|
TemperaturesType,
|
|
30
|
+
TotalCountersType,
|
|
31
|
+
UsageAnalyticsType,
|
|
23
32
|
UserParametersType,
|
|
24
33
|
} from "./types";
|
|
34
|
+
export { AlarmCode, AlarmDescriptions } from "./types";
|
|
25
35
|
|
|
26
36
|
export const {
|
|
27
37
|
deviceInfo,
|