edilkamin 1.7.4 → 1.9.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.
Files changed (40) hide show
  1. package/.github/dependabot.yml +5 -1
  2. package/README.md +92 -4
  3. package/dist/cjs/package.json +16 -5
  4. package/dist/cjs/src/bluetooth-utils.d.ts +13 -0
  5. package/dist/cjs/src/bluetooth-utils.js +28 -0
  6. package/dist/cjs/src/bluetooth-utils.test.d.ts +1 -0
  7. package/dist/cjs/src/bluetooth-utils.test.js +35 -0
  8. package/dist/cjs/src/bluetooth.d.ts +40 -0
  9. package/dist/cjs/src/bluetooth.js +107 -0
  10. package/dist/cjs/src/cli.js +130 -0
  11. package/dist/cjs/src/index.d.ts +2 -1
  12. package/dist/cjs/src/index.js +3 -1
  13. package/dist/cjs/src/library.d.ts +26 -0
  14. package/dist/cjs/src/library.js +361 -0
  15. package/dist/cjs/src/library.test.js +266 -0
  16. package/dist/cjs/src/types.d.ts +30 -1
  17. package/dist/esm/package.json +16 -5
  18. package/dist/esm/src/bluetooth-utils.d.ts +13 -0
  19. package/dist/esm/src/bluetooth-utils.js +25 -0
  20. package/dist/esm/src/bluetooth-utils.test.d.ts +1 -0
  21. package/dist/esm/src/bluetooth-utils.test.js +33 -0
  22. package/dist/esm/src/bluetooth.d.ts +40 -0
  23. package/dist/esm/src/bluetooth.js +100 -0
  24. package/dist/esm/src/cli.js +130 -0
  25. package/dist/esm/src/index.d.ts +2 -1
  26. package/dist/esm/src/index.js +1 -0
  27. package/dist/esm/src/library.d.ts +26 -0
  28. package/dist/esm/src/library.js +361 -0
  29. package/dist/esm/src/library.test.js +266 -0
  30. package/dist/esm/src/types.d.ts +30 -1
  31. package/package.json +16 -5
  32. package/src/bluetooth-utils.test.ts +46 -0
  33. package/src/bluetooth-utils.ts +29 -0
  34. package/src/bluetooth.ts +115 -0
  35. package/src/cli.ts +249 -0
  36. package/src/index.ts +2 -0
  37. package/src/library.test.ts +372 -0
  38. package/src/library.ts +426 -0
  39. package/src/types.ts +35 -0
  40. package/tsconfig.json +1 -0
