@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/.env.example +1 -1
  2. package/README.md +24 -2
  3. package/dist/omadaClient/accessControl.d.ts +290 -0
  4. package/dist/omadaClient/accessControl.js +509 -0
  5. package/dist/omadaClient/accessControl.js.map +1 -0
  6. package/dist/omadaClient/client.d.ts +6 -1
  7. package/dist/omadaClient/client.js +12 -1
  8. package/dist/omadaClient/client.js.map +1 -1
  9. package/dist/omadaClient/diagnose.d.ts +68 -0
  10. package/dist/omadaClient/diagnose.js +164 -0
  11. package/dist/omadaClient/diagnose.js.map +1 -0
  12. package/dist/omadaClient/index.d.ts +9 -0
  13. package/dist/omadaClient/index.js +19 -0
  14. package/dist/omadaClient/index.js.map +1 -1
  15. package/dist/omadaClient/macGroups.d.ts +58 -0
  16. package/dist/omadaClient/macGroups.js +110 -0
  17. package/dist/omadaClient/macGroups.js.map +1 -0
  18. package/dist/omadaClient/serialize.d.ts +13 -0
  19. package/dist/omadaClient/serialize.js +29 -0
  20. package/dist/omadaClient/serialize.js.map +1 -0
  21. package/dist/omadaClient/timeRange.d.ts +41 -0
  22. package/dist/omadaClient/timeRange.js +109 -0
  23. package/dist/omadaClient/timeRange.js.map +1 -0
  24. package/dist/tools/infra/actions.d.ts +1 -1
  25. package/dist/tools/infra/actions.js +1 -1
  26. package/dist/tools/omada/accessControl.d.ts +151 -0
  27. package/dist/tools/omada/accessControl.js +158 -0
  28. package/dist/tools/omada/accessControl.js.map +1 -0
  29. package/dist/tools/omada/index.js +9 -0
  30. package/dist/tools/omada/index.js.map +1 -1
  31. package/dist/tools/omada/macGroups.d.ts +19 -0
  32. package/dist/tools/omada/macGroups.js +77 -0
  33. package/dist/tools/omada/macGroups.js.map +1 -0
  34. package/dist/tools/omada/namespace.js +16 -0
  35. package/dist/tools/omada/namespace.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +5 -1
  39. package/.codex/review-rules.md +0 -16
  40. package/.dockerignore +0 -8
  41. package/.gitattributes +0 -30
  42. package/.github/workflows/ci.yml +0 -193
  43. package/AGENTS.md +0 -389
  44. package/Dockerfile +0 -50
  45. package/docker-compose.yml +0 -23
  46. package/docs/CHANGELOG.md +0 -341
  47. package/docs/COVERAGE.md +0 -219
  48. package/docs/QUICK_START.md +0 -240
  49. package/eslint.config.js +0 -33
  50. package/n8n/ai-agent-safety-monitor.json +0 -355
  51. package/openapi.json +0 -1259
  52. package/scripts/check-version-sync.mjs +0 -69
  53. package/scripts/generate-jwt.ts +0 -121
  54. package/src/config.ts +0 -440
  55. package/src/haClient/automations.ts +0 -261
  56. package/src/haClient/calendars.ts +0 -94
  57. package/src/haClient/config.ts +0 -52
  58. package/src/haClient/devices.ts +0 -653
  59. package/src/haClient/entities.ts +0 -128
  60. package/src/haClient/events.ts +0 -689
  61. package/src/haClient/history.ts +0 -73
  62. package/src/haClient/index.ts +0 -336
  63. package/src/haClient/request.ts +0 -142
  64. package/src/haClient/services.ts +0 -45
  65. package/src/haClient/states.ts +0 -60
  66. package/src/haClient/updates.ts +0 -58
  67. package/src/haClient/wsCommand.ts +0 -44
  68. package/src/index.ts +0 -164
  69. package/src/localAI/index.ts +0 -88
  70. package/src/localAI/prompts.ts +0 -50
  71. package/src/localAI/providers/base.ts +0 -120
  72. package/src/localAI/providers/ollama.ts +0 -78
  73. package/src/localAI/providers/openai.ts +0 -86
  74. package/src/localAI/types.ts +0 -67
  75. package/src/omadaClient/auth.ts +0 -178
  76. package/src/omadaClient/client.ts +0 -337
  77. package/src/omadaClient/device.ts +0 -118
  78. package/src/omadaClient/index.ts +0 -310
  79. package/src/omadaClient/network.ts +0 -284
  80. package/src/omadaClient/request.ts +0 -365
  81. package/src/omadaClient/security.ts +0 -51
  82. package/src/omadaClient/site.ts +0 -344
  83. package/src/ownerQuestions/service.ts +0 -536
  84. package/src/permissions/index.ts +0 -143
  85. package/src/resources/index.ts +0 -213
  86. package/src/server/auth.ts +0 -175
  87. package/src/server/chatFace.ts +0 -789
  88. package/src/server/chatSlice.ts +0 -57
  89. package/src/server/common.ts +0 -170
  90. package/src/server/eventSubscription.ts +0 -343
  91. package/src/server/http.ts +0 -626
  92. package/src/server/instructions.ts +0 -97
  93. package/src/server/rateLimiter.ts +0 -227
  94. package/src/server/stdio.ts +0 -41
  95. package/src/server/stream.ts +0 -205
  96. package/src/tools/ai/analyzeSensors.ts +0 -33
  97. package/src/tools/ai/index.ts +0 -20
  98. package/src/tools/common.ts +0 -442
  99. package/src/tools/homeassistant/askOwner.ts +0 -133
  100. package/src/tools/homeassistant/automationControls.ts +0 -66
  101. package/src/tools/homeassistant/calendar.ts +0 -97
  102. package/src/tools/homeassistant/callService.ts +0 -25
  103. package/src/tools/homeassistant/controlClimate.ts +0 -68
  104. package/src/tools/homeassistant/controlCover.ts +0 -48
  105. package/src/tools/homeassistant/controlFan.ts +0 -44
  106. package/src/tools/homeassistant/controlLight.ts +0 -38
  107. package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
  108. package/src/tools/homeassistant/createAutomation.ts +0 -35
  109. package/src/tools/homeassistant/deleteAutomation.ts +0 -24
  110. package/src/tools/homeassistant/entityAction.ts +0 -35
  111. package/src/tools/homeassistant/entityGuard.ts +0 -28
  112. package/src/tools/homeassistant/getAllSensors.ts +0 -46
  113. package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
  114. package/src/tools/homeassistant/getDomainSummary.ts +0 -29
  115. package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
  116. package/src/tools/homeassistant/getHistory.ts +0 -24
  117. package/src/tools/homeassistant/getState.ts +0 -24
  118. package/src/tools/homeassistant/getStates.ts +0 -46
  119. package/src/tools/homeassistant/getVersion.ts +0 -21
  120. package/src/tools/homeassistant/index.ts +0 -121
  121. package/src/tools/homeassistant/listAutomations.ts +0 -35
  122. package/src/tools/homeassistant/listEntities.ts +0 -44
  123. package/src/tools/homeassistant/listPersons.ts +0 -46
  124. package/src/tools/homeassistant/sceneAndScript.ts +0 -53
  125. package/src/tools/homeassistant/searchEntities.ts +0 -58
  126. package/src/tools/homeassistant/sendNotification.ts +0 -179
  127. package/src/tools/homeassistant/systemTools.ts +0 -52
  128. package/src/tools/homeassistant/triggerAutomation.ts +0 -24
  129. package/src/tools/index.ts +0 -78
  130. package/src/tools/infra/actions.ts +0 -95
  131. package/src/tools/infra/index.ts +0 -138
  132. package/src/tools/omada/blockClient.ts +0 -28
  133. package/src/tools/omada/cyclePoePort.ts +0 -32
  134. package/src/tools/omada/deleteClient.ts +0 -27
  135. package/src/tools/omada/disableClientRateLimit.ts +0 -23
  136. package/src/tools/omada/getClient.ts +0 -14
  137. package/src/tools/omada/getDevice.ts +0 -14
  138. package/src/tools/omada/getFirewallSetting.ts +0 -14
  139. package/src/tools/omada/getInternetInfo.ts +0 -14
  140. package/src/tools/omada/getLanNetworkList.ts +0 -14
  141. package/src/tools/omada/getLanProfileList.ts +0 -14
  142. package/src/tools/omada/getPortForwardingStatus.ts +0 -26
  143. package/src/tools/omada/getRateLimitProfiles.ts +0 -14
  144. package/src/tools/omada/getSiteNtpStatus.ts +0 -14
  145. package/src/tools/omada/getSsidDetail.ts +0 -22
  146. package/src/tools/omada/getSsidList.ts +0 -21
  147. package/src/tools/omada/getSwitchStackDetail.ts +0 -14
  148. package/src/tools/omada/getThreatList.ts +0 -42
  149. package/src/tools/omada/getWlanGroupList.ts +0 -14
  150. package/src/tools/omada/graph.ts +0 -187
  151. package/src/tools/omada/index.ts +0 -153
  152. package/src/tools/omada/listBlockedClients.ts +0 -17
  153. package/src/tools/omada/listClients.ts +0 -14
  154. package/src/tools/omada/listClientsActivity.ts +0 -25
  155. package/src/tools/omada/listClientsPastConnections.ts +0 -44
  156. package/src/tools/omada/listDevices.ts +0 -14
  157. package/src/tools/omada/listDevicesStats.ts +0 -41
  158. package/src/tools/omada/listMostActiveClients.ts +0 -14
  159. package/src/tools/omada/listSites.ts +0 -18
  160. package/src/tools/omada/namespace.ts +0 -1164
  161. package/src/tools/omada/searchDevices.ts +0 -20
  162. package/src/tools/omada/setClientRateLimit.ts +0 -26
  163. package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
  164. package/src/tools/omada/setSiteNtpServers.ts +0 -36
  165. package/src/tools/omada/setSsidEnabled.ts +0 -38
  166. package/src/tools/omada/unblockClient.ts +0 -26
  167. package/src/tools/types.ts +0 -11
  168. package/src/tools/utils/pagination-schema.ts +0 -27
  169. package/src/types/ai/index.ts +0 -11
  170. package/src/types/ai/types.ts +0 -67
  171. package/src/types/homeassistant/automations.ts +0 -43
  172. package/src/types/homeassistant/calendars.ts +0 -26
  173. package/src/types/homeassistant/config.ts +0 -14
  174. package/src/types/homeassistant/entities.ts +0 -14
  175. package/src/types/homeassistant/errors.ts +0 -33
  176. package/src/types/homeassistant/haConfig.ts +0 -7
  177. package/src/types/homeassistant/history.ts +0 -13
  178. package/src/types/homeassistant/index.ts +0 -46
  179. package/src/types/homeassistant/request.ts +0 -6
  180. package/src/types/homeassistant/serviceCall.ts +0 -18
  181. package/src/types/homeassistant/states.ts +0 -58
  182. package/src/types/homeassistant/updates.ts +0 -28
  183. package/src/types/index.ts +0 -12
  184. package/src/types/omada/activeClientInfo.ts +0 -17
  185. package/src/types/omada/clientActivity.ts +0 -32
  186. package/src/types/omada/clientBlockStatus.ts +0 -11
  187. package/src/types/omada/clientPastConnection.ts +0 -82
  188. package/src/types/omada/index.ts +0 -14
  189. package/src/types/omada/omadaApiResponse.ts +0 -5
  190. package/src/types/omada/omadaClientInfo.ts +0 -11
  191. package/src/types/omada/omadaDeviceInfo.ts +0 -11
  192. package/src/types/omada/omadaDeviceStats.ts +0 -31
  193. package/src/types/omada/omadaSiteSummary.ts +0 -5
  194. package/src/types/omada/oswStackDetail.ts +0 -11
  195. package/src/types/omada/paginatedResult.ts +0 -6
  196. package/src/types/omada/rateLimitProfile.ts +0 -58
  197. package/src/types/omada/threatInfo.ts +0 -38
  198. package/src/types/omada/tokenResult.ts +0 -6
  199. package/src/types.ts +0 -2
  200. package/src/utils/config-validations.ts +0 -68
  201. package/src/utils/jwt.ts +0 -112
  202. package/src/utils/logger.ts +0 -121
  203. package/src/utils/plainText.ts +0 -48
  204. package/src/utils/redact.ts +0 -59
  205. package/src/utils/sanitizeError.ts +0 -18
  206. package/src/utils/tlsDispatcher.ts +0 -69
  207. package/src/version.ts +0 -5
  208. package/tests/bdd/automation-creation.spec.ts +0 -214
  209. package/tests/bdd/automation-management.spec.ts +0 -267
  210. package/tests/bdd/calendar.spec.ts +0 -190
  211. package/tests/bdd/configuration.spec.ts +0 -283
  212. package/tests/bdd/device-control.spec.ts +0 -344
  213. package/tests/bdd/entity-listing.spec.ts +0 -208
  214. package/tests/bdd/entity-search.spec.ts +0 -183
  215. package/tests/bdd/history-retrieval.spec.ts +0 -195
  216. package/tests/bdd/notifications.spec.ts +0 -238
  217. package/tests/bdd/service-calls.spec.ts +0 -144
  218. package/tests/bdd/state-retrieval.spec.ts +0 -204
  219. package/tests/bench/hotpaths.bench.ts +0 -68
  220. package/tests/config.test.ts +0 -355
  221. package/tests/features/automation-creation.feature +0 -36
  222. package/tests/features/automation-management.feature +0 -54
  223. package/tests/features/calendar.feature +0 -37
  224. package/tests/features/configuration.feature +0 -70
  225. package/tests/features/device-control.feature +0 -100
  226. package/tests/features/entity-listing.feature +0 -51
  227. package/tests/features/entity-search.feature +0 -44
  228. package/tests/features/history-retrieval.feature +0 -38
  229. package/tests/features/notifications.feature +0 -37
  230. package/tests/features/service-calls.feature +0 -35
  231. package/tests/features/state-retrieval.feature +0 -37
  232. package/tests/fixtures/openapi-default-slice.json +0 -290
  233. package/tests/haClient/automationTrace.test.ts +0 -59
  234. package/tests/haClient/automations.test.ts +0 -149
  235. package/tests/haClient/config.test.ts +0 -117
  236. package/tests/haClient/entities.test.ts +0 -188
  237. package/tests/haClient/events.test.ts +0 -455
  238. package/tests/haClient/eventsTls.test.ts +0 -52
  239. package/tests/haClient/history.test.ts +0 -228
  240. package/tests/haClient/index.test.ts +0 -243
  241. package/tests/haClient/request.test.ts +0 -217
  242. package/tests/haClient/services.test.ts +0 -120
  243. package/tests/haClient/states.test.ts +0 -119
  244. package/tests/haClient/updates.test.ts +0 -193
  245. package/tests/index.test.ts +0 -160
  246. package/tests/integration/homeassistant.test.ts +0 -144
  247. package/tests/integration/omada.test.ts +0 -167
  248. package/tests/integration/server.test.ts +0 -151
  249. package/tests/localAI/index.test.ts +0 -177
  250. package/tests/localAI/ollama.test.ts +0 -187
  251. package/tests/localAI/openai.test.ts +0 -257
  252. package/tests/omadaClient/actions.test.ts +0 -161
  253. package/tests/omadaClient/clientBlocks.test.ts +0 -170
  254. package/tests/omadaClient/ntp.test.ts +0 -381
  255. package/tests/omadaClient/site.test.ts +0 -37
  256. package/tests/ownerQuestions/service.test.ts +0 -499
  257. package/tests/ownerQuestions/tools.test.ts +0 -186
  258. package/tests/permissions/index.test.ts +0 -217
  259. package/tests/resources/index.test.ts +0 -288
  260. package/tests/scripts/generateJwt.test.ts +0 -44
  261. package/tests/security/accessControl.test.ts +0 -223
  262. package/tests/security/authentication.test.ts +0 -117
  263. package/tests/security/configuration.test.ts +0 -170
  264. package/tests/security/cryptoTransport.test.ts +0 -172
  265. package/tests/security/dependencies.test.ts +0 -152
  266. package/tests/security/injection.test.ts +0 -117
  267. package/tests/security/insecureDesign.test.ts +0 -121
  268. package/tests/security/logging.test.ts +0 -131
  269. package/tests/security/resourceConsumption.test.ts +0 -142
  270. package/tests/server/auth.test.ts +0 -249
  271. package/tests/server/chatFace.test.ts +0 -320
  272. package/tests/server/common.test.ts +0 -84
  273. package/tests/server/http.test.ts +0 -561
  274. package/tests/server/instructions.test.ts +0 -152
  275. package/tests/server/memoizeJsonSchema.test.ts +0 -71
  276. package/tests/server/stdio.test.ts +0 -99
  277. package/tests/server/stream.test.ts +0 -285
  278. package/tests/server/toolVisibility.http.test.ts +0 -111
  279. package/tests/server/toolVisibility.test.ts +0 -129
  280. package/tests/tools/ai/analyzeSensors.test.ts +0 -227
  281. package/tests/tools/common.test.ts +0 -123
  282. package/tests/tools/homeassistant/handlers.test.ts +0 -872
  283. package/tests/tools/infra/invokeAction.test.ts +0 -280
  284. package/tests/tools/omada/graph.test.ts +0 -385
  285. package/tests/tools/omada/handlers.test.ts +0 -897
  286. package/tests/tools/omada/namespace.test.ts +0 -483
  287. package/tests/tools/registry.test.ts +0 -168
  288. package/tests/utils/config-validations.test.ts +0 -352
  289. package/tests/utils/jwt.test.ts +0 -115
  290. package/tests/utils/logger.test.ts +0 -327
  291. package/tests/utils/redact.test.ts +0 -105
  292. package/tsconfig.json +0 -24
  293. package/vitest.config.ts +0 -38
