iobroker.homewizard 0.1.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.
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var homewizard_client_exports = {};
30
+ __export(homewizard_client_exports, {
31
+ HomeWizardApiError: () => HomeWizardApiError,
32
+ HomeWizardClient: () => HomeWizardClient
33
+ });
34
+ module.exports = __toCommonJS(homewizard_client_exports);
35
+ var https = __toESM(require("node:https"));
36
+ class HomeWizardClient {
37
+ ip;
38
+ token;
39
+ /**
40
+ * @param ip Device IP address
41
+ * @param token Bearer token (empty string for pairing requests)
42
+ */
43
+ constructor(ip, token = "") {
44
+ this.ip = ip;
45
+ this.token = token;
46
+ }
47
+ /** Get device info (GET /api) */
48
+ async getDeviceInfo() {
49
+ return this.request("GET", "/api");
50
+ }
51
+ /** Request pairing token (POST /api/user) — 403 until button pressed */
52
+ async requestPairing() {
53
+ return this.request("POST", "/api/user", {
54
+ name: "local/iobroker"
55
+ });
56
+ }
57
+ /** Get current measurement (REST fallback) */
58
+ async getMeasurement() {
59
+ return this.request("GET", "/api/measurement");
60
+ }
61
+ /** Get system info */
62
+ async getSystem() {
63
+ return this.request("GET", "/api/system");
64
+ }
65
+ /**
66
+ * Update system settings
67
+ *
68
+ * @param settings System settings to update
69
+ */
70
+ async setSystem(settings) {
71
+ return this.request("PUT", "/api/system", settings);
72
+ }
73
+ /** Reboot device */
74
+ async reboot() {
75
+ await this.request("PUT", "/api/system/reboot");
76
+ }
77
+ /** Identify device (blink LED) */
78
+ async identify() {
79
+ await this.request("PUT", "/api/system/identify");
80
+ }
81
+ /** Get battery control status */
82
+ async getBatteries() {
83
+ return this.request("GET", "/api/batteries");
84
+ }
85
+ /**
86
+ * Set battery control
87
+ *
88
+ * @param settings Battery control settings to update
89
+ */
90
+ async setBatteries(settings) {
91
+ return this.request("PUT", "/api/batteries", settings);
92
+ }
93
+ /**
94
+ * @param method HTTP method
95
+ * @param path API path
96
+ * @param body Optional request body
97
+ */
98
+ request(method, path, body) {
99
+ return new Promise((resolve, reject) => {
100
+ const bodyStr = body ? JSON.stringify(body) : void 0;
101
+ const headers = {
102
+ "X-Api-Version": "2"
103
+ };
104
+ if (this.token) {
105
+ headers.Authorization = `Bearer ${this.token}`;
106
+ }
107
+ if (bodyStr) {
108
+ headers["Content-Type"] = "application/json";
109
+ headers["Content-Length"] = Buffer.byteLength(bodyStr).toString();
110
+ }
111
+ const req = https.request(
112
+ {
113
+ hostname: this.ip,
114
+ port: 443,
115
+ path,
116
+ method,
117
+ headers,
118
+ rejectUnauthorized: false,
119
+ // HomeWizard uses self-signed certs
120
+ timeout: 1e4
121
+ },
122
+ (res) => {
123
+ const chunks = [];
124
+ res.on("data", (chunk) => chunks.push(chunk));
125
+ res.on("end", () => {
126
+ var _a;
127
+ const data = Buffer.concat(chunks).toString();
128
+ if (!res.statusCode || res.statusCode >= 400) {
129
+ const error = new HomeWizardApiError(
130
+ (_a = res.statusCode) != null ? _a : 0,
131
+ data,
132
+ `${method} ${path}`
133
+ );
134
+ reject(error);
135
+ return;
136
+ }
137
+ if (!data) {
138
+ resolve(void 0);
139
+ return;
140
+ }
141
+ try {
142
+ resolve(JSON.parse(data));
143
+ } catch {
144
+ reject(
145
+ new Error(
146
+ `Invalid JSON from ${method} ${path}: ${data.substring(0, 200)}`
147
+ )
148
+ );
149
+ }
150
+ });
151
+ }
152
+ );
153
+ req.on("error", reject);
154
+ req.on("timeout", () => {
155
+ req.destroy(new Error(`Timeout: ${method} ${path}`));
156
+ });
157
+ if (bodyStr) {
158
+ req.write(bodyStr);
159
+ }
160
+ req.end();
161
+ });
162
+ }
163
+ }
164
+ class HomeWizardApiError extends Error {
165
+ statusCode;
166
+ errorCode;
167
+ /**
168
+ * @param statusCode HTTP status code
169
+ * @param body Response body
170
+ * @param context Request context for error message
171
+ */
172
+ constructor(statusCode, body, context) {
173
+ var _a, _b, _c, _d, _e, _f, _g;
174
+ let errorCode = "unknown";
175
+ let description = body;
176
+ try {
177
+ const parsed = JSON.parse(body);
178
+ errorCode = (_c = (_b = (_a = parsed.error) == null ? void 0 : _a.code) != null ? _b : parsed.error) != null ? _c : "unknown";
179
+ description = (_g = (_f = (_d = parsed.error) == null ? void 0 : _d.description) != null ? _f : (_e = parsed.error) == null ? void 0 : _e.code) != null ? _g : body;
180
+ } catch {
181
+ }
182
+ super(`${context}: HTTP ${statusCode} \u2014 ${description}`);
183
+ this.name = "HomeWizardApiError";
184
+ this.statusCode = statusCode;
185
+ this.errorCode = errorCode;
186
+ }
187
+ }
188
+ // Annotate the CommonJS export names for ESM import in node:
189
+ 0 && (module.exports = {
190
+ HomeWizardApiError,
191
+ HomeWizardClient
192
+ });
193
+ //# sourceMappingURL=homewizard-client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/homewizard-client.ts"],
4
+ "sourcesContent": ["import * as https from \"node:https\";\nimport type {\n BatteryControl,\n DeviceInfo,\n Measurement,\n PairingResponse,\n SystemInfo,\n} from \"./types\";\n\n/** HTTPS client for HomeWizard API v2 */\nexport class HomeWizardClient {\n private readonly ip: string;\n private readonly token: string;\n\n /**\n * @param ip Device IP address\n * @param token Bearer token (empty string for pairing requests)\n */\n constructor(ip: string, token: string = \"\") {\n this.ip = ip;\n this.token = token;\n }\n\n /** Get device info (GET /api) */\n async getDeviceInfo(): Promise<DeviceInfo> {\n return this.request<DeviceInfo>(\"GET\", \"/api\");\n }\n\n /** Request pairing token (POST /api/user) \u2014 403 until button pressed */\n async requestPairing(): Promise<PairingResponse> {\n return this.request<PairingResponse>(\"POST\", \"/api/user\", {\n name: \"local/iobroker\",\n });\n }\n\n /** Get current measurement (REST fallback) */\n async getMeasurement(): Promise<Measurement> {\n return this.request<Measurement>(\"GET\", \"/api/measurement\");\n }\n\n /** Get system info */\n async getSystem(): Promise<SystemInfo> {\n return this.request<SystemInfo>(\"GET\", \"/api/system\");\n }\n\n /**\n * Update system settings\n *\n * @param settings System settings to update\n */\n async setSystem(settings: Partial<SystemInfo>): Promise<SystemInfo> {\n return this.request<SystemInfo>(\"PUT\", \"/api/system\", settings);\n }\n\n /** Reboot device */\n async reboot(): Promise<void> {\n await this.request(\"PUT\", \"/api/system/reboot\");\n }\n\n /** Identify device (blink LED) */\n async identify(): Promise<void> {\n await this.request(\"PUT\", \"/api/system/identify\");\n }\n\n /** Get battery control status */\n async getBatteries(): Promise<BatteryControl> {\n return this.request<BatteryControl>(\"GET\", \"/api/batteries\");\n }\n\n /**\n * Set battery control\n *\n * @param settings Battery control settings to update\n */\n async setBatteries(\n settings: Partial<BatteryControl>,\n ): Promise<BatteryControl> {\n return this.request<BatteryControl>(\"PUT\", \"/api/batteries\", settings);\n }\n\n /**\n * @param method HTTP method\n * @param path API path\n * @param body Optional request body\n */\n private request<T>(method: string, path: string, body?: unknown): Promise<T> {\n return new Promise((resolve, reject) => {\n const bodyStr = body ? JSON.stringify(body) : undefined;\n const headers: Record<string, string> = {\n \"X-Api-Version\": \"2\",\n };\n\n if (this.token) {\n headers.Authorization = `Bearer ${this.token}`;\n }\n if (bodyStr) {\n headers[\"Content-Type\"] = \"application/json\";\n headers[\"Content-Length\"] = Buffer.byteLength(bodyStr).toString();\n }\n\n const req = https.request(\n {\n hostname: this.ip,\n port: 443,\n path,\n method,\n headers,\n rejectUnauthorized: false, // HomeWizard uses self-signed certs\n timeout: 10_000,\n },\n (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const data = Buffer.concat(chunks).toString();\n if (!res.statusCode || res.statusCode >= 400) {\n const error = new HomeWizardApiError(\n res.statusCode ?? 0,\n data,\n `${method} ${path}`,\n );\n reject(error);\n return;\n }\n if (!data) {\n resolve(undefined as T);\n return;\n }\n try {\n resolve(JSON.parse(data) as T);\n } catch {\n reject(\n new Error(\n `Invalid JSON from ${method} ${path}: ${data.substring(0, 200)}`,\n ),\n );\n }\n });\n },\n );\n\n req.on(\"error\", reject);\n req.on(\"timeout\", () => {\n req.destroy(new Error(`Timeout: ${method} ${path}`));\n });\n\n if (bodyStr) {\n req.write(bodyStr);\n }\n req.end();\n });\n }\n}\n\n/** API error with status code and parsed error body */\nexport class HomeWizardApiError extends Error {\n readonly statusCode: number;\n readonly errorCode: string;\n\n /**\n * @param statusCode HTTP status code\n * @param body Response body\n * @param context Request context for error message\n */\n constructor(statusCode: number, body: string, context: string) {\n let errorCode = \"unknown\";\n let description = body;\n try {\n const parsed = JSON.parse(body);\n errorCode = parsed.error?.code ?? parsed.error ?? \"unknown\";\n description = parsed.error?.description ?? parsed.error?.code ?? body;\n } catch {\n // body is not JSON\n }\n super(`${context}: HTTP ${statusCode} \u2014 ${description}`);\n this.name = \"HomeWizardApiError\";\n this.statusCode = statusCode;\n this.errorCode = errorCode;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }