edilkamin 1.7.2 → 1.7.4

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.
Files changed (65) hide show
  1. package/.github/workflows/cli-tests.yml +6 -0
  2. package/.github/workflows/publish.yml +1 -1
  3. package/README.md +1 -1
  4. package/dist/cjs/package.json +84 -0
  5. package/dist/cjs/src/browser-bundle.test.js +64 -0
  6. package/dist/cjs/src/buffer-utils.js +78 -0
  7. package/dist/cjs/src/buffer-utils.test.js +186 -0
  8. package/dist/cjs/src/cli.js +253 -0
  9. package/dist/cjs/src/configureAmplify.test.js +42 -0
  10. package/dist/cjs/src/constants.js +9 -0
  11. package/dist/{esm → cjs/src}/index.d.ts +1 -2
  12. package/dist/cjs/src/index.js +22 -0
  13. package/dist/{esm → cjs/src}/library.d.ts +4 -4
  14. package/dist/cjs/src/library.js +324 -0
  15. package/dist/cjs/src/library.test.js +547 -0
  16. package/dist/cjs/src/serial-utils.js +50 -0
  17. package/dist/cjs/src/serial-utils.test.d.ts +1 -0
  18. package/dist/cjs/src/serial-utils.test.js +50 -0
  19. package/dist/cjs/src/token-storage.js +119 -0
  20. package/dist/cjs/src/types.js +2 -0
  21. package/dist/esm/package.json +84 -0
  22. package/dist/esm/src/browser-bundle.test.d.ts +1 -0
  23. package/dist/esm/src/browser-bundle.test.js +29 -0
  24. package/dist/esm/src/buffer-utils.d.ts +25 -0
  25. package/dist/esm/src/buffer-utils.test.d.ts +1 -0
  26. package/dist/esm/src/cli.d.ts +3 -0
  27. package/dist/esm/src/configureAmplify.test.d.ts +1 -0
  28. package/dist/esm/src/configureAmplify.test.js +37 -0
  29. package/dist/esm/src/constants.d.ts +4 -0
  30. package/dist/esm/src/index.d.ts +6 -0
  31. package/dist/esm/{index.js → src/index.js} +0 -1
  32. package/dist/esm/src/library.d.ts +55 -0
  33. package/dist/esm/{library.js → src/library.js} +59 -51
  34. package/dist/esm/src/library.test.d.ts +1 -0
  35. package/dist/esm/{library.test.js → src/library.test.js} +167 -190
  36. package/dist/esm/src/serial-utils.d.ts +33 -0
  37. package/dist/esm/src/serial-utils.test.d.ts +1 -0
  38. package/dist/esm/src/token-storage.d.ts +14 -0
  39. package/dist/esm/src/types.d.ts +73 -0
  40. package/dist/esm/src/types.js +1 -0
  41. package/package.json +19 -7
  42. package/src/browser-bundle.test.ts +21 -0
  43. package/src/configureAmplify.test.ts +47 -0
  44. package/src/index.ts +0 -1
  45. package/src/library.test.ts +195 -197
  46. package/src/library.ts +76 -62
  47. package/tsconfig.cjs.json +2 -2
  48. package/tsconfig.json +2 -3
  49. /package/dist/{esm/buffer-utils.test.d.ts → cjs/src/browser-bundle.test.d.ts} +0 -0
  50. /package/dist/{esm → cjs/src}/buffer-utils.d.ts +0 -0
  51. /package/dist/{esm/library.test.d.ts → cjs/src/buffer-utils.test.d.ts} +0 -0
  52. /package/dist/{esm → cjs/src}/cli.d.ts +0 -0
  53. /package/dist/{esm/serial-utils.test.d.ts → cjs/src/configureAmplify.test.d.ts} +0 -0
  54. /package/dist/{esm → cjs/src}/constants.d.ts +0 -0
  55. /package/dist/{esm/types.js → cjs/src/library.test.d.ts} +0 -0
  56. /package/dist/{esm → cjs/src}/serial-utils.d.ts +0 -0
  57. /package/dist/{esm → cjs/src}/token-storage.d.ts +0 -0
  58. /package/dist/{esm → cjs/src}/types.d.ts +0 -0
  59. /package/dist/esm/{buffer-utils.js → src/buffer-utils.js} +0 -0
  60. /package/dist/esm/{buffer-utils.test.js → src/buffer-utils.test.js} +0 -0
  61. /package/dist/esm/{cli.js → src/cli.js} +0 -0
  62. /package/dist/esm/{constants.js → src/constants.js} +0 -0
  63. /package/dist/esm/{serial-utils.js → src/serial-utils.js} +0 -0
  64. /package/dist/esm/{serial-utils.test.js → src/serial-utils.test.js} +0 -0
  65. /package/dist/esm/{token-storage.js → src/token-storage.js} +0 -0
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { strict as assert } from "assert";
11
- import axios from "axios";
12
11
  import pako from "pako";
