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,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const assert_1 = require("assert");
|
|
7
|
+
const pako_1 = __importDefault(require("pako"));
|
|
8
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
9
|
+
const buffer_utils_1 = require("./buffer-utils");
|
|
10
|
+
/**
|
|
11
|
+
* Helper to create a gzip-compressed Buffer object for testing.
|
|
12
|
+
*/
|
|
13
|
+
const createGzippedBuffer = (data) => {
|
|
14
|
+
const json = JSON.stringify(data);
|
|
15
|
+
const compressed = pako_1.default.gzip(json);
|
|
16
|
+
return {
|
|
17
|
+
type: "Buffer",
|
|
18
|
+
data: Array.from(compressed),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
describe("buffer-utils", () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
sinon_1.default.restore();
|
|
24
|
+
});
|
|
25
|
+
describe("isBuffer", () => {
|
|
26
|
+
it("should detect valid Buffer objects", () => {
|
|
27
|
+
const buffer = { type: "Buffer", data: [31, 139, 8, 0] };
|
|
28
|
+
assert_1.strict.ok((0, buffer_utils_1.isBuffer)(buffer));
|
|
29
|
+
});
|
|
30
|
+
it("should detect empty Buffer objects", () => {
|
|
31
|
+
const buffer = { type: "Buffer", data: [] };
|
|
32
|
+
assert_1.strict.ok((0, buffer_utils_1.isBuffer)(buffer));
|
|
33
|
+
});
|
|
34
|
+
it("should reject non-Buffer objects with wrong type", () => {
|
|
35
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)({ type: "NotBuffer", data: [] }));
|
|
36
|
+
});
|
|
37
|
+
it("should reject objects without type field", () => {
|
|
38
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)({ data: [1, 2, 3] }));
|
|
39
|
+
});
|
|
40
|
+
it("should reject objects without data field", () => {
|
|
41
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)({ type: "Buffer" }));
|
|
42
|
+
});
|
|
43
|
+
it("should reject objects with non-array data", () => {
|
|
44
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)({ type: "Buffer", data: "not an array" }));
|
|
45
|
+
});
|
|
46
|
+
it("should reject null", () => {
|
|
47
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)(null));
|
|
48
|
+
});
|
|
49
|
+
it("should reject undefined", () => {
|
|
50
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)(undefined));
|
|
51
|
+
});
|
|
52
|
+
it("should reject primitives", () => {
|
|
53
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)("string"));
|
|
54
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)(123));
|
|
55
|
+
assert_1.strict.ok(!(0, buffer_utils_1.isBuffer)(true));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("decompressBuffer", () => {
|
|
59
|
+
it("should decompress gzipped JSON buffer", () => {
|
|
60
|
+
const originalData = { test: "value", nested: { key: 123 } };
|
|
61
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
62
|
+
const result = (0, buffer_utils_1.decompressBuffer)(bufferObj);
|
|
63
|
+
assert_1.strict.deepEqual(result, originalData);
|
|
64
|
+
});
|
|
65
|
+
it("should handle gzipped arrays", () => {
|
|
66
|
+
const originalData = [1, 2, 3, "test"];
|
|
67
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
68
|
+
const result = (0, buffer_utils_1.decompressBuffer)(bufferObj);
|
|
69
|
+
assert_1.strict.deepEqual(result, originalData);
|
|
70
|
+
});
|
|
71
|
+
it("should handle gzipped strings", () => {
|
|
72
|
+
const originalData = "test string";
|
|
73
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
74
|
+
const result = (0, buffer_utils_1.decompressBuffer)(bufferObj);
|
|
75
|
+
assert_1.strict.equal(result, originalData);
|
|
76
|
+
});
|
|
77
|
+
it("should return original value if decompression fails", () => {
|
|
78
|
+
const consoleWarnStub = sinon_1.default.stub(console, "warn");
|
|
79
|
+
const invalidBuffer = { type: "Buffer", data: [1, 2, 3] };
|
|
80
|
+
const result = (0, buffer_utils_1.decompressBuffer)(invalidBuffer);
|
|
81
|
+
assert_1.strict.deepEqual(result, invalidBuffer);
|
|
82
|
+
assert_1.strict.ok(consoleWarnStub.calledOnce);
|
|
83
|
+
});
|
|
84
|
+
it("should return original value if JSON parsing fails", () => {
|
|
85
|
+
const consoleWarnStub = sinon_1.default.stub(console, "warn");
|
|
86
|
+
// Create valid gzip but invalid JSON
|
|
87
|
+
const invalidJson = "not valid json {";
|
|
88
|
+
const compressed = pako_1.default.gzip(invalidJson);
|
|
89
|
+
const bufferObj = {
|
|
90
|
+
type: "Buffer",
|
|
91
|
+
data: Array.from(compressed),
|
|
92
|
+
};
|
|
93
|
+
const result = (0, buffer_utils_1.decompressBuffer)(bufferObj);
|
|
94
|
+
assert_1.strict.deepEqual(result, bufferObj);
|
|
95
|
+
assert_1.strict.ok(consoleWarnStub.calledOnce);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe("processResponse", () => {
|
|
99
|
+
it("should pass through null", () => {
|
|
100
|
+
assert_1.strict.equal((0, buffer_utils_1.processResponse)(null), null);
|
|
101
|
+
});
|
|
102
|
+
it("should pass through undefined", () => {
|
|
103
|
+
assert_1.strict.equal((0, buffer_utils_1.processResponse)(undefined), undefined);
|
|
104
|
+
});
|
|
105
|
+
it("should pass through primitives", () => {
|
|
106
|
+
assert_1.strict.equal((0, buffer_utils_1.processResponse)("string"), "string");
|
|
107
|
+
assert_1.strict.equal((0, buffer_utils_1.processResponse)(123), 123);
|
|
108
|
+
assert_1.strict.equal((0, buffer_utils_1.processResponse)(true), true);
|
|
109
|
+
});
|
|
110
|
+
it("should pass through plain objects", () => {
|
|
111
|
+
const obj = { key: "value", nested: { num: 42 } };
|
|
112
|
+
assert_1.strict.deepEqual((0, buffer_utils_1.processResponse)(obj), obj);
|
|
113
|
+
});
|
|
114
|
+
it("should pass through plain arrays", () => {
|
|
115
|
+
const arr = [1, "two", { three: 3 }];
|
|
116
|
+
assert_1.strict.deepEqual((0, buffer_utils_1.processResponse)(arr), arr);
|
|
117
|
+
});
|
|
118
|
+
it("should decompress Buffer at root level", () => {
|
|
119
|
+
const originalData = { decompressed: true };
|
|
120
|
+
const buffer = createGzippedBuffer(originalData);
|
|
121
|
+
const result = (0, buffer_utils_1.processResponse)(buffer);
|
|
122
|
+
assert_1.strict.deepEqual(result, originalData);
|
|
123
|
+
});
|
|
124
|
+
it("should decompress nested Buffer fields", () => {
|
|
125
|
+
const statusData = { commands: { power: true } };
|
|
126
|
+
const response = {
|
|
127
|
+
plain: "data",
|
|
128
|
+
status: createGzippedBuffer(statusData),
|
|
129
|
+
};
|
|
130
|
+
const result = (0, buffer_utils_1.processResponse)(response);
|
|
131
|
+
assert_1.strict.equal(result.plain, "data");
|
|
132
|
+
assert_1.strict.deepEqual(result.status, statusData);
|
|
133
|
+
});
|
|
134
|
+
it("should recursively decompress deeply nested Buffers", () => {
|
|
135
|
+
const innerData = { value: 42 };
|
|
136
|
+
const middleData = { inner: createGzippedBuffer(innerData) };
|
|
137
|
+
const response = {
|
|
138
|
+
outer: createGzippedBuffer(middleData),
|
|
139
|
+
};
|
|
140
|
+
const result = (0, buffer_utils_1.processResponse)(response);
|
|
141
|
+
assert_1.strict.deepEqual(result, { outer: { inner: { value: 42 } } });
|
|
142
|
+
});
|
|
143
|
+
it("should handle arrays containing Buffers", () => {
|
|
144
|
+
const itemData = { id: 1 };
|
|
145
|
+
const response = {
|
|
146
|
+
items: [createGzippedBuffer(itemData), { id: 2 }],
|
|
147
|
+
};
|
|
148
|
+
const result = (0, buffer_utils_1.processResponse)(response);
|
|
149
|
+
assert_1.strict.deepEqual(result.items, [{ id: 1 }, { id: 2 }]);
|
|
150
|
+
});
|
|
151
|
+
it("should handle mixed compressed and uncompressed fields", () => {
|
|
152
|
+
const compressedStatus = { commands: { power: true } };
|
|
153
|
+
const response = {
|
|
154
|
+
status: createGzippedBuffer(compressedStatus),
|
|
155
|
+
nvm: { user_parameters: { temperature: 22 } },
|
|
156
|
+
plain_field: "unchanged",
|
|
157
|
+
};
|
|
158
|
+
const result = (0, buffer_utils_1.processResponse)(response);
|
|
159
|
+
assert_1.strict.deepEqual(result.status, compressedStatus);
|
|
160
|
+
assert_1.strict.deepEqual(result.nvm, { user_parameters: { temperature: 22 } });
|
|
161
|
+
assert_1.strict.equal(result.plain_field, "unchanged");
|
|
162
|
+
});
|
|
163
|
+
it("should handle real-world DeviceInfo structure with compressed status", () => {
|
|
164
|
+
const statusData = {
|
|
165
|
+
commands: { power: true },
|
|
166
|
+
temperatures: { board: 25, enviroment: 20 },
|
|
167
|
+
};
|
|
168
|
+
const nvmData = {
|
|
169
|
+
user_parameters: {
|
|
170
|
+
enviroment_1_temperature: 22,
|
|
171
|
+
enviroment_2_temperature: 0,
|
|
172
|
+
enviroment_3_temperature: 0,
|
|
173
|
+
is_auto: false,
|
|
174
|
+
is_sound_active: true,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
const response = {
|
|
178
|
+
status: createGzippedBuffer(statusData),
|
|
179
|
+
nvm: createGzippedBuffer(nvmData),
|
|
180
|
+
};
|
|
181
|
+
const result = (0, buffer_utils_1.processResponse)(response);
|
|
182
|
+
assert_1.strict.deepEqual(result.status, statusData);
|
|
183
|
+
assert_1.strict.deepEqual(result.nvm, nvmData);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.main = void 0;
|
|
17
|
+
const commander_1 = require("commander");
|
|
18
|
+
const readline_1 = __importDefault(require("readline"));
|
|
19
|
+
const package_json_1 = require("../package.json");
|
|
20
|
+
const constants_1 = require("./constants");
|
|
21
|
+
const library_1 = require("./library");
|
|
22
|
+
const token_storage_1 = require("./token-storage");
|
|
23
|
+
const promptPassword = () => {
|
|
24
|
+
const rl = readline_1.default.createInterface({
|
|
25
|
+
input: process.stdin,
|
|
26
|
+
output: process.stdout,
|
|
27
|
+
terminal: true,
|
|
28
|
+
});
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
rl.question("Enter password: ", (password) => {
|
|
31
|
+
// Hide the password input
|
|
32
|
+
readline_1.default.moveCursor(process.stdout, 0, -1);
|
|
33
|
+
readline_1.default.clearLine(process.stdout, 0);
|
|
34
|
+
rl.close();
|
|
35
|
+
resolve(password);
|
|
36
|
+
});
|
|
37
|
+
// Disable input echoing for password
|
|
38
|
+
process.stdin.on("data", (char) => {
|
|
39
|
+
if (char.toString("hex") === "0d0a")
|
|
40
|
+
return; // Enter key
|
|
41
|
+
process.stdout.write("*");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Adds common options (username and password) to a command.
|
|
47
|
+
* Username is optional if a session already exists.
|
|
48
|
+
* @param command The command to which options should be added.
|
|
49
|
+
* @returns The command with options added.
|
|
50
|
+
*/
|
|
51
|
+
const addAuthOptions = (command) => command
|
|
52
|
+
.option("-u, --username <username>", "Username (optional if session exists)")
|
|
53
|
+
.option("-p, --password <password>", "Password");
|
|
54
|
+
/**
|
|
55
|
+
* Adds MAC address option to a command.
|
|
56
|
+
* @param command The command to which the MAC address option should be added.
|
|
57
|
+
* @returns The command with the MAC address option added.
|
|
58
|
+
*/
|
|
59
|
+
const addMacOption = (command) => command.requiredOption("-m, --mac <macAddress>", "MAC address of the device");
|
|
60
|
+
/**
|
|
61
|
+
* Adds legacy API option to a command.
|
|
62
|
+
* @param command The command to which the legacy option should be added.
|
|
63
|
+
* @returns The command with the legacy option added.
|
|
64
|
+
*/
|
|
65
|
+
const addLegacyOption = (command) => command.option("--legacy", "Use legacy API endpoint (old AWS Gateway)");
|
|
66
|
+
/**
|
|
67
|
+
* Handles common authentication and API initialization logic.
|
|
68
|
+
* Tries to use existing session first, falls back to sign-in if needed.
|
|
69
|
+
* @param options The options passed from the CLI command.
|
|
70
|
+
* @returns An object containing the normalized MAC, JWT token, and configured API instance.
|
|
71
|
+
*/
|
|
72
|
+
const initializeCommand = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
const { username, password, mac, legacy = false } = options;
|
|
74
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
75
|
+
// Initialize file storage for session persistence
|
|
76
|
+
const storage = (0, token_storage_1.createFileStorage)();
|
|
77
|
+
(0, library_1.configureAmplify)(storage);
|
|
78
|
+
let jwtToken;
|
|
79
|
+
try {
|
|
80
|
+
// Try to get existing session first
|
|
81
|
+
jwtToken = yield (0, library_1.getSession)(false, legacy);
|
|
82
|
+
}
|
|
83
|
+
catch (_a) {
|
|
84
|
+
// No session, need to sign in
|
|
85
|
+
if (!username) {
|
|
86
|
+
throw new Error("No session found. Please provide --username to sign in.");
|
|
87
|
+
}
|
|
88
|
+
const pwd = password || (yield promptPassword());
|
|
89
|
+
jwtToken = yield (0, library_1.signIn)(username, pwd, legacy);
|
|
90
|
+
}
|
|
91
|
+
const apiUrl = legacy ? constants_1.OLD_API_URL : constants_1.NEW_API_URL;
|
|
92
|
+
const api = (0, library_1.configure)(apiUrl);
|
|
93
|
+
return { normalizedMac, jwtToken, api };
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Executes a getter command by handling common steps (authentication, API initialization).
|
|
97
|
+
* @param options The options passed from the CLI command.
|
|
98
|
+
* @param getter A function to call on the configured API object.
|
|
99
|
+
*/
|
|
100
|
+
const executeGetter = (options, getter) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
const { normalizedMac, jwtToken, api } = yield initializeCommand(options);
|
|
102
|
+
const result = yield getter(api, jwtToken, normalizedMac);
|
|
103
|
+
console.log(result);
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* Executes a setter command by handling common steps (authentication, API initialization).
|
|
107
|
+
* @param options The options passed from the CLI command.
|
|
108
|
+
* @param setter A function to call on the configured API object.
|
|
109
|
+
*/
|
|
110
|
+
const executeSetter = (options, setter) => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
+
const { normalizedMac, jwtToken, api } = yield initializeCommand(options);
|
|
112
|
+
const result = yield setter(api, jwtToken, normalizedMac, options.value);
|
|
113
|
+
console.log(result);
|
|
114
|
+
});
|
|
115
|
+
const createProgram = () => {
|
|
116
|
+
const program = new commander_1.Command();
|
|
117
|
+
program
|
|
118
|
+
.name("edilkamin-cli")
|
|
119
|
+
.description("CLI tool for interacting with the Edilkamin API")
|
|
120
|
+
.version(package_json_1.version);
|
|
121
|
+
// Command: signIn
|
|
122
|
+
program
|
|
123
|
+
.command("signIn")
|
|
124
|
+
.description("Sign in and retrieve a JWT token")
|
|
125
|
+
.requiredOption("-u, --username <username>", "Username")
|
|
126
|
+
.option("-p, --password <password>", "Password")
|
|
127
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
const { username, password } = options;
|
|
129
|
+
// Initialize file storage for session persistence
|
|
130
|
+
const storage = (0, token_storage_1.createFileStorage)();
|
|
131
|
+
(0, library_1.configureAmplify)(storage);
|
|
132
|
+
const pwd = password || (yield promptPassword());
|
|
133
|
+
const jwtToken = yield (0, library_1.signIn)(username, pwd);
|
|
134
|
+
console.log("JWT Token:", jwtToken);
|
|
135
|
+
}));
|
|
136
|
+
// Command: logout
|
|
137
|
+
program
|
|
138
|
+
.command("logout")
|
|
139
|
+
.description("Clear stored session")
|
|
140
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
141
|
+
yield (0, token_storage_1.clearSession)();
|
|
142
|
+
console.log("Session cleared successfully");
|
|
143
|
+
}));
|
|
144
|
+
// Generic getter commands
|
|
145
|
+
[
|
|
146
|
+
{
|
|
147
|
+
commandName: "deviceInfo",
|
|
148
|
+
description: "Retrieve device info for a specific MAC address",
|
|
149
|
+
getter: (api, jwtToken, mac) => api.deviceInfo(jwtToken, mac),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
commandName: "getPower",
|
|
153
|
+
description: "Retrieve device power status",
|
|
154
|
+
getter: (api, jwtToken, mac) => api.getPower(jwtToken, mac),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
commandName: "getEnvironmentTemperature",
|
|
158
|
+
description: "Retrieve environment temperature",
|
|
159
|
+
getter: (api, jwtToken, mac) => api.getEnvironmentTemperature(jwtToken, mac),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
commandName: "getTargetTemperature",
|
|
163
|
+
description: "Retrieve target temperature",
|
|
164
|
+
getter: (api, jwtToken, mac) => api.getTargetTemperature(jwtToken, mac),
|
|
165
|
+
},
|
|
166
|
+
].forEach(({ commandName, description, getter }) => {
|
|
167
|
+
addLegacyOption(addMacOption(addAuthOptions(program.command(commandName).description(description)))).action((options) => executeGetter(options, getter));
|
|
168
|
+
});
|
|
169
|
+
// Generic setter commands
|
|
170
|
+
[
|
|
171
|
+
{
|
|
172
|
+
commandName: "setPower",
|
|
173
|
+
description: "Set the power state of the device (1 for ON, 0 for OFF)",
|
|
174
|
+
setter: (api, jwtToken, mac, value) => api.setPower(jwtToken, mac, value),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
commandName: "setTargetTemperature",
|
|
178
|
+
description: "Set the target temperature (degree celsius) for a device",
|
|
179
|
+
setter: (api, jwtToken, mac, value) => api.setTargetTemperature(jwtToken, mac, value),
|
|
180
|
+
},
|
|
181
|
+
].forEach(({ commandName, description, setter }) => {
|
|
182
|
+
addLegacyOption(addMacOption(addAuthOptions(program.command(commandName).description(description)).requiredOption("-v, --value <number>", "Value to set", parseFloat))).action((options) => executeSetter(options, setter));
|
|
183
|
+
});
|
|
184
|
+
// Command: register
|
|
185
|
+
addLegacyOption(addAuthOptions(program
|
|
186
|
+
.command("register")
|
|
187
|
+
.description("Register a device with your account")))
|
|
188
|
+
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")
|
|
189
|
+
.requiredOption("-s, --serial <serialNumber>", "Device serial number")
|
|
190
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
191
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
192
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
193
|
+
const { username, password, mac, serial, name, room, legacy = false, } = options;
|
|
194
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
195
|
+
// Initialize file storage for session persistence
|
|
196
|
+
const storage = (0, token_storage_1.createFileStorage)();
|
|
197
|
+
(0, library_1.configureAmplify)(storage);
|
|
198
|
+
let jwtToken;
|
|
199
|
+
try {
|
|
200
|
+
jwtToken = yield (0, library_1.getSession)(false, legacy);
|
|
201
|
+
}
|
|
202
|
+
catch (_a) {
|
|
203
|
+
if (!username) {
|
|
204
|
+
throw new Error("No session found. Please provide --username to sign in.");
|
|
205
|
+
}
|
|
206
|
+
const pwd = password || (yield promptPassword());
|
|
207
|
+
jwtToken = yield (0, library_1.signIn)(username, pwd, legacy);
|
|
208
|
+
}
|
|
209
|
+
const apiUrl = legacy ? constants_1.OLD_API_URL : constants_1.NEW_API_URL;
|
|
210
|
+
const api = (0, library_1.configure)(apiUrl);
|
|
211
|
+
const result = yield api.registerDevice(jwtToken, normalizedMac, serial, name, room);
|
|
212
|
+
console.log("Device registered successfully:");
|
|
213
|
+
console.log(JSON.stringify(result, null, 2));
|
|
214
|
+
}));
|
|
215
|
+
// Command: editDevice
|
|
216
|
+
addLegacyOption(addMacOption(addAuthOptions(program
|
|
217
|
+
.command("editDevice")
|
|
218
|
+
.description("Update device name and room"))))
|
|
219
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
220
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
221
|
+
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
222
|
+
const { username, password, mac, name, room, legacy = false } = options;
|
|
223
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
224
|
+
// Initialize file storage for session persistence
|
|
225
|
+
const storage = (0, token_storage_1.createFileStorage)();
|
|
226
|
+
(0, library_1.configureAmplify)(storage);
|
|
227
|
+
let jwtToken;
|
|
228
|
+
try {
|
|
229
|
+
jwtToken = yield (0, library_1.getSession)(false, legacy);
|
|
230
|
+
}
|
|
231
|
+
catch (_a) {
|
|
232
|
+
if (!username) {
|
|
233
|
+
throw new Error("No session found. Please provide --username to sign in.");
|
|
234
|
+
}
|
|
235
|
+
const pwd = password || (yield promptPassword());
|
|
236
|
+
jwtToken = yield (0, library_1.signIn)(username, pwd, legacy);
|
|
237
|
+
}
|
|
238
|
+
const apiUrl = legacy ? constants_1.OLD_API_URL : constants_1.NEW_API_URL;
|
|
239
|
+
const api = (0, library_1.configure)(apiUrl);
|
|
240
|
+
const result = yield api.editDevice(jwtToken, normalizedMac, name, room);
|
|
241
|
+
console.log("Device updated successfully:");
|
|
242
|
+
console.log(JSON.stringify(result, null, 2));
|
|
243
|
+
}));
|
|
244
|
+
return program;
|
|
245
|
+
};
|
|
246
|
+
const main = () => {
|
|
247
|
+
const program = createProgram();
|
|
248
|
+
program.parse(process.argv);
|
|
249
|
+
};
|
|
250
|
+
exports.main = main;
|
|
251
|
+
if (require.main === module) {
|
|
252
|
+
main();
|
|
253
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const assert_1 = require("assert");
|
|
7
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
8
|
+
const library_1 = require("../src/library");
|
|
9
|
+
/**
|
|
10
|
+
* This test file specifically tests the configureAmplify function with custom storage.
|
|
11
|
+
* It tests line 61 in library.ts:
|
|
12
|
+
* cognitoUserPoolsTokenProvider.setKeyValueStorage(storage)
|
|
13
|
+
*
|
|
14
|
+
* IMPORTANT: This file is named to run BEFORE library.test.ts (alphabetically)
|
|
15
|
+
* to ensure amplifyConfigured is still false when these tests run.
|
|
16
|
+
*/
|
|
17
|
+
describe("configureAmplify", () => {
|
|
18
|
+
it("should configure Amplify with custom storage", () => {
|
|
19
|
+
const mockStorage = {
|
|
20
|
+
setItem: sinon_1.default.stub().resolves(),
|
|
21
|
+
getItem: sinon_1.default.stub().resolves(null),
|
|
22
|
+
removeItem: sinon_1.default.stub().resolves(),
|
|
23
|
+
clear: sinon_1.default.stub().resolves(),
|
|
24
|
+
};
|
|
25
|
+
// Call configureAmplify with custom storage
|
|
26
|
+
// This is the first call in the test suite, so amplifyConfigured is false
|
|
27
|
+
// This should trigger line 61 in library.ts
|
|
28
|
+
(0, library_1.configureAmplify)(mockStorage);
|
|
29
|
+
// The test passes if no error is thrown
|
|
30
|
+
// Coverage confirms line 61 is executed
|
|
31
|
+
assert_1.strict.ok(true, "configureAmplify with storage completed without error");
|
|
32
|
+
});
|
|
33
|
+
it("should only configure Amplify once (idempotent)", () => {
|
|
34
|
+
// Call configureAmplify multiple times without storage
|
|
35
|
+
(0, library_1.configureAmplify)();
|
|
36
|
+
(0, library_1.configureAmplify)();
|
|
37
|
+
(0, library_1.configureAmplify)();
|
|
38
|
+
// Should not throw or have any side effects
|
|
39
|
+
// The function returns early if already configured (line 58)
|
|
40
|
+
assert_1.strict.ok(true, "Multiple calls to configureAmplify completed without error");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OLD_API_URL = exports.NEW_API_URL = exports.API_URL = void 0;
|
|
4
|
+
const OLD_API_URL = "https://fxtj7xkgc6.execute-api.eu-central-1.amazonaws.com/prod/";
|
|
5
|
+
exports.OLD_API_URL = OLD_API_URL;
|
|
6
|
+
const NEW_API_URL = "https://the-mind-api.edilkamin.com/";
|
|
7
|
+
exports.NEW_API_URL = NEW_API_URL;
|
|
8
|
+
const API_URL = NEW_API_URL;
|
|
9
|
+
exports.API_URL = API_URL;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
export { bleToWifiMac } from "./bluetooth-utils";
|
|
1
2
|
export { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
2
3
|
export { API_URL, NEW_API_URL, OLD_API_URL } from "./constants";
|
|
3
4
|
export { configure, getSession, signIn } from "./library";
|
|
4
5
|
export { serialNumberDisplay, serialNumberFromHex, serialNumberToHex, } from "./serial-utils";
|
|
5
|
-
export { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, } from "./types";
|
|
6
|
+
export { BufferEncodedType, CommandsType, DeviceAssociationBody, DeviceAssociationResponse, DeviceInfoRawType, DeviceInfoType, DiscoveredDevice, EditDeviceAssociationBody, StatusType, TemperaturesType, UserParametersType, } from "./types";
|
|
6
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>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.setTargetTemperature = exports.getTargetTemperature = exports.getEnvironmentTemperature = exports.getPower = exports.setPowerOn = exports.setPowerOff = exports.setPower = exports.editDevice = exports.registerDevice = exports.deviceInfo = exports.serialNumberToHex = exports.serialNumberFromHex = exports.serialNumberDisplay = exports.signIn = exports.getSession = exports.configure = exports.OLD_API_URL = exports.NEW_API_URL = exports.API_URL = exports.processResponse = exports.isBuffer = exports.decompressBuffer = exports.bleToWifiMac = void 0;
|
|
5
|
+
const library_1 = require("./library");
|
|
6
|
+
var bluetooth_utils_1 = require("./bluetooth-utils");
|
|
7
|
+
Object.defineProperty(exports, "bleToWifiMac", { enumerable: true, get: function () { return bluetooth_utils_1.bleToWifiMac; } });
|
|
8
|
+
var buffer_utils_1 = require("./buffer-utils");
|
|
9
|
+
Object.defineProperty(exports, "decompressBuffer", { enumerable: true, get: function () { return buffer_utils_1.decompressBuffer; } });
|
|
10
|
+
Object.defineProperty(exports, "isBuffer", { enumerable: true, get: function () { return buffer_utils_1.isBuffer; } });
|
|
11
|
+
Object.defineProperty(exports, "processResponse", { enumerable: true, get: function () { return buffer_utils_1.processResponse; } });
|
|
12
|
+
var constants_1 = require("./constants");
|
|
13
|
+
Object.defineProperty(exports, "API_URL", { enumerable: true, get: function () { return constants_1.API_URL; } });
|
|
14
|
+
Object.defineProperty(exports, "NEW_API_URL", { enumerable: true, get: function () { return constants_1.NEW_API_URL; } });
|
|
15
|
+
Object.defineProperty(exports, "OLD_API_URL", { enumerable: true, get: function () { return constants_1.OLD_API_URL; } });
|
|
16
|
+
var library_2 = require("./library");
|
|
17
|
+
Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return library_2.configure; } });
|
|
18
|
+
Object.defineProperty(exports, "getSession", { enumerable: true, get: function () { return library_2.getSession; } });
|
|
19
|
+
Object.defineProperty(exports, "signIn", { enumerable: true, get: function () { return library_2.signIn; } });
|
|
20
|
+
var serial_utils_1 = require("./serial-utils");
|
|
21
|
+
Object.defineProperty(exports, "serialNumberDisplay", { enumerable: true, get: function () { return serial_utils_1.serialNumberDisplay; } });
|
|
22
|
+
Object.defineProperty(exports, "serialNumberFromHex", { enumerable: true, get: function () { return serial_utils_1.serialNumberFromHex; } });
|
|
23
|
+
Object.defineProperty(exports, "serialNumberToHex", { enumerable: true, get: function () { return serial_utils_1.serialNumberToHex; } });
|
|
24
|
+
_a = (0, library_1.configure)(), exports.deviceInfo = _a.deviceInfo, exports.registerDevice = _a.registerDevice, exports.editDevice = _a.editDevice, exports.setPower = _a.setPower, exports.setPowerOff = _a.setPowerOff, exports.setPowerOn = _a.setPowerOn, exports.getPower = _a.getPower, exports.getEnvironmentTemperature = _a.getEnvironmentTemperature, exports.getTargetTemperature = _a.getTargetTemperature, exports.setTargetTemperature = _a.setTargetTemperature;
|