@@ -187,9 +187,35 @@ describe("library", () => {
187
187
  "setPowerOff",
188
188
  "setPowerOn",
189
189
  "getPower",
190
+ "setPowerLevel",
191
+ "getPowerLevel",
192
+ "setFan1Speed",
193
+ "setFan2Speed",
194
+ "setFan3Speed",
195
+ "getFan1Speed",
196
+ "getFan2Speed",
197
+ "getFan3Speed",
198
+ "setAirkare",
199
+ "setRelax",
200
+ "setStandby",
201
+ "getStandby",
202
+ "setStandbyTime",
203
+ "getStandbyTime",
204
+ "setAuto",
205
+ "getAuto",
190
206
  "getEnvironmentTemperature",
191
207
  "getTargetTemperature",
192
208
  "setTargetTemperature",
209
+ "setEnvironment2Temperature",
210
+ "getEnvironment2Temperature",
211
+ "setEnvironment3Temperature",
212
+ "getEnvironment3Temperature",
213
+ "setMeasureUnit",
214
+ "getMeasureUnit",
215
+ "setLanguage",
216
+ "getLanguage",
217
+ "getPelletInReserve",
218
+ "getPelletAutonomyTime",
193
219
  ];
194
220
  it("should create API methods with the correct baseURL", () => __awaiter(void 0, void 0, void 0, function* () {
195
221
  const baseURL = "https://example.com/api/";
@@ -220,10 +246,27 @@ describe("library", () => {
220
246
  temperatures: {
221
247
  enviroment: 19,
222
248
  },
249
+ flags: {
250
+ is_pellet_in_reserve: false,
251
+ },
252
+ pellet: {
253
+ autonomy_time: 180,
254
+ },
223
255
  },
224
256
  nvm: {
225
257
  user_parameters: {
226
258
  enviroment_1_temperature: 22,
259
+ enviroment_2_temperature: 18,
260
+ enviroment_3_temperature: 20,
261
+ manual_power: 3,
262
+ fan_1_ventilation: 2,
263
+ fan_2_ventilation: 3,
264
+ fan_3_ventilation: 4,
265
+ is_standby_active: true,
266
+ standby_waiting_time: 30,
267
+ is_auto: true,
268
+ is_fahrenheit: false,
269
+ language: 2,
227
270
  },
228
271
  },
229
272
  };
@@ -279,6 +322,26 @@ describe("library", () => {
279
322
  call: (api, token, mac) => api.getPower(token, mac),
280
323
  expectedResult: true,
281
324
  },
325
+ {
326
+ method: "getPowerLevel",
327
+ call: (api, token, mac) => api.getPowerLevel(token, mac),
328
+ expectedResult: 3,
329
+ },
330
+ {
331
+ method: "getFan1Speed",
332
+ call: (api, token, mac) => api.getFan1Speed(token, mac),
333
+ expectedResult: 2,
334
+ },
335
+ {
336
+ method: "getFan2Speed",
337
+ call: (api, token, mac) => api.getFan2Speed(token, mac),
338
+ expectedResult: 3,
339
+ },
340
+ {
341
+ method: "getFan3Speed",
342
+ call: (api, token, mac) => api.getFan3Speed(token, mac),
343
+ expectedResult: 4,
344
+ },
282
345
  {
283
346
  method: "getEnvironmentTemperature",
284
347
  call: (api, token, mac) => api.getEnvironmentTemperature(token, mac),
@@ -289,6 +352,51 @@ describe("library", () => {
289
352
  call: (api, token, mac) => api.getTargetTemperature(token, mac),
290
353
  expectedResult: 22,
291
354
  },
355
+ {
356
+ method: "getStandby",
357
+ call: (api, token, mac) => api.getStandby(token, mac),
358
+ expectedResult: true,
359
+ },
360
+ {
361
+ method: "getStandbyTime",
362
+ call: (api, token, mac) => api.getStandbyTime(token, mac),
363
+ expectedResult: 30,
364
+ },
365
+ {
366
+ method: "getAuto",
367
+ call: (api, token, mac) => api.getAuto(token, mac),
368
+ expectedResult: true,
369
+ },
370
+ {
371
+ method: "getEnvironment2Temperature",
372
+ call: (api, token, mac) => api.getEnvironment2Temperature(token, mac),
373
+ expectedResult: 18,
374
+ },
375
+ {
376
+ method: "getEnvironment3Temperature",
377
+ call: (api, token, mac) => api.getEnvironment3Temperature(token, mac),
378
+ expectedResult: 20,
379
+ },
380
+ {
381
+ method: "getMeasureUnit",
382
+ call: (api, token, mac) => api.getMeasureUnit(token, mac),
383
+ expectedResult: false,
384
+ },
385
+ {
386
+ method: "getLanguage",
387
+ call: (api, token, mac) => api.getLanguage(token, mac),
388
+ expectedResult: 2,
389
+ },
390
+ {
391
+ method: "getPelletInReserve",
392
+ call: (api, token, mac) => api.getPelletInReserve(token, mac),
393
+ expectedResult: false,
394
+ },
395
+ {
396
+ method: "getPelletAutonomyTime",
397
+ call: (api, token, mac) => api.getPelletAutonomyTime(token, mac),
398
+ expectedResult: 180,
399
+ },
292
400
  ];
293
401
  getterTests.forEach(({ method, call, expectedResult }) => {
294
402
  it(`should call fetch and return the correct value for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
@@ -306,6 +414,38 @@ describe("library", () => {
306
414
  });
307
415
  // Setter tests
308
416
  const setterTests = [
417
+ {
418
+ method: "setPowerLevel",
419
+ call: (api, token, mac, value) => api.setPowerLevel(token, mac, value),
420
+ payload: {
421
+ name: "power_level",
422
+ value: 4,
423
+ },
424
+ },
425
+ {
426
+ method: "setFan1Speed",
427
+ call: (api, token, mac, value) => api.setFan1Speed(token, mac, value),
428
+ payload: {
429
+ name: "fan_1_speed",
430
+ value: 3,
431
+ },
432
+ },
433
+ {
434
+ method: "setFan2Speed",
435
+ call: (api, token, mac, value) => api.setFan2Speed(token, mac, value),
436
+ payload: {
437
+ name: "fan_2_speed",
438
+ value: 4,
439
+ },
440
+ },
441
+ {
442
+ method: "setFan3Speed",
443
+ call: (api, token, mac, value) => api.setFan3Speed(token, mac, value),
444
+ payload: {
445
+ name: "fan_3_speed",
446
+ value: 5,
447
+ },
448
+ },
309
449
  {
310
450
  method: "setTargetTemperature",
311
451
  call: (api, token, mac, value) => api.setTargetTemperature(token, mac, value),
@@ -314,6 +454,38 @@ describe("library", () => {
314
454
  value: 20,
315
455
  },
316
456
  },
457
+ {
458
+ method: "setStandbyTime",
459
+ call: (api, token, mac, value) => api.setStandbyTime(token, mac, value),
460
+ payload: {
461
+ name: "standby_time",
462
+ value: 45,
463
+ },
464
+ },
465
+ {
466
+ method: "setEnvironment2Temperature",
467
+ call: (api, token, mac, value) => api.setEnvironment2Temperature(token, mac, value),
468
+ payload: {
469
+ name: "enviroment_2_temperature",
470
+ value: 21,
471
+ },
472
+ },
473
+ {
474
+ method: "setEnvironment3Temperature",
475
+ call: (api, token, mac, value) => api.setEnvironment3Temperature(token, mac, value),
476
+ payload: {
477
+ name: "enviroment_3_temperature",
478
+ value: 23,
479
+ },
480
+ },
481
+ {
482
+ method: "setLanguage",
483
+ call: (api, token, mac, value) => api.setLanguage(token, mac, value),
484
+ payload: {
485
+ name: "language",
486
+ value: 2,
487
+ },
488
+ },
317
489
  ];
318
490
  setterTests.forEach(({ method, call, payload }) => {
319
491
  it(`should call fetch and send the correct payload for ${method}`, () => __awaiter(void 0, void 0, void 0, function* () {
@@ -332,6 +504,69 @@ describe("library", () => {
332
504
  });
333
505
  }));
334
506
  });
507
+ // Boolean setter tests (for mode controls)
508
+ const booleanSetterTests = [
509
+ {
510
+ method: "setAirkare",
511
+ call: (api, token, mac, enabled) => api.setAirkare(token, mac, enabled),
512
+ truePayload: { name: "airkare_function", value: 1 },
513
+ falsePayload: { name: "airkare_function", value: 0 },
514
+ },
515
+ {
516
+ method: "setRelax",
517
+ call: (api, token, mac, enabled) => api.setRelax(token, mac, enabled),
518
+ truePayload: { name: "relax_mode", value: true },
519
+ falsePayload: { name: "relax_mode", value: false },
520
+ },
521
+ {
522
+ method: "setStandby",
523
+ call: (api, token, mac, enabled) => api.setStandby(token, mac, enabled),
524
+ truePayload: { name: "standby_mode", value: true },
525
+ falsePayload: { name: "standby_mode", value: false },
526
+ },
527
+ {
528
+ method: "setAuto",
529
+ call: (api, token, mac, enabled) => api.setAuto(token, mac, enabled),
530
+ truePayload: { name: "auto_mode", value: true },
531
+ falsePayload: { name: "auto_mode", value: false },
532
+ },
533
+ {
534
+ method: "setMeasureUnit",
535
+ call: (api, token, mac, enabled) => api.setMeasureUnit(token, mac, enabled),
536
+ truePayload: { name: "measure_unit", value: true },
537
+ falsePayload: { name: "measure_unit", value: false },
538
+ },
539
+ ];
540
+ booleanSetterTests.forEach(({ method, call, truePayload, falsePayload }) => {
541
+ it(`should call fetch with correct payload for ${method}(true)`, () => __awaiter(void 0, void 0, void 0, function* () {
542
+ fetchStub.resolves(mockResponse({ success: true }));
543
+ const api = (0, library_1.configure)("https://example.com/api/");
544
+ yield call(api, expectedToken, "mockMacAddress", true);
545
+ assert_1.strict.ok(fetchStub.calledOnce);
546
+ assert_1.strict.deepEqual(fetchStub.firstCall.args[1], {
547
+ method: "PUT",
548
+ headers: {
549
+ "Content-Type": "application/json",
550
+ Authorization: `Bearer ${expectedToken}`,
551
+ },
552
+ body: JSON.stringify(Object.assign({ mac_address: "mockMacAddress" }, truePayload)),
553
+ });
554
+ }));
555
+ it(`should call fetch with correct payload for ${method}(false)`, () => __awaiter(void 0, void 0, void 0, function* () {
556
+ fetchStub.resolves(mockResponse({ success: true }));
557
+ const api = (0, library_1.configure)("https://example.com/api/");
558
+ yield call(api, expectedToken, "mockMacAddress", false);
559
+ assert_1.strict.ok(fetchStub.calledOnce);
560
+ assert_1.strict.deepEqual(fetchStub.firstCall.args[1], {
561
+ method: "PUT",
562
+ headers: {
563
+ "Content-Type": "application/json",
564
+ Authorization: `Bearer ${expectedToken}`,
565
+ },
566
+ body: JSON.stringify(Object.assign({ mac_address: "mockMacAddress" }, falsePayload)),
567
+ });
568
+ }));
569
+ });
335
570
  });
336
571
  describe("registerDevice", () => {
337
572
  it("should call POST /device with correct payload", () => __awaiter(void 0, void 0, void 0, function* () {
@@ -520,6 +755,37 @@ describe("library", () => {
520
755
  const result = yield api.getTargetTemperature(expectedToken, "mockMacAddress");
521
756
  assert_1.strict.equal(result, 22);
522
757
  }));
758
+ it("should work with getPelletInReserve on compressed response", () => __awaiter(void 0, void 0, void 0, function* () {
759
+ const statusData = {
760
+ commands: { power: true },
761
+ temperatures: { enviroment: 19 },
762
+ flags: { is_pellet_in_reserve: true },
763
+ pellet: { autonomy_time: 120 },
764
+ };
765
+ const mockResponseData = {
766
+ status: createGzippedBuffer(statusData),
767
+ nvm: { user_parameters: { enviroment_1_temperature: 22 } },
768
+ };
769
+ fetchStub.resolves(mockResponse(mockResponseData));
770
+ const api = (0, library_1.configure)("https://example.com/api/");
771
+ const result = yield api.getPelletInReserve(expectedToken, "mockMacAddress");
772
+ assert_1.strict.equal(result, true);
773
+ }));
774
+ it("should work with getPelletAutonomyTime on response", () => __awaiter(void 0, void 0, void 0, function* () {
775
+ const mockResponseData = {
776
+ status: {
777
+ commands: { power: true },
778
+ temperatures: { enviroment: 19 },
779
+ flags: { is_pellet_in_reserve: false },
780
+ pellet: { autonomy_time: 240 },
781
+ },
782
+ nvm: { user_parameters: { enviroment_1_temperature: 22 } },
783
+ };
784
+ fetchStub.resolves(mockResponse(mockResponseData));
785
+ const api = (0, library_1.configure)("https://example.com/api/");
786
+ const result = yield api.getPelletAutonomyTime(expectedToken, "mockMacAddress");
787
+ assert_1.strict.equal(result, 240);
788
+ }));
523
789
  });
524
790
  describe("Error Handling", () => {
525
791
  const errorTests = [
@@ -13,9 +13,17 @@ interface TemperaturesType {
13
13
  board: number;
14
14
  enviroment: number;
15
15
  }
16
+ interface GeneralFlagsType {
17
+ is_pellet_in_reserve: boolean;
18
+ }
19
+ interface PelletAutonomyType {
20
+ autonomy_time: number;
21
+ }
16
22
  interface StatusType {
17
23
  commands: CommandsType;
18
24
  temperatures: TemperaturesType;
25
+ flags: GeneralFlagsType;
26
+ pellet: PelletAutonomyType;
19
27
  }
20
28
  interface UserParametersType {
21
29
  enviroment_1_temperature: number;
@@ -23,6 +31,14 @@ interface UserParametersType {
23
31
  enviroment_3_temperature: number;
24
32
  is_auto: boolean;
25
33
  is_sound_active: boolean;
34
+ manual_power: number;
35
+ fan_1_ventilation: number;
36
+ fan_2_ventilation: number;
37
+ fan_3_ventilation: number;
38
+ is_standby_active: boolean;
39
+ standby_waiting_time: number;
40
+ is_fahrenheit: boolean;
41
+ language: number;
26
42
  }
27
43
  interface DeviceInfoType {
28
44
  status: StatusType;
@@ -70,4 +86,17 @@ interface DeviceAssociationResponse {
70
86
  deviceRoom: string;
71
87
  serialNumber: string;
72
88
  }
73
- export type { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, };
89
+ /**
90
+ * Represents a discovered Edilkamin device from Bluetooth scanning.
91
+ */
92
+ interface DiscoveredDevice {
93
+ /** BLE MAC address as discovered */
94
+ bleMac: string;
95
+ /** WiFi MAC address (BLE MAC - 2), used for API calls */
96
+ wifiMac: string;
97
+ /** Device name (typically "EDILKAMIN_EP") */
98
+ name: string;
99
+ /** Signal strength in dBm (optional, not all platforms provide this) */
100
+ rssi?: number;
101
+ }
102
+ export type { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, GeneralFlagsType, PelletAutonomyType, StatusType, TemperaturesType, UserParametersType, };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edilkamin",
3
- "version": "1.7.4",
3
+ "version": "1.9.0",
4
4
  "description": "",
5
5
  "main": "dist/cjs/src/index.js",
6
6
  "module": "dist/esm/src/index.js",
@@ -15,6 +15,16 @@
15
15
  "types": "./dist/cjs/src/index.d.ts",
16
16
  "default": "./dist/cjs/src/index.js"
17
17
  }
18
+ },
19
+ "./bluetooth": {
20
+ "import": {
21
+ "types": "./dist/esm/src/bluetooth.d.ts",
22
+ "default": "./dist/esm/src/bluetooth.js"
23
+ },
24
+ "require": {
25
+ "types": "./dist/cjs/src/bluetooth.d.ts",
26
+ "default": "./dist/cjs/src/bluetooth.js"
27
+ }
18
28
  }
19
29
  },
20
30
  "scripts": {
@@ -60,9 +70,10 @@
60
70
  "@eslint/eslintrc": "^3.2.0",
61
71
  "@eslint/js": "^9.16.0",
62
72
  "@types/mocha": "^10.0.10",
63
- "@types/node": "^25.0.2",
73
+ "@types/node": "^24",
64
74
  "@types/pako": "^2.0.4",
65
- "@types/sinon": "^17.0.3",
75
+ "@types/sinon": "^21.0.0",
76
+ "@types/web-bluetooth": "^0.0.21",
66
77
  "@typescript-eslint/eslint-plugin": "^8.17.0",
67
78
  "@typescript-eslint/parser": "^8.17.0",
68
79
  "esbuild": "^0.27.1",
@@ -73,12 +84,12 @@
73
84
  "mocha": "^11.7.5",
74
85
  "nyc": "^17.1.0",
75
86
  "prettier": "^3.7.4",
76
- "sinon": "^19.0.2",
87
+ "sinon": "^21.0.1",
77
88
  "ts-node": "^10.9.1",
78
89
  "typedoc": "^0.28.15",
79
90
  "typescript": "^5.7.2"
80
91
  },
81
92
  "optionalDependencies": {
82
- "commander": "^12.1.0"
93
+ "commander": "^14.0.2"
83
94
  }
84
95
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Converts a BLE MAC address to WiFi MAC address.
3
+ * The WiFi MAC is the BLE MAC minus 2 in hexadecimal.
4
+ *
5
+ * @param bleMac - BLE MAC address (with or without colons/dashes)
6
+ * @returns WiFi MAC address in lowercase without separators
7
+ *
8
+ * @example
9
+ * bleToWifiMac("A8:03:2A:FE:D5:0A") // returns "a8032afed508"
10
+ * bleToWifiMac("a8032afed50a") // returns "a8032afed508"
11
+ */
12
+ declare const bleToWifiMac: (bleMac: string) => string;
13
+ export { bleToWifiMac };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Converts a BLE MAC address to WiFi MAC address.
3
+ * The WiFi MAC is the BLE MAC minus 2 in hexadecimal.
4
+ *
5
+ * @param bleMac - BLE MAC address (with or without colons/dashes)
6
+ * @returns WiFi MAC address in lowercase without separators
7
+ *
8
+ * @example
9
+ * bleToWifiMac("A8:03:2A:FE:D5:0A") // returns "a8032afed508"
10
+ * bleToWifiMac("a8032afed50a") // returns "a8032afed508"
11
+ */
12
+ const bleToWifiMac = (bleMac) => {
13
+ // Remove colons, dashes, and convert to lowercase
14
+ const normalized = bleMac.replace(/[:-]/g, "").toLowerCase();
15
+ // Validate MAC address format (12 hex characters)
16
+ if (!/^[0-9a-f]{12}$/.test(normalized)) {
17
+ throw new Error(`Invalid MAC address format: ${bleMac}`);
18
+ }
19
+ // Convert to number, subtract 2, convert back to hex
20
+ const bleValue = BigInt(`0x${normalized}`);
21
+ const wifiValue = bleValue - BigInt(2);
22
+ // Pad to 12 characters and return lowercase
23
+ return wifiValue.toString(16).padStart(12, "0");
24
+ };
25
+ export { bleToWifiMac };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { strict as assert } from "assert";
2
+ import { bleToWifiMac } from "./bluetooth-utils";
3
+ describe("bleToWifiMac", () => {
4
+ it("converts BLE MAC with colons to WiFi MAC", () => {
5
+ assert.equal(bleToWifiMac("A8:03:2A:FE:D5:0A"), "a8032afed508");
6
+ });
7
+ it("converts BLE MAC without separators", () => {
8
+ assert.equal(bleToWifiMac("a8032afed50a"), "a8032afed508");
9
+ });
10
+ it("converts BLE MAC with dashes", () => {
11
+ assert.equal(bleToWifiMac("A8-03-2A-FE-D5-0A"), "a8032afed508");
12
+ });
13
+ it("handles lowercase input", () => {
14
+ assert.equal(bleToWifiMac("a8:03:2a:fe:d5:0a"), "a8032afed508");
15
+ });
16
+ it("handles edge case where subtraction crosses byte boundary", () => {
17
+ // FF:FF:FF:FF:FF:01 - 2 = FF:FF:FF:FF:FE:FF
18
+ assert.equal(bleToWifiMac("FF:FF:FF:FF:FF:01"), "fffffffffeff");
19
+ });
20
+ it("handles minimum value edge case", () => {
21
+ // 00:00:00:00:00:02 - 2 = 00:00:00:00:00:00
22
+ assert.equal(bleToWifiMac("00:00:00:00:00:02"), "000000000000");
23
+ });
24
+ it("throws on invalid MAC format - too short", () => {
25
+ assert.throws(() => bleToWifiMac("A8:03:2A"), /Invalid MAC address format/);
26
+ });
27
+ it("throws on invalid MAC format - invalid characters", () => {
28
+ assert.throws(() => bleToWifiMac("G8:03:2A:FE:D5:0A"), /Invalid MAC address format/);
29
+ });
30
+ it("throws on empty string", () => {
31
+ assert.throws(() => bleToWifiMac(""), /Invalid MAC address format/);
32
+ });
33
+ });
@@ -0,0 +1,40 @@
1
+ import { DiscoveredDevice } from "./types";
2
+ /** Device name broadcast by Edilkamin stoves */
3
+ declare const EDILKAMIN_DEVICE_NAME = "EDILKAMIN_EP";
4
+ /** GATT Service UUID for Edilkamin devices (0xABF0) */
5
+ declare const EDILKAMIN_SERVICE_UUID = 44016;
6
+ /**
7
+ * Check if Web Bluetooth API is available in the current browser.
8
+ *
9
+ * @returns true if Web Bluetooth is supported
10
+ */
11
+ declare const isWebBluetoothSupported: () => boolean;
12
+ /**
13
+ * Scan for nearby Edilkamin stoves using the Web Bluetooth API.
14
+ *
15
+ * This function triggers the browser's Bluetooth device picker dialog,
16
+ * filtered to show only devices named "EDILKAMIN_EP".
17
+ *
18
+ * Note: Web Bluetooth requires:
19
+ * - HTTPS or localhost
20
+ * - User gesture (button click)
21
+ * - Chrome/Edge/Opera (not Firefox/Safari)
22
+ *
23
+ * @returns Promise resolving to array of discovered devices
24
+ * @throws Error if Web Bluetooth is not supported or user cancels
25
+ *
26
+ * @example
27
+ * const devices = await scanForDevices();
28
+ * console.log(devices[0].wifiMac); // Use this for API calls
29
+ */
30
+ declare const scanForDevices: () => Promise<DiscoveredDevice[]>;
31
+ /**
32
+ * Scan for devices with a custom filter.
33
+ * Advanced function for users who need more control over device selection.
34
+ *
35
+ * @param options - Web Bluetooth requestDevice options
36
+ * @returns Promise resolving to the selected BluetoothDevice
37
+ */
38
+ declare const scanWithOptions: (options: RequestDeviceOptions) => Promise<BluetoothDevice>;
39
+ export { EDILKAMIN_DEVICE_NAME, EDILKAMIN_SERVICE_UUID, isWebBluetoothSupported, scanForDevices, scanWithOptions, };
40
+ export type { DiscoveredDevice } from "./types";
@@ -0,0 +1,100 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { bleToWifiMac } from "./bluetooth-utils";
11
+ /** Device name broadcast by Edilkamin stoves */
12
+ const EDILKAMIN_DEVICE_NAME = "EDILKAMIN_EP";
13
+ /** GATT Service UUID for Edilkamin devices (0xABF0) */
14
+ const EDILKAMIN_SERVICE_UUID = 0xabf0;
15
+ /**
16
+ * Check if Web Bluetooth API is available in the current browser.
17
+ *
18
+ * @returns true if Web Bluetooth is supported
19
+ */
20
+ const isWebBluetoothSupported = () => {
21
+ return typeof navigator !== "undefined" && "bluetooth" in navigator;
22
+ };
23
+ /**
24
+ * Scan for nearby Edilkamin stoves using the Web Bluetooth API.
25
+ *
26
+ * This function triggers the browser's Bluetooth device picker dialog,
27
+ * filtered to show only devices named "EDILKAMIN_EP".
28
+ *
29
+ * Note: Web Bluetooth requires:
30
+ * - HTTPS or localhost
31
+ * - User gesture (button click)
32
+ * - Chrome/Edge/Opera (not Firefox/Safari)
33
+ *
34
+ * @returns Promise resolving to array of discovered devices
35
+ * @throws Error if Web Bluetooth is not supported or user cancels
36
+ *
37
+ * @example
38
+ * const devices = await scanForDevices();
39
+ * console.log(devices[0].wifiMac); // Use this for API calls
40
+ */
41
+ const scanForDevices = () => __awaiter(void 0, void 0, void 0, function* () {
42
+ if (!isWebBluetoothSupported()) {
43
+ throw new Error("Web Bluetooth API is not supported in this browser. " +
44
+ "Use Chrome, Edge, or Opera on desktop/Android. " +
45
+ "On iOS, use the Bluefy browser app.");
46
+ }
47
+ try {
48
+ // Request device - this opens the browser's device picker
49
+ const device = yield navigator.bluetooth.requestDevice({
50
+ filters: [{ name: EDILKAMIN_DEVICE_NAME }],
51
+ optionalServices: [EDILKAMIN_SERVICE_UUID],
52
+ });
53
+ // Extract BLE MAC from device ID if available
54
+ // Note: device.id format varies by platform, may need adjustment
55
+ const bleMac = device.id || "";
56
+ const name = device.name || EDILKAMIN_DEVICE_NAME;
57
+ // Calculate WiFi MAC for API calls
58
+ let wifiMac = "";
59
+ if (bleMac && /^[0-9a-f:-]{12,17}$/i.test(bleMac)) {
60
+ try {
61
+ wifiMac = bleToWifiMac(bleMac);
62
+ }
63
+ catch (_a) {
64
+ // device.id may not be a valid MAC format on all platforms
65
+ wifiMac = "";
66
+ }
67
+ }
68
+ const discoveredDevice = {
69
+ bleMac,
70
+ wifiMac,
71
+ name,
72
+ // RSSI not directly available from requestDevice
73
+ };
74
+ return [discoveredDevice];
75
+ }
76
+ catch (error) {
77
+ if (error instanceof Error) {
78
+ if (error.name === "NotFoundError") {
79
+ // User cancelled the device picker
80
+ return [];
81
+ }
82
+ throw error;
83
+ }
84
+ throw new Error("Unknown error during Bluetooth scan");
85
+ }
86
+ });
87
+ /**
88
+ * Scan for devices with a custom filter.
89
+ * Advanced function for users who need more control over device selection.
90
+ *
91
+ * @param options - Web Bluetooth requestDevice options
92
+ * @returns Promise resolving to the selected BluetoothDevice
93
+ */
94
+ const scanWithOptions = (options) => __awaiter(void 0, void 0, void 0, function* () {
95
+ if (!isWebBluetoothSupported()) {
96
+ throw new Error("Web Bluetooth API is not supported in this browser.");
97
+ }
98
+ return navigator.bluetooth.requestDevice(options);
99
+ });
100
+ export { EDILKAMIN_DEVICE_NAME, EDILKAMIN_SERVICE_UUID, isWebBluetoothSupported, scanForDevices, scanWithOptions, };