13
12
  import sinon from "sinon";
14
13
  import { configure, createAuthService } from "../src/library";
@@ -24,15 +23,20 @@ const createGzippedBuffer = (data) => {
24
23
  data: Array.from(compressed),
25
24
  };
26
25
  };
26
+ /**
27
+ * Helper to create a mock Response object for fetch.
28
+ */
29
+ const mockResponse = (data, status = 200) => ({
30
+ ok: status >= 200 && status < 300,
31
+ status,
32
+ statusText: status >= 200 && status < 300 ? "OK" : "Error",
33
+ json: () => Promise.resolve(data),
34
+ });
27
35
  describe("library", () => {
28
- let axiosStub;
36
+ let fetchStub;
29
37
  const expectedToken = "mockJwtToken";
30
38
  beforeEach(() => {
31
- axiosStub = sinon.stub(axios, "create").returns({
32
- get: sinon.stub(),
33
- put: sinon.stub(),
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- });
39
+ fetchStub = sinon.stub(globalThis, "fetch");
36
40
  });
37
41
  afterEach(() => {
38
42
  sinon.restore();
@@ -182,29 +186,25 @@ describe("library", () => {
182
186
  "getTargetTemperature",
183
187
  "setTargetTemperature",
184
188
  ];
185
- it("should create API methods with the correct baseURL", () => {
186
- const baseURL = "https://example.com/api";
189
+ it("should create API methods with the correct baseURL", () => __awaiter(void 0, void 0, void 0, function* () {
190
+ const baseURL = "https://example.com/api/";
191
+ fetchStub.resolves(mockResponse({ test: "data" }));
187
192
  const api = configure(baseURL);
188
- assert.deepEqual(axiosStub.args, [
189
- [
190
- {
191
- baseURL,
192
- },
193
- ],
194
- ]);
195
193
  assert.deepEqual(Object.keys(api), expectedApi);
196
- });
197
- it("should create API methods with the default baseURL", () => {
194
+ // Verify baseURL is used when making a request
195
+ yield api.deviceInfo(expectedToken, "mockMac");
196
+ assert.ok(fetchStub.calledOnce);
197
+ assert.ok(fetchStub.firstCall.args[0].startsWith(baseURL));
198
+ }));
199
+ it("should create API methods with the default baseURL", () => __awaiter(void 0, void 0, void 0, function* () {
200
+ fetchStub.resolves(mockResponse({ test: "data" }));
198
201
  const api = configure();
199
- assert.deepEqual(axiosStub.args, [
200
- [
201
- {
202
- baseURL: API_URL,
203
- },
204
- ],
205
- ]);
206
202
  assert.deepEqual(Object.keys(api), expectedApi);
207
- });
203
+ // Verify default baseURL is used when making a request
204
+ yield api.deviceInfo(expectedToken, "mockMac");
205
+ assert.ok(fetchStub.calledOnce);
206
+ assert.ok(fetchStub.firstCall.args[0].startsWith(API_URL));
207
+ }));
208
208
  });
209
209
  describe("API Methods", () => {
210
210
  const mockDeviceInfo = {
@@ -222,19 +222,16 @@ describe("library", () => {
222
222
  },
223
223
  },
224
224
  };
225
- it("should call axios for deviceInfo", () => __awaiter(void 0, void 0, void 0, function* () {
226
- const mockAxios = {
227
- get: sinon.stub().resolves({ data: mockDeviceInfo }),
228
- };
229
- axiosStub.returns(mockAxios);
230
- const api = configure("https://example.com/api");
225
+ it("should call fetch for deviceInfo", () => __awaiter(void 0, void 0, void 0, function* () {
226
+ fetchStub.resolves(mockResponse(mockDeviceInfo));
227
+ const api = configure("https://example.com/api/");
231
228
  const result = yield api.deviceInfo(expectedToken, "mockMacAddress");
232
- assert.deepEqual(mockAxios.get.args, [
233
- [
234
- "device/mockMacAddress/info",
235
- { headers: { Authorization: `Bearer ${expectedToken}` } },
236
- ],
237
- ]);
229
+ assert.ok(fetchStub.calledOnce);
230
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/device/mockMacAddress/info");
231
+ assert.deepEqual(fetchStub.firstCall.args[1], {
232
+ method: "GET",
233
+ headers: { Authorization: `Bearer ${expectedToken}` },
234
+ });
238
235
  assert.deepEqual(result, mockDeviceInfo);
239
236
  }));
240
237
  // Tests for setPowerOn and setPowerOff
@@ -250,28 +247,25 @@ describe("library", () => {
250
247
  expectedValue: 0,
251
248
  },
252
249
  ].forEach(({ method, call, expectedValue }) => {
253
- it(`should call axios for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
254
- const mockAxios = {
255
- put: sinon.stub().resolves({ status: 200 }),
256
- };
257
- axiosStub.returns(mockAxios);
258
- const api = configure("https://example.com/api");
250
+ it(`should call fetch for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
251
+ fetchStub.resolves(mockResponse({ success: true }));
252
+ const api = configure("https://example.com/api/");
259
253
  // Invoke the method using the mapped call function
260
- const result = yield call(api);
261
- assert.deepEqual(mockAxios.put.args, [
262
- [
263
- "mqtt/command",
264
- {
265
- mac_address: "mockMacAddress",
266
- name: "power",
267
- value: expectedValue,
268
- },
269
- {
270
- headers: { Authorization: "Bearer mockToken" },
271
- },
272
- ],
273
- ]);
274
- assert.equal(result.status, 200);
254
+ yield call(api);
255
+ assert.ok(fetchStub.calledOnce);
256
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/mqtt/command");
257
+ assert.deepEqual(fetchStub.firstCall.args[1], {
258
+ method: "PUT",
259
+ headers: {
260
+ "Content-Type": "application/json",
261
+ Authorization: "Bearer mockToken",
262
+ },
263
+ body: JSON.stringify({
264
+ mac_address: "mockMacAddress",
265
+ name: "power",
266
+ value: expectedValue,
267
+ }),
268
+ });
275
269
  }));
276
270
  });
277
271
  const getterTests = [
@@ -292,19 +286,16 @@ describe("library", () => {
292
286
  },
293
287
  ];
294
288
  getterTests.forEach(({ method, call, expectedResult }) => {
295
- it(`should call axios and return the correct value for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
296
- const mockAxios = {
297
- get: sinon.stub().resolves({ data: mockDeviceInfo }),
298
- };
299
- axiosStub.returns(mockAxios);
300
- const api = configure("https://example.com/api");
289
+ it(`should call fetch and return the correct value for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
290
+ fetchStub.resolves(mockResponse(mockDeviceInfo));
291
+ const api = configure("https://example.com/api/");
301
292
  const result = yield call(api, expectedToken, "mockMacAddress");
302
- assert.deepEqual(mockAxios.get.args, [
303
- [
304
- "device/mockMacAddress/info",
305
- { headers: { Authorization: `Bearer ${expectedToken}` } },
306
- ],
307
- ]);
293
+ assert.ok(fetchStub.calledOnce);
294
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/device/mockMacAddress/info");
295
+ assert.deepEqual(fetchStub.firstCall.args[1], {
296
+ method: "GET",
297
+ headers: { Authorization: `Bearer ${expectedToken}` },
298
+ });
308
299
  assert.equal(result, expectedResult);
309
300
  }));
310
301
  });
@@ -320,119 +311,100 @@ describe("library", () => {
320
311
  },
321
312
  ];
322
313
  setterTests.forEach(({ method, call, payload }) => {
323
- it(`should call axios and send the correct payload for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
324
- const mockAxios = {
325
- put: sinon.stub().resolves({ status: 200 }),
326
- };
327
- axiosStub.returns(mockAxios);
328
- const api = configure("https://example.com/api");
329
- const result = yield call(api, expectedToken, "mockMacAddress", payload.value);
330
- assert.deepEqual(mockAxios.put.args, [
331
- [
332
- "mqtt/command",
333
- Object.assign({ mac_address: "mockMacAddress" }, payload),
334
- {
335
- headers: { Authorization: `Bearer ${expectedToken}` },
336
- },
337
- ],
338
- ]);
339
- assert.equal(result.status, 200);
314
+ it(`should call fetch and send the correct payload for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
315
+ fetchStub.resolves(mockResponse({ success: true }));
316
+ const api = configure("https://example.com/api/");
317
+ yield call(api, expectedToken, "mockMacAddress", payload.value);
318
+ assert.ok(fetchStub.calledOnce);
319
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/mqtt/command");
320
+ assert.deepEqual(fetchStub.firstCall.args[1], {
321
+ method: "PUT",
322
+ headers: {
323
+ "Content-Type": "application/json",
324
+ Authorization: `Bearer ${expectedToken}`,
325
+ },
326
+ body: JSON.stringify(Object.assign({ mac_address: "mockMacAddress" }, payload)),
327
+ });
340
328
  }));
341
329
  });
342
330
  });
343
331
  describe("registerDevice", () => {
344
332
  it("should call POST /device with correct payload", () => __awaiter(void 0, void 0, void 0, function* () {
345
- const mockResponse = {
333
+ const mockResponseData = {
346
334
  macAddress: "AABBCCDDEEFF",
347
335
  deviceName: "Test Stove",
348
336
  deviceRoom: "Living Room",
349
337
  serialNumber: "EDK123",
350
338
  };
351
- const mockAxios = {
352
- post: sinon.stub().resolves({ data: mockResponse }),
353
- get: sinon.stub(),
354
- put: sinon.stub(),
355
- };
356
- axiosStub.returns(mockAxios);
357
- const api = configure("https://example.com/api");
339
+ fetchStub.resolves(mockResponse(mockResponseData));
340
+ const api = configure("https://example.com/api/");
358
341
  const result = yield api.registerDevice(expectedToken, "AA:BB:CC:DD:EE:FF", "EDK123", "Test Stove", "Living Room");
359
- assert.deepEqual(mockAxios.post.args, [
360
- [
361
- "device",
362
- {
363
- macAddress: "AABBCCDDEEFF",
364
- deviceName: "Test Stove",
365
- deviceRoom: "Living Room",
366
- serialNumber: "EDK123",
367
- },
368
- { headers: { Authorization: `Bearer ${expectedToken}` } },
369
- ],
370
- ]);
371
- assert.deepEqual(result, mockResponse);
342
+ assert.ok(fetchStub.calledOnce);
343
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/device");
344
+ assert.deepEqual(fetchStub.firstCall.args[1], {
345
+ method: "POST",
346
+ headers: {
347
+ "Content-Type": "application/json",
348
+ Authorization: `Bearer ${expectedToken}`,
349
+ },
350
+ body: JSON.stringify({
351
+ macAddress: "AABBCCDDEEFF",
352
+ deviceName: "Test Stove",
353
+ deviceRoom: "Living Room",
354
+ serialNumber: "EDK123",
355
+ }),
356
+ });
357
+ assert.deepEqual(result, mockResponseData);
372
358
  }));
373
359
  it("should normalize MAC address by removing colons", () => __awaiter(void 0, void 0, void 0, function* () {
374
- const mockAxios = {
375
- post: sinon.stub().resolves({ data: {} }),
376
- get: sinon.stub(),
377
- put: sinon.stub(),
378
- };
379
- axiosStub.returns(mockAxios);
380
- const api = configure("https://example.com/api");
360
+ fetchStub.resolves(mockResponse({}));
361
+ const api = configure("https://example.com/api/");
381
362
  yield api.registerDevice(expectedToken, "AA:BB:CC:DD:EE:FF", "EDK123");
382
- assert.equal(mockAxios.post.args[0][1].macAddress, "AABBCCDDEEFF");
363
+ const body = JSON.parse(fetchStub.firstCall.args[1].body);
364
+ assert.equal(body.macAddress, "AABBCCDDEEFF");
383
365
  }));
384
366
  it("should use empty strings as defaults for name and room", () => __awaiter(void 0, void 0, void 0, function* () {
385
- const mockAxios = {
386
- post: sinon.stub().resolves({ data: {} }),
387
- get: sinon.stub(),
388
- put: sinon.stub(),
389
- };
390
- axiosStub.returns(mockAxios);
391
- const api = configure("https://example.com/api");
367
+ fetchStub.resolves(mockResponse({}));
368
+ const api = configure("https://example.com/api/");
392
369
  yield api.registerDevice(expectedToken, "AABBCCDDEEFF", "EDK123");
393
- assert.equal(mockAxios.post.args[0][1].deviceName, "");
394
- assert.equal(mockAxios.post.args[0][1].deviceRoom, "");
370
+ const body = JSON.parse(fetchStub.firstCall.args[1].body);
371
+ assert.equal(body.deviceName, "");
372
+ assert.equal(body.deviceRoom, "");
395
373
  }));
396
374
  });
397
375
  describe("editDevice", () => {
398
376
  it("should call PUT /device/{mac} with correct payload", () => __awaiter(void 0, void 0, void 0, function* () {
399
- const mockResponse = {
377
+ const mockResponseData = {
400
378
  macAddress: "AABBCCDDEEFF",
401
379
  deviceName: "Updated Name",
402
380
  deviceRoom: "Basement",
403
381
  serialNumber: "EDK123",
404
382
  };
405
- const mockAxios = {
406
- put: sinon.stub().resolves({ data: mockResponse }),
407
- get: sinon.stub(),
408
- post: sinon.stub(),
409
- };
410
- axiosStub.returns(mockAxios);
411
- const api = configure("https://example.com/api");
383
+ fetchStub.resolves(mockResponse(mockResponseData));
384
+ const api = configure("https://example.com/api/");
412
385
  const result = yield api.editDevice(expectedToken, "AA:BB:CC:DD:EE:FF", "Updated Name", "Basement");
413
- assert.deepEqual(mockAxios.put.args, [
414
- [
415
- "device/AABBCCDDEEFF",
416
- {
417
- deviceName: "Updated Name",
418
- deviceRoom: "Basement",
419
- },
420
- { headers: { Authorization: `Bearer ${expectedToken}` } },
421
- ],
422
- ]);
423
- assert.deepEqual(result, mockResponse);
386
+ assert.ok(fetchStub.calledOnce);
387
+ assert.equal(fetchStub.firstCall.args[0], "https://example.com/api/device/AABBCCDDEEFF");
388
+ assert.deepEqual(fetchStub.firstCall.args[1], {
389
+ method: "PUT",
390
+ headers: {
391
+ "Content-Type": "application/json",
392
+ Authorization: `Bearer ${expectedToken}`,
393
+ },
394
+ body: JSON.stringify({
395
+ deviceName: "Updated Name",
396
+ deviceRoom: "Basement",
397
+ }),
398
+ });
399
+ assert.deepEqual(result, mockResponseData);
424
400
  }));
425
401
  it("should use empty strings as defaults for name and room", () => __awaiter(void 0, void 0, void 0, function* () {
426
- const mockAxios = {
427
- put: sinon.stub().resolves({ data: {} }),
428
- get: sinon.stub(),
429
- post: sinon.stub(),
430
- };
431
- axiosStub.returns(mockAxios);
432
- const api = configure("https://example.com/api");
402
+ fetchStub.resolves(mockResponse({}));
403
+ const api = configure("https://example.com/api/");
433
404
  yield api.editDevice(expectedToken, "AABBCCDDEEFF");
434
- assert.equal(mockAxios.put.args[0][1].deviceName, "");
435
- assert.equal(mockAxios.put.args[0][1].deviceRoom, "");
405
+ const body = JSON.parse(fetchStub.firstCall.args[1].body);
406
+ assert.equal(body.deviceName, "");
407
+ assert.equal(body.deviceRoom, "");
436
408
  }));
437
409
  });
438
410
  describe("deviceInfo with compressed responses", () => {
@@ -441,7 +413,7 @@ describe("library", () => {
441
413
  commands: { power: true },
442
414
  temperatures: { enviroment: 19, board: 25 },
443
415
  };
444
- const mockResponse = {
416
+ const mockResponseData = {
445
417
  status: createGzippedBuffer(statusData),
446
418
  nvm: {
447
419
  user_parameters: {
@@ -449,11 +421,8 @@ describe("library", () => {
449
421
  },
450
422
  },
451
423
  };
452
- const mockAxios = {
453
- get: sinon.stub().resolves({ data: mockResponse }),
454
- };
455
- axiosStub.returns(mockAxios);
456
- const api = configure("https://example.com/api");
424
+ fetchStub.resolves(mockResponse(mockResponseData));
425
+ const api = configure("https://example.com/api/");
457
426
  const result = yield api.deviceInfo(expectedToken, "mockMacAddress");
458
427
  assert.deepEqual(result.status, statusData);
459
428
  }));
@@ -467,18 +436,15 @@ describe("library", () => {
467
436
  is_sound_active: true,
468
437
  },
469
438
  };
470
- const mockResponse = {
439
+ const mockResponseData = {
471
440
  status: {
472
441
  commands: { power: true },
473
442
  temperatures: { enviroment: 19 },
474
443
  },
475
444
  nvm: createGzippedBuffer(nvmData),
476
445
  };
477
- const mockAxios = {
478
- get: sinon.stub().resolves({ data: mockResponse }),
479
- };
480
- axiosStub.returns(mockAxios);
481
- const api = configure("https://example.com/api");
446
+ fetchStub.resolves(mockResponse(mockResponseData));
447
+ const api = configure("https://example.com/api/");
482
448
  const result = yield api.deviceInfo(expectedToken, "mockMacAddress");
483
449
  assert.deepEqual(result.nvm, nvmData);
484
450
  }));
@@ -496,15 +462,12 @@ describe("library", () => {
496
462
  is_sound_active: false,
497
463
  },
498
464
  };
499
- const mockResponse = {
465
+ const mockResponseData = {
500
466
  status: createGzippedBuffer(statusData),
501
467
  nvm: createGzippedBuffer(nvmData),
502
468
  };
503
- const mockAxios = {
504
- get: sinon.stub().resolves({ data: mockResponse }),
505
- };
506
- axiosStub.returns(mockAxios);
507
- const api = configure("https://example.com/api");
469
+ fetchStub.resolves(mockResponse(mockResponseData));
470
+ const api = configure("https://example.com/api/");
508
471
  const result = yield api.deviceInfo(expectedToken, "mockMacAddress");
509
472
  assert.deepEqual(result.status, statusData);
510
473
  assert.deepEqual(result.nvm, nvmData);
@@ -514,15 +477,12 @@ describe("library", () => {
514
477
  commands: { power: true },
515
478
  temperatures: { enviroment: 19 },
516
479
  };
517
- const mockResponse = {
480
+ const mockResponseData = {
518
481
  status: createGzippedBuffer(statusData),
519
482
  nvm: { user_parameters: { enviroment_1_temperature: 22 } },
520
483
  };
521
- const mockAxios = {
522
- get: sinon.stub().resolves({ data: mockResponse }),
523
- };
524
- axiosStub.returns(mockAxios);
525
- const api = configure("https://example.com/api");
484
+ fetchStub.resolves(mockResponse(mockResponseData));
485
+ const api = configure("https://example.com/api/");
526
486
  const result = yield api.getPower(expectedToken, "mockMacAddress");
527
487
  assert.equal(result, true);
528
488
  }));
@@ -531,15 +491,12 @@ describe("library", () => {
531
491
  commands: { power: true },
532
492
  temperatures: { enviroment: 19, board: 25 },
533
493
  };
534
- const mockResponse = {
494
+ const mockResponseData = {
535
495
  status: createGzippedBuffer(statusData),
536
496
  nvm: { user_parameters: { enviroment_1_temperature: 22 } },
537
497
  };
538
- const mockAxios = {
539
- get: sinon.stub().resolves({ data: mockResponse }),
540
- };
541
- axiosStub.returns(mockAxios);
542
- const api = configure("https://example.com/api");
498
+ fetchStub.resolves(mockResponse(mockResponseData));
499
+ const api = configure("https://example.com/api/");
543
500
  const result = yield api.getEnvironmentTemperature(expectedToken, "mockMacAddress");
544
501
  assert.equal(result, 19);
545
502
  }));
@@ -549,17 +506,37 @@ describe("library", () => {
549
506
  enviroment_1_temperature: 22,
550
507
  },
551
508
  };
552
- const mockResponse = {
509
+ const mockResponseData = {
553
510
  status: { commands: { power: true }, temperatures: { enviroment: 19 } },
554
511
  nvm: createGzippedBuffer(nvmData),
555
512
  };
556
- const mockAxios = {
557
- get: sinon.stub().resolves({ data: mockResponse }),
558
- };
559
- axiosStub.returns(mockAxios);
560
- const api = configure("https://example.com/api");
513
+ fetchStub.resolves(mockResponse(mockResponseData));
514
+ const api = configure("https://example.com/api/");
561
515
  const result = yield api.getTargetTemperature(expectedToken, "mockMacAddress");
562
516
  assert.equal(result, 22);
563
517
  }));
564
518
  });
