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/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 { configure, signIn } from "./library";
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
- .requiredOption("-u, --username <username>", "Username")
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: string;
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
- const pwd = password || (await promptPassword());
65
- const jwtToken = await signIn(username, pwd);
66
- const api = configure();
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: { username: string; password?: string; mac: string },
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: { username: string; password?: string; mac: string; value: number },
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
- addAuthOptions(
115
- program.command("signIn").description("Sign in and retrieve a JWT token")
116
- ).action(async (options) => {
117
- const { username, password } = options;
118
- const pwd = password || (await promptPassword());
119
- const jwtToken = await signIn(username, pwd);
120
- console.log("JWT Token:", jwtToken);
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
- addMacOption(
162
- addAuthOptions(program.command(commandName).description(description))
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
- addMacOption(
189
- addAuthOptions(
190
- program.command(commandName).description(description)
191
- ).requiredOption("-v, --value <number>", "Value to set", parseFloat)
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 API_URL = "https://the-mind-api.edilkamin.com/";
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 { API_URL } from "./constants";
4
- export { configure, signIn } from "./library";
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,