@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.3.1

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 (281) hide show
  1. package/.env.example +1 -1
  2. package/README.md +11 -2
  3. package/dist/omadaClient/client.d.ts +6 -1
  4. package/dist/omadaClient/client.js +12 -1
  5. package/dist/omadaClient/client.js.map +1 -1
  6. package/dist/omadaClient/diagnose.d.ts +68 -0
  7. package/dist/omadaClient/diagnose.js +164 -0
  8. package/dist/omadaClient/diagnose.js.map +1 -0
  9. package/dist/omadaClient/index.d.ts +6 -0
  10. package/dist/omadaClient/index.js +15 -0
  11. package/dist/omadaClient/index.js.map +1 -1
  12. package/dist/omadaClient/macGroups.d.ts +58 -0
  13. package/dist/omadaClient/macGroups.js +124 -0
  14. package/dist/omadaClient/macGroups.js.map +1 -0
  15. package/dist/tools/infra/actions.d.ts +1 -1
  16. package/dist/tools/infra/actions.js +1 -1
  17. package/dist/tools/omada/index.js +6 -0
  18. package/dist/tools/omada/index.js.map +1 -1
  19. package/dist/tools/omada/macGroups.d.ts +19 -0
  20. package/dist/tools/omada/macGroups.js +77 -0
  21. package/dist/tools/omada/macGroups.js.map +1 -0
  22. package/dist/tools/omada/namespace.js +8 -0
  23. package/dist/tools/omada/namespace.js.map +1 -1
  24. package/dist/version.d.ts +1 -1
  25. package/dist/version.js +1 -1
  26. package/package.json +5 -1
  27. package/.codex/review-rules.md +0 -16
  28. package/.dockerignore +0 -8
  29. package/.gitattributes +0 -30
  30. package/.github/workflows/ci.yml +0 -193
  31. package/AGENTS.md +0 -389
  32. package/Dockerfile +0 -50
  33. package/docker-compose.yml +0 -23
  34. package/docs/CHANGELOG.md +0 -341
  35. package/docs/COVERAGE.md +0 -219
  36. package/docs/QUICK_START.md +0 -240
  37. package/eslint.config.js +0 -33
  38. package/n8n/ai-agent-safety-monitor.json +0 -355
  39. package/openapi.json +0 -1259
  40. package/scripts/check-version-sync.mjs +0 -69
  41. package/scripts/generate-jwt.ts +0 -121
  42. package/src/config.ts +0 -440
  43. package/src/haClient/automations.ts +0 -261
  44. package/src/haClient/calendars.ts +0 -94
  45. package/src/haClient/config.ts +0 -52
  46. package/src/haClient/devices.ts +0 -653
  47. package/src/haClient/entities.ts +0 -128
  48. package/src/haClient/events.ts +0 -689
  49. package/src/haClient/history.ts +0 -73
  50. package/src/haClient/index.ts +0 -336
  51. package/src/haClient/request.ts +0 -142
  52. package/src/haClient/services.ts +0 -45
  53. package/src/haClient/states.ts +0 -60
  54. package/src/haClient/updates.ts +0 -58
  55. package/src/haClient/wsCommand.ts +0 -44
  56. package/src/index.ts +0 -164
  57. package/src/localAI/index.ts +0 -88
  58. package/src/localAI/prompts.ts +0 -50
  59. package/src/localAI/providers/base.ts +0 -120
  60. package/src/localAI/providers/ollama.ts +0 -78
  61. package/src/localAI/providers/openai.ts +0 -86
  62. package/src/localAI/types.ts +0 -67
  63. package/src/omadaClient/auth.ts +0 -178
  64. package/src/omadaClient/client.ts +0 -337
  65. package/src/omadaClient/device.ts +0 -118
  66. package/src/omadaClient/index.ts +0 -310
  67. package/src/omadaClient/network.ts +0 -284
  68. package/src/omadaClient/request.ts +0 -365
  69. package/src/omadaClient/security.ts +0 -51
  70. package/src/omadaClient/site.ts +0 -344
  71. package/src/ownerQuestions/service.ts +0 -536
  72. package/src/permissions/index.ts +0 -143
  73. package/src/resources/index.ts +0 -213
  74. package/src/server/auth.ts +0 -175
  75. package/src/server/chatFace.ts +0 -789
  76. package/src/server/chatSlice.ts +0 -57
  77. package/src/server/common.ts +0 -170
  78. package/src/server/eventSubscription.ts +0 -343
  79. package/src/server/http.ts +0 -626
  80. package/src/server/instructions.ts +0 -97
  81. package/src/server/rateLimiter.ts +0 -227
  82. package/src/server/stdio.ts +0 -41
  83. package/src/server/stream.ts +0 -205
  84. package/src/tools/ai/analyzeSensors.ts +0 -33
  85. package/src/tools/ai/index.ts +0 -20
  86. package/src/tools/common.ts +0 -442
  87. package/src/tools/homeassistant/askOwner.ts +0 -133
  88. package/src/tools/homeassistant/automationControls.ts +0 -66
  89. package/src/tools/homeassistant/calendar.ts +0 -97
  90. package/src/tools/homeassistant/callService.ts +0 -25
  91. package/src/tools/homeassistant/controlClimate.ts +0 -68
  92. package/src/tools/homeassistant/controlCover.ts +0 -48
  93. package/src/tools/homeassistant/controlFan.ts +0 -44
  94. package/src/tools/homeassistant/controlLight.ts +0 -38
  95. package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
  96. package/src/tools/homeassistant/createAutomation.ts +0 -35
  97. package/src/tools/homeassistant/deleteAutomation.ts +0 -24
  98. package/src/tools/homeassistant/entityAction.ts +0 -35
  99. package/src/tools/homeassistant/entityGuard.ts +0 -28
  100. package/src/tools/homeassistant/getAllSensors.ts +0 -46
  101. package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
  102. package/src/tools/homeassistant/getDomainSummary.ts +0 -29
  103. package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
  104. package/src/tools/homeassistant/getHistory.ts +0 -24
  105. package/src/tools/homeassistant/getState.ts +0 -24
  106. package/src/tools/homeassistant/getStates.ts +0 -46
  107. package/src/tools/homeassistant/getVersion.ts +0 -21
  108. package/src/tools/homeassistant/index.ts +0 -121
  109. package/src/tools/homeassistant/listAutomations.ts +0 -35
  110. package/src/tools/homeassistant/listEntities.ts +0 -44
  111. package/src/tools/homeassistant/listPersons.ts +0 -46
  112. package/src/tools/homeassistant/sceneAndScript.ts +0 -53
  113. package/src/tools/homeassistant/searchEntities.ts +0 -58
  114. package/src/tools/homeassistant/sendNotification.ts +0 -179
  115. package/src/tools/homeassistant/systemTools.ts +0 -52
  116. package/src/tools/homeassistant/triggerAutomation.ts +0 -24
  117. package/src/tools/index.ts +0 -78
  118. package/src/tools/infra/actions.ts +0 -95
  119. package/src/tools/infra/index.ts +0 -138
  120. package/src/tools/omada/blockClient.ts +0 -28
  121. package/src/tools/omada/cyclePoePort.ts +0 -32
  122. package/src/tools/omada/deleteClient.ts +0 -27
  123. package/src/tools/omada/disableClientRateLimit.ts +0 -23
  124. package/src/tools/omada/getClient.ts +0 -14
  125. package/src/tools/omada/getDevice.ts +0 -14
  126. package/src/tools/omada/getFirewallSetting.ts +0 -14
  127. package/src/tools/omada/getInternetInfo.ts +0 -14
  128. package/src/tools/omada/getLanNetworkList.ts +0 -14
  129. package/src/tools/omada/getLanProfileList.ts +0 -14
  130. package/src/tools/omada/getPortForwardingStatus.ts +0 -26
  131. package/src/tools/omada/getRateLimitProfiles.ts +0 -14
  132. package/src/tools/omada/getSiteNtpStatus.ts +0 -14
  133. package/src/tools/omada/getSsidDetail.ts +0 -22
  134. package/src/tools/omada/getSsidList.ts +0 -21
  135. package/src/tools/omada/getSwitchStackDetail.ts +0 -14
  136. package/src/tools/omada/getThreatList.ts +0 -42
  137. package/src/tools/omada/getWlanGroupList.ts +0 -14
  138. package/src/tools/omada/graph.ts +0 -187
  139. package/src/tools/omada/index.ts +0 -153
  140. package/src/tools/omada/listBlockedClients.ts +0 -17
  141. package/src/tools/omada/listClients.ts +0 -14
  142. package/src/tools/omada/listClientsActivity.ts +0 -25
  143. package/src/tools/omada/listClientsPastConnections.ts +0 -44
  144. package/src/tools/omada/listDevices.ts +0 -14
  145. package/src/tools/omada/listDevicesStats.ts +0 -41
  146. package/src/tools/omada/listMostActiveClients.ts +0 -14
  147. package/src/tools/omada/listSites.ts +0 -18
  148. package/src/tools/omada/namespace.ts +0 -1164
  149. package/src/tools/omada/searchDevices.ts +0 -20
  150. package/src/tools/omada/setClientRateLimit.ts +0 -26
  151. package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
  152. package/src/tools/omada/setSiteNtpServers.ts +0 -36
  153. package/src/tools/omada/setSsidEnabled.ts +0 -38
  154. package/src/tools/omada/unblockClient.ts +0 -26
  155. package/src/tools/types.ts +0 -11
  156. package/src/tools/utils/pagination-schema.ts +0 -27
  157. package/src/types/ai/index.ts +0 -11
  158. package/src/types/ai/types.ts +0 -67
  159. package/src/types/homeassistant/automations.ts +0 -43
  160. package/src/types/homeassistant/calendars.ts +0 -26
  161. package/src/types/homeassistant/config.ts +0 -14
  162. package/src/types/homeassistant/entities.ts +0 -14
  163. package/src/types/homeassistant/errors.ts +0 -33
  164. package/src/types/homeassistant/haConfig.ts +0 -7
  165. package/src/types/homeassistant/history.ts +0 -13
  166. package/src/types/homeassistant/index.ts +0 -46
  167. package/src/types/homeassistant/request.ts +0 -6
  168. package/src/types/homeassistant/serviceCall.ts +0 -18
  169. package/src/types/homeassistant/states.ts +0 -58
  170. package/src/types/homeassistant/updates.ts +0 -28
  171. package/src/types/index.ts +0 -12
  172. package/src/types/omada/activeClientInfo.ts +0 -17
  173. package/src/types/omada/clientActivity.ts +0 -32
  174. package/src/types/omada/clientBlockStatus.ts +0 -11
  175. package/src/types/omada/clientPastConnection.ts +0 -82
  176. package/src/types/omada/index.ts +0 -14
  177. package/src/types/omada/omadaApiResponse.ts +0 -5
  178. package/src/types/omada/omadaClientInfo.ts +0 -11
  179. package/src/types/omada/omadaDeviceInfo.ts +0 -11
  180. package/src/types/omada/omadaDeviceStats.ts +0 -31
  181. package/src/types/omada/omadaSiteSummary.ts +0 -5
  182. package/src/types/omada/oswStackDetail.ts +0 -11
  183. package/src/types/omada/paginatedResult.ts +0 -6
  184. package/src/types/omada/rateLimitProfile.ts +0 -58
  185. package/src/types/omada/threatInfo.ts +0 -38
  186. package/src/types/omada/tokenResult.ts +0 -6
  187. package/src/types.ts +0 -2
  188. package/src/utils/config-validations.ts +0 -68
  189. package/src/utils/jwt.ts +0 -112
  190. package/src/utils/logger.ts +0 -121
  191. package/src/utils/plainText.ts +0 -48
  192. package/src/utils/redact.ts +0 -59
  193. package/src/utils/sanitizeError.ts +0 -18
  194. package/src/utils/tlsDispatcher.ts +0 -69
  195. package/src/version.ts +0 -5
  196. package/tests/bdd/automation-creation.spec.ts +0 -214
  197. package/tests/bdd/automation-management.spec.ts +0 -267
  198. package/tests/bdd/calendar.spec.ts +0 -190
  199. package/tests/bdd/configuration.spec.ts +0 -283
  200. package/tests/bdd/device-control.spec.ts +0 -344
  201. package/tests/bdd/entity-listing.spec.ts +0 -208
  202. package/tests/bdd/entity-search.spec.ts +0 -183
  203. package/tests/bdd/history-retrieval.spec.ts +0 -195
  204. package/tests/bdd/notifications.spec.ts +0 -238
  205. package/tests/bdd/service-calls.spec.ts +0 -144
  206. package/tests/bdd/state-retrieval.spec.ts +0 -204
  207. package/tests/bench/hotpaths.bench.ts +0 -68
  208. package/tests/config.test.ts +0 -355
  209. package/tests/features/automation-creation.feature +0 -36
  210. package/tests/features/automation-management.feature +0 -54
  211. package/tests/features/calendar.feature +0 -37
  212. package/tests/features/configuration.feature +0 -70
  213. package/tests/features/device-control.feature +0 -100
  214. package/tests/features/entity-listing.feature +0 -51
  215. package/tests/features/entity-search.feature +0 -44
  216. package/tests/features/history-retrieval.feature +0 -38
  217. package/tests/features/notifications.feature +0 -37
  218. package/tests/features/service-calls.feature +0 -35
  219. package/tests/features/state-retrieval.feature +0 -37
  220. package/tests/fixtures/openapi-default-slice.json +0 -290
  221. package/tests/haClient/automationTrace.test.ts +0 -59
  222. package/tests/haClient/automations.test.ts +0 -149
  223. package/tests/haClient/config.test.ts +0 -117
  224. package/tests/haClient/entities.test.ts +0 -188
  225. package/tests/haClient/events.test.ts +0 -455
  226. package/tests/haClient/eventsTls.test.ts +0 -52
  227. package/tests/haClient/history.test.ts +0 -228
  228. package/tests/haClient/index.test.ts +0 -243
  229. package/tests/haClient/request.test.ts +0 -217
  230. package/tests/haClient/services.test.ts +0 -120
  231. package/tests/haClient/states.test.ts +0 -119
  232. package/tests/haClient/updates.test.ts +0 -193
  233. package/tests/index.test.ts +0 -160
  234. package/tests/integration/homeassistant.test.ts +0 -144
  235. package/tests/integration/omada.test.ts +0 -167
  236. package/tests/integration/server.test.ts +0 -151
  237. package/tests/localAI/index.test.ts +0 -177
  238. package/tests/localAI/ollama.test.ts +0 -187
  239. package/tests/localAI/openai.test.ts +0 -257
  240. package/tests/omadaClient/actions.test.ts +0 -161
  241. package/tests/omadaClient/clientBlocks.test.ts +0 -170
  242. package/tests/omadaClient/ntp.test.ts +0 -381
  243. package/tests/omadaClient/site.test.ts +0 -37
  244. package/tests/ownerQuestions/service.test.ts +0 -499
  245. package/tests/ownerQuestions/tools.test.ts +0 -186
  246. package/tests/permissions/index.test.ts +0 -217
  247. package/tests/resources/index.test.ts +0 -288
  248. package/tests/scripts/generateJwt.test.ts +0 -44
  249. package/tests/security/accessControl.test.ts +0 -223
  250. package/tests/security/authentication.test.ts +0 -117
  251. package/tests/security/configuration.test.ts +0 -170
  252. package/tests/security/cryptoTransport.test.ts +0 -172
  253. package/tests/security/dependencies.test.ts +0 -152
  254. package/tests/security/injection.test.ts +0 -117
  255. package/tests/security/insecureDesign.test.ts +0 -121
  256. package/tests/security/logging.test.ts +0 -131
  257. package/tests/security/resourceConsumption.test.ts +0 -142
  258. package/tests/server/auth.test.ts +0 -249
  259. package/tests/server/chatFace.test.ts +0 -320
  260. package/tests/server/common.test.ts +0 -84
  261. package/tests/server/http.test.ts +0 -561
  262. package/tests/server/instructions.test.ts +0 -152
  263. package/tests/server/memoizeJsonSchema.test.ts +0 -71
  264. package/tests/server/stdio.test.ts +0 -99
  265. package/tests/server/stream.test.ts +0 -285
  266. package/tests/server/toolVisibility.http.test.ts +0 -111
  267. package/tests/server/toolVisibility.test.ts +0 -129
  268. package/tests/tools/ai/analyzeSensors.test.ts +0 -227
  269. package/tests/tools/common.test.ts +0 -123
  270. package/tests/tools/homeassistant/handlers.test.ts +0 -872
  271. package/tests/tools/infra/invokeAction.test.ts +0 -280
  272. package/tests/tools/omada/graph.test.ts +0 -385
  273. package/tests/tools/omada/handlers.test.ts +0 -897
  274. package/tests/tools/omada/namespace.test.ts +0 -483
  275. package/tests/tools/registry.test.ts +0 -168
  276. package/tests/utils/config-validations.test.ts +0 -352
  277. package/tests/utils/jwt.test.ts +0 -115
  278. package/tests/utils/logger.test.ts +0 -327
  279. package/tests/utils/redact.test.ts +0 -105
  280. package/tsconfig.json +0 -24
  281. package/vitest.config.ts +0 -38