519
+ describe("Error Handling", () => {
520
+ const errorTests = [
521
+ { status: 400, statusText: "Bad Request" },
522
+ { status: 401, statusText: "Unauthorized" },
523
+ { status: 404, statusText: "Not Found" },
524
+ { status: 500, statusText: "Internal Server Error" },
525
+ ];
526
+ errorTests.forEach(({ status, statusText }) => {
527
+ it(`should throw error when fetch returns ${status}`, () => __awaiter(void 0, void 0, void 0, function* () {
528
+ const errorResponse = {
529
+ ok: false,
530
+ status,
531
+ statusText,
532
+ json: () => Promise.resolve({ error: statusText }),
533
+ };
534
+ fetchStub.resolves(errorResponse);
535
+ const api = configure("https://example.com/api/");
536
+ yield assert.rejects(() => __awaiter(void 0, void 0, void 0, function* () { return api.deviceInfo(expectedToken, "mockMac"); }), {
537
+ message: `HTTP ${status}: ${statusText}`,
538
+ });
539
+ }));
540
+ });
541
+ });
565
542
  });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Converts a raw serial number string to hex-encoded format.
3
+ * This is useful when serial numbers contain non-printable characters.
4
+ *
5
+ * @param serial - The raw serial number string
6
+ * @returns Hex-encoded string representation
7
+ *
8
+ * @example
9
+ * serialNumberToHex("EDK123") // returns "45444b313233"
10
+ */
11
+ declare const serialNumberToHex: (serial: string) => string;
12
+ /**
13
+ * Converts a hex-encoded serial number back to raw string format.
14
+ *
15
+ * @param hex - The hex-encoded serial number
16
+ * @returns Raw serial number string
17
+ *
18
+ * @example
19
+ * serialNumberFromHex("45444b313233") // returns "EDK123"
20
+ */
21
+ declare const serialNumberFromHex: (hex: string) => string;
22
+ /**
23
+ * Produces a display-friendly version of a serial number by removing
24
+ * non-printable characters and collapsing whitespace.
25
+ *
26
+ * @param serial - The raw serial number string
27
+ * @returns Display-friendly serial number
28
+ *
29
+ * @example
30
+ * serialNumberDisplay("EDK\x00123\x1F") // returns "EDK123"
31
+ */
32
+ declare const serialNumberDisplay: (serial: string) => string;
33
+ export { serialNumberDisplay, serialNumberFromHex, serialNumberToHex };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Custom storage adapter for AWS Amplify that persists to file system.
3
+ * Used for CLI to maintain sessions between invocations.
4
+ */
5
+ export declare const createFileStorage: () => {
6
+ setItem: (key: string, value: string) => Promise<void>;
7
+ getItem: (key: string) => Promise<string | null>;
8
+ removeItem: (key: string) => Promise<void>;
9
+ clear: () => Promise<void>;
10
+ };
11
+ /**
12
+ * Clears all stored session data.
13
+ */
14
+ export declare const clearSession: () => Promise<void>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Represents a Node.js Buffer object serialized to JSON.
3
+ * This format is used by the Edilkamin API for gzip-compressed fields.
4
+ */
5
+ interface BufferEncodedType {
6
+ type: "Buffer";
7
+ data: number[];
8
+ }
9
+ interface CommandsType {
10
+ power: boolean;
11
+ }
12
+ interface TemperaturesType {
13
+ board: number;
14
+ enviroment: number;
15
+ }
16
+ interface StatusType {
17
+ commands: CommandsType;
18
+ temperatures: TemperaturesType;
19
+ }
20
+ interface UserParametersType {
21
+ enviroment_1_temperature: number;
22
+ enviroment_2_temperature: number;
23
+ enviroment_3_temperature: number;
24
+ is_auto: boolean;
25
+ is_sound_active: boolean;
26
+ }
27
+ interface DeviceInfoType {
28
+ status: StatusType;
29
+ nvm: {
30
+ user_parameters: UserParametersType;
31
+ };
32
+ }
33
+ /**
34
+ * Raw device info response that may contain Buffer-encoded compressed fields.
35
+ * Used internally before processing; external callers receive DeviceInfoType.
36
+ */
37
+ interface DeviceInfoRawType {
38
+ status: StatusType | BufferEncodedType;
39
+ nvm: {
40
+ user_parameters: UserParametersType;
41
+ } | BufferEncodedType;
42
+ component_info?: BufferEncodedType | Record<string, unknown>;
43
+ }
44
+ /**
45
+ * Request body for registering a device with a user account.
46
+ * All fields are required by the API.
47
+ */
48
+ interface DeviceAssociationBody {
49
+ macAddress: string;
50
+ deviceName: string;
51
+ deviceRoom: string;
52
+ serialNumber: string;
53
+ }
54
+ /**
55
+ * Request body for editing a device's name and room.
56
+ * MAC address is specified in the URL path, not the body.
57
+ * Serial number cannot be changed after registration.
58
+ */
59
+ interface EditDeviceAssociationBody {
60
+ deviceName: string;
61
+ deviceRoom: string;
62
+ }
63
+ /**
64
+ * Response from device registration endpoint.
65
+ * Structure based on Android app behavior - may need adjustment after testing.
66
+ */
67
+ interface DeviceAssociationResponse {
68
+ macAddress: string;
69
+ deviceName: string;
70
+ deviceRoom: string;
71
+ serialNumber: string;
72
+ }
73
+ export type { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, };
@@ -0,0 +1 @@
1
+ export {};