edilkamin 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +25 -0
- package/.github/workflows/cli-tests.yml +2 -2
- package/.github/workflows/documentation.yml +2 -2
- package/.github/workflows/publish.yml +2 -2
- package/.github/workflows/tests.yml +2 -2
- package/README.md +30 -0
- package/dist/esm/buffer-utils.d.ts +25 -0
- package/dist/esm/buffer-utils.js +70 -0
- package/dist/esm/buffer-utils.test.d.ts +1 -0
- package/dist/esm/buffer-utils.test.js +181 -0
- package/dist/esm/cli.js +47 -5
- package/dist/esm/constants.d.ts +3 -1
- package/dist/esm/constants.js +4 -2
- package/dist/esm/index.d.ts +5 -3
- package/dist/esm/index.js +4 -2
- package/dist/esm/library.d.ts +9 -7
- package/dist/esm/library.js +59 -5
- package/dist/esm/library.test.js +260 -1
- package/dist/esm/serial-utils.d.ts +33 -0
- package/dist/esm/serial-utils.js +45 -0
- package/dist/esm/serial-utils.test.d.ts +1 -0
- package/dist/esm/serial-utils.test.js +48 -0
- package/dist/esm/types.d.ts +49 -1
- package/package.json +7 -5
- package/src/buffer-utils.test.ts +225 -0
- package/src/buffer-utils.ts +83 -0
- package/src/cli.ts +97 -11
- package/src/constants.ts +5 -2
- package/src/index.ts +14 -1
- package/src/library.test.ts +323 -1
- package/src/library.ts +93 -8
- package/src/serial-utils.test.ts +64 -0
- package/src/serial-utils.ts +50 -0
- package/src/types.ts +60 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { strict as assert } from "assert";
|
|
2
|
+
import pako from "pako";
|
|
3
|
+
import sinon from "sinon";
|
|
4
|
+
|
|
5
|
+
import { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper to create a gzip-compressed Buffer object for testing.
|
|
9
|
+
*/
|
|
10
|
+
const createGzippedBuffer = (
|
|
11
|
+
data: unknown
|
|
12
|
+
): { type: "Buffer"; data: number[] } => {
|
|
13
|
+
const json = JSON.stringify(data);
|
|
14
|
+
const compressed = pako.gzip(json);
|
|
15
|
+
return {
|
|
16
|
+
type: "Buffer",
|
|
17
|
+
data: Array.from(compressed),
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
describe("buffer-utils", () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
sinon.restore();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("isBuffer", () => {
|
|
27
|
+
it("should detect valid Buffer objects", () => {
|
|
28
|
+
const buffer = { type: "Buffer", data: [31, 139, 8, 0] };
|
|
29
|
+
assert.ok(isBuffer(buffer));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should detect empty Buffer objects", () => {
|
|
33
|
+
const buffer = { type: "Buffer", data: [] };
|
|
34
|
+
assert.ok(isBuffer(buffer));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should reject non-Buffer objects with wrong type", () => {
|
|
38
|
+
assert.ok(!isBuffer({ type: "NotBuffer", data: [] }));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should reject objects without type field", () => {
|
|
42
|
+
assert.ok(!isBuffer({ data: [1, 2, 3] }));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should reject objects without data field", () => {
|
|
46
|
+
assert.ok(!isBuffer({ type: "Buffer" }));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should reject objects with non-array data", () => {
|
|
50
|
+
assert.ok(!isBuffer({ type: "Buffer", data: "not an array" }));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should reject null", () => {
|
|
54
|
+
assert.ok(!isBuffer(null));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should reject undefined", () => {
|
|
58
|
+
assert.ok(!isBuffer(undefined));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should reject primitives", () => {
|
|
62
|
+
assert.ok(!isBuffer("string"));
|
|
63
|
+
assert.ok(!isBuffer(123));
|
|
64
|
+
assert.ok(!isBuffer(true));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe("decompressBuffer", () => {
|
|
69
|
+
it("should decompress gzipped JSON buffer", () => {
|
|
70
|
+
const originalData = { test: "value", nested: { key: 123 } };
|
|
71
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
72
|
+
|
|
73
|
+
const result = decompressBuffer(bufferObj);
|
|
74
|
+
assert.deepEqual(result, originalData);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should handle gzipped arrays", () => {
|
|
78
|
+
const originalData = [1, 2, 3, "test"];
|
|
79
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
80
|
+
|
|
81
|
+
const result = decompressBuffer(bufferObj);
|
|
82
|
+
assert.deepEqual(result, originalData);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle gzipped strings", () => {
|
|
86
|
+
const originalData = "test string";
|
|
87
|
+
const bufferObj = createGzippedBuffer(originalData);
|
|
88
|
+
|
|
89
|
+
const result = decompressBuffer(bufferObj);
|
|
90
|
+
assert.equal(result, originalData);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should return original value if decompression fails", () => {
|
|
94
|
+
const consoleWarnStub = sinon.stub(console, "warn");
|
|
95
|
+
const invalidBuffer = { type: "Buffer" as const, data: [1, 2, 3] };
|
|
96
|
+
|
|
97
|
+
const result = decompressBuffer(invalidBuffer);
|
|
98
|
+
|
|
99
|
+
assert.deepEqual(result, invalidBuffer);
|
|
100
|
+
assert.ok(consoleWarnStub.calledOnce);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should return original value if JSON parsing fails", () => {
|
|
104
|
+
const consoleWarnStub = sinon.stub(console, "warn");
|
|
105
|
+
// Create valid gzip but invalid JSON
|
|
106
|
+
const invalidJson = "not valid json {";
|
|
107
|
+
const compressed = pako.gzip(invalidJson);
|
|
108
|
+
const bufferObj = {
|
|
109
|
+
type: "Buffer" as const,
|
|
110
|
+
data: Array.from(compressed),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const result = decompressBuffer(bufferObj);
|
|
114
|
+
|
|
115
|
+
assert.deepEqual(result, bufferObj);
|
|
116
|
+
assert.ok(consoleWarnStub.calledOnce);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("processResponse", () => {
|
|
121
|
+
it("should pass through null", () => {
|
|
122
|
+
assert.equal(processResponse(null), null);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should pass through undefined", () => {
|
|
126
|
+
assert.equal(processResponse(undefined), undefined);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should pass through primitives", () => {
|
|
130
|
+
assert.equal(processResponse("string"), "string");
|
|
131
|
+
assert.equal(processResponse(123), 123);
|
|
132
|
+
assert.equal(processResponse(true), true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should pass through plain objects", () => {
|
|
136
|
+
const obj = { key: "value", nested: { num: 42 } };
|
|
137
|
+
assert.deepEqual(processResponse(obj), obj);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should pass through plain arrays", () => {
|
|
141
|
+
const arr = [1, "two", { three: 3 }];
|
|
142
|
+
assert.deepEqual(processResponse(arr), arr);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should decompress Buffer at root level", () => {
|
|
146
|
+
const originalData = { decompressed: true };
|
|
147
|
+
const buffer = createGzippedBuffer(originalData);
|
|
148
|
+
|
|
149
|
+
const result = processResponse(buffer);
|
|
150
|
+
assert.deepEqual(result, originalData);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should decompress nested Buffer fields", () => {
|
|
154
|
+
const statusData = { commands: { power: true } };
|
|
155
|
+
const response = {
|
|
156
|
+
plain: "data",
|
|
157
|
+
status: createGzippedBuffer(statusData),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const result = processResponse(response);
|
|
161
|
+
assert.equal(result.plain, "data");
|
|
162
|
+
assert.deepEqual(result.status, statusData);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should recursively decompress deeply nested Buffers", () => {
|
|
166
|
+
const innerData = { value: 42 };
|
|
167
|
+
const middleData = { inner: createGzippedBuffer(innerData) };
|
|
168
|
+
const response = {
|
|
169
|
+
outer: createGzippedBuffer(middleData),
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const result = processResponse(response);
|
|
173
|
+
assert.deepEqual(result, { outer: { inner: { value: 42 } } });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should handle arrays containing Buffers", () => {
|
|
177
|
+
const itemData = { id: 1 };
|
|
178
|
+
const response = {
|
|
179
|
+
items: [createGzippedBuffer(itemData), { id: 2 }],
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = processResponse(response);
|
|
183
|
+
assert.deepEqual(result.items, [{ id: 1 }, { id: 2 }]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should handle mixed compressed and uncompressed fields", () => {
|
|
187
|
+
const compressedStatus = { commands: { power: true } };
|
|
188
|
+
const response = {
|
|
189
|
+
status: createGzippedBuffer(compressedStatus),
|
|
190
|
+
nvm: { user_parameters: { temperature: 22 } },
|
|
191
|
+
plain_field: "unchanged",
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const result = processResponse(response);
|
|
195
|
+
assert.deepEqual(result.status, compressedStatus);
|
|
196
|
+
assert.deepEqual(result.nvm, { user_parameters: { temperature: 22 } });
|
|
197
|
+
assert.equal(result.plain_field, "unchanged");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should handle real-world DeviceInfo structure with compressed status", () => {
|
|
201
|
+
const statusData = {
|
|
202
|
+
commands: { power: true },
|
|
203
|
+
temperatures: { board: 25, enviroment: 20 },
|
|
204
|
+
};
|
|
205
|
+
const nvmData = {
|
|
206
|
+
user_parameters: {
|
|
207
|
+
enviroment_1_temperature: 22,
|
|
208
|
+
enviroment_2_temperature: 0,
|
|
209
|
+
enviroment_3_temperature: 0,
|
|
210
|
+
is_auto: false,
|
|
211
|
+
is_sound_active: true,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const response = {
|
|
216
|
+
status: createGzippedBuffer(statusData),
|
|
217
|
+
nvm: createGzippedBuffer(nvmData),
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const result = processResponse(response);
|
|
221
|
+
assert.deepEqual(result.status, statusData);
|
|
222
|
+
assert.deepEqual(result.nvm, nvmData);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import pako from "pako";
|
|
2
|
+
|
|
3
|
+
import { BufferEncodedType } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is a serialized Node.js Buffer.
|
|
7
|
+
* Node.js Buffers serialize to JSON as: {type: "Buffer", data: [...]}
|
|
8
|
+
*
|
|
9
|
+
* @param value - The value to check
|
|
10
|
+
* @returns True if the value is a Buffer-encoded object
|
|
11
|
+
*/
|
|
12
|
+
const isBuffer = (value: unknown): value is BufferEncodedType => {
|
|
13
|
+
return (
|
|
14
|
+
typeof value === "object" &&
|
|
15
|
+
value !== null &&
|
|
16
|
+
"type" in value &&
|
|
17
|
+
(value as Record<string, unknown>).type === "Buffer" &&
|
|
18
|
+
"data" in value &&
|
|
19
|
+
Array.isArray((value as Record<string, unknown>).data)
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Decompresses a Buffer-encoded gzip object and parses the resulting JSON.
|
|
25
|
+
*
|
|
26
|
+
* @param bufferObj - A serialized Buffer object containing gzip data
|
|
27
|
+
* @returns The decompressed and parsed JSON data, or the original object on failure
|
|
28
|
+
*/
|
|
29
|
+
const decompressBuffer = (bufferObj: BufferEncodedType): unknown => {
|
|
30
|
+
try {
|
|
31
|
+
// Convert data array to Uint8Array for pako
|
|
32
|
+
const compressed = new Uint8Array(bufferObj.data);
|
|
33
|
+
|
|
34
|
+
// Decompress with gzip
|
|
35
|
+
const decompressed = pako.ungzip(compressed, { to: "string" });
|
|
36
|
+
|
|
37
|
+
// Parse JSON
|
|
38
|
+
return JSON.parse(decompressed);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// Log warning but return original to maintain backward compatibility
|
|
41
|
+
console.warn("Failed to decompress buffer:", error);
|
|
42
|
+
return bufferObj;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Recursively processes an API response to decompress any Buffer-encoded fields.
|
|
48
|
+
* Handles nested objects and arrays, preserving structure while decompressing.
|
|
49
|
+
*
|
|
50
|
+
* @param data - The API response data to process
|
|
51
|
+
* @returns The processed data with all Buffer fields decompressed
|
|
52
|
+
*/
|
|
53
|
+
const processResponse = <T>(data: T): T => {
|
|
54
|
+
if (data === null || data === undefined) {
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if this is a Buffer object
|
|
59
|
+
if (isBuffer(data)) {
|
|
60
|
+
const decompressed = decompressBuffer(data);
|
|
61
|
+
// Recursively process the decompressed result (may contain nested buffers)
|
|
62
|
+
return processResponse(decompressed) as T;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Recursively process arrays
|
|
66
|
+
if (Array.isArray(data)) {
|
|
67
|
+
return data.map((item) => processResponse(item)) as T;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Recursively process objects
|
|
71
|
+
if (typeof data === "object") {
|
|
72
|
+
const processed: Record<string, unknown> = {};
|
|
73
|
+
for (const [key, value] of Object.entries(data)) {
|
|
74
|
+
processed[key] = processResponse(value);
|
|
75
|
+
}
|
|
76
|
+
return processed as T;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Primitive value, return as-is
|
|
80
|
+
return data;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export { decompressBuffer, isBuffer, processResponse };
|
package/src/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from "commander";
|
|
|
3
3
|
import readline from "readline";
|
|
4
4
|
|
|
5
5
|
import { version } from "../package.json";
|
|
6
|
+
import { NEW_API_URL, OLD_API_URL } from "./constants";
|
|
6
7
|
import { configure, signIn } from "./library";
|
|
7
8
|
|
|
8
9
|
const promptPassword = (): Promise<string> => {
|
|
@@ -45,6 +46,14 @@ const addAuthOptions = (command: Command): Command =>
|
|
|
45
46
|
const addMacOption = (command: Command): Command =>
|
|
46
47
|
command.requiredOption("-m, --mac <macAddress>", "MAC address of the device");
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Adds legacy API option to a command.
|
|
51
|
+
* @param command The command to which the legacy option should be added.
|
|
52
|
+
* @returns The command with the legacy option added.
|
|
53
|
+
*/
|
|
54
|
+
const addLegacyOption = (command: Command): Command =>
|
|
55
|
+
command.option("--legacy", "Use legacy API endpoint (old AWS Gateway)");
|
|
56
|
+
|
|
48
57
|
/**
|
|
49
58
|
* Handles common authentication and API initialization logic.
|
|
50
59
|
* @param options The options passed from the CLI command.
|
|
@@ -54,16 +63,18 @@ const initializeCommand = async (options: {
|
|
|
54
63
|
username: string;
|
|
55
64
|
password?: string;
|
|
56
65
|
mac: string;
|
|
66
|
+
legacy?: boolean;
|
|
57
67
|
}): Promise<{
|
|
58
68
|
normalizedMac: string;
|
|
59
69
|
jwtToken: string;
|
|
60
70
|
api: ReturnType<typeof configure>;
|
|
61
71
|
}> => {
|
|
62
|
-
const { username, password, mac } = options;
|
|
72
|
+
const { username, password, mac, legacy = false } = options;
|
|
63
73
|
const normalizedMac = mac.replace(/:/g, "");
|
|
64
74
|
const pwd = password || (await promptPassword());
|
|
65
|
-
const jwtToken = await signIn(username, pwd);
|
|
66
|
-
const
|
|
75
|
+
const jwtToken = await signIn(username, pwd, legacy);
|
|
76
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
77
|
+
const api = configure(apiUrl);
|
|
67
78
|
return { normalizedMac, jwtToken, api };
|
|
68
79
|
};
|
|
69
80
|
|
|
@@ -73,7 +84,12 @@ const initializeCommand = async (options: {
|
|
|
73
84
|
* @param getter A function to call on the configured API object.
|
|
74
85
|
*/
|
|
75
86
|
const executeGetter = async (
|
|
76
|
-
options: {
|
|
87
|
+
options: {
|
|
88
|
+
username: string;
|
|
89
|
+
password?: string;
|
|
90
|
+
mac: string;
|
|
91
|
+
legacy?: boolean;
|
|
92
|
+
},
|
|
77
93
|
getter: (
|
|
78
94
|
api: ReturnType<typeof configure>,
|
|
79
95
|
jwtToken: string,
|
|
@@ -91,7 +107,13 @@ const executeGetter = async (
|
|
|
91
107
|
* @param setter A function to call on the configured API object.
|
|
92
108
|
*/
|
|
93
109
|
const executeSetter = async (
|
|
94
|
-
options: {
|
|
110
|
+
options: {
|
|
111
|
+
username: string;
|
|
112
|
+
password?: string;
|
|
113
|
+
mac: string;
|
|
114
|
+
value: number;
|
|
115
|
+
legacy?: boolean;
|
|
116
|
+
},
|
|
95
117
|
setter: (
|
|
96
118
|
api: ReturnType<typeof configure>,
|
|
97
119
|
jwtToken: string,
|
|
@@ -158,8 +180,10 @@ const createProgram = (): Command => {
|
|
|
158
180
|
) => api.getTargetTemperature(jwtToken, mac),
|
|
159
181
|
},
|
|
160
182
|
].forEach(({ commandName, description, getter }) => {
|
|
161
|
-
|
|
162
|
-
|
|
183
|
+
addLegacyOption(
|
|
184
|
+
addMacOption(
|
|
185
|
+
addAuthOptions(program.command(commandName).description(description))
|
|
186
|
+
)
|
|
163
187
|
).action((options) => executeGetter(options, getter));
|
|
164
188
|
});
|
|
165
189
|
// Generic setter commands
|
|
@@ -185,13 +209,75 @@ const createProgram = (): Command => {
|
|
|
185
209
|
) => api.setTargetTemperature(jwtToken, mac, value),
|
|
186
210
|
},
|
|
187
211
|
].forEach(({ commandName, description, setter }) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
212
|
+
addLegacyOption(
|
|
213
|
+
addMacOption(
|
|
214
|
+
addAuthOptions(
|
|
215
|
+
program.command(commandName).description(description)
|
|
216
|
+
).requiredOption("-v, --value <number>", "Value to set", parseFloat)
|
|
217
|
+
)
|
|
192
218
|
).action((options) => executeSetter(options, setter));
|
|
193
219
|
});
|
|
194
220
|
|
|
221
|
+
// Command: register
|
|
222
|
+
addLegacyOption(
|
|
223
|
+
addAuthOptions(
|
|
224
|
+
program
|
|
225
|
+
.command("register")
|
|
226
|
+
.description("Register a device with your account")
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
.requiredOption("-m, --mac <macAddress>", "MAC address of the device")
|
|
230
|
+
.requiredOption("-s, --serial <serialNumber>", "Device serial number")
|
|
231
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
232
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
233
|
+
.action(async (options) => {
|
|
234
|
+
const {
|
|
235
|
+
username,
|
|
236
|
+
password,
|
|
237
|
+
mac,
|
|
238
|
+
serial,
|
|
239
|
+
name,
|
|
240
|
+
room,
|
|
241
|
+
legacy = false,
|
|
242
|
+
} = options;
|
|
243
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
244
|
+
const pwd = password || (await promptPassword());
|
|
245
|
+
const jwtToken = await signIn(username, pwd, legacy);
|
|
246
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
247
|
+
const api = configure(apiUrl);
|
|
248
|
+
const result = await api.registerDevice(
|
|
249
|
+
jwtToken,
|
|
250
|
+
normalizedMac,
|
|
251
|
+
serial,
|
|
252
|
+
name,
|
|
253
|
+
room
|
|
254
|
+
);
|
|
255
|
+
console.log("Device registered successfully:");
|
|
256
|
+
console.log(JSON.stringify(result, null, 2));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Command: editDevice
|
|
260
|
+
addLegacyOption(
|
|
261
|
+
addMacOption(
|
|
262
|
+
addAuthOptions(
|
|
263
|
+
program.command("editDevice").description("Update device name and room")
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
.requiredOption("-n, --name <deviceName>", "Device name")
|
|
268
|
+
.requiredOption("-r, --room <deviceRoom>", "Room name")
|
|
269
|
+
.action(async (options) => {
|
|
270
|
+
const { username, password, mac, name, room, legacy = false } = options;
|
|
271
|
+
const normalizedMac = mac.replace(/:/g, "");
|
|
272
|
+
const pwd = password || (await promptPassword());
|
|
273
|
+
const jwtToken = await signIn(username, pwd, legacy);
|
|
274
|
+
const apiUrl = legacy ? OLD_API_URL : NEW_API_URL;
|
|
275
|
+
const api = configure(apiUrl);
|
|
276
|
+
const result = await api.editDevice(jwtToken, normalizedMac, name, room);
|
|
277
|
+
console.log("Device updated successfully:");
|
|
278
|
+
console.log(JSON.stringify(result, null, 2));
|
|
279
|
+
});
|
|
280
|
+
|
|
195
281
|
return program;
|
|
196
282
|
};
|
|
197
283
|
|
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
const
|
|
1
|
+
const OLD_API_URL =
|
|
2
|
+
"https://fxtj7xkgc6.execute-api.eu-central-1.amazonaws.com/prod/";
|
|
3
|
+
const NEW_API_URL = "https://the-mind-api.edilkamin.com/";
|
|
4
|
+
const API_URL = NEW_API_URL;
|
|
2
5
|
|
|
3
|
-
export { API_URL };
|
|
6
|
+
export { API_URL, NEW_API_URL, OLD_API_URL };
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { configure } from "./library";
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export { decompressBuffer, isBuffer, processResponse } from "./buffer-utils";
|
|
4
|
+
export { API_URL, NEW_API_URL, OLD_API_URL } from "./constants";
|
|
4
5
|
export { configure, signIn } from "./library";
|
|
5
6
|
export {
|
|
7
|
+
serialNumberDisplay,
|
|
8
|
+
serialNumberFromHex,
|
|
9
|
+
serialNumberToHex,
|
|
10
|
+
} from "./serial-utils";
|
|
11
|
+
export {
|
|
12
|
+
BufferEncodedType,
|
|
6
13
|
CommandsType,
|
|
14
|
+
DeviceAssociationBody,
|
|
15
|
+
DeviceAssociationResponse,
|
|
16
|
+
DeviceInfoRawType,
|
|
7
17
|
DeviceInfoType,
|
|
18
|
+
EditDeviceAssociationBody,
|
|
8
19
|
StatusType,
|
|
9
20
|
TemperaturesType,
|
|
10
21
|
UserParametersType,
|
|
@@ -12,6 +23,8 @@ export {
|
|
|
12
23
|
|
|
13
24
|
export const {
|
|
14
25
|
deviceInfo,
|
|
26
|
+
registerDevice,
|
|
27
|
+
editDevice,
|
|
15
28
|
setPower,
|
|
16
29
|
setPowerOff,
|
|
17
30
|
setPowerOn,
|