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.
Files changed (142) hide show
  1. package/dist/adapters/addon/addon.adapter.d.ts +22 -0
  2. package/dist/adapters/addon/addon.adapter.d.ts.map +1 -0
  3. package/dist/adapters/addon/addon.adapter.js +92 -0
  4. package/dist/adapters/addon/addon.adapter.js.map +1 -0
  5. package/dist/adapters/browser/playwright.adapter.d.ts +27 -0
  6. package/dist/adapters/browser/playwright.adapter.d.ts.map +1 -0
  7. package/dist/adapters/browser/playwright.adapter.js +156 -0
  8. package/dist/adapters/browser/playwright.adapter.js.map +1 -0
  9. package/dist/adapters/ha/ha-rest.adapter.d.ts +19 -0
  10. package/dist/adapters/ha/ha-rest.adapter.d.ts.map +1 -0
  11. package/dist/adapters/ha/ha-rest.adapter.js +70 -0
  12. package/dist/adapters/ha/ha-rest.adapter.js.map +1 -0
  13. package/dist/adapters/ha/ha-ws.adapter.d.ts +29 -0
  14. package/dist/adapters/ha/ha-ws.adapter.d.ts.map +1 -0
  15. package/dist/adapters/ha/ha-ws.adapter.js +178 -0
  16. package/dist/adapters/ha/ha-ws.adapter.js.map +1 -0
  17. package/dist/browser/renderer.d.ts +3 -0
  18. package/dist/browser/renderer.d.ts.map +1 -0
  19. package/dist/browser/renderer.js +3 -0
  20. package/dist/browser/renderer.js.map +1 -0
  21. package/dist/clients/addon-client.d.ts +3 -0
  22. package/dist/clients/addon-client.d.ts.map +1 -0
  23. package/dist/clients/addon-client.js +3 -0
  24. package/dist/clients/addon-client.js.map +1 -0
  25. package/dist/clients/ha-rest.d.ts +3 -0
  26. package/dist/clients/ha-rest.d.ts.map +1 -0
  27. package/dist/clients/ha-rest.js +3 -0
  28. package/dist/clients/ha-rest.js.map +1 -0
  29. package/dist/clients/ha-ws.d.ts +3 -0
  30. package/dist/clients/ha-ws.d.ts.map +1 -0
  31. package/dist/clients/ha-ws.js +3 -0
  32. package/dist/clients/ha-ws.js.map +1 -0
  33. package/dist/config.d.ts +2 -0
  34. package/dist/config.d.ts.map +1 -0
  35. package/dist/config.js +2 -0
  36. package/dist/config.js.map +1 -0
  37. package/dist/core/config.d.ts +30 -0
  38. package/dist/core/config.d.ts.map +1 -0
  39. package/dist/core/config.js +27 -0
  40. package/dist/core/config.js.map +1 -0
  41. package/dist/core/errors.d.ts +18 -0
  42. package/dist/core/errors.d.ts.map +1 -0
  43. package/dist/core/errors.js +29 -0
  44. package/dist/core/errors.js.map +1 -0
  45. package/dist/core/logger.d.ts +13 -0
  46. package/dist/core/logger.d.ts.map +1 -0
  47. package/dist/core/logger.js +25 -0
  48. package/dist/core/logger.js.map +1 -0
  49. package/dist/domain/models/automation.d.ts +19 -0
  50. package/dist/domain/models/automation.d.ts.map +1 -0
  51. package/dist/domain/models/automation.js +2 -0
  52. package/dist/domain/models/automation.js.map +1 -0
  53. package/dist/domain/models/dashboard.d.ts +21 -0
  54. package/dist/domain/models/dashboard.d.ts.map +1 -0
  55. package/dist/domain/models/dashboard.js +2 -0
  56. package/dist/domain/models/dashboard.js.map +1 -0
  57. package/dist/domain/models/entity.d.ts +13 -0
  58. package/dist/domain/models/entity.d.ts.map +1 -0
  59. package/dist/domain/models/entity.js +2 -0
  60. package/dist/domain/models/entity.js.map +1 -0
  61. package/dist/domain/models/system.d.ts +35 -0
  62. package/dist/domain/models/system.d.ts.map +1 -0
  63. package/dist/domain/models/system.js +2 -0
  64. package/dist/domain/models/system.js.map +1 -0
  65. package/dist/domain/ports/addon-client.port.d.ts +31 -0
  66. package/dist/domain/ports/addon-client.port.d.ts.map +1 -0
  67. package/dist/domain/ports/addon-client.port.js +2 -0
  68. package/dist/domain/ports/addon-client.port.js.map +1 -0
  69. package/dist/domain/ports/ha-client.port.d.ts +17 -0
  70. package/dist/domain/ports/ha-client.port.d.ts.map +1 -0
  71. package/dist/domain/ports/ha-client.port.js +2 -0
  72. package/dist/domain/ports/ha-client.port.js.map +1 -0
  73. package/dist/domain/ports/renderer.port.d.ts +7 -0
  74. package/dist/domain/ports/renderer.port.d.ts.map +1 -0
  75. package/dist/domain/ports/renderer.port.js +2 -0
  76. package/dist/domain/ports/renderer.port.js.map +1 -0
  77. package/dist/index.d.ts +10 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +87 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/services/automation.service.d.ts +20 -0
  82. package/dist/services/automation.service.d.ts.map +1 -0
  83. package/dist/services/automation.service.js +118 -0
  84. package/dist/services/automation.service.js.map +1 -0
  85. package/dist/services/dashboard.service.d.ts +22 -0
  86. package/dist/services/dashboard.service.d.ts.map +1 -0
  87. package/dist/services/dashboard.service.js +72 -0
  88. package/dist/services/dashboard.service.js.map +1 -0
  89. package/dist/services/system.service.d.ts +19 -0
  90. package/dist/services/system.service.d.ts.map +1 -0
  91. package/dist/services/system.service.js +83 -0
  92. package/dist/services/system.service.js.map +1 -0
  93. package/dist/tools/automation.d.ts +33 -0
  94. package/dist/tools/automation.d.ts.map +1 -0
  95. package/dist/tools/automation.js +246 -0
  96. package/dist/tools/automation.js.map +1 -0
  97. package/dist/tools/dashboard.d.ts +33 -0
  98. package/dist/tools/dashboard.d.ts.map +1 -0
  99. package/dist/tools/dashboard.js +166 -0
  100. package/dist/tools/dashboard.js.map +1 -0
  101. package/dist/tools/system.d.ts +44 -0
  102. package/dist/tools/system.d.ts.map +1 -0
  103. package/dist/tools/system.js +306 -0
  104. package/dist/tools/system.js.map +1 -0
  105. package/dist/tools/types.d.ts +26 -0
  106. package/dist/tools/types.d.ts.map +1 -0
  107. package/dist/tools/types.js +2 -0
  108. package/dist/tools/types.js.map +1 -0
  109. package/package.json +39 -0
  110. package/src/adapters/addon/addon.adapter.ts +113 -0
  111. package/src/adapters/browser/playwright.adapter.ts +186 -0
  112. package/src/adapters/ha/ha-rest.adapter.ts +82 -0
  113. package/src/adapters/ha/ha-ws.adapter.ts +212 -0
  114. package/src/browser/renderer.ts +7 -0
  115. package/src/clients/addon-client.ts +2 -0
  116. package/src/clients/ha-rest.ts +2 -0
  117. package/src/clients/ha-ws.ts +2 -0
  118. package/src/config.ts +1 -0
  119. package/src/core/config.ts +39 -0
  120. package/src/core/errors.ts +28 -0
  121. package/src/core/logger.ts +27 -0
  122. package/src/domain/models/automation.ts +19 -0
  123. package/src/domain/models/dashboard.ts +23 -0
  124. package/src/domain/models/entity.ts +13 -0
  125. package/src/domain/models/system.ts +37 -0
  126. package/src/domain/ports/addon-client.port.ts +35 -0
  127. package/src/domain/ports/ha-client.port.ts +16 -0
  128. package/src/domain/ports/renderer.port.ts +7 -0
  129. package/src/index.ts +103 -0
  130. package/src/services/automation.service.ts +142 -0
  131. package/src/services/dashboard.service.ts +83 -0
  132. package/src/services/system.service.ts +94 -0
  133. package/src/tools/automation.ts +300 -0
  134. package/src/tools/dashboard.ts +206 -0
  135. package/src/tools/system.ts +368 -0
  136. package/src/tools/types.ts +30 -0
  137. package/tests/clients.test.ts +522 -0
  138. package/tests/e2e-smoke.test.ts +620 -0
  139. package/tests/renderer.test.ts +286 -0
  140. package/tests/skills.test.ts +189 -0
  141. package/tests/tools.test.ts +530 -0
  142. package/tsconfig.json +10 -0