@@ -1,144 +0,0 @@
1
- /**
2
- * Integration tests for Home Assistant
3
- * These tests run against a real Home Assistant instance
4
- * Requires HA_URL and HA_TOKEN environment variables
5
- */
6
-
7
- import { describe, it, expect, beforeAll } from 'vitest';
8
- import { HaClient } from '../../src/haClient/index.js';
9
-
10
- describe('Home Assistant Integration', () => {
11
- let client: HaClient;
12
-
13
- beforeAll(() => {
14
- const baseUrl = process.env.HA_URL;
15
- const token = process.env.HA_TOKEN;
16
-
17
- if (!baseUrl || !token) {
18
- throw new Error('HA_URL and HA_TOKEN environment variables are required for integration tests');
19
- }
20
-
21
- client = new HaClient({
22
- baseUrl,
23
- token,
24
- strictSsl: process.env.HA_STRICT_SSL !== 'false',
25
- timeout: parseInt(process.env.HA_TIMEOUT || '30000'),
26
- });
27
- });
28
-
29
- describe('API Connection', () => {
30
- it('should connect to Home Assistant API', async () => {
31
- const result = await client.checkApi();
32
- expect(result.message).toBe('API running.');
33
- });
34
-
35
- it('should get Home Assistant version', async () => {
36
- const version = await client.getVersion();
37
- expect(version).toHaveProperty('version');
38
- expect(version.version).toMatch(/^\d+\.\d+\.\d+/);
39
- });
40
- });
41
-
42
- describe('Entity States', () => {
43
- it('should get all states', async () => {
44
- const states = await client.getStates();
45
- expect(Array.isArray(states)).toBe(true);
46
- expect(states.length).toBeGreaterThan(0);
47
- });
48
-
49
- it('should get state of a specific entity', async () => {
50
- // First get all states to find a valid entity_id
51
- const states = await client.getStates();
52
- expect(states.length).toBeGreaterThan(0);
53
-
54
- const entity = await client.getState(states[0].entity_id);
55
- expect(entity).not.toBeNull();
56
- expect(entity).toHaveProperty('entity_id');
57
- expect(entity).toHaveProperty('state');
58
- expect(entity).toHaveProperty('attributes');
59
- });
60
- });
61
-
62
- describe('Entity Search', () => {
63
- it('should search entities by query', async () => {
64
- const results = await client.searchEntities('sensor');
65
- expect(Array.isArray(results)).toBe(true);
66
- });
67
- });
68
-
69
- describe('Domain Operations', () => {
70
- it('should get entities by domain', async () => {
71
- const entities = await client.getEntitiesByDomain('sensor');
72
- expect(Array.isArray(entities)).toBe(true);
73
- entities.forEach((entity) => {
74
- expect(entity.entity_id).toMatch(/^sensor\./);
75
- });
76
- });
77
-
78
- it('should get domain summary', async () => {
79
- const result = await client.getDomainSummary('sensor');
80
- expect(result).toHaveProperty('domain');
81
- expect(result).toHaveProperty('count');
82
- expect(result).toHaveProperty('states');
83
- expect(result.domain).toBe('sensor');
84
- expect(result.count).toBeGreaterThan(0);
85
- });
86
- });
87
-
88
- describe('Sensors', () => {
89
- it('should get all sensors', async () => {
90
- const sensors = await client.getAllSensors();
91
- expect(Array.isArray(sensors)).toBe(true);
92
- sensors.forEach((entity) => {
93
- expect(entity.entity_id).toMatch(/^(sensor|binary_sensor)\./);
94
- });
95
- });
96
- });
97
-
98
- describe('Persons', () => {
99
- it('should get person entities via domain lookup', async () => {
100
- // The HaClient doesn't have a getPersons method directly
101
- // Persons are accessed via getEntitiesByDomain('person')
102
- const persons = await client.getEntitiesByDomain('person');
103
- expect(Array.isArray(persons)).toBe(true);
104
- persons.forEach((person) => {
105
- expect(person.entity_id).toMatch(/^person\./);
106
- });
107
- });
108
- });
109
-
110
- describe('Automations', () => {
111
- it('should list automations', async () => {
112
- const result = await client.getAutomations();
113
- expect(Array.isArray(result)).toBe(true);
114
- if (result.length > 0) {
115
- // Automation type uses 'id' not 'entity_id'
116
- expect(result[0]).toHaveProperty('id');
117
- expect(result[0]).toHaveProperty('state');
118
- expect(result[0]).toHaveProperty('alias');
119
- }
120
- });
121
- });
122
-
123
- describe('Calendars', () => {
124
- it('should list calendars (or report the integration absent)', async () => {
125
- // HA only serves /api/calendars when a calendar integration is loaded;
126
- // a bare instance 404s. Both are valid live-system states — the test
127
- // asserts the client surfaces them correctly, not that the user has
128
- // calendars configured.
129
- try {
130
- const result = await client.getCalendars();
131
- expect(Array.isArray(result)).toBe(true);
132
- } catch (error) {
133
- expect((error as { statusCode?: number; message: string }).message).toMatch(/Not Found/i);
134
- }
135
- });
136
- });
137
-
138
- describe('System', () => {
139
- it('should get system log', async () => {
140
- const result = await client.getSystemLog({ hours: 1, limit: 10 });
141
- expect(Array.isArray(result)).toBe(true);
142
- });
143
- });
144
- });
@@ -1,167 +0,0 @@
1
- /**
2
- * Integration tests for TP-Link Omada Controller
3
- * These tests run against a real Omada controller
4
- * Requires OMADA_* environment variables
5
- */
6
-
7
- import { describe, it, expect, beforeAll } from 'vitest';
8
- import { OmadaClient } from '../../src/omadaClient/index.js';
9
-
10
- describe('Omada Integration', () => {
11
- let client: OmadaClient;
12
- let siteId: string;
13
-
14
- beforeAll(async () => {
15
- const baseUrl = process.env.OMADA_BASE_URL;
16
- const clientId = process.env.OMADA_CLIENT_ID;
17
- const clientSecret = process.env.OMADA_CLIENT_SECRET;
18
- const omadacId = process.env.OMADA_OMADAC_ID;
19
-
20
- if (!baseUrl || !clientId || !clientSecret || !omadacId) {
21
- throw new Error('OMADA_BASE_URL, OMADA_CLIENT_ID, OMADA_CLIENT_SECRET, and OMADA_OMADAC_ID are required');
22
- }
23
-
24
- client = new OmadaClient({
25
- baseUrl,
26
- clientId,
27
- clientSecret,
28
- omadacId,
29
- siteId: process.env.OMADA_SITE_ID,
30
- strictSsl: process.env.OMADA_STRICT_SSL !== 'false',
31
- timeout: parseInt(process.env.OMADA_TIMEOUT || '30000'),
32
- });
33
-
34
- // Get siteId for tests
35
- siteId = process.env.OMADA_SITE_ID || '';
36
- });
37
-
38
- describe('Sites', () => {
39
- it('should list all sites', async () => {
40
- const sites = await client.listSites();
41
- expect(Array.isArray(sites)).toBe(true);
42
- expect(sites.length).toBeGreaterThan(0);
43
- expect(sites[0]).toHaveProperty('siteId');
44
- expect(sites[0]).toHaveProperty('name');
45
-
46
- // Use first site if not configured
47
- if (!siteId) {
48
- siteId = sites[0].siteId;
49
- }
50
- });
51
- });
52
-
53
- describe('Devices', () => {
54
- it('should list network devices', async () => {
55
- const devices = await client.listDevices(siteId);
56
- expect(Array.isArray(devices)).toBe(true);
57
- if (devices.length > 0) {
58
- expect(devices[0]).toHaveProperty('mac');
59
- expect(devices[0]).toHaveProperty('name');
60
- expect(devices[0]).toHaveProperty('type');
61
- expect(devices[0]).toHaveProperty('ip');
62
- }
63
- });
64
-
65
- it('should search devices globally', async () => {
66
- // searchDevices returns an array of OmadaDeviceInfo
67
- // Note: If no devices match, the API may return an empty object {} instead of []
68
- // so we need to handle both cases
69
- const results = await client.searchDevices('gateway');
70
- // Results could be an array or an empty object if no matches
71
- const isArrayOrEmpty = Array.isArray(results) || (typeof results === 'object' && results !== null);
72
- expect(isArrayOrEmpty).toBe(true);
73
- if (Array.isArray(results) && results.length > 0) {
74
- expect(results[0]).toHaveProperty('mac');
75
- }
76
- });
77
- });
78
-
79
- describe('Clients', () => {
80
- it('should list connected clients', async () => {
81
- const clients = await client.listClients(siteId);
82
- expect(Array.isArray(clients)).toBe(true);
83
- if (clients.length > 0) {
84
- expect(clients[0]).toHaveProperty('mac');
85
- expect(clients[0]).toHaveProperty('ip');
86
- }
87
- });
88
-
89
- it('should list most active clients', async () => {
90
- const clients = await client.listMostActiveClients(siteId);
91
- expect(Array.isArray(clients)).toBe(true);
92
- if (clients.length > 0) {
93
- expect(clients[0]).toHaveProperty('name');
94
- // Controller v5 exposed `totalTraffic`; OC200 v6 renamed the traffic
95
- // field. Assert a traffic-ish field exists rather than pinning the
96
- // v5 name (discovered live on OC200 6.2, 2026-07-11).
97
- const keys = Object.keys(clients[0] as Record<string, unknown>);
98
- expect(keys.some((k) => /traffic|activity/i.test(k))).toBe(true);
99
- }
100
- });
101
- });
102
-
103
- describe('Network Configuration', () => {
104
- it('should get internet info', async () => {
105
- const info = await client.getInternetInfo(siteId);
106
- expect(info).toHaveProperty('wanPortSettings');
107
- });
108
-
109
- it('should get LAN network list', async () => {
110
- const networks = await client.getLanNetworkList(siteId);
111
- expect(Array.isArray(networks)).toBe(true);
112
- if (networks.length > 0) {
113
- expect(networks[0]).toHaveProperty('name');
114
- expect(networks[0]).toHaveProperty('vlan');
115
- }
116
- });
117
-
118
- it('should get WLAN group list', async () => {
119
- const groups = await client.getWlanGroupList(siteId);
120
- expect(Array.isArray(groups)).toBe(true);
121
- });
122
- });
123
-
124
- describe('Rate Limiting', () => {
125
- it('should get rate limit profiles', async () => {
126
- const profiles = await client.getRateLimitProfiles(siteId);
127
- expect(Array.isArray(profiles)).toBe(true);
128
- if (profiles.length > 0) {
129
- expect(profiles[0]).toHaveProperty('profileId');
130
- expect(profiles[0]).toHaveProperty('name');
131
- }
132
- });
133
- });
134
-
135
- describe('Security', () => {
136
- it('should get threat list (may be empty)', async () => {
137
- // The threat list API may return 400 if IDS/IPS is not enabled or configured
138
- // This test catches the error and passes if no threats are available.
139
- // NB: archived/page/pageSize/startTime/endTime are REQUIRED — controller
140
- // v5 tolerated their absence, OC200 v6 rejects nulls ("archived should
141
- // not be null", live finding 2026-07-11). The MCP tool schema already
142
- // requires them; this test used to cheat with `{}`.
143
- try {
144
- const now = Math.floor(Date.now() / 1000);
145
- const result = await client.getThreatList({
146
- archived: false,
147
- page: 1,
148
- pageSize: 10,
149
- startTime: now - 7 * 24 * 3600,
150
- endTime: now,
151
- });
152
- // If we get here, the API worked - check structure
153
- expect(result).toHaveProperty('data');
154
- expect(Array.isArray(result.data)).toBe(true);
155
- } catch (error) {
156
- // Some Omada setups may not have threat detection enabled
157
- // This is acceptable - skip rather than fail
158
- if (error instanceof Error && error.message.includes('400')) {
159
- // API returned 400 - threat detection may not be enabled
160
- expect(true).toBe(true);
161
- } else {
162
- throw error;
163
- }
164
- }
165
- });
166
- });
167
- });
@@ -1,151 +0,0 @@
1
- /**
2
- * Integration tests for MCP Server
3
- * Tests server initialization with real clients
4
- */
5
-
6
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
7
- import { HaClient } from '../../src/haClient/index.js';
8
- import { OmadaClient } from '../../src/omadaClient/index.js';
9
- import { createServer } from '../../src/server/common.js';
10
- import { generateInstructions } from '../../src/server/instructions.js';
11
-
12
- describe('Server Integration', () => {
13
- let haClient: HaClient | undefined;
14
- let omadaClient: OmadaClient | undefined;
15
-
16
- beforeAll(async () => {
17
- // Initialize HA client if configured
18
- if (process.env.HA_URL && process.env.HA_TOKEN) {
19
- haClient = new HaClient({
20
- baseUrl: process.env.HA_URL,
21
- token: process.env.HA_TOKEN,
22
- strictSsl: process.env.HA_STRICT_SSL !== 'false',
23
- timeout: parseInt(process.env.HA_TIMEOUT || '30000'),
24
- });
25
- }
26
-
27
- // Initialize Omada client if configured
28
- if (
29
- process.env.OMADA_BASE_URL &&
30
- process.env.OMADA_CLIENT_ID &&
31
- process.env.OMADA_CLIENT_SECRET &&
32
- process.env.OMADA_OMADAC_ID
33
- ) {
34
- omadaClient = new OmadaClient({
35
- baseUrl: process.env.OMADA_BASE_URL,
36
- clientId: process.env.OMADA_CLIENT_ID,
37
- clientSecret: process.env.OMADA_CLIENT_SECRET,
38
- omadacId: process.env.OMADA_OMADAC_ID,
39
- siteId: process.env.OMADA_SITE_ID,
40
- strictSsl: process.env.OMADA_STRICT_SSL !== 'false',
41
- timeout: parseInt(process.env.OMADA_TIMEOUT || '30000'),
42
- });
43
- }
44
- });
45
-
46
- describe('Server Creation', () => {
47
- it('should create server with HA client', () => {
48
- if (!haClient) {
49
- console.log('Skipping: HA not configured');
50
- return;
51
- }
52
-
53
- const server = createServer({ haClient });
54
- expect(server).toBeDefined();
55
- expect(server.server).toBeDefined();
56
- });
57
-
58
- it('should create server with Omada client', () => {
59
- if (!omadaClient) {
60
- console.log('Skipping: Omada not configured');
61
- return;
62
- }
63
-
64
- const server = createServer({ omadaClient });
65
- expect(server).toBeDefined();
66
- });
67
-
68
- it('should create server with all clients', () => {
69
- const server = createServer({
70
- haClient,
71
- omadaClient,
72
- });
73
- expect(server).toBeDefined();
74
- });
75
- });
76
-
77
- describe('Server Instructions', () => {
78
- it('should generate instructions for HA only', () => {
79
- const instructions = generateInstructions({
80
- haEnabled: true,
81
- omadaEnabled: false,
82
- aiEnabled: false,
83
- });
84
-
85
- expect(instructions).toContain('MCP-HA-Connect');
86
- expect(instructions).toContain('ENTITY QUERIES');
87
- expect(instructions).not.toContain('OMADA');
88
- });
89
-
90
- it('should generate instructions for Omada only', () => {
91
- const instructions = generateInstructions({
92
- haEnabled: false,
93
- omadaEnabled: true,
94
- aiEnabled: false,
95
- });
96
-
97
- expect(instructions).toContain('MCP-HA-Connect');
98
- expect(instructions).toContain('OMADA');
99
- expect(instructions).not.toContain('ENTITY QUERIES');
100
- });
101
-
102
- it('should generate instructions for all plugins', () => {
103
- const instructions = generateInstructions({
104
- haEnabled: true,
105
- omadaEnabled: true,
106
- aiEnabled: true,
107
- });
108
-
109
- expect(instructions).toContain('MCP-HA-Connect');
110
- expect(instructions).toContain('ENTITY QUERIES');
111
- expect(instructions).toContain('OMADA');
112
- expect(instructions).toContain('AI ANALYSIS');
113
- expect(instructions).toContain('INTEGRATION');
114
- });
115
-
116
- it('should generate concise instructions under the size ceiling', () => {
117
- const instructions = generateInstructions({
118
- haEnabled: true,
119
- omadaEnabled: true,
120
- aiEnabled: true,
121
- });
122
-
123
- // Keep in sync with tests/server/instructions.test.ts — the ceiling was
124
- // raised 2000 -> 2200 in v1.6.0 (ACTIONS line); this integration twin was
125
- // stale until the suite was actually run (2026-07-11).
126
- expect(instructions.length).toBeLessThan(2200);
127
- });
128
- });
129
-
130
- describe('Client Connectivity', () => {
131
- it('should verify HA connection', async () => {
132
- if (!haClient) {
133
- console.log('Skipping: HA not configured');
134
- return;
135
- }
136
-
137
- const result = await haClient.checkApi();
138
- expect(result.message).toBe('API running.');
139
- });
140
-
141
- it('should verify Omada connection', async () => {
142
- if (!omadaClient) {
143
- console.log('Skipping: Omada not configured');
144
- return;
145
- }
146
-
147
- const sites = await omadaClient.listSites();
148
- expect(sites.length).toBeGreaterThan(0);
149
- });
150
- });
151
- });
@@ -1,177 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import type { EnvironmentConfig } from '../../src/config.js';
3
- import type { AIProviderType } from '../../src/localAI/types.js';
4
-
5
- // Create mock functions that will be used by the mock implementations
6
- const mockOllamaAnalyze = vi.fn();
7
- const mockOllamaHealth = vi.fn();
8
- const mockOpenAIAnalyze = vi.fn();
9
- const mockOpenAIHealth = vi.fn();
10
-
11
- // Mock the providers with class-style constructors
12
- vi.mock('../../src/localAI/providers/ollama.js', () => {
13
- return {
14
- OllamaProvider: class MockOllamaProvider {
15
- analyzeSensors = mockOllamaAnalyze;
16
- checkHealth = mockOllamaHealth;
17
- getProviderName() {
18
- return 'ollama';
19
- }
20
- },
21
- };
22
- });
23
-
24
- vi.mock('../../src/localAI/providers/openai.js', () => {
25
- return {
26
- OpenAICompatibleProvider: class MockOpenAIProvider {
27
- analyzeSensors = mockOpenAIAnalyze;
28
- checkHealth = mockOpenAIHealth;
29
- getProviderName() {
30
- return 'openai-compatible';
31
- }
32
- },
33
- };
34
- });
35
-
36
- import { LocalAIClient } from '../../src/localAI/index.js';
37
-
38
- describe('LocalAIClient', () => {
39
- const createConfig = (provider: AIProviderType = 'ollama'): EnvironmentConfig =>
40
- ({
41
- aiProvider: provider,
42
- aiUrl: 'http://ollama.10.0.0.17.nip.io:11434',
43
- aiModel: 'qwen3:14b',
44
- aiTimeout: 60000,
45
- aiApiKey: undefined,
46
- // Other required config fields
47
- baseUrl: 'http://homeassistant.10.0.0.19.nip.io:8123',
48
- token: 'test-token',
49
- strictSsl: true,
50
- timeout: 30000,
51
- logLevel: 'info',
52
- logFormat: 'plain',
53
- useHttp: false,
54
- stateful: false,
55
-
56
- httpEnableHealthcheck: true,
57
- httpAllowCors: true,
58
- }) as EnvironmentConfig;
59
-
60
- beforeEach(() => {
61
- vi.clearAllMocks();
62
- });
63
-
64
- describe('constructor', () => {
65
- it('should create OllamaProvider for ollama provider type', () => {
66
- const config = createConfig('ollama');
67
- const client = new LocalAIClient(config);
68
-
69
- expect(client.getProviderName()).toBe('ollama');
70
- });
71
-
72
- it('should create OpenAICompatibleProvider for openai provider type', () => {
73
- const config = createConfig('openai');
74
- const client = new LocalAIClient(config);
75
-
76
- expect(client.getProviderName()).toBe('openai-compatible');
77
- });
78
-
79
- it('should default to OllamaProvider when provider is unknown', () => {
80
- const config = createConfig('ollama');
81
- // Force unknown provider
82
- (config as unknown as { aiProvider: string }).aiProvider = 'unknown';
83
- const client = new LocalAIClient(config);
84
-
85
- // Should fall back to ollama
86
- expect(client.getProviderName()).toBe('ollama');
87
- });
88
- });
89
-
90
- describe('analyzeSensors', () => {
91
- it('should delegate to the provider', async () => {
92
- const mockResult = {
93
- severity: 'critical' as const,
94
- issue: 'Test issue',
95
- reasoning: 'Test reasoning',
96
- recommended_actions: [],
97
- ssh_diagnostics: [],
98
- confidence: 0.9,
99
- model: 'qwen3:14b',
100
- provider: 'ollama',
101
- response_time: 1.5,
102
- };
103
- mockOllamaAnalyze.mockResolvedValueOnce(mockResult);
104
-
105
- const config = createConfig('ollama');
106
- const client = new LocalAIClient(config);
107
- const sensors = { 'sensor.test': { state: '25' } };
108
-
109
- const result = await client.analyzeSensors(sensors);
110
-
111
- expect(mockOllamaAnalyze).toHaveBeenCalledWith(sensors);
112
- expect(result).toEqual(mockResult);
113
- });
114
- });
115
-
116
- describe('checkHealth', () => {
117
- it('should delegate to the provider', async () => {
118
- mockOllamaHealth.mockResolvedValueOnce(true);
119
-
120
- const config = createConfig('ollama');
121
- const client = new LocalAIClient(config);
122
-
123
- const result = await client.checkHealth();
124
-
125
- expect(mockOllamaHealth).toHaveBeenCalled();
126
- expect(result).toBe(true);
127
- });
128
-
129
- it('should return false when provider is unhealthy', async () => {
130
- mockOllamaHealth.mockResolvedValueOnce(false);
131
-
132
- const config = createConfig('ollama');
133
- const client = new LocalAIClient(config);
134
-
135
- const result = await client.checkHealth();
136
-
137
- expect(result).toBe(false);
138
- });
139
- });
140
-
141
- describe('getModelName', () => {
142
- it('should return the configured model name', () => {
143
- const config = createConfig('ollama');
144
- config.aiModel = 'phi4:14b';
145
- const client = new LocalAIClient(config);
146
-
147
- expect(client.getModelName()).toBe('phi4:14b');
148
- });
149
- });
150
-
151
- describe('getConfig', () => {
152
- it('should return the provider configuration', () => {
153
- const config = createConfig('ollama');
154
- config.aiApiKey = 'test-key';
155
- const client = new LocalAIClient(config);
156
-
157
- const providerConfig = client.getConfig();
158
-
159
- expect(providerConfig.provider).toBe('ollama');
160
- expect(providerConfig.baseUrl).toBe('http://ollama.10.0.0.17.nip.io:11434');
161
- expect(providerConfig.model).toBe('qwen3:14b');
162
- expect(providerConfig.timeout).toBe(60000);
163
- expect(providerConfig.apiKey).toBe('test-key');
164
- });
165
-
166
- it('should return a copy of the config (immutable)', () => {
167
- const config = createConfig('ollama');
168
- const client = new LocalAIClient(config);
169
-
170
- const providerConfig1 = client.getConfig();
171
- const providerConfig2 = client.getConfig();
172
-
173
- expect(providerConfig1).not.toBe(providerConfig2);
174
- expect(providerConfig1).toEqual(providerConfig2);
175
- });
176
- });
177
- });