edilkamin 1.6.1 → 1.7.2
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/.github/workflows/cli-tests.yml +1 -1
- package/.github/workflows/documentation.yml +1 -1
- package/.github/workflows/publish.yml +5 -3
- package/.github/workflows/tests.yml +1 -1
- package/README.md +61 -7
- package/dist/esm/buffer-utils.d.ts +25 -0
- package/dist/esm/buffer-utils.js +70 -0
- package/dist/esm/buffer-utils.test.d.ts +1 -0
- package/dist/esm/buffer-utils.test.js +181 -0
- package/dist/esm/cli.js +110 -9
- package/dist/esm/constants.d.ts +3 -1
- package/dist/esm/constants.js +4 -2
- package/dist/esm/index.d.ts +7 -4
- package/dist/esm/index.js +6 -3
- package/dist/esm/library.d.ts +18 -4
- package/dist/esm/library.js +87 -9
- package/dist/esm/library.test.js +321 -1
- package/dist/esm/serial-utils.d.ts +33 -0
- package/dist/esm/serial-utils.js +45 -0
- package/dist/esm/serial-utils.test.d.ts +1 -0
- package/dist/esm/serial-utils.test.js +48 -0
- package/dist/esm/token-storage.d.ts +14 -0
- package/dist/esm/token-storage.js +81 -0
- package/dist/esm/types.d.ts +49 -1
- package/eslint.config.mjs +12 -1
- package/package.json +5 -3
- package/src/buffer-utils.test.ts +225 -0
- package/src/buffer-utils.ts +83 -0
- package/src/cli.ts +192 -33
- package/src/constants.ts +5 -2
- package/src/index.ts +16 -2
- package/src/library.test.ts +402 -5
- package/src/library.ts +140 -14
- package/src/serial-utils.test.ts +64 -0
- package/src/serial-utils.ts +50 -0
- package/src/token-storage.ts +78 -0
- package/src/types.ts +60 -0
package/src/cli.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { Command } from "commander";
|
|
|
3
3
|
import readline from "readline";
|
|
4
4
|
|
|
5
5
|
import { version } from "../package.json";
|
|
6
|
-
import {
|
|
6
|
+
import { NEW_API_URL, OLD_API_URL } from "./constants";
|
|
7
|
+
import { configure, configureAmplify, getSession, signIn } from "./library";
|
|
8
|
+
import { clearSession, createFileStorage } from "./token-storage";
|
|
7
9
|
|
|
8
10
|
const promptPassword = (): Promise<string> => {
|
|
9
11
|
const rl = readline.createInterface({
|
|
@@ -29,12 +31,16 @@ const promptPassword = (): Promise<string> => {
|
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Adds common options (username and password) to a command.
|
|
34
|
+
* Username is optional if a session already exists.
|
|
32
35
|
* @param command The command to which options should be added.
|
|
33
36
|
* @returns The command with options added.
|
|
34
37
|
*/
|
|
35
38
|
const addAuthOptions = (command: Command): Command =>
|
|
36
39
|
command
|
|
37
|
-
.
|
|
40
|
+
.option(
|
|
41
|
+
"-u, --username <username>",
|
|
42
|
+
"Username (optional if session exists)",
|
|
43
|
+
)
|
|
38
44
|
.option("-p, --password <password>", "Password");
|
|
39
45
|
|
|
40
46
|
/**
|
|
@@ -45,25 +51,54 @@ const addAuthOptions = (command: Command): Command =>
|
|
|
45
51
|
const addMacOption = (command: Command): Command =>
|
|
46
52
|
command.requiredOption("-m, --mac <macAddress>", "MAC address of the device");
|
|
47
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Adds legacy API option to a command.
|
|
56
|
+
* @param command The command to which the legacy option should be added.
|
|
57
|
+
* @returns The command with the legacy option added.
|
|
58
|
+
*/
|
|
59
|
+
const addLegacyOption = (command: Command): Command =>
|
|
60
|
+
command.option("--legacy", "Use legacy API endpoint (old AWS Gateway)");
|
|
61
|
+
|
|
48
62
|
/**
|
|
49
63
|
* Handles common authentication and API initialization logic.
|
|
64
|
+
* Tries to use existing session first, falls back to sign-in if needed.
|
|
50
65
|
* @param options The options passed from the CLI command.
|
|
51
66
|
* @returns An object containing the normalized MAC, JWT token, and configured API instance.
|
|
52
67
|
*/
|
|
53
68
|
const initializeCommand = async (options: {
|
|
54
|
-
username
|
|
69
|
+
username?: string;
|
|
55
70
|
password?: string;
|
|
56
71
|
mac: string;
|
|
72
|
+
legacy?: boolean;
|
|
57
73
|
}): Promise<{
|
|
58
74
|
normalizedMac: string;
|
|
59
75
|
jwtToken: string;
|
|
60
76
|
api: ReturnType<typeof configure>;
|
|
61
77
|
}> => {
|
|
62
|
-
const { username, password, mac } = options;
|
|
78
|
+
const { username, password, mac, legacy = false } = options;
|
|
63
79
|
const normalizedMac = mac.replace(/:/g, "");
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
80
|
+
|
|
81
|
+
// Initialize file storage for session persistence
|
|
82
|
+
const storage = createFileStorage();
|
|
83
|
+
configureAmplify(storage);
|
|
84
|
+
|
|
85
|
+
let jwtToken: string;
|
|
86
|
+
try {
|
|
87
|
+
// Try to get existing session first
|
|
88
|
+
jwtToken = await getSession(false, legacy);
|
|
89
|
+
} catch {
|
|
90
|
+
// No session, need to sign in
|
|
91
|
+
if (!username) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"No session found. Please provide --username to sign in.",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const pwd = password || (await promptPassword());
|
|
97
|
+
jwtToken = await signIn(username, pwd, legacy);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
101
|
+
const api = configure(apiUrl);
|
|
67
102
|
return { normalizedMac, jwtToken, api };
|
|
68
103
|
};
|
|
69
104
|
|
|
@@ -73,12 +108,17 @@ const initializeCommand = async (options: {
|
|
|
73
108
|
* @param getter A function to call on the configured API object.
|
|
74
109
|
*/
|
|
75
110
|
const executeGetter = async (
|
|
76
|
-
options: {
|
|
111
|
+
options: {
|
|
112
|
+
username?: string;
|
|
113
|
+
password?: string;
|
|
114
|
+
mac: string;
|
|
115
|
+
legacy?: boolean;
|
|
116
|
+
},
|
|
77
117
|
getter: (
|
|
78
118
|
api: ReturnType<typeof configure>,
|
|
79
119
|
jwtToken: string,
|
|
80
|
-
mac: string
|
|
81
|
-
) => Promise<unknown
|
|
120
|
+
mac: string,
|
|
121
|
+
) => Promise<unknown>,
|
|
82
122
|
): Promise<void> => {
|
|
83
123
|
const { normalizedMac, jwtToken, api } = await initializeCommand(options);
|
|
84
124
|
const result = await getter(api, jwtToken, normalizedMac);
|
|
@@ -91,13 +131,19 @@ const executeGetter = async (
|
|
|
91
131
|
* @param setter A function to call on the configured API object.
|
|
92
132
|
*/
|
|
93
133
|
const executeSetter = async (
|
|
94
|
-
options: {
|
|
134
|
+
options: {
|
|
135
|
+
username?: string;
|
|
136
|
+
password?: string;
|
|
137
|
+
mac: string;
|
|
138
|
+
value: number;
|
|
139
|
+
legacy?: boolean;
|
|
140
|
+
},
|
|
95
141
|
setter: (
|
|
96
142
|
api: ReturnType<typeof configure>,
|
|
97
143
|
jwtToken: string,
|
|
98
144
|
mac: string,
|
|
99
|
-
value: number
|
|
100
|
-
) => Promise<unknown
|
|
145
|
+
value: number,
|
|
146
|
+
) => Promise<unknown>,
|
|
101
147
|
): Promise<void> => {
|
|
102
148
|
const { normalizedMac, jwtToken, api } = await initializeCommand(options);
|
|
103
149
|
const result = await setter(api, jwtToken, normalizedMac, options.value);
|
|
@@ -111,14 +157,29 @@ const createProgram = (): Command => {
|
|
|
111
157
|
.description("CLI tool for interacting with the Edilkamin API")
|
|
112
158
|
.version(version);
|
|
113
159
|
// Command: signIn
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
160
|
+
program
|
|
161
|
+
.command("signIn")
|
|
162
|
+
.description("Sign in and retrieve a JWT token")
|
|
163
|
+
.requiredOption("-u, --username <username>", "Username")
|
|
164
|
+
.option("-p, --password <password>", "Password")
|
|
165
|
+
.action(async (options) => {
|
|
166
|
+
const { username, password } = options;
|
|
167
|
+
// Initialize file storage for session persistence
|
|
168
|
+
const storage = createFileStorage();
|
|
169
|
+
configureAmplify(storage);
|
|
170
|
+
const pwd = password || (await promptPassword());
|
|
171
|
+
const jwtToken = await signIn(username, pwd);
|
|
172
|
+
console.log("JWT Token:", jwtToken);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Command: logout
|
|
176
|
+
program
|
|
177
|
+
.command("logout")
|
|
178
|
+
.description("Clear stored session")
|
|
179
|
+
.action(async () => {
|
|
180
|
+
await clearSession();
|
|
181
|
+
console.log("Session cleared successfully");
|
|
182
|
+
});
|
|
122
183
|
// Generic getter commands
|
|
123
184
|
[
|
|
124
185
|
{
|
|
@@ -127,7 +188,7 @@ const createProgram = (): Command => {
|
|
|
127
188
|
getter: (
|
|
128
189
|
api: ReturnType<typeof configure>,
|
|
129
190
|
jwtToken: string,
|
|
130
|
-
mac: string
|
|
191
|
+
mac: string,
|
|
131
192
|
) => api.deviceInfo(jwtToken, mac),
|
|
132
193
|
},
|
|
133
194
|
{
|
|
@@ -136,7 +197,7 @@ const createProgram = (): Command => {
|
|
|
136
197
|
getter: (
|
|
137
198
|
api: ReturnType<typeof configure>,
|
|
138
199
|
jwtToken: string,
|
|
139
|
-
mac: string
|
|
200
|
+
mac: string,
|
|
140
201
|
) => api.getPower(jwtToken, mac),
|
|
141
202
|
},
|
|
142
203
|
{
|
|
@@ -145,7 +206,7 @@ const createProgram = (): Command => {
|
|
|
145
206
|
getter: (
|
|
146
207
|
api: ReturnType<typeof configure>,
|
|
147
208
|
jwtToken: string,
|
|
148
|
-
mac: string
|
|
209
|
+
mac: string,
|
|
149
210
|
) => api.getEnvironmentTemperature(jwtToken, mac),
|
|
150
211
|
},
|
|
151
212
|
{
|
|
@@ -154,12 +215,14 @@ const createProgram = (): Command => {
|
|
|
154
215
|
getter: (
|
|
155
216
|
api: ReturnType<typeof configure>,
|
|
156
217
|
jwtToken: string,
|
|
157
|
-
mac: string
|
|
218
|
+
mac: string,
|
|
158
219
|
) => api.getTargetTemperature(jwtToken, mac),
|
|
159
220
|
},
|
|
160
221
|
].forEach(({ commandName, description, getter }) => {
|
|
161
|
-
|
|
162
|
-
|
|
222
|
+
addLegacyOption(
|
|
223
|
+
addMacOption(
|
|
224
|
+
addAuthOptions(program.command(commandName).description(description)),
|
|
225
|
+
),
|
|
163
226
|
).action((options) => executeGetter(options, getter));
|
|
164
227
|
});
|
|
165
228
|
// Generic setter commands
|
|
@@ -171,7 +234,7 @@ const createProgram = (): Command => {
|
|
|
171
234
|
api: ReturnType<typeof configure>,
|
|
172
235
|
jwtToken: string,
|
|
173
236
|
mac: string,
|
|
174
|
-
value: number
|
|
237
|
+
value: number,
|
|
175
238
|
) => api.setPower(jwtToken, mac, value),
|
|
176
239
|
},
|
|
177
240
|
{
|
|
@@ -181,17 +244,113 @@ const createProgram = (): Command => {
|
|
|
181
244
|
api: ReturnType<typeof configure>,
|
|
182
245
|
jwtToken: string,
|
|
183
246
|
mac: string,
|
|
184
|
-
value: number
|
|
247
|
+
value: number,
|
|
185
248
|
) => api.setTargetTemperature(jwtToken, mac, value),
|
|
186
249
|
},
|
|
187
250
|
].forEach(({ commandName, description, setter }) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
251
|
+
addLegacyOption(
|
|
252
|
+
addMacOption(
|
|
253
|
+
addAuthOptions(
|
|
254
|
+
program.command(commandName).description(description),
|
|
255
|
+
).requiredOption("-v, --value <number>", "Value to set", parseFloat),
|
|
256
|
+
),
|
|
192
257
|
).action((options) => executeSetter(options, setter));
|
|
193
258
|
});
|
|
194
259
|
|
|
260
|
+
// Command: register
|
|
261
|
+
addLegacyOption(
|
|
262
|
+
addAuthOptions(
|
|
263
|
+
program
|
|
264
|
+
.command("register")
|
|
265
|
+
.description("Register a device with your account"),
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")
|
|
269
|
+
.requiredOption("-s, --serial <serialNumber>", "Device serial number")
|
|
270
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
271
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
272
|
+
.action(async (options) => {
|
|
273
|
+
const {
|
|
274
|
+
username,
|
|
275
|
+
password,
|
|
276
|
+
mac,
|
|
277
|
+
serial,
|
|
278
|
+
name,
|
|
279
|
+
room,
|
|
280
|
+
legacy = false,
|
|
281
|
+
} = options;
|
|
282
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
283
|
+
|
|
284
|
+
// Initialize file storage for session persistence
|
|
285
|
+
const storage = createFileStorage();
|
|
286
|
+
configureAmplify(storage);
|
|
287
|
+
|
|
288
|
+
let jwtToken: string;
|
|
289
|
+
try {
|
|
290
|
+
jwtToken = await getSession(false, legacy);
|
|
291
|
+
} catch {
|
|
292
|
+
if (!username) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
"No session found. Please provide --username to sign in.",
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const pwd = password || (await promptPassword());
|
|
298
|
+
jwtToken = await signIn(username, pwd, legacy);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
302
|
+
const api = configure(apiUrl);
|
|
303
|
+
const result = await api.registerDevice(
|
|
304
|
+
jwtToken,
|
|
305
|
+
normalizedMac,
|
|
306
|
+
serial,
|
|
307
|
+
name,
|
|
308
|
+
room,
|
|
309
|
+
);
|
|
310
|
+
console.log("Device registered successfully:");
|
|
311
|
+
console.log(JSON.stringify(result, null, 2));
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Command: editDevice
|
|
315
|
+
addLegacyOption(
|
|
316
|
+
addMacOption(
|
|
317
|
+
addAuthOptions(
|
|
318
|
+
program
|
|
319
|
+
.command("editDevice")
|
|
320
|
+
.description("Update device name and room"),
|
|
321
|
+
),
|
|
322
|
+
),
|
|
323
|
+
)
|
|
324
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
325
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
326
|
+
.action(async (options) => {
|
|
327
|
+
const { username, password, mac, name, room, legacy = false } = options;
|
|
328
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
329
|
+
|
|
330
|
+
// Initialize file storage for session persistence
|
|
331
|
+
const storage = createFileStorage();
|
|
332
|
+
configureAmplify(storage);
|
|
333
|
+
|
|
334
|
+
let jwtToken: string;
|
|
335
|
+
try {
|
|
336
|
+
jwtToken = await getSession(false, legacy);
|
|
337
|
+
} catch {
|
|
338
|
+
if (!username) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
"No session found. Please provide --username to sign in.",
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
const pwd = password || (await promptPassword());
|
|
344
|
+
jwtToken = await signIn(username, pwd, legacy);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
348
|
+
const api = configure(apiUrl);
|
|
349
|
+
const result = await api.editDevice(jwtToken, normalizedMac, name, room);
|
|
350
|
+
console.log("Device updated successfully:");
|
|
351
|
+
console.log(JSON.stringify(result, null, 2));
|
|
352
|
+
});
|
|
353
|
+
|
|
195
354
|
return program;
|
|
196
355
|
};
|
|
197
356
|
|
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
const
|
|
1
|
+
const OLD_API_URL =
|
|
2
|
+
"https://fxtj7xkgc6.execute-api.eu-central-1.amazonaws.com/prod/";
|
|
3
|
+
const NEW_API_URL = "https://the-mind-api.edilkamin.com/";
|
|
4
|
+
const API_URL = NEW_API_URL;
|
|
2
5
|
|
|
3
|
-
export { API_URL };
|
|
6
|
+
export { API_URL, NEW_API_URL, OLD_API_URL };
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { configure } from "./library";
|
|
2
2
|
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
3
|
+
export { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
4
|
+
export { API_URL, NEW_API_URL, OLD_API_URL } from "./constants";
|
|
5
|
+
export { configure, getSession, signIn } from "./library";
|
|
5
6
|
export {
|
|
7
|
+
serialNumberDisplay,
|
|
8
|
+
serialNumberFromHex,
|
|
9
|
+
serialNumberToHex,
|
|
10
|
+
} from "./serial-utils";
|
|
11
|
+
export { clearSession } from "./token-storage";
|
|
12
|
+
export {
|
|
13
|
+
BufferEncodedType,
|
|
6
14
|
CommandsType,
|
|
15
|
+
DeviceAssociationBody,
|
|
16
|
+
DeviceAssociationResponse,
|
|
17
|
+
DeviceInfoRawType,
|
|
7
18
|
DeviceInfoType,
|
|
19
|
+
EditDeviceAssociationBody,
|
|
8
20
|
StatusType,
|
|
9
21
|
TemperaturesType,
|
|
10
22
|
UserParametersType,
|
|
@@ -12,6 +24,8 @@ export {
|
|
|
12
24
|
|
|
13
25
|
export const {
|
|
14
26
|
deviceInfo,
|
|
27
|
+
registerDevice,
|
|
28
|
+
editDevice,
|
|
15
29
|
setPower,
|
|
16
30
|
setPowerOff,
|
|
17
31
|
setPowerOn,
|