edilkamin 1.7.3 → 1.8.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.
- package/.github/workflows/cli-tests.yml +6 -0
- package/README.md +36 -3
- package/dist/cjs/package.json +95 -0
- package/dist/cjs/src/bluetooth-utils.d.ts +13 -0
- package/dist/cjs/src/bluetooth-utils.js +28 -0
- package/dist/cjs/src/bluetooth-utils.test.js +35 -0
- package/dist/cjs/src/bluetooth.d.ts +40 -0
- package/dist/cjs/src/bluetooth.js +107 -0
- package/dist/cjs/src/browser-bundle.test.js +64 -0
- package/dist/cjs/src/buffer-utils.js +78 -0
- package/dist/cjs/src/buffer-utils.test.js +186 -0
- package/dist/cjs/src/cli.js +253 -0
- package/dist/cjs/src/configureAmplify.test.js +42 -0
- package/dist/cjs/src/constants.js +9 -0
- package/dist/{esm → cjs/src}/index.d.ts +2 -1
- package/dist/cjs/src/index.js +24 -0
- package/dist/cjs/src/library.js +324 -0
- package/dist/cjs/src/library.test.js +547 -0
- package/dist/cjs/src/serial-utils.js +50 -0
- package/dist/cjs/src/serial-utils.test.js +50 -0
- package/dist/cjs/src/token-storage.js +119 -0
- package/dist/{esm → cjs/src}/types.d.ts +14 -1
- package/dist/cjs/src/types.js +2 -0
- package/dist/esm/package.json +95 -0
- package/dist/esm/src/bluetooth-utils.d.ts +13 -0
- package/dist/esm/src/bluetooth-utils.js +25 -0
- package/dist/esm/src/bluetooth-utils.test.d.ts +1 -0
- package/dist/esm/src/bluetooth-utils.test.js +33 -0
- package/dist/esm/src/bluetooth.d.ts +40 -0
- package/dist/esm/src/bluetooth.js +100 -0
- package/dist/esm/src/browser-bundle.test.d.ts +1 -0
- package/dist/esm/{browser-bundle.test.js → src/browser-bundle.test.js} +1 -1
- package/dist/esm/src/buffer-utils.d.ts +25 -0
- package/dist/esm/src/buffer-utils.test.d.ts +1 -0
- package/dist/esm/src/cli.d.ts +3 -0
- package/dist/esm/src/configureAmplify.test.d.ts +1 -0
- package/dist/esm/src/constants.d.ts +4 -0
- package/dist/esm/src/index.d.ts +7 -0
- package/dist/esm/{index.js → src/index.js} +1 -0
- package/dist/esm/src/library.d.ts +55 -0
- package/dist/esm/src/library.test.d.ts +1 -0
- package/dist/esm/src/serial-utils.d.ts +33 -0
- package/dist/esm/src/serial-utils.test.d.ts +1 -0
- package/dist/esm/src/token-storage.d.ts +14 -0
- package/dist/esm/src/types.d.ts +86 -0
- package/dist/esm/src/types.js +1 -0
- package/package.json +22 -11
- package/src/bluetooth-utils.test.ts +46 -0
- package/src/bluetooth-utils.ts +29 -0
- package/src/bluetooth.ts +115 -0
- package/src/browser-bundle.test.ts +1 -1
- package/src/index.ts +2 -0
- package/src/types.ts +15 -0
- package/tsconfig.cjs.json +2 -2
- package/tsconfig.json +3 -3
- /package/dist/{esm/browser-bundle.test.d.ts → cjs/src/bluetooth-utils.test.d.ts} +0 -0
- /package/dist/{esm/buffer-utils.test.d.ts → cjs/src/browser-bundle.test.d.ts} +0 -0
- /package/dist/{esm → cjs/src}/buffer-utils.d.ts +0 -0
- /package/dist/{esm/configureAmplify.test.d.ts → cjs/src/buffer-utils.test.d.ts} +0 -0
- /package/dist/{esm → cjs/src}/cli.d.ts +0 -0
- /package/dist/{esm/library.test.d.ts → cjs/src/configureAmplify.test.d.ts} +0 -0
- /package/dist/{esm → cjs/src}/constants.d.ts +0 -0
- /package/dist/{esm → cjs/src}/library.d.ts +0 -0
- /package/dist/{esm/serial-utils.test.d.ts → cjs/src/library.test.d.ts} +0 -0
- /package/dist/{esm → cjs/src}/serial-utils.d.ts +0 -0
- /package/dist/{esm/types.js → cjs/src/serial-utils.test.d.ts} +0 -0
- /package/dist/{esm → cjs/src}/token-storage.d.ts +0 -0
- /package/dist/esm/{buffer-utils.js → src/buffer-utils.js} +0 -0
- /package/dist/esm/{buffer-utils.test.js → src/buffer-utils.test.js} +0 -0
- /package/dist/esm/{cli.js → src/cli.js} +0 -0
- /package/dist/esm/{configureAmplify.test.js → src/configureAmplify.test.js} +0 -0
- /package/dist/esm/{constants.js → src/constants.js} +0 -0
- /package/dist/esm/{library.js → src/library.js} +0 -0
- /package/dist/esm/{library.test.js → src/library.test.js} +0 -0
- /package/dist/esm/{serial-utils.js → src/serial-utils.js} +0 -0
- /package/dist/esm/{serial-utils.test.js → src/serial-utils.test.js} +0 -0
- /package/dist/esm/{token-storage.js → src/token-storage.js} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.clearSession = exports.createFileStorage = void 0;
|
|
46
|
+
const fs_1 = require("fs");
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const TOKEN_DIR = path.join(os.homedir(), ".edilkamin");
|
|
50
|
+
const TOKEN_FILE = path.join(TOKEN_DIR, "session.json");
|
|
51
|
+
/**
|
|
52
|
+
* Custom storage adapter for AWS Amplify that persists to file system.
|
|
53
|
+
* Used for CLI to maintain sessions between invocations.
|
|
54
|
+
*/
|
|
55
|
+
const createFileStorage = () => {
|
|
56
|
+
let cache = {};
|
|
57
|
+
let loaded = false;
|
|
58
|
+
const ensureDir = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
try {
|
|
60
|
+
yield fs_1.promises.mkdir(TOKEN_DIR, { recursive: true, mode: 0o700 });
|
|
61
|
+
}
|
|
62
|
+
catch (_a) {
|
|
63
|
+
// Directory may already exist
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const load = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
+
if (loaded)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
const data = yield fs_1.promises.readFile(TOKEN_FILE, "utf-8");
|
|
71
|
+
cache = JSON.parse(data);
|
|
72
|
+
}
|
|
73
|
+
catch (_a) {
|
|
74
|
+
cache = {};
|
|
75
|
+
}
|
|
76
|
+
loaded = true;
|
|
77
|
+
});
|
|
78
|
+
const save = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
yield ensureDir();
|
|
80
|
+
yield fs_1.promises.writeFile(TOKEN_FILE, JSON.stringify(cache), {
|
|
81
|
+
encoding: "utf-8",
|
|
82
|
+
mode: 0o600,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
setItem: (key, value) => __awaiter(void 0, void 0, void 0, function* () {
|
|
87
|
+
yield load();
|
|
88
|
+
cache[key] = value;
|
|
89
|
+
yield save();
|
|
90
|
+
}),
|
|
91
|
+
getItem: (key) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
+
var _a;
|
|
93
|
+
yield load();
|
|
94
|
+
return (_a = cache[key]) !== null && _a !== void 0 ? _a : null;
|
|
95
|
+
}),
|
|
96
|
+
removeItem: (key) => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
yield load();
|
|
98
|
+
delete cache[key];
|
|
99
|
+
yield save();
|
|
100
|
+
}),
|
|
101
|
+
clear: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
+
cache = {};
|
|
103
|
+
yield save();
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
exports.createFileStorage = createFileStorage;
|
|
108
|
+
/**
|
|
109
|
+
* Clears all stored session data.
|
|
110
|
+
*/
|
|
111
|
+
const clearSession = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
try {
|
|
113
|
+
yield fs_1.promises.unlink(TOKEN_FILE);
|
|
114
|
+
}
|
|
115
|
+
catch (_a) {
|
|
116
|
+
// File may not exist
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
exports.clearSession = clearSession;
|
|
@@ -70,4 +70,17 @@ interface DeviceAssociationResponse {
|
|
|
70
70
|
deviceRoom: string;
|
|
71
71
|
serialNumber: string;
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Represents a discovered Edilkamin device from Bluetooth scanning.
|
|
75
|
+
*/
|
|
76
|
+
interface DiscoveredDevice {
|
|
77
|
+
/** BLE MAC address as discovered */
|
|
78
|
+
bleMac: string;
|
|
79
|
+
/** WiFi MAC address (BLE MAC - 2), used for API calls */
|
|
80
|
+
wifiMac: string;
|
|
81
|
+
/** Device name (typically "EDILKAMIN_EP") */
|
|
82
|
+
name: string;
|
|
83
|
+
/** Signal strength in dBm (optional, not all platforms provide this) */
|
|
84
|
+
rssi?: number;
|
|
85
|
+
}
|
|
86
|
+
export type { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "edilkamin",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/cjs/src/index.js",
|
|
6
|
+
"module": "dist/esm/src/index.js",
|
|
7
|
+
"types": "dist/esm/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/esm/src/index.d.ts",
|
|
12
|
+
"default": "./dist/esm/src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/cjs/src/index.d.ts",
|
|
16
|
+
"default": "./dist/cjs/src/index.js"
|
|
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
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"cli": "ts-node src/cli.ts",
|
|
32
|
+
"cli:debug": "node --inspect --require ts-node/register/transpile-only src/cli.ts",
|
|
33
|
+
"test": "nyc mocha --require ts-node/register src/*.test.ts",
|
|
34
|
+
"test:debug": "nyc mocha --require ts-node/register/transpile-only --inspect src/*.test.ts",
|
|
35
|
+
"lint:prettier": "prettier --check src docs .github *.json *.md *.mjs",
|
|
36
|
+
"format:prettier": "prettier --write src docs .github *.json *.md *.mjs",
|
|
37
|
+
"lint:eslint": "eslint src",
|
|
38
|
+
"format:eslint": "eslint --fix src",
|
|
39
|
+
"lint": "yarn lint:prettier && yarn lint:eslint",
|
|
40
|
+
"format": "yarn format:prettier && yarn format:eslint",
|
|
41
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
42
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
43
|
+
"build": "npm run build:cjs && npm run build:esm"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/AndreMiras/edilkamin.js.git"
|
|
48
|
+
},
|
|
49
|
+
"author": "Andre Miras",
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/AndreMiras/edilkamin.js/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/AndreMiras/edilkamin.js#readme",
|
|
55
|
+
"bin": {
|
|
56
|
+
"edilkamin": "dist/cjs/src/cli.js"
|
|
57
|
+
},
|
|
58
|
+
"nyc": {
|
|
59
|
+
"reporter": [
|
|
60
|
+
"html",
|
|
61
|
+
"lcov",
|
|
62
|
+
"text"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"aws-amplify": "^6.10.0",
|
|
67
|
+
"pako": "^2.1.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
71
|
+
"@eslint/js": "^9.16.0",
|
|
72
|
+
"@types/mocha": "^10.0.10",
|
|
73
|
+
"@types/node": "^25.0.2",
|
|
74
|
+
"@types/pako": "^2.0.4",
|
|
75
|
+
"@types/sinon": "^17.0.3",
|
|
76
|
+
"@types/web-bluetooth": "^0.0.21",
|
|
77
|
+
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
|
78
|
+
"@typescript-eslint/parser": "^8.17.0",
|
|
79
|
+
"esbuild": "^0.27.1",
|
|
80
|
+
"eslint": "^9.16.0",
|
|
81
|
+
"eslint-config-prettier": "^10.1.8",
|
|
82
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
83
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
84
|
+
"mocha": "^11.7.5",
|
|
85
|
+
"nyc": "^17.1.0",
|
|
86
|
+
"prettier": "^3.7.4",
|
|
87
|
+
"sinon": "^19.0.2",
|
|
88
|
+
"ts-node": "^10.9.1",
|
|
89
|
+
"typedoc": "^0.28.15",
|
|
90
|
+
"typescript": "^5.7.2"
|
|
91
|
+
},
|
|
92
|
+
"optionalDependencies": {
|
|
93
|
+
"commander": "^14.0.2"
|
|
94
|
+
}
|
|
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, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -15,7 +15,7 @@ describe("browser-bundle", () => {
|
|
|
15
15
|
// without requiring Node.js built-in modules (fs, os, path).
|
|
16
16
|
// If this test fails, it means Node.js-only code has leaked into the main exports.
|
|
17
17
|
const result = yield esbuild.build({
|
|
18
|
-
entryPoints: ["dist/esm/index.js"],
|
|
18
|
+
entryPoints: ["dist/esm/src/index.js"],
|
|
19
19
|
platform: "browser",
|
|
20
20
|
bundle: true,
|
|
21
21
|
write: false,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BufferEncodedType } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Type guard to check if a value is a serialized Node.js Buffer.
|
|
4
|
+
* Node.js Buffers serialize to JSON as: {type: "Buffer", data: [...]}
|
|
5
|
+
*
|
|
6
|
+
* @param value - The value to check
|
|
7
|
+
* @returns True if the value is a Buffer-encoded object
|
|
8
|
+
*/
|
|
9
|
+
declare const isBuffer: (value: unknown) => value is BufferEncodedType;
|
|
10
|
+
/**
|
|
11
|
+
* Decompresses a Buffer-encoded gzip object and parses the resulting JSON.
|
|
12
|
+
*
|
|
13
|
+
* @param bufferObj - A serialized Buffer object containing gzip data
|
|
14
|
+
* @returns The decompressed and parsed JSON data, or the original object on failure
|
|
15
|
+
*/
|
|
16
|
+
declare const decompressBuffer: (bufferObj: BufferEncodedType) => unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Recursively processes an API response to decompress any Buffer-encoded fields.
|
|
19
|
+
* Handles nested objects and arrays, preserving structure while decompressing.
|
|
20
|
+
*
|
|
21
|
+
* @param data - The API response data to process
|
|
22
|
+
* @returns The processed data with all Buffer fields decompressed
|
|
23
|
+
*/
|
|
24
|
+
declare const processResponse: <T>(data: T) => T;
|
|
25
|
+
export { decompressBuffer, isBuffer, processResponse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const OLD_API_URL = "https://fxtj7xkgc6.execute-api.eu-central-1.amazonaws.com/prod/";
|
|
2
|
+
declare const NEW_API_URL = "https://the-mind-api.edilkamin.com/";
|
|
3
|
+
declare const API_URL = "https://the-mind-api.edilkamin.com/";
|
|
4
|
+
export { API_URL, NEW_API_URL, OLD_API_URL };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { bleToWifiMac } from "./bluetooth-utils";
|
|
2
|
+
export { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
3
|
+
export { API_URL, NEW_API_URL, OLD_API_URL } from "./constants";
|
|
4
|
+
export { configure, getSession, signIn } from "./library";
|
|
5
|
+
export { serialNumberDisplay, serialNumberFromHex, serialNumberToHex, } from "./serial-utils";
|
|
6
|
+
export { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, } from "./types";
|
|
7
|
+
export declare const deviceInfo: (jwtToken: string, macAddress: string) => Promise<import("./types").DeviceInfoType>, registerDevice: (jwtToken: string, macAddress: string, serialNumber: string, deviceName?: string, deviceRoom?: string) => Promise<import("./types").DeviceAssociationResponse>, editDevice: (jwtToken: string, macAddress: string, deviceName?: string, deviceRoom?: string) => Promise<import("./types").DeviceAssociationResponse>, setPower: (jwtToken: string, macAddress: string, value: number) => Promise<unknown>, setPowerOff: (jwtToken: string, macAddress: string) => Promise<unknown>, setPowerOn: (jwtToken: string, macAddress: string) => Promise<unknown>, getPower: (jwtToken: string, macAddress: string) => Promise<boolean>, getEnvironmentTemperature: (jwtToken: string, macAddress: string) => Promise<number>, getTargetTemperature: (jwtToken: string, macAddress: string) => Promise<number>, setTargetTemperature: (jwtToken: string, macAddress: string, temperature: number) => Promise<unknown>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { configure } from "./library";
|
|
2
|
+
export { bleToWifiMac } from "./bluetooth-utils";
|
|
2
3
|
export { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
3
4
|
export { API_URL, NEW_API_URL, OLD_API_URL } from "./constants";
|
|
4
5
|
export { configure, getSession, signIn } from "./library";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as amplifyAuth from "aws-amplify/auth";
|
|
2
|
+
import { DeviceAssociationResponse, DeviceInfoType } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Generates headers with a JWT token for authenticated requests.
|
|
5
|
+
* @param {string} jwtToken - The JWT token for authorization.
|
|
6
|
+
* @returns {object} - The headers object with the Authorization field.
|
|
7
|
+
*/
|
|
8
|
+
declare const headers: (jwtToken: string) => {
|
|
9
|
+
Authorization: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Configures Amplify if not already configured.
|
|
13
|
+
* Uses a local flag to avoid calling getConfig() which prints a warning.
|
|
14
|
+
* @param {object} [storage] - Optional custom storage adapter for token persistence
|
|
15
|
+
*/
|
|
16
|
+
declare const configureAmplify: (storage?: {
|
|
17
|
+
setItem: (key: string, value: string) => Promise<void>;
|
|
18
|
+
getItem: (key: string) => Promise<string | null>;
|
|
19
|
+
removeItem: (key: string) => Promise<void>;
|
|
20
|
+
clear: () => Promise<void>;
|
|
21
|
+
}) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Creates an authentication service with sign-in functionality.
|
|
24
|
+
* @param {typeof amplifyAuth} auth - The authentication module to use.
|
|
25
|
+
* @returns {object} - An object containing authentication-related methods.
|
|
26
|
+
*/
|
|
27
|
+
declare const createAuthService: (auth: typeof amplifyAuth) => {
|
|
28
|
+
signIn: (username: string, password: string, legacy?: boolean) => Promise<string>;
|
|
29
|
+
getSession: (forceRefresh?: boolean, legacy?: boolean) => Promise<string>;
|
|
30
|
+
};
|
|
31
|
+
declare const signIn: (username: string, password: string, legacy?: boolean) => Promise<string>, getSession: (forceRefresh?: boolean, legacy?: boolean) => Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Configures the library for API interactions.
|
|
34
|
+
* Initializes API methods with a specified base URL.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} [baseURL=API_URL] - The base URL for the API.
|
|
37
|
+
* @returns {object} - An object containing methods for interacting with the API.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* const api = configure();
|
|
41
|
+
* const power = await api.getPower(jwtToken, macAddress);
|
|
42
|
+
*/
|
|
43
|
+
declare const configure: (baseURL?: string) => {
|
|
44
|
+
deviceInfo: (jwtToken: string, macAddress: string) => Promise<DeviceInfoType>;
|
|
45
|
+
registerDevice: (jwtToken: string, macAddress: string, serialNumber: string, deviceName?: string, deviceRoom?: string) => Promise<DeviceAssociationResponse>;
|
|
46
|
+
editDevice: (jwtToken: string, macAddress: string, deviceName?: string, deviceRoom?: string) => Promise<DeviceAssociationResponse>;
|
|
47
|
+
setPower: (jwtToken: string, macAddress: string, value: number) => Promise<unknown>;
|
|
48
|
+
setPowerOff: (jwtToken: string, macAddress: string) => Promise<unknown>;
|
|
49
|
+
setPowerOn: (jwtToken: string, macAddress: string) => Promise<unknown>;
|
|
50
|
+
getPower: (jwtToken: string, macAddress: string) => Promise<boolean>;
|
|
51
|
+
getEnvironmentTemperature: (jwtToken: string, macAddress: string) => Promise<number>;
|
|
52
|
+
getTargetTemperature: (jwtToken: string, macAddress: string) => Promise<number>;
|
|
53
|
+
setTargetTemperature: (jwtToken: string, macAddress: string, temperature: number) => Promise<unknown>;
|
|
54
|
+
};
|
|
55
|
+
export { configure, configureAmplify, createAuthService, getSession, headers, signIn, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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>;
|