@@ -1,73 +0,0 @@
1
- /**
2
- * History Operations for Home Assistant API
3
- * Handles historical data and system logs
4
- */
5
-
6
- import type { Entity, LogbookEntry } from '../types/index.js';
7
- import { logger } from '../utils/logger.js';
8
- import type { RequestHandler } from './request.js';
9
-
10
- /**
11
- * History-related operations for the Home Assistant API.
12
- */
13
- export class HistoryOperations {
14
- constructor(private readonly request: RequestHandler) {}
15
-
16
- /**
17
- * Get historical data for an entity
18
- */
19
- async getHistory(entityId: string, hours: number = 24): Promise<Entity[][]> {
20
- logger.debug('Fetching history', { entityId, hours });
21
-
22
- const endTime = new Date();
23
- const startTime = new Date(endTime.getTime() - hours * 60 * 60 * 1000);
24
-
25
- const path = `/history/period/${startTime.toISOString()}`;
26
- const history = await this.request.get<Entity[][]>(path, {
27
- filter_entity_id: entityId,
28
- end_time: endTime.toISOString(),
29
- });
30
-
31
- logger.info('Fetched history', {
32
- entityId,
33
- hours,
34
- records: history[0]?.length ?? 0
35
- });
36
-
37
- return history;
38
- }
39
-
40
- /**
41
- * Get system log entries from the logbook
42
- * Returns recent events/state changes, optionally filtered by entity
43
- */
44
- async getSystemLog(options: {
45
- hours?: number;
46
- entity_id?: string;
47
- limit?: number;
48
- } = {}): Promise<LogbookEntry[]> {
49
- const hours = Math.min(options.hours || 24, 168); // Cap at 1 week
50
- const limit = options.limit || 100;
51
- logger.debug('Fetching system log', { hours, entity_id: options.entity_id, limit });
52
-
53
- const endTime = new Date();
54
- const startTime = new Date(endTime.getTime() - hours * 60 * 60 * 1000);
55
-
56
- const path = `/logbook/${startTime.toISOString()}`;
57
- const params: Record<string, string> = {
58
- end_time: endTime.toISOString(),
59
- };
60
-
61
- if (options.entity_id) {
62
- params.entity = options.entity_id;
63
- }
64
-
65
- const entries = await this.request.get<LogbookEntry[]>(path, params);
66
-
67
- // Apply limit - return most recent entries first
68
- const limited = entries.slice(-limit);
69
-
70
- logger.info('Fetched system log', { count: entries.length, returned: limited.length, hours, limit });
71
- return limited;
72
- }
73
- }
@@ -1,336 +0,0 @@
1
- /**
2
- * Home Assistant API Client
3
- * Main client class that composes all operation modules
4
- */
5
-
6
- import type {
7
- Entity,
8
- ServiceCallData,
9
- ServiceCallResponse,
10
- Automation,
11
- AutomationConfig,
12
- DomainSummary,
13
- HaVersion,
14
- LogbookEntry,
15
- UpdateInfo,
16
- Calendar,
17
- CalendarEvent,
18
- } from '../types/index.js';
19
- import { logger } from '../utils/logger.js';
20
-
21
- import { AutomationOperations, type AutomationTraceSummary } from './automations.js';
22
- import { createHaWebSocketCommand } from './wsCommand.js';
23
- import { ConfigOperations } from './config.js';
24
- import { DeviceOperations } from './devices.js';
25
- import { EntityOperations } from './entities.js';
26
- import { HistoryOperations } from './history.js';
27
- import { RequestHandler } from './request.js';
28
- import { ServiceOperations } from './services.js';
29
- import { StateOperations } from './states.js';
30
- import { UpdateOperations } from './updates.js';
31
- import { CalendarOperations } from './calendars.js';
32
-
33
- export interface HaClientOptions {
34
- baseUrl: string;
35
- token: string;
36
- timeout: number;
37
- strictSsl: boolean;
38
- }
39
-
40
- /**
41
- * Main client for interacting with the Home Assistant API.
42
- * Organized with dedicated operation classes for each domain.
43
- */
44
- export class HaClient {
45
- private readonly request: RequestHandler;
46
- private readonly stateOps: StateOperations;
47
- private readonly serviceOps: ServiceOperations;
48
- private readonly entityOps: EntityOperations;
49
- private readonly automationOps: AutomationOperations;
50
- private readonly historyOps: HistoryOperations;
51
- private readonly updateOps: UpdateOperations;
52
- private readonly configOps: ConfigOperations;
53
- private readonly calendarOps: CalendarOperations;
54
- public readonly devices: DeviceOperations;
55
-
56
- constructor(config: HaClientOptions) {
57
- logger.debug('Initializing HaClient');
58
-
59
- // Initialize request handler
60
- this.request = new RequestHandler({
61
- baseUrl: config.baseUrl,
62
- token: config.token,
63
- timeout: config.timeout,
64
- strictSsl: config.strictSsl,
65
- });
66
-
67
- // Initialize operation modules with dependencies
68
- this.stateOps = new StateOperations(this.request);
69
- this.serviceOps = new ServiceOperations(this.request);
70
- this.entityOps = new EntityOperations(this.stateOps);
71
- this.automationOps = new AutomationOperations(
72
- this.stateOps,
73
- this.serviceOps,
74
- this.request,
75
- createHaWebSocketCommand({ baseUrl: config.baseUrl, token: config.token, strictSsl: config.strictSsl })
76
- );
77
- this.historyOps = new HistoryOperations(this.request);
78
- this.updateOps = new UpdateOperations(this.entityOps);
79
- this.configOps = new ConfigOperations(this.request);
80
- this.calendarOps = new CalendarOperations(this.request);
81
- this.devices = new DeviceOperations(this.serviceOps, this.stateOps);
82
-
83
- logger.info('HaClient initialized');
84
- }
85
-
86
- // ===== State Operations =====
87
-
88
- /**
89
- * Get all states (entities)
90
- */
91
- async getStates(): Promise<Entity[]> {
92
- return this.stateOps.getStates();
93
- }
94
-
95
- /**
96
- * Get state of a specific entity
97
- */
98
- async getState(entityId: string): Promise<Entity | null> {
99
- return this.stateOps.getState(entityId);
100
- }
101
-
102
- /**
103
- * Get all sensors (sensor.* and binary_sensor.* entities)
104
- */
105
- async getAllSensors(): Promise<Entity[]> {
106
- return this.stateOps.getAllSensors();
107
- }
108
-
109
- // ===== Service Operations =====
110
-
111
- /**
112
- * Call a service
113
- */
114
- async callService(data: ServiceCallData): Promise<ServiceCallResponse> {
115
- return this.serviceOps.callService(data);
116
- }
117
-
118
- /**
119
- * Restart Home Assistant
120
- */
121
- async restartServer(): Promise<void> {
122
- return this.serviceOps.restartServer();
123
- }
124
-
125
- // ===== Entity Operations =====
126
-
127
- /**
128
- * Get entities filtered by domain
129
- */
130
- async getEntitiesByDomain(domain: string): Promise<Entity[]> {
131
- return this.entityOps.getEntitiesByDomain(domain);
132
- }
133
-
134
- /**
135
- * Search entities by name or entity_id
136
- */
137
- async searchEntities(query: string): Promise<Entity[]> {
138
- return this.entityOps.searchEntities(query);
139
- }
140
-
141
- /**
142
- * Get domain summary with entity counts and state breakdown
143
- */
144
- async getDomainSummary(domain: string): Promise<DomainSummary> {
145
- return this.entityOps.getDomainSummary(domain);
146
- }
147
-
148
- /**
149
- * List entities with optional filtering
150
- */
151
- async listEntities(options: {
152
- domain?: string;
153
- search?: string;
154
- limit?: number;
155
- state?: string;
156
- } = {}): Promise<Entity[]> {
157
- return this.entityOps.listEntities(options);
158
- }
159
-
160
- // ===== Automation Operations =====
161
-
162
- /**
163
- * Get all automations
164
- */
165
- async getAutomations(): Promise<Automation[]> {
166
- return this.automationOps.getAutomations();
167
- }
168
-
169
- /**
170
- * Get a single automation by entity_id
171
- */
172
- async getAutomation(entityId: string): Promise<Automation | null> {
173
- return this.automationOps.getAutomation(entityId);
174
- }
175
-
176
- /**
177
- * Trigger an automation manually
178
- */
179
- async triggerAutomation(entityId: string, variables?: Record<string, unknown>): Promise<ServiceCallResponse> {
180
- return this.automationOps.triggerAutomation(entityId, variables);
181
- }
182
-
183
- /**
184
- * Enable an automation
185
- */
186
- async enableAutomation(entityId: string): Promise<ServiceCallResponse> {
187
- return this.automationOps.enableAutomation(entityId);
188
- }
189
-
190
- /**
191
- * Disable an automation
192
- */
193
- async disableAutomation(entityId: string): Promise<ServiceCallResponse> {
194
- return this.automationOps.disableAutomation(entityId);
195
- }
196
-
197
- /**
198
- * Toggle an automation
199
- */
200
- async toggleAutomation(entityId: string): Promise<ServiceCallResponse> {
201
- return this.automationOps.toggleAutomation(entityId);
202
- }
203
-
204
- /**
205
- * Reload all automations from configuration
206
- */
207
- async reloadAutomations(): Promise<void> {
208
- return this.automationOps.reloadAutomations();
209
- }
210
-
211
- /**
212
- * Create a new automation
213
- */
214
- async createAutomation(config: AutomationConfig): Promise<{ id: string }> {
215
- return this.automationOps.createAutomation(config);
216
- }
217
-
218
- /**
219
- * Update an existing automation
220
- */
221
- async updateAutomation(automationId: string, config: Partial<AutomationConfig>): Promise<void> {
222
- return this.automationOps.updateAutomation(automationId, config);
223
- }
224
-
225
- /**
226
- * Delete an automation
227
- */
228
- async deleteAutomation(automationId: string): Promise<void> {
229
- return this.automationOps.deleteAutomation(automationId);
230
- }
231
-
232
- /**
233
- * Get automation execution trace
234
- */
235
- async getAutomationTrace(entityId: string, limit?: number): Promise<AutomationTraceSummary[]> {
236
- return this.automationOps.getAutomationTrace(entityId, limit);
237
- }
238
-
239
- // ===== History Operations =====
240
-
241
- /**
242
- * Get historical data for an entity
243
- */
244
- async getHistory(entityId: string, hours: number = 24): Promise<Entity[][]> {
245
- return this.historyOps.getHistory(entityId, hours);
246
- }
247
-
248
- /**
249
- * Get system log entries from the logbook
250
- */
251
- async getSystemLog(options: {
252
- hours?: number;
253
- entity_id?: string;
254
- limit?: number;
255
- } = {}): Promise<LogbookEntry[]> {
256
- return this.historyOps.getSystemLog(options);
257
- }
258
-
259
- // ===== Update Operations =====
260
-
261
- /**
262
- * Get available updates by checking update.* entities
263
- */
264
- async getAvailableUpdates(): Promise<{
265
- updates: UpdateInfo[];
266
- entities: Entity[];
267
- }> {
268
- return this.updateOps.getAvailableUpdates();
269
- }
270
-
271
- // ===== Config Operations =====
272
-
273
- /**
274
- * Get config
275
- */
276
- async getConfig(): Promise<Record<string, unknown>> {
277
- return this.configOps.getConfig();
278
- }
279
-
280
- /**
281
- * Check API (health check)
282
- */
283
- async checkApi(): Promise<{ message: string }> {
284
- return this.configOps.checkApi();
285
- }
286
-
287
- /**
288
- * Get Home Assistant version and configuration info
289
- */
290
- async getVersion(): Promise<HaVersion> {
291
- return this.configOps.getVersion();
292
- }
293
-
294
- // ===== Calendar Operations =====
295
-
296
- /**
297
- * Get all calendar entities
298
- */
299
- async getCalendars(): Promise<Calendar[]> {
300
- return this.calendarOps.getCalendars();
301
- }
302
-
303
- /**
304
- * Get events from a calendar
305
- */
306
- async getCalendarEvents(
307
- entityId: string,
308
- startDate?: Date,
309
- endDate?: Date
310
- ): Promise<CalendarEvent[]> {
311
- return this.calendarOps.getCalendarEvents(entityId, startDate, endDate);
312
- }
313
-
314
- /**
315
- * Get events from all calendars
316
- */
317
- async getAllCalendarEvents(
318
- startDate?: Date,
319
- endDate?: Date
320
- ): Promise<Array<{ calendar: string; events: CalendarEvent[] }>> {
321
- return this.calendarOps.getAllCalendarEvents(startDate, endDate);
322
- }
323
- }
324
-
325
- // Re-export operation classes for testing
326
- export { RequestHandler } from './request.js';
327
- export { StateOperations } from './states.js';
328
- export { ServiceOperations } from './services.js';
329
- export { EntityOperations } from './entities.js';
330
- export { AutomationOperations } from './automations.js';
331
- export { HistoryOperations } from './history.js';
332
- export { UpdateOperations } from './updates.js';
333
- export { ConfigOperations } from './config.js';
334
- export { DeviceOperations } from './devices.js';
335
- export { CalendarOperations } from './calendars.js';
336
- export type { LightControlOptions, ClimateControlOptions, MediaPlayerControlOptions, CoverControlOptions, FanControlOptions } from './devices.js';
@@ -1,142 +0,0 @@
1
- /**
2
- * HTTP Request Handler for Home Assistant API
3
- * Handles authentication and HTTP requests
4
- */
5
-
6
- import type { Dispatcher } from 'undici';
7
- import type { RequestOptions } from '../types/index.js';
8
- import { AuthenticationError, ApiError } from '../types/index.js';
9
- import { logger } from '../utils/logger.js';
10
- import { createTlsDispatcher, tlsAwareFetch, type FetchInitWithDispatcher } from '../utils/tlsDispatcher.js';
11
-
12
- export interface RequestHandlerConfig {
13
- baseUrl: string;
14
- token: string;
15
- timeout: number;
16
- strictSsl: boolean;
17
- }
18
-
19
- /**
20
- * HTTP request handler for Home Assistant API calls.
21
- */
22
- export class RequestHandler {
23
- /** Per-client undici dispatcher; set only when strictSsl is disabled (H2). */
24
- private dispatcher?: Dispatcher;
25
- private baseUrl: string;
26
- private token: string;
27
- private timeout: number;
28
-
29
- constructor(config: RequestHandlerConfig) {
30
- this.baseUrl = config.baseUrl;
31
- this.token = config.token;
32
- this.timeout = config.timeout;
33
-
34
- // Relax TLS validation for THIS client only, via a per-instance undici
35
- // dispatcher. Node's global fetch ignores the legacy `agent` option, so the
36
- // previous https.Agent was silently a no-op for self-signed HA instances.
37
- // baseUrl is passed so plain-http targets never carry a dispatcher (the
38
- // v1.5.5-incident config: http HA + HA_STRICT_SSL=false on a node:26 base).
39
- this.dispatcher = createTlsDispatcher(config.strictSsl, config.baseUrl);
40
- if (this.dispatcher) {
41
- logger.warn('SSL certificate validation is disabled');
42
- }
43
- }
44
-
45
- /**
46
- * Make authenticated request to Home Assistant API
47
- */
48
- async request<T>(path: string, options: RequestOptions = {}): Promise<T> {
49
- const url = new URL(`${this.baseUrl}/api${path}`);
50
-
51
- // H4 defense-in-depth: refuse any path that resolves outside /api/.
52
- // Call sites encode user-controlled segments, but this guard catches any
53
- // raw "../" traversal that would otherwise escape the API namespace.
54
- if (url.pathname !== '/api' && !url.pathname.startsWith('/api/')) {
55
- throw new ApiError('Invalid API path', 400, { path });
56
- }
57
-
58
- // Add query parameters
59
- if (options.params) {
60
- Object.entries(options.params).forEach(([key, value]) => {
61
- url.searchParams.append(key, String(value));
62
- });
63
- }
64
-
65
- const headers: Record<string, string> = {
66
- 'Authorization': `Bearer ${this.token}`,
67
- 'Content-Type': 'application/json',
68
- ...options.headers,
69
- };
70
-
71
- logger.debug('Making API request', {
72
- method: options.method ?? 'GET',
73
- path,
74
- params: options.params,
75
- });
76
-
77
- try {
78
- const init: FetchInitWithDispatcher = {
79
- method: options.method ?? 'GET',
80
- headers,
81
- body: options.body ? JSON.stringify(options.body) : undefined,
82
- signal: AbortSignal.timeout(this.timeout),
83
- };
84
- if (this.dispatcher) {
85
- init.dispatcher = this.dispatcher;
86
- }
87
-
88
- // tlsAwareFetch: dispatcher requests use undici's own fetch (pairing rule).
89
- const response = await tlsAwareFetch(url.toString(), init);
90
-
91
- if (!response.ok) {
92
- const errorText = await response.text();
93
-
94
- if (response.status === 401) {
95
- throw new AuthenticationError(
96
- 'Invalid Home Assistant token',
97
- { status: response.status, body: errorText }
98
- );
99
- }
100
-
101
- throw new ApiError(
102
- `API request failed: ${response.statusText}`,
103
- response.status,
104
- { path, body: errorText }
105
- );
106
- }
107
-
108
- // Handle empty responses
109
- const contentType = response.headers.get('content-type');
110
- if (!contentType?.includes('application/json')) {
111
- return {} as T;
112
- }
113
-
114
- const data = await response.json();
115
- return data as T;
116
- } catch (error) {
117
- if (error instanceof AuthenticationError || error instanceof ApiError) {
118
- throw error;
119
- }
120
-
121
- throw new ApiError(
122
- `Request failed: ${error instanceof Error ? error.message : String(error)}`,
123
- 500,
124
- { path }
125
- );
126
- }
127
- }
128
-
129
- /**
130
- * Make a GET request
131
- */
132
- async get<T>(path: string, params?: Record<string, string | number>): Promise<T> {
133
- return this.request<T>(path, { params });
134
- }
135
-
136
- /**
137
- * Make a POST request
138
- */
139
- async post<T>(path: string, body?: unknown): Promise<T> {
140
- return this.request<T>(path, { method: 'POST', body });
141
- }
142
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Service Operations for Home Assistant API
3
- * Handles service calls and server management
4
- */
5
-
6
- import type { ServiceCallData, ServiceCallResponse } from '../types/index.js';
7
- import { logger } from '../utils/logger.js';
8
- import type { RequestHandler } from './request.js';
9
-
10
- /**
11
- * Service-related operations for the Home Assistant API.
12
- */
13
- export class ServiceOperations {
14
- constructor(private readonly request: RequestHandler) {}
15
-
16
- /**
17
- * Call a service
18
- */
19
- async callService(data: ServiceCallData): Promise<ServiceCallResponse> {
20
- logger.info('Calling service', {
21
- domain: data.domain,
22
- service: data.service,
23
- hasTarget: !!data.target,
24
- });
25
-
26
- const path = `/services/${encodeURIComponent(data.domain)}/${encodeURIComponent(data.service)}`;
27
- const body = {
28
- ...data.service_data,
29
- ...data.target,
30
- };
31
-
32
- return await this.request.post<ServiceCallResponse>(path, body);
33
- }
34
-
35
- /**
36
- * Restart Home Assistant
37
- */
38
- async restartServer(): Promise<void> {
39
- logger.warn('Restarting Home Assistant');
40
-
41
- await this.request.post('/services/homeassistant/restart', {});
42
-
43
- logger.info('Home Assistant restart initiated');
44
- }
45
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * State Operations for Home Assistant API
3
- * Handles entity state retrieval
4
- */
5
-
6
- import type { Entity } from '../types/index.js';
7
- import { ApiError } from '../types/index.js';
8
- import { logger } from '../utils/logger.js';
9
- import type { RequestHandler } from './request.js';
10
-
11
- /**
12
- * State-related operations for the Home Assistant API.
13
- */
14
- export class StateOperations {
15
- constructor(private readonly request: RequestHandler) {}
16
-
17
- /**
18
- * Get all states (entities)
19
- */
20
- async getStates(): Promise<Entity[]> {
21
- logger.debug('Fetching all states');
22
- const states = await this.request.get<Entity[]>('/states');
23
- logger.info('Fetched states', { count: states.length });
24
- return states;
25
- }
26
-
27
- /**
28
- * Get state of a specific entity
29
- */
30
- async getState(entityId: string): Promise<Entity | null> {
31
- logger.debug('Fetching state', { entityId });
32
-
33
- try {
34
- const state = await this.request.get<Entity>(`/states/${encodeURIComponent(entityId)}`);
35
- return state;
36
- } catch (error) {
37
- if (error instanceof ApiError && error.statusCode === 404) {
38
- logger.warn('Entity not found', { entityId });
39
- return null;
40
- }
41
- throw error;
42
- }
43
- }
44
-
45
- /**
46
- * Get all sensors (sensor.* and binary_sensor.* entities)
47
- */
48
- async getAllSensors(): Promise<Entity[]> {
49
- logger.debug('Fetching all sensors');
50
-
51
- const allStates = await this.getStates();
52
- const sensors = allStates.filter(entity =>
53
- entity.entity_id.startsWith('sensor.') ||
54
- entity.entity_id.startsWith('binary_sensor.')
55
- );
56
-
57
- logger.info('Fetched sensors', { count: sensors.length });
58
- return sensors;
59
- }
60
- }