ha-ai-mcp-server 0.3.8
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/dist/adapters/addon/addon.adapter.d.ts +22 -0
- package/dist/adapters/addon/addon.adapter.d.ts.map +1 -0
- package/dist/adapters/addon/addon.adapter.js +92 -0
- package/dist/adapters/addon/addon.adapter.js.map +1 -0
- package/dist/adapters/browser/playwright.adapter.d.ts +27 -0
- package/dist/adapters/browser/playwright.adapter.d.ts.map +1 -0
- package/dist/adapters/browser/playwright.adapter.js +156 -0
- package/dist/adapters/browser/playwright.adapter.js.map +1 -0
- package/dist/adapters/ha/ha-rest.adapter.d.ts +19 -0
- package/dist/adapters/ha/ha-rest.adapter.d.ts.map +1 -0
- package/dist/adapters/ha/ha-rest.adapter.js +70 -0
- package/dist/adapters/ha/ha-rest.adapter.js.map +1 -0
- package/dist/adapters/ha/ha-ws.adapter.d.ts +29 -0
- package/dist/adapters/ha/ha-ws.adapter.d.ts.map +1 -0
- package/dist/adapters/ha/ha-ws.adapter.js +178 -0
- package/dist/adapters/ha/ha-ws.adapter.js.map +1 -0
- package/dist/browser/renderer.d.ts +3 -0
- package/dist/browser/renderer.d.ts.map +1 -0
- package/dist/browser/renderer.js +3 -0
- package/dist/browser/renderer.js.map +1 -0
- package/dist/clients/addon-client.d.ts +3 -0
- package/dist/clients/addon-client.d.ts.map +1 -0
- package/dist/clients/addon-client.js +3 -0
- package/dist/clients/addon-client.js.map +1 -0
- package/dist/clients/ha-rest.d.ts +3 -0
- package/dist/clients/ha-rest.d.ts.map +1 -0
- package/dist/clients/ha-rest.js +3 -0
- package/dist/clients/ha-rest.js.map +1 -0
- package/dist/clients/ha-ws.d.ts +3 -0
- package/dist/clients/ha-ws.d.ts.map +1 -0
- package/dist/clients/ha-ws.js +3 -0
- package/dist/clients/ha-ws.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -0
- package/dist/core/config.d.ts +30 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +27 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/errors.d.ts +18 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +29 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/logger.d.ts +13 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +25 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/domain/models/automation.d.ts +19 -0
- package/dist/domain/models/automation.d.ts.map +1 -0
- package/dist/domain/models/automation.js +2 -0
- package/dist/domain/models/automation.js.map +1 -0
- package/dist/domain/models/dashboard.d.ts +21 -0
- package/dist/domain/models/dashboard.d.ts.map +1 -0
- package/dist/domain/models/dashboard.js +2 -0
- package/dist/domain/models/dashboard.js.map +1 -0
- package/dist/domain/models/entity.d.ts +13 -0
- package/dist/domain/models/entity.d.ts.map +1 -0
- package/dist/domain/models/entity.js +2 -0
- package/dist/domain/models/entity.js.map +1 -0
- package/dist/domain/models/system.d.ts +35 -0
- package/dist/domain/models/system.d.ts.map +1 -0
- package/dist/domain/models/system.js +2 -0
- package/dist/domain/models/system.js.map +1 -0
- package/dist/domain/ports/addon-client.port.d.ts +31 -0
- package/dist/domain/ports/addon-client.port.d.ts.map +1 -0
- package/dist/domain/ports/addon-client.port.js +2 -0
- package/dist/domain/ports/addon-client.port.js.map +1 -0
- package/dist/domain/ports/ha-client.port.d.ts +17 -0
- package/dist/domain/ports/ha-client.port.d.ts.map +1 -0
- package/dist/domain/ports/ha-client.port.js +2 -0
- package/dist/domain/ports/ha-client.port.js.map +1 -0
- package/dist/domain/ports/renderer.port.d.ts +7 -0
- package/dist/domain/ports/renderer.port.d.ts.map +1 -0
- package/dist/domain/ports/renderer.port.js +2 -0
- package/dist/domain/ports/renderer.port.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/services/automation.service.d.ts +20 -0
- package/dist/services/automation.service.d.ts.map +1 -0
- package/dist/services/automation.service.js +118 -0
- package/dist/services/automation.service.js.map +1 -0
- package/dist/services/dashboard.service.d.ts +22 -0
- package/dist/services/dashboard.service.d.ts.map +1 -0
- package/dist/services/dashboard.service.js +72 -0
- package/dist/services/dashboard.service.js.map +1 -0
- package/dist/services/system.service.d.ts +19 -0
- package/dist/services/system.service.d.ts.map +1 -0
- package/dist/services/system.service.js +83 -0
- package/dist/services/system.service.js.map +1 -0
- package/dist/tools/automation.d.ts +33 -0
- package/dist/tools/automation.d.ts.map +1 -0
- package/dist/tools/automation.js +246 -0
- package/dist/tools/automation.js.map +1 -0
- package/dist/tools/dashboard.d.ts +33 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +166 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/system.d.ts +44 -0
- package/dist/tools/system.d.ts.map +1 -0
- package/dist/tools/system.js +306 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/types.d.ts +26 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +39 -0
- package/src/adapters/addon/addon.adapter.ts +113 -0
- package/src/adapters/browser/playwright.adapter.ts +186 -0
- package/src/adapters/ha/ha-rest.adapter.ts +82 -0
- package/src/adapters/ha/ha-ws.adapter.ts +212 -0
- package/src/browser/renderer.ts +7 -0
- package/src/clients/addon-client.ts +2 -0
- package/src/clients/ha-rest.ts +2 -0
- package/src/clients/ha-ws.ts +2 -0
- package/src/config.ts +1 -0
- package/src/core/config.ts +39 -0
- package/src/core/errors.ts +28 -0
- package/src/core/logger.ts +27 -0
- package/src/domain/models/automation.ts +19 -0
- package/src/domain/models/dashboard.ts +23 -0
- package/src/domain/models/entity.ts +13 -0
- package/src/domain/models/system.ts +37 -0
- package/src/domain/ports/addon-client.port.ts +35 -0
- package/src/domain/ports/ha-client.port.ts +16 -0
- package/src/domain/ports/renderer.port.ts +7 -0
- package/src/index.ts +103 -0
- package/src/services/automation.service.ts +142 -0
- package/src/services/dashboard.service.ts +83 -0
- package/src/services/system.service.ts +94 -0
- package/src/tools/automation.ts +300 -0
- package/src/tools/dashboard.ts +206 -0
- package/src/tools/system.ts +368 -0
- package/src/tools/types.ts +30 -0
- package/tests/clients.test.ts +522 -0
- package/tests/e2e-smoke.test.ts +620 -0
- package/tests/renderer.test.ts +286 -0
- package/tests/skills.test.ts +189 -0
- package/tests/tools.test.ts +530 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AddonHealthResult, FileReadResult, FileWriteResult, IAddonClient } from "../../domain/ports/addon-client.port.js";
|
|
2
|
+
import { BackupRestoreResult, LogsTailResult, SnapshotInfo } from "../../domain/models/system.js";
|
|
3
|
+
export interface AddonClientOptions {
|
|
4
|
+
addonUrl: string;
|
|
5
|
+
addonKey: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class AddonAdapter implements IAddonClient {
|
|
9
|
+
private readonly client;
|
|
10
|
+
constructor(options: AddonClientOptions | string, addonKey?: string);
|
|
11
|
+
checkHealth(): Promise<AddonHealthResult>;
|
|
12
|
+
readFile(path: string): Promise<FileReadResult>;
|
|
13
|
+
writeFile(path: string, content: string, options?: {
|
|
14
|
+
validateYaml?: boolean;
|
|
15
|
+
label?: string;
|
|
16
|
+
}): Promise<FileWriteResult>;
|
|
17
|
+
listSnapshots(): Promise<SnapshotInfo[]>;
|
|
18
|
+
restoreSnapshot(snapshotId: string): Promise<BackupRestoreResult>;
|
|
19
|
+
getLogs(lines?: number): Promise<LogsTailResult>;
|
|
20
|
+
}
|
|
21
|
+
export { AddonAdapter as AddonClient };
|
|
22
|
+
//# sourceMappingURL=addon.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addon.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/addon/addon.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,YAAY,EACb,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAa,YAAW,YAAY;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,OAAO,EAAE,kBAAkB,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;IA0B7D,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IASzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAS/C,SAAS,CACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GACvD,OAAO,CAAC,eAAe,CAAC;IAcrB,aAAa,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IASxC,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAWjE,OAAO,CAAC,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,CAAC;CAQ5D;AAGD,OAAO,EAAE,YAAY,IAAI,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { ClientError } from "../../core/errors.js";
|
|
3
|
+
export class AddonAdapter {
|
|
4
|
+
client;
|
|
5
|
+
constructor(options, addonKey) {
|
|
6
|
+
let url;
|
|
7
|
+
let key;
|
|
8
|
+
let timeout = 10000;
|
|
9
|
+
if (typeof options === "string") {
|
|
10
|
+
url = options;
|
|
11
|
+
key = addonKey ?? "";
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
url = options.addonUrl;
|
|
15
|
+
key = options.addonKey;
|
|
16
|
+
timeout = options.timeoutMs ?? 10000;
|
|
17
|
+
}
|
|
18
|
+
const cleanUrl = url.replace(/\/+$/, "");
|
|
19
|
+
this.client = axios.create({
|
|
20
|
+
baseURL: cleanUrl,
|
|
21
|
+
timeout,
|
|
22
|
+
headers: {
|
|
23
|
+
"X-Addon-API-Key": key,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async checkHealth() {
|
|
29
|
+
try {
|
|
30
|
+
const resp = await this.client.get("/api/v1/health");
|
|
31
|
+
return resp.data;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new ClientError(`Addon health check failed: ${err.message}`, err.response?.status);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async readFile(path) {
|
|
38
|
+
try {
|
|
39
|
+
const resp = await this.client.post("/api/v1/file/read", { path });
|
|
40
|
+
return resp.data;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new ClientError(`Addon readFile failed: ${err.message}`, err.response?.status);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async writeFile(path, content, options = {}) {
|
|
47
|
+
try {
|
|
48
|
+
const resp = await this.client.post("/api/v1/file/write", {
|
|
49
|
+
path,
|
|
50
|
+
content,
|
|
51
|
+
validate_yaml: options.validateYaml ?? true,
|
|
52
|
+
label: options.label ?? "",
|
|
53
|
+
});
|
|
54
|
+
return resp.data;
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
throw new ClientError(`Addon writeFile failed: ${err.message}`, err.response?.status);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async listSnapshots() {
|
|
61
|
+
try {
|
|
62
|
+
const resp = await this.client.get("/api/v1/backup/list");
|
|
63
|
+
return resp.data;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
throw new ClientError(`Addon listSnapshots failed: ${err.message}`, err.response?.status);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async restoreSnapshot(snapshotId) {
|
|
70
|
+
try {
|
|
71
|
+
const resp = await this.client.post("/api/v1/backup/restore", {
|
|
72
|
+
snapshot_id: snapshotId,
|
|
73
|
+
});
|
|
74
|
+
return resp.data;
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
throw new ClientError(`Addon restoreSnapshot failed: ${err.message}`, err.response?.status);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async getLogs(lines = 100) {
|
|
81
|
+
try {
|
|
82
|
+
const resp = await this.client.get(`/api/v1/logs/tail?lines=${lines}`);
|
|
83
|
+
return resp.data;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
throw new ClientError(`Addon getLogs failed: ${err.message}`, err.response?.status);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Backward-compatible alias
|
|
91
|
+
export { AddonAdapter as AddonClient };
|
|
92
|
+
//# sourceMappingURL=addon.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addon.adapter.js","sourceRoot":"","sources":["../../../src/adapters/addon/addon.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BrowserContext } from "playwright";
|
|
2
|
+
import { IDashboardRenderer } from "../../domain/ports/renderer.port.js";
|
|
3
|
+
import { DevicePreset, RenderOptions, RenderResult, ViewportDimensions } from "../../domain/models/dashboard.js";
|
|
4
|
+
export interface RendererConfig {
|
|
5
|
+
haUrl: string;
|
|
6
|
+
haToken: string;
|
|
7
|
+
browserStateDir?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const DEVICE_PRESETS: Record<DevicePreset, ViewportDimensions>;
|
|
10
|
+
export declare function resolveViewport(options: RenderOptions): ViewportDimensions;
|
|
11
|
+
export declare function resolveUrl(baseUrl: string, urlPath: string): string;
|
|
12
|
+
export declare class PlaywrightDashboardAdapter implements IDashboardRenderer {
|
|
13
|
+
private readonly haUrl;
|
|
14
|
+
private readonly haToken;
|
|
15
|
+
private readonly browserStateDir;
|
|
16
|
+
private browser;
|
|
17
|
+
constructor(config: RendererConfig);
|
|
18
|
+
get isInitialized(): boolean;
|
|
19
|
+
initBrowser(headless?: boolean): Promise<void>;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
resolveViewport(options: RenderOptions): ViewportDimensions;
|
|
22
|
+
resolveUrl(urlPath: string): string;
|
|
23
|
+
setupAuth(context: BrowserContext): Promise<void>;
|
|
24
|
+
captureDashboard(options: RenderOptions): Promise<RenderResult>;
|
|
25
|
+
}
|
|
26
|
+
export { PlaywrightDashboardAdapter as DashboardRenderer };
|
|
27
|
+
//# sourceMappingURL=playwright.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/browser/playwright.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,cAAc,EAAY,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGjH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,YAAY,EAAE,kBAAkB,CAInE,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,kBAAkB,CAQ1E;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOnE;AAED,qBAAa,0BAA2B,YAAW,kBAAkB;IACnE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,MAAM,EAAE,cAAc;IAMlC,IAAI,aAAa,IAAI,OAAO,CAE3B;IAEK,WAAW,CAAC,QAAQ,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAapD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,kBAAkB;IAI3D,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI7B,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjD,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA6EtE;AAED,OAAO,EAAE,0BAA0B,IAAI,iBAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import { chromium } from "playwright";
|
|
4
|
+
import { ClientError } from "../../core/errors.js";
|
|
5
|
+
export const DEVICE_PRESETS = {
|
|
6
|
+
desktop: { width: 1920, height: 1080 },
|
|
7
|
+
tablet: { width: 768, height: 1024 },
|
|
8
|
+
mobile: { width: 375, height: 812 },
|
|
9
|
+
};
|
|
10
|
+
export function resolveViewport(options) {
|
|
11
|
+
if (options.viewport) {
|
|
12
|
+
return options.viewport;
|
|
13
|
+
}
|
|
14
|
+
if (options.devicePreset && DEVICE_PRESETS[options.devicePreset]) {
|
|
15
|
+
return DEVICE_PRESETS[options.devicePreset];
|
|
16
|
+
}
|
|
17
|
+
return DEVICE_PRESETS.desktop;
|
|
18
|
+
}
|
|
19
|
+
export function resolveUrl(baseUrl, urlPath) {
|
|
20
|
+
if (urlPath.startsWith("http://") || urlPath.startsWith("https://")) {
|
|
21
|
+
return urlPath;
|
|
22
|
+
}
|
|
23
|
+
const cleanBase = baseUrl.replace(/\/+$/, "");
|
|
24
|
+
const cleanPath = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
25
|
+
return cleanPath ? `${cleanBase}/${cleanPath}` : `${cleanBase}/`;
|
|
26
|
+
}
|
|
27
|
+
export class PlaywrightDashboardAdapter {
|
|
28
|
+
haUrl;
|
|
29
|
+
haToken;
|
|
30
|
+
browserStateDir;
|
|
31
|
+
browser = null;
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.haUrl = config.haUrl.replace(/\/+$/, "");
|
|
34
|
+
this.haToken = config.haToken;
|
|
35
|
+
this.browserStateDir = config.browserStateDir || path.join(os.homedir(), ".ha-ai");
|
|
36
|
+
}
|
|
37
|
+
get isInitialized() {
|
|
38
|
+
return this.browser !== null;
|
|
39
|
+
}
|
|
40
|
+
async initBrowser(headless = true) {
|
|
41
|
+
if (!this.browser) {
|
|
42
|
+
try {
|
|
43
|
+
this.browser = await chromium.launch({
|
|
44
|
+
headless,
|
|
45
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new ClientError(`Failed to launch Chromium browser via Playwright: ${err.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async close() {
|
|
54
|
+
if (this.browser) {
|
|
55
|
+
await this.browser.close();
|
|
56
|
+
this.browser = null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
resolveViewport(options) {
|
|
60
|
+
return resolveViewport(options);
|
|
61
|
+
}
|
|
62
|
+
resolveUrl(urlPath) {
|
|
63
|
+
return resolveUrl(this.haUrl, urlPath);
|
|
64
|
+
}
|
|
65
|
+
async setupAuth(context) {
|
|
66
|
+
const authData = {
|
|
67
|
+
access_token: this.haToken,
|
|
68
|
+
token_type: "Bearer",
|
|
69
|
+
expires_in: 315360000,
|
|
70
|
+
hassUrl: this.haUrl,
|
|
71
|
+
};
|
|
72
|
+
await context.addInitScript((data) => {
|
|
73
|
+
try {
|
|
74
|
+
const storage = globalThis.localStorage;
|
|
75
|
+
if (storage) {
|
|
76
|
+
storage.setItem("hassTokens", JSON.stringify(data));
|
|
77
|
+
storage.setItem("dockedSidebar", '"auto"');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// ignore in environments without localStorage
|
|
82
|
+
}
|
|
83
|
+
}, authData);
|
|
84
|
+
}
|
|
85
|
+
async captureDashboard(options) {
|
|
86
|
+
await this.initBrowser(true);
|
|
87
|
+
if (!this.browser) {
|
|
88
|
+
throw new ClientError("Browser instance is not initialized");
|
|
89
|
+
}
|
|
90
|
+
const viewport = this.resolveViewport(options);
|
|
91
|
+
const targetUrl = this.resolveUrl(options.urlPath);
|
|
92
|
+
const timeoutMs = options.timeoutMs ?? 15000;
|
|
93
|
+
let colorScheme = "no-preference";
|
|
94
|
+
if (options.darkMode === true) {
|
|
95
|
+
colorScheme = "dark";
|
|
96
|
+
}
|
|
97
|
+
else if (options.darkMode === false) {
|
|
98
|
+
colorScheme = "light";
|
|
99
|
+
}
|
|
100
|
+
const context = await this.browser.newContext({
|
|
101
|
+
viewport,
|
|
102
|
+
colorScheme,
|
|
103
|
+
deviceScaleFactor: 1,
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
await this.setupAuth(context);
|
|
107
|
+
const page = await context.newPage();
|
|
108
|
+
try {
|
|
109
|
+
await page.goto(targetUrl, {
|
|
110
|
+
waitUntil: "domcontentloaded",
|
|
111
|
+
timeout: timeoutMs,
|
|
112
|
+
});
|
|
113
|
+
await Promise.race([
|
|
114
|
+
page.waitForLoadState("networkidle", { timeout: 3000 }),
|
|
115
|
+
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
116
|
+
]).catch(() => { });
|
|
117
|
+
try {
|
|
118
|
+
await page.waitForSelector("home-assistant, home-assistant-main, hui-view, ha-card", {
|
|
119
|
+
timeout: 2000,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Continue if selector is delayed
|
|
124
|
+
}
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
throw new ClientError(`Failed loading dashboard at ${targetUrl}: ${err.message}`);
|
|
129
|
+
}
|
|
130
|
+
let buffer;
|
|
131
|
+
if (options.elementSelector) {
|
|
132
|
+
const element = await page.$(options.elementSelector);
|
|
133
|
+
if (!element) {
|
|
134
|
+
throw new ClientError(`Element selector not found on page: ${options.elementSelector}`);
|
|
135
|
+
}
|
|
136
|
+
buffer = await element.screenshot({ type: "png" });
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
buffer = await page.screenshot({ type: "png", fullPage: false });
|
|
140
|
+
}
|
|
141
|
+
const base64Png = buffer.toString("base64");
|
|
142
|
+
return {
|
|
143
|
+
imageBuffer: buffer,
|
|
144
|
+
base64Png,
|
|
145
|
+
width: viewport.width,
|
|
146
|
+
height: viewport.height,
|
|
147
|
+
url: targetUrl,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
await context.close();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export { PlaywrightDashboardAdapter as DashboardRenderer };
|
|
156
|
+
//# sourceMappingURL=playwright.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.adapter.js","sourceRoot":"","sources":["../../../src/adapters/browser/playwright.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IHARestClient } from "../../domain/ports/ha-client.port.js";
|
|
2
|
+
import { HAEntityState } from "../../domain/models/entity.js";
|
|
3
|
+
export interface HARestOptions {
|
|
4
|
+
haUrl: string;
|
|
5
|
+
haToken: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class HARestAdapter implements IHARestClient {
|
|
9
|
+
private readonly client;
|
|
10
|
+
constructor(options: HARestOptions | string, haToken?: string);
|
|
11
|
+
checkApi(): Promise<{
|
|
12
|
+
message: string;
|
|
13
|
+
}>;
|
|
14
|
+
getStates(): Promise<HAEntityState[]>;
|
|
15
|
+
callService(domain: string, service: string, serviceData?: Record<string, any>): Promise<any>;
|
|
16
|
+
getSupervisorLogs(linesCount?: number): Promise<string[]>;
|
|
17
|
+
}
|
|
18
|
+
export { HARestAdapter as HARestClient };
|
|
19
|
+
//# sourceMappingURL=ha-rest.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ha-rest.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/ha/ha-rest.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAG9D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,OAAO,EAAE,aAAa,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IA0BvD,QAAQ,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IASxC,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IASrC,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IASjG,iBAAiB,CAAC,UAAU,SAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAW7D;AAGD,OAAO,EAAE,aAAa,IAAI,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { ClientError } from "../../core/errors.js";
|
|
3
|
+
export class HARestAdapter {
|
|
4
|
+
client;
|
|
5
|
+
constructor(options, haToken) {
|
|
6
|
+
let url;
|
|
7
|
+
let token;
|
|
8
|
+
let timeout = 10000;
|
|
9
|
+
if (typeof options === "string") {
|
|
10
|
+
url = options;
|
|
11
|
+
token = haToken ?? "";
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
url = options.haUrl;
|
|
15
|
+
token = options.haToken;
|
|
16
|
+
timeout = options.timeoutMs ?? 10000;
|
|
17
|
+
}
|
|
18
|
+
const cleanUrl = url.replace(/\/+$/, "");
|
|
19
|
+
this.client = axios.create({
|
|
20
|
+
baseURL: cleanUrl,
|
|
21
|
+
timeout,
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${token}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async checkApi() {
|
|
29
|
+
try {
|
|
30
|
+
const resp = await this.client.get("/api/");
|
|
31
|
+
return resp.data;
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new ClientError(`HA REST /api/ check failed: ${err.message}`, err.response?.status);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async getStates() {
|
|
38
|
+
try {
|
|
39
|
+
const resp = await this.client.get("/api/states");
|
|
40
|
+
return resp.data;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new ClientError(`Failed to fetch HA states: ${err.message}`, err.response?.status);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async callService(domain, service, serviceData = {}) {
|
|
47
|
+
try {
|
|
48
|
+
const resp = await this.client.post(`/api/services/${domain}/${service}`, serviceData);
|
|
49
|
+
return resp.data;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new ClientError(`Failed calling service ${domain}.${service}: ${err.message}`, err.response?.status);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async getSupervisorLogs(linesCount = 100) {
|
|
56
|
+
try {
|
|
57
|
+
const resp = await this.client.get("/api/hassio/supervisor/logs", {
|
|
58
|
+
responseType: "text",
|
|
59
|
+
});
|
|
60
|
+
const lines = String(resp.data).split("\n").filter(Boolean);
|
|
61
|
+
return lines.slice(-linesCount);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Backward-compatible alias
|
|
69
|
+
export { HARestAdapter as HARestClient };
|
|
70
|
+
//# sourceMappingURL=ha-rest.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ha-rest.adapter.js","sourceRoot":"","sources":["../../../src/adapters/ha/ha-rest.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IHAWsClient } from "../../domain/ports/ha-client.port.js";
|
|
2
|
+
import { HAEntityState } from "../../domain/models/entity.js";
|
|
3
|
+
export interface HAWsOptions {
|
|
4
|
+
haUrl: string;
|
|
5
|
+
haToken: string;
|
|
6
|
+
connectTimeoutMs?: number;
|
|
7
|
+
requestTimeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class HAWsAdapter implements IHAWsClient {
|
|
10
|
+
private readonly wsUrl;
|
|
11
|
+
private readonly haToken;
|
|
12
|
+
private readonly connectTimeoutMs;
|
|
13
|
+
private readonly requestTimeoutMs;
|
|
14
|
+
private ws;
|
|
15
|
+
private messageId;
|
|
16
|
+
private pendingRequests;
|
|
17
|
+
private isAuthenticated;
|
|
18
|
+
constructor(options: HAWsOptions | string, haToken?: string);
|
|
19
|
+
get isConnected(): boolean;
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
disconnect(): Promise<void>;
|
|
22
|
+
sendMessage<T = any>(command: Record<string, any>): Promise<T>;
|
|
23
|
+
private sendCommand;
|
|
24
|
+
getLovelaceConfig(urlPath?: string | null): Promise<Record<string, any>>;
|
|
25
|
+
saveLovelaceConfig(config: Record<string, any>, urlPath?: string | null): Promise<null>;
|
|
26
|
+
fetchStates(): Promise<HAEntityState[]>;
|
|
27
|
+
}
|
|
28
|
+
export { HAWsAdapter as HAWsClient };
|
|
29
|
+
//# sourceMappingURL=ha-ws.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ha-ws.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/ha/ha-ws.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAG9D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAQD,qBAAa,WAAY,YAAW,WAAW;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,eAAe,CAAS;gBAEpB,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IA6B3D,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyFxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;YAItD,WAAW;IAuBnB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAQxE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvF,WAAW,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAG9C;AAGD,OAAO,EAAE,WAAW,IAAI,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import { ClientError } from "../../core/errors.js";
|
|
3
|
+
export class HAWsAdapter {
|
|
4
|
+
wsUrl;
|
|
5
|
+
haToken;
|
|
6
|
+
connectTimeoutMs;
|
|
7
|
+
requestTimeoutMs;
|
|
8
|
+
ws = null;
|
|
9
|
+
messageId = 1;
|
|
10
|
+
pendingRequests = new Map();
|
|
11
|
+
isAuthenticated = false;
|
|
12
|
+
constructor(options, haToken) {
|
|
13
|
+
let url;
|
|
14
|
+
let token;
|
|
15
|
+
if (typeof options === "string") {
|
|
16
|
+
url = options;
|
|
17
|
+
token = haToken ?? "";
|
|
18
|
+
this.connectTimeoutMs = 10000;
|
|
19
|
+
this.requestTimeoutMs = 15000;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
url = options.haUrl;
|
|
23
|
+
token = options.haToken;
|
|
24
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? 10000;
|
|
25
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 15000;
|
|
26
|
+
}
|
|
27
|
+
this.haToken = token;
|
|
28
|
+
const cleanUrl = url.replace(/\/+$/, "");
|
|
29
|
+
if (cleanUrl.startsWith("http://")) {
|
|
30
|
+
this.wsUrl = `ws://${cleanUrl.slice("http://".length)}/api/websocket`;
|
|
31
|
+
}
|
|
32
|
+
else if (cleanUrl.startsWith("https://")) {
|
|
33
|
+
this.wsUrl = `wss://${cleanUrl.slice("https://".length)}/api/websocket`;
|
|
34
|
+
}
|
|
35
|
+
else if (cleanUrl.startsWith("ws://") || cleanUrl.startsWith("wss://")) {
|
|
36
|
+
this.wsUrl = cleanUrl.endsWith("/api/websocket") ? cleanUrl : `${cleanUrl}/api/websocket`;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.wsUrl = `ws://${cleanUrl}/api/websocket`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
get isConnected() {
|
|
43
|
+
return this.isAuthenticated && this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
44
|
+
}
|
|
45
|
+
async connect() {
|
|
46
|
+
if (this.ws && this.isAuthenticated && this.ws.readyState === WebSocket.OPEN) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
let isSettled = false;
|
|
51
|
+
const timeout = setTimeout(() => {
|
|
52
|
+
if (!isSettled) {
|
|
53
|
+
isSettled = true;
|
|
54
|
+
this.disconnect();
|
|
55
|
+
reject(new ClientError("HA WebSocket connection timed out"));
|
|
56
|
+
}
|
|
57
|
+
}, this.connectTimeoutMs);
|
|
58
|
+
try {
|
|
59
|
+
this.ws = new WebSocket(this.wsUrl);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
return reject(new ClientError(`WebSocket initialization failed: ${err.message}`));
|
|
64
|
+
}
|
|
65
|
+
this.ws.on("open", () => { });
|
|
66
|
+
this.ws.on("message", (raw) => {
|
|
67
|
+
try {
|
|
68
|
+
const msg = JSON.parse(raw.toString("utf-8"));
|
|
69
|
+
if (msg.type === "auth_required") {
|
|
70
|
+
this.ws?.send(JSON.stringify({ type: "auth", access_token: this.haToken }));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (msg.type === "auth_ok") {
|
|
74
|
+
this.isAuthenticated = true;
|
|
75
|
+
if (!isSettled) {
|
|
76
|
+
isSettled = true;
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
resolve();
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (msg.type === "auth_invalid") {
|
|
83
|
+
this.isAuthenticated = false;
|
|
84
|
+
if (!isSettled) {
|
|
85
|
+
isSettled = true;
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
reject(new ClientError(`HA WebSocket authentication failed: ${msg.message || "Invalid token"}`));
|
|
88
|
+
}
|
|
89
|
+
this.disconnect();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (typeof msg.id === "number" && this.pendingRequests.has(msg.id)) {
|
|
93
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
94
|
+
this.pendingRequests.delete(msg.id);
|
|
95
|
+
clearTimeout(pending.timer);
|
|
96
|
+
if (msg.success) {
|
|
97
|
+
pending.resolve(msg.result ?? null);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
pending.reject(new ClientError(msg.error?.message || `WebSocket command ${msg.id} failed`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// Ignore non-json frames
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
this.ws.on("error", (err) => {
|
|
109
|
+
if (!isSettled) {
|
|
110
|
+
isSettled = true;
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
reject(new ClientError(`HA WebSocket error: ${err.message}`));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
this.ws.on("close", () => {
|
|
116
|
+
this.isAuthenticated = false;
|
|
117
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
118
|
+
clearTimeout(pending.timer);
|
|
119
|
+
pending.reject(new ClientError(`WebSocket closed before request ${id} resolved`));
|
|
120
|
+
}
|
|
121
|
+
this.pendingRequests.clear();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async disconnect() {
|
|
126
|
+
if (this.ws) {
|
|
127
|
+
try {
|
|
128
|
+
this.ws.terminate();
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
133
|
+
this.ws = null;
|
|
134
|
+
}
|
|
135
|
+
this.isAuthenticated = false;
|
|
136
|
+
}
|
|
137
|
+
async sendMessage(command) {
|
|
138
|
+
return this.sendCommand(command);
|
|
139
|
+
}
|
|
140
|
+
async sendCommand(command) {
|
|
141
|
+
await this.connect();
|
|
142
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
143
|
+
throw new ClientError("WebSocket is not connected");
|
|
144
|
+
}
|
|
145
|
+
const id = this.messageId++;
|
|
146
|
+
const payload = { ...command, id };
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const timer = setTimeout(() => {
|
|
149
|
+
if (this.pendingRequests.has(id)) {
|
|
150
|
+
this.pendingRequests.delete(id);
|
|
151
|
+
reject(new ClientError(`WebSocket command ${id} timed out after ${this.requestTimeoutMs}ms`));
|
|
152
|
+
}
|
|
153
|
+
}, this.requestTimeoutMs);
|
|
154
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
155
|
+
this.ws.send(JSON.stringify(payload));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async getLovelaceConfig(urlPath) {
|
|
159
|
+
const cmd = { type: "lovelace/config" };
|
|
160
|
+
if (urlPath) {
|
|
161
|
+
cmd.url_path = urlPath;
|
|
162
|
+
}
|
|
163
|
+
return this.sendCommand(cmd);
|
|
164
|
+
}
|
|
165
|
+
async saveLovelaceConfig(config, urlPath) {
|
|
166
|
+
const cmd = { type: "lovelace/config/save", config };
|
|
167
|
+
if (urlPath) {
|
|
168
|
+
cmd.url_path = urlPath;
|
|
169
|
+
}
|
|
170
|
+
return this.sendCommand(cmd);
|
|
171
|
+
}
|
|
172
|
+
async fetchStates() {
|
|
173
|
+
return this.sendCommand({ type: "get_states" });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Backward-compatible alias
|
|
177
|
+
export { HAWsAdapter as HAWsClient };
|
|
178
|
+
//# sourceMappingURL=ha-ws.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ha-ws.adapter.js","sourceRoot":"","sources":["../../../src/adapters/ha/ha-ws.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAG3B,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAenD,MAAM,OAAO,WAAW;IACL,KAAK,CAAS;IACd,OAAO,CAAS;IAChB,gBAAgB,CAAS;IACzB,gBAAgB,CAAS;IAClC,EAAE,GAAqB,IAAI,CAAC;IAC5B,SAAS,GAAG,CAAC,CAAC;IACd,eAAe,GAAG,IAAI,GAAG,EAA0B,CAAC;IACpD,eAAe,GAAG,KAAK,CAAC;IAEhC,YAAY,OAA6B,EAAE,OAAgB;QACzD,IAAI,GAAW,CAAC;QAChB,IAAI,KAAa,CAAC;QAElB,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,GAAG,GAAG,OAAO,CAAC;YACd,KAAK,GAAG,OAAO,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;YAC9B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC;YACpB,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;YACxB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC;YAC1D,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,KAAK,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzC,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,GAAG,QAAQ,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACxE,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK,GAAG,SAAS,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC1E,CAAC;aAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,gBAAgB,CAAC;QAC5F,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,GAAG,QAAQ,QAAQ,gBAAgB,CAAC;QAChD,CAAC;IACH,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,CAAC;IAC3F,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YAC7E,OAAO;QACT,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,SAAS,GAAG,IAAI,CAAC;oBACjB,IAAI,CAAC,UAAU,EAAE,CAAC;oBAClB,MAAM,CAAC,IAAI,WAAW,CAAC,mCAAmC,CAAC,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE1B,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtC,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,OAAO,MAAM,CAAC,IAAI,WAAW,CAAC,oCAAoC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACpF,CAAC;YAED,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAE7B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAsB,EAAE,EAAE;gBAC/C,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;oBAE9C,IAAI,GAAG,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;wBACjC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;wBAC5E,OAAO;oBACT,CAAC;oBAED,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;wBAC3B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;wBAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,SAAS,GAAG,IAAI,CAAC;4BACjB,YAAY,CAAC,OAAO,CAAC,CAAC;4BACtB,OAAO,EAAE,CAAC;wBACZ,CAAC;wBACD,OAAO;oBACT,CAAC;oBAED,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;wBAChC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;wBAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,SAAS,GAAG,IAAI,CAAC;4BACjB,YAAY,CAAC,OAAO,CAAC,CAAC;4BACtB,MAAM,CAAC,IAAI,WAAW,CAAC,uCAAuC,GAAG,CAAC,OAAO,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC;wBACnG,CAAC;wBACD,IAAI,CAAC,UAAU,EAAE,CAAC;wBAClB,OAAO;oBACT,CAAC;oBAED,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;wBACnE,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;wBAClD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBACpC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;wBAE5B,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;4BAChB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;wBACtC,CAAC;6BAAM,CAAC;4BACN,OAAO,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,IAAI,qBAAqB,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;wBAC9F,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,yBAAyB;gBAC3B,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBACjC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,SAAS,GAAG,IAAI,CAAC;oBACjB,YAAY,CAAC,OAAO,CAAC,CAAC;oBACtB,MAAM,CAAC,IAAI,WAAW,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACvB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;gBAC7B,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;oBACjD,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,mCAAmC,EAAE,WAAW,CAAC,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC;YACtB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,WAAW,CAAU,OAA4B;QACrD,OAAO,IAAI,CAAC,WAAW,CAAI,OAAO,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,WAAW,CAAU,OAA4B;QAC7D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QAErB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,WAAW,CAAC,4BAA4B,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,EAAE,EAAE,CAAC;QAEnC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACjC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;oBAChC,MAAM,CAAC,IAAI,WAAW,CAAC,qBAAqB,EAAE,oBAAoB,IAAI,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC;gBAChG,CAAC;YACH,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE1B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;YACzD,IAAI,CAAC,EAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,OAAuB;QAC7C,MAAM,GAAG,GAAwB,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;QAC7D,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAsB,GAAG,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAA2B,EAAE,OAAuB;QAC3E,MAAM,GAAG,GAAwB,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC;QAC1E,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAO,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,WAAW,CAAkB,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IACnE,CAAC;CACF;AAED,4BAA4B;AAC5B,OAAO,EAAE,WAAW,IAAI,UAAU,EAAE,CAAC"}
|