@@ -0,0 +1,20 @@
1
+ import { IHARestClient } from "../domain/ports/ha-client.port.js";
2
+ import { IAddonClient } from "../domain/ports/addon-client.port.js";
3
+ import { AutomationSummary } from "../domain/models/automation.js";
4
+ export declare class AutomationService {
5
+ private readonly restClient;
6
+ private readonly addonClient;
7
+ constructor(restClient: IHARestClient, addonClient: IAddonClient);
8
+ listAutomations(domain?: "automation" | "script" | "scene"): Promise<AutomationSummary[]>;
9
+ readAutomation(automationId: string): Promise<string>;
10
+ writeAutomation(automationId: string, yamlCode: string, label?: string): Promise<{
11
+ success: boolean;
12
+ snapshotId: string;
13
+ path: string;
14
+ }>;
15
+ triggerAutomation(entityId: string): Promise<{
16
+ success: boolean;
17
+ entityId: string;
18
+ }>;
19
+ }
20
+ //# sourceMappingURL=automation.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation.service.d.ts","sourceRoot":"","sources":["../../src/services/automation.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAwCnE,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,UAAU,EAAE,aAAa,EACzB,WAAW,EAAE,YAAY;IAGtC,eAAe,CAAC,MAAM,GAAE,YAAY,GAAG,QAAQ,GAAG,OAAsB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAgBvG,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAarD,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAiD5D,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAW3F"}
@@ -0,0 +1,118 @@
1
+ import { ValidationError } from "../core/errors.js";
2
+ function splitYamlBlocks(yamlContent) {
3
+ if (!yamlContent.trim()) {
4
+ return [];
5
+ }
6
+ const lines = yamlContent.split("\n");
7
+ const blocks = [];
8
+ let currentBlock = [];
9
+ for (const line of lines) {
10
+ if (line.startsWith("- ") && currentBlock.length > 0) {
11
+ blocks.push(currentBlock.join("\n").trim());
12
+ currentBlock = [line];
13
+ }
14
+ else {
15
+ currentBlock.push(line);
16
+ }
17
+ }
18
+ if (currentBlock.length > 0) {
19
+ const trimmed = currentBlock.join("\n").trim();
20
+ if (trimmed) {
21
+ blocks.push(trimmed);
22
+ }
23
+ }
24
+ return blocks;
25
+ }
26
+ function escapeRegex(str) {
27
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ }
29
+ function blockMatchesIdOrAlias(block, targetId) {
30
+ const idRegex = new RegExp(`(^|\\n)\\s*-\\s*id:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)|(^|\\n)\\s*id:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)`, "i");
31
+ const aliasRegex = new RegExp(`(^|\\n)\\s*alias:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)`, "i");
32
+ return idRegex.test(block) || aliasRegex.test(block);
33
+ }
34
+ export class AutomationService {
35
+ restClient;
36
+ addonClient;
37
+ constructor(restClient, addonClient) {
38
+ this.restClient = restClient;
39
+ this.addonClient = addonClient;
40
+ }
41
+ async listAutomations(domain = "automation") {
42
+ const states = await this.restClient.getStates();
43
+ const prefix = `${domain}.`;
44
+ return states
45
+ .filter((s) => s.entity_id.startsWith(prefix))
46
+ .map((s) => ({
47
+ entity_id: s.entity_id,
48
+ id: s.attributes?.id || s.entity_id.replace(prefix, ""),
49
+ alias: s.attributes?.friendly_name || s.attributes?.alias,
50
+ state: s.state,
51
+ description: s.attributes?.description,
52
+ last_triggered: s.attributes?.last_triggered || null,
53
+ }));
54
+ }
55
+ async readAutomation(automationId) {
56
+ const cleanId = automationId.replace(/^automation\./, "").trim();
57
+ const file = await this.addonClient.readFile("automations.yaml");
58
+ const blocks = splitYamlBlocks(file.content);
59
+ const matchedBlock = blocks.find((b) => blockMatchesIdOrAlias(b, cleanId));
60
+ if (!matchedBlock) {
61
+ throw new ValidationError(`Automation "${cleanId}" not found in automations.yaml`);
62
+ }
63
+ return matchedBlock;
64
+ }
65
+ async writeAutomation(automationId, yamlCode, label) {
66
+ const cleanId = automationId.replace(/^automation\./, "").trim();
67
+ let existingContent = "";
68
+ try {
69
+ const fileRes = await this.addonClient.readFile("automations.yaml");
70
+ existingContent = fileRes.content;
71
+ }
72
+ catch {
73
+ existingContent = "";
74
+ }
75
+ const blocks = splitYamlBlocks(existingContent);
76
+ let formattedNewBlock = yamlCode.trim();
77
+ if (!formattedNewBlock.startsWith("- ")) {
78
+ const lines = formattedNewBlock.split("\n");
79
+ const firstLine = `- ${lines[0]}`;
80
+ const restLines = lines.slice(1).map((l) => (l.trim() ? ` ${l}` : l));
81
+ formattedNewBlock = [firstLine, ...restLines].join("\n");
82
+ }
83
+ const existingIndex = blocks.findIndex((b) => blockMatchesIdOrAlias(b, cleanId));
84
+ if (existingIndex >= 0) {
85
+ blocks[existingIndex] = formattedNewBlock;
86
+ }
87
+ else {
88
+ blocks.push(formattedNewBlock);
89
+ }
90
+ const updatedYaml = blocks.join("\n\n") + "\n";
91
+ const snapshotLabel = label || `Update automation ${cleanId}`;
92
+ const writeRes = await this.addonClient.writeFile("automations.yaml", updatedYaml, {
93
+ validateYaml: true,
94
+ label: snapshotLabel,
95
+ });
96
+ try {
97
+ await this.restClient.callService("automation", "reload");
98
+ }
99
+ catch {
100
+ // Reload is best-effort
101
+ }
102
+ return {
103
+ success: true,
104
+ snapshotId: writeRes.snapshot_id,
105
+ path: writeRes.path,
106
+ };
107
+ }
108
+ async triggerAutomation(entityId) {
109
+ const cleanEntityId = entityId.trim();
110
+ const domain = cleanEntityId.split(".")[0] || "automation";
111
+ await this.restClient.callService(domain, "trigger", { entity_id: cleanEntityId });
112
+ return {
113
+ success: true,
114
+ entityId: cleanEntityId,
115
+ };
116
+ }
117
+ }
118
+ //# sourceMappingURL=automation.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation.service.js","sourceRoot":"","sources":["../../src/services/automation.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,SAAS,eAAe,CAAC,WAAmB;IAC1C,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,YAAY,GAAa,EAAE,CAAC;IAEhC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5C,YAAY,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa,EAAE,QAAgB;IAC5D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,+BAA+B,WAAW,CAAC,QAAQ,CAAC,uCAAuC,WAAW,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IAChK,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,6BAA6B,WAAW,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACrG,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,OAAO,iBAAiB;IAET;IACA;IAFnB,YACmB,UAAyB,EACzB,WAAyB;QADzB,eAAU,GAAV,UAAU,CAAe;QACzB,gBAAW,GAAX,WAAW,CAAc;IACzC,CAAC;IAEJ,KAAK,CAAC,eAAe,CAAC,SAA4C,YAAY;QAC5E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC;QAE5B,OAAO,MAAM;aACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACX,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACvD,KAAK,EAAE,CAAC,CAAC,UAAU,EAAE,aAAa,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK;YACzD,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,WAAW,EAAE,CAAC,CAAC,UAAU,EAAE,WAAW;YACtC,cAAc,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,IAAI,IAAI;SACrD,CAAC,CAAC,CAAC;IACR,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,YAAoB;QACvC,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE7C,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,eAAe,CAAC,eAAe,OAAO,iCAAiC,CAAC,CAAC;QACrF,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,YAAoB,EACpB,QAAgB,EAChB,KAAc;QAEd,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAEjE,IAAI,eAAe,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;YACpE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,eAAe,GAAG,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;QAChD,IAAI,iBAAiB,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAExC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,iBAAiB,GAAG,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACjF,IAAI,aAAa,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QAC/C,MAAM,aAAa,GAAG,KAAK,IAAI,qBAAqB,OAAO,EAAE,CAAC;QAE9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,kBAAkB,EAAE,WAAW,EAAE;YACjF,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,aAAa;SACrB,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,QAAQ,CAAC,WAAW;YAChC,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,QAAgB;QACtC,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC;QAE3D,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;QAEnF,OAAO;YACL,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,aAAa;SACxB,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import { IHAWsClient } from "../domain/ports/ha-client.port.js";
2
+ import { IAddonClient } from "../domain/ports/addon-client.port.js";
3
+ import { IDashboardRenderer } from "../domain/ports/renderer.port.js";
4
+ import { RenderOptions, RenderResult } from "../domain/models/dashboard.js";
5
+ export declare class DashboardService {
6
+ private readonly wsClient;
7
+ private readonly addonClient;
8
+ private readonly renderer;
9
+ constructor(wsClient: IHAWsClient, addonClient: IAddonClient, renderer: IDashboardRenderer);
10
+ getConfig(dashboardSlug?: string): Promise<{
11
+ source: string;
12
+ config: any;
13
+ rawContent?: string;
14
+ }>;
15
+ saveConfig(configYaml: string, dashboardSlug?: string, label?: string): Promise<{
16
+ success: boolean;
17
+ snapshotId?: string;
18
+ message: string;
19
+ }>;
20
+ renderScreenshot(options: RenderOptions): Promise<RenderResult>;
21
+ }
22
+ //# sourceMappingURL=dashboard.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.service.d.ts","sourceRoot":"","sources":["../../src/services/dashboard.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE5E,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAFR,QAAQ,EAAE,WAAW,EACrB,WAAW,EAAE,YAAY,EACzB,QAAQ,EAAE,kBAAkB;IAGzC,SAAS,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,GAAG,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAsChG,UAAU,CACd,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,MAAM,EACtB,KAAK,GAAE,MAA+B,GACrC,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBhE,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAGtE"}
@@ -0,0 +1,72 @@
1
+ export class DashboardService {
2
+ wsClient;
3
+ addonClient;
4
+ renderer;
5
+ constructor(wsClient, addonClient, renderer) {
6
+ this.wsClient = wsClient;
7
+ this.addonClient = addonClient;
8
+ this.renderer = renderer;
9
+ }
10
+ async getConfig(dashboardSlug) {
11
+ try {
12
+ const config = await this.wsClient.getLovelaceConfig(dashboardSlug ?? null);
13
+ if (config) {
14
+ return {
15
+ source: "websocket",
16
+ config,
17
+ rawContent: JSON.stringify(config, null, 2),
18
+ };
19
+ }
20
+ }
21
+ catch {
22
+ // Fallback to Addon file read
23
+ }
24
+ const candidatePaths = dashboardSlug && dashboardSlug !== "lovelace"
25
+ ? [`dashboards/${dashboardSlug}.yaml`, `.storage/lovelace.${dashboardSlug}`]
26
+ : ["ui-lovelace.yaml", ".storage/lovelace"];
27
+ for (const filePath of candidatePaths) {
28
+ try {
29
+ const file = await this.addonClient.readFile(filePath);
30
+ return {
31
+ source: `addon_file:${filePath}`,
32
+ config: file.content,
33
+ rawContent: file.content,
34
+ };
35
+ }
36
+ catch {
37
+ continue;
38
+ }
39
+ }
40
+ return {
41
+ source: "empty",
42
+ config: { title: "Home", views: [] },
43
+ rawContent: "title: Home\nviews: []\n",
44
+ };
45
+ }
46
+ async saveConfig(configYaml, dashboardSlug, label = "mcp-dashboard-update") {
47
+ const slug = dashboardSlug?.trim() || "lovelace";
48
+ const targetFile = slug !== "lovelace" ? `dashboards/${slug}.yaml` : "ui-lovelace.yaml";
49
+ const writeResult = await this.addonClient.writeFile(targetFile, configYaml, {
50
+ validateYaml: true,
51
+ label,
52
+ });
53
+ try {
54
+ const parsed = JSON.parse(configYaml);
55
+ if (typeof parsed === "object" && parsed !== null) {
56
+ await this.wsClient.saveLovelaceConfig(parsed, slug === "lovelace" ? null : slug);
57
+ }
58
+ }
59
+ catch {
60
+ // Not JSON or WS save skipped; YAML file write succeeded
61
+ }
62
+ return {
63
+ success: true,
64
+ snapshotId: writeResult.snapshot_id,
65
+ message: `Dashboard configuration successfully saved to "${targetFile}". Pre-edit snapshot ID: ${writeResult.snapshot_id}.`,
66
+ };
67
+ }
68
+ async renderScreenshot(options) {
69
+ return this.renderer.captureDashboard(options);
70
+ }
71
+ }
72
+ //# sourceMappingURL=dashboard.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.service.js","sourceRoot":"","sources":["../../src/services/dashboard.service.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,gBAAgB;IAER;IACA;IACA;IAHnB,YACmB,QAAqB,EACrB,WAAyB,EACzB,QAA4B;QAF5B,aAAQ,GAAR,QAAQ,CAAa;QACrB,gBAAW,GAAX,WAAW,CAAc;QACzB,aAAQ,GAAR,QAAQ,CAAoB;IAC5C,CAAC;IAEJ,KAAK,CAAC,SAAS,CAAC,aAAsB;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC;YAC5E,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO;oBACL,MAAM,EAAE,WAAW;oBACnB,MAAM;oBACN,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;iBAC5C,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;QAED,MAAM,cAAc,GAAG,aAAa,IAAI,aAAa,KAAK,UAAU;YAClE,CAAC,CAAC,CAAC,cAAc,aAAa,OAAO,EAAE,qBAAqB,aAAa,EAAE,CAAC;YAC5E,CAAC,CAAC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;QAE9C,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACvD,OAAO;oBACL,MAAM,EAAE,cAAc,QAAQ,EAAE;oBAChC,MAAM,EAAE,IAAI,CAAC,OAAO;oBACpB,UAAU,EAAE,IAAI,CAAC,OAAO;iBACzB,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;YACpC,UAAU,EAAE,0BAA0B;SACvC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CACd,UAAkB,EAClB,aAAsB,EACtB,QAAgB,sBAAsB;QAEtC,MAAM,IAAI,GAAG,aAAa,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC;QACjD,MAAM,UAAU,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc,IAAI,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAExF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,EAAE;YAC3E,YAAY,EAAE,IAAI;YAClB,KAAK;SACN,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACtC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBAClD,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,MAAM,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;QAC3D,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,WAAW,CAAC,WAAW;YACnC,OAAO,EAAE,kDAAkD,UAAU,4BAA4B,WAAW,CAAC,WAAW,GAAG;SAC5H,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,OAAsB;QAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import { IHARestClient } from "../domain/ports/ha-client.port.js";
2
+ import { IAddonClient } from "../domain/ports/addon-client.port.js";
3
+ import { BackupRestoreResult, LogsTailResult, SnapshotInfo, SystemHealth } from "../domain/models/system.js";
4
+ import { HAEntityState } from "../domain/models/entity.js";
5
+ export declare class SystemService {
6
+ private readonly restClient;
7
+ private readonly addonClient;
8
+ constructor(restClient: IHARestClient, addonClient: IAddonClient);
9
+ getHealth(): Promise<SystemHealth>;
10
+ listEntities(domainFilter?: string, searchQuery?: string): Promise<HAEntityState[]>;
11
+ getLogs(lines?: number): Promise<LogsTailResult>;
12
+ createBackup(label?: string): Promise<{
13
+ snapshot_id: string;
14
+ label: string;
15
+ }>;
16
+ listSnapshots(): Promise<SnapshotInfo[]>;
17
+ restoreBackup(snapshotId: string): Promise<BackupRestoreResult>;
18
+ }
19
+ //# sourceMappingURL=system.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.service.d.ts","sourceRoot":"","sources":["../../src/services/system.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC7G,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,UAAU,EAAE,aAAa,EACzB,WAAW,EAAE,YAAY;IAGtC,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAyClC,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAmBnF,OAAO,CAAC,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,cAAc,CAAC;IAIrD,YAAY,CAAC,KAAK,GAAE,MAA4B,GAAG,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAWlG,aAAa,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAIxC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAGtE"}
@@ -0,0 +1,83 @@
1
+ export class SystemService {
2
+ restClient;
3
+ addonClient;
4
+ constructor(restClient, addonClient) {
5
+ this.restClient = restClient;
6
+ this.addonClient = addonClient;
7
+ }
8
+ async getHealth() {
9
+ let haOk = false;
10
+ let haMsg = "";
11
+ try {
12
+ const resp = await this.restClient.checkApi();
13
+ haOk = true;
14
+ haMsg = resp.message || "API running";
15
+ }
16
+ catch (err) {
17
+ haMsg = err.message;
18
+ }
19
+ let addonOk = false;
20
+ let addonInfo = {};
21
+ try {
22
+ const health = await this.addonClient.checkHealth();
23
+ addonOk = true;
24
+ addonInfo = health;
25
+ }
26
+ catch (err) {
27
+ addonInfo = { error: err.message };
28
+ }
29
+ const overallStatus = haOk && addonOk ? "ok" : haOk || addonOk ? "degraded" : "error";
30
+ return {
31
+ status: overallStatus,
32
+ homeAssistant: {
33
+ reachable: haOk,
34
+ message: haMsg,
35
+ },
36
+ addon: {
37
+ reachable: addonOk,
38
+ version: addonInfo.version,
39
+ configRoot: addonInfo.config_root,
40
+ snapshotsCount: addonInfo.snapshots_count,
41
+ memoryMb: addonInfo.memory_mb,
42
+ error: addonInfo.error,
43
+ },
44
+ };
45
+ }
46
+ async listEntities(domainFilter, searchQuery) {
47
+ const states = await this.restClient.getStates();
48
+ return states.filter((s) => {
49
+ if (domainFilter && !s.entity_id.startsWith(`${domainFilter}.`)) {
50
+ return false;
51
+ }
52
+ if (searchQuery) {
53
+ const q = searchQuery.toLowerCase();
54
+ const entityMatches = s.entity_id.toLowerCase().includes(q);
55
+ const nameMatches = String(s.attributes?.friendly_name || "").toLowerCase().includes(q);
56
+ if (!entityMatches && !nameMatches) {
57
+ return false;
58
+ }
59
+ }
60
+ return true;
61
+ });
62
+ }
63
+ async getLogs(lines = 100) {
64
+ return this.addonClient.getLogs(lines);
65
+ }
66
+ async createBackup(label = "manual-checkpoint") {
67
+ const res = await this.addonClient.writeFile("automations.yaml", "", {
68
+ validateYaml: false,
69
+ label,
70
+ });
71
+ return {
72
+ snapshot_id: res.snapshot_id,
73
+ label,
74
+ };
75
+ }
76
+ async listSnapshots() {
77
+ return this.addonClient.listSnapshots();
78
+ }
79
+ async restoreBackup(snapshotId) {
80
+ return this.addonClient.restoreSnapshot(snapshotId);
81
+ }
82
+ }
83
+ //# sourceMappingURL=system.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.service.js","sourceRoot":"","sources":["../../src/services/system.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import type { ToolClients, McpToolResult } from "./types.js";
4
+ export declare const listAutomationsSchema: {
5
+ domain: z.ZodOptional<z.ZodEnum<["automation", "script", "scene"]>>;
6
+ };
7
+ export declare const readAutomationSchema: {
8
+ automation_id: z.ZodString;
9
+ };
10
+ export declare const writeAutomationSchema: {
11
+ automation_id: z.ZodString;
12
+ yaml_code: z.ZodString;
13
+ label: z.ZodOptional<z.ZodString>;
14
+ };
15
+ export declare const triggerAutomationSchema: {
16
+ entity_id: z.ZodString;
17
+ };
18
+ export declare function handleAutomationList(clients: ToolClients, args: {
19
+ domain?: "automation" | "script" | "scene";
20
+ }): Promise<McpToolResult>;
21
+ export declare function handleAutomationRead(clients: ToolClients, args: {
22
+ automation_id: string;
23
+ }): Promise<McpToolResult>;
24
+ export declare function handleAutomationWrite(clients: ToolClients, args: {
25
+ automation_id: string;
26
+ yaml_code: string;
27
+ label?: string;
28
+ }): Promise<McpToolResult>;
29
+ export declare function handleAutomationTrigger(clients: ToolClients, args: {
30
+ entity_id: string;
31
+ }): Promise<McpToolResult>;
32
+ export declare function registerAutomationTools(server: McpServer, clients: ToolClients): void;
33
+ //# sourceMappingURL=automation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automation.d.ts","sourceRoot":"","sources":["../../src/tools/automation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7D,eAAO,MAAM,qBAAqB;;CAKjC,CAAC;AAEF,eAAO,MAAM,oBAAoB;;CAIhC,CAAC;AAEF,eAAO,MAAM,qBAAqB;;;;CAWjC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;CAInC,CAAC;AAuCF,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAA;CAAE,GACnD,OAAO,CAAC,aAAa,CAAC,CAoCxB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAC9B,OAAO,CAAC,aAAa,CAAC,CAuCxB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACjE,OAAO,CAAC,aAAa,CAAC,CAqExB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAoCrF"}
@@ -0,0 +1,246 @@
1
+ import { z } from "zod";
2
+ export const listAutomationsSchema = {
3
+ domain: z
4
+ .enum(["automation", "script", "scene"])
5
+ .optional()
6
+ .describe("Domain to list (defaults to 'automation')"),
7
+ };
8
+ export const readAutomationSchema = {
9
+ automation_id: z
10
+ .string()
11
+ .describe("Automation ID or alias to locate in automations.yaml"),
12
+ };
13
+ export const writeAutomationSchema = {
14
+ automation_id: z
15
+ .string()
16
+ .describe("Automation ID to insert or update"),
17
+ yaml_code: z
18
+ .string()
19
+ .describe("YAML configuration block for the automation"),
20
+ label: z
21
+ .string()
22
+ .optional()
23
+ .describe("Optional snapshot label for safety backup"),
24
+ };
25
+ export const triggerAutomationSchema = {
26
+ entity_id: z
27
+ .string()
28
+ .describe("Automation entity_id to trigger (e.g. 'automation.evening_lights')"),
29
+ };
30
+ function splitYamlBlocks(yamlContent) {
31
+ if (!yamlContent.trim()) {
32
+ return [];
33
+ }
34
+ const lines = yamlContent.split("\n");
35
+ const blocks = [];
36
+ let currentBlock = [];
37
+ for (const line of lines) {
38
+ if (line.startsWith("- ") && currentBlock.length > 0) {
39
+ blocks.push(currentBlock.join("\n").trim());
40
+ currentBlock = [line];
41
+ }
42
+ else {
43
+ currentBlock.push(line);
44
+ }
45
+ }
46
+ if (currentBlock.length > 0) {
47
+ const trimmed = currentBlock.join("\n").trim();
48
+ if (trimmed) {
49
+ blocks.push(trimmed);
50
+ }
51
+ }
52
+ return blocks;
53
+ }
54
+ function escapeRegex(str) {
55
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
56
+ }
57
+ function blockMatchesIdOrAlias(block, targetId) {
58
+ const idRegex = new RegExp(`(^|\\n)\\s*-\\s*id:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)|(^|\\n)\\s*id:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)`, "i");
59
+ const aliasRegex = new RegExp(`(^|\\n)\\s*alias:\\s*['"]?${escapeRegex(targetId)}['"]?(\\s|$)`, "i");
60
+ return idRegex.test(block) || aliasRegex.test(block);
61
+ }
62
+ export async function handleAutomationList(clients, args) {
63
+ try {
64
+ const domain = args.domain || "automation";
65
+ const states = await clients.restClient.getStates();
66
+ const filtered = states
67
+ .filter((s) => s.entity_id.startsWith(`${domain}.`))
68
+ .map((s) => ({
69
+ entity_id: s.entity_id,
70
+ state: s.state,
71
+ friendly_name: s.attributes?.friendly_name || s.entity_id,
72
+ last_triggered: s.attributes?.last_triggered || null,
73
+ description: s.attributes?.description,
74
+ mode: s.attributes?.mode,
75
+ current: s.attributes?.current,
76
+ }));
77
+ return {
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: JSON.stringify(filtered, null, 2),
82
+ },
83
+ ],
84
+ };
85
+ }
86
+ catch (error) {
87
+ return {
88
+ isError: true,
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: `Failed to list automations: ${error.message || String(error)}`,
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ }
98
+ export async function handleAutomationRead(clients, args) {
99
+ try {
100
+ const fileRes = await clients.addonClient.readFile("automations.yaml");
101
+ const blocks = splitYamlBlocks(fileRes.content);
102
+ const targetId = args.automation_id.trim();
103
+ const matchedBlock = blocks.find((b) => blockMatchesIdOrAlias(b, targetId));
104
+ if (!matchedBlock) {
105
+ return {
106
+ isError: true,
107
+ content: [
108
+ {
109
+ type: "text",
110
+ text: `Automation "${targetId}" not found in automations.yaml`,
111
+ },
112
+ ],
113
+ };
114
+ }
115
+ return {
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: matchedBlock,
120
+ },
121
+ ],
122
+ };
123
+ }
124
+ catch (error) {
125
+ return {
126
+ isError: true,
127
+ content: [
128
+ {
129
+ type: "text",
130
+ text: `Failed to read automation: ${error.message || String(error)}`,
131
+ },
132
+ ],
133
+ };
134
+ }
135
+ }
136
+ export async function handleAutomationWrite(clients, args) {
137
+ try {
138
+ let existingContent = "";
139
+ try {
140
+ const fileRes = await clients.addonClient.readFile("automations.yaml");
141
+ existingContent = fileRes.content;
142
+ }
143
+ catch {
144
+ existingContent = "";
145
+ }
146
+ const blocks = splitYamlBlocks(existingContent);
147
+ const targetId = args.automation_id.trim();
148
+ let formattedNewBlock = args.yaml_code.trim();
149
+ if (!formattedNewBlock.startsWith("- ")) {
150
+ const lines = formattedNewBlock.split("\n");
151
+ const firstLine = `- ${lines[0]}`;
152
+ const restLines = lines.slice(1).map((l) => (l.trim() ? ` ${l}` : l));
153
+ formattedNewBlock = [firstLine, ...restLines].join("\n");
154
+ }
155
+ const existingIndex = blocks.findIndex((b) => blockMatchesIdOrAlias(b, targetId));
156
+ if (existingIndex >= 0) {
157
+ blocks[existingIndex] = formattedNewBlock;
158
+ }
159
+ else {
160
+ blocks.push(formattedNewBlock);
161
+ }
162
+ const updatedYaml = blocks.join("\n\n") + "\n";
163
+ const label = args.label || `Update automation ${targetId}`;
164
+ const writeRes = await clients.addonClient.writeFile("automations.yaml", updatedYaml, {
165
+ validateYaml: true,
166
+ label,
167
+ });
168
+ try {
169
+ await clients.restClient.callService("automation", "reload");
170
+ }
171
+ catch (reloadErr) {
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: `Automation "${targetId}" written to automations.yaml (Snapshot: ${writeRes.snapshot_id}), but service reload failed: ${reloadErr.message}`,
177
+ },
178
+ ],
179
+ };
180
+ }
181
+ return {
182
+ content: [
183
+ {
184
+ type: "text",
185
+ text: `Automation "${targetId}" successfully saved and automations reloaded. Snapshot ID: ${writeRes.snapshot_id}.`,
186
+ },
187
+ ],
188
+ };
189
+ }
190
+ catch (error) {
191
+ return {
192
+ isError: true,
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: `Failed to write automation: ${error.message || String(error)}`,
197
+ },
198
+ ],
199
+ };
200
+ }
201
+ }
202
+ export async function handleAutomationTrigger(clients, args) {
203
+ try {
204
+ const entityId = args.entity_id.trim();
205
+ const domain = entityId.split(".")[0] || "automation";
206
+ await clients.restClient.callService(domain, "trigger", { entity_id: entityId });
207
+ return {
208
+ content: [
209
+ {
210
+ type: "text",
211
+ text: `Successfully triggered ${entityId}`,
212
+ },
213
+ ],
214
+ };
215
+ }
216
+ catch (error) {
217
+ return {
218
+ isError: true,
219
+ content: [
220
+ {
221
+ type: "text",
222
+ text: `Failed to trigger ${args.entity_id}: ${error.message || String(error)}`,
223
+ },
224
+ ],
225
+ };
226
+ }
227
+ }
228
+ export function registerAutomationTools(server, clients) {
229
+ server.registerTool("ha_automation_list", {
230
+ description: "List all Home Assistant automations, scripts, or scenes with their current states.",
231
+ inputSchema: listAutomationsSchema,
232
+ }, async (args) => handleAutomationList(clients, args));
233
+ server.registerTool("ha_automation_read", {
234
+ description: "Read the YAML configuration block of a specific automation from automations.yaml.",
235
+ inputSchema: readAutomationSchema,
236
+ }, async (args) => handleAutomationRead(clients, args));
237
+ server.registerTool("ha_automation_write", {
238
+ description: "Safely create or update an automation in automations.yaml with automated backup snapshot and service reload.",
239
+ inputSchema: writeAutomationSchema,
240
+ }, async (args) => handleAutomationWrite(clients, args));
241
+ server.registerTool("ha_automation_trigger", {
242
+ description: "Trigger a Home Assistant automation or script entity immediately.",
243
+ inputSchema: triggerAutomationSchema,
244
+ }, async (args) => handleAutomationTrigger(clients, args));
245
+ }
246
+ //# sourceMappingURL=automation.js.map