@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.
- package/.env.example +1 -1
- package/README.md +24 -2
- package/dist/omadaClient/accessControl.d.ts +290 -0
- package/dist/omadaClient/accessControl.js +509 -0
- package/dist/omadaClient/accessControl.js.map +1 -0
- package/dist/omadaClient/client.d.ts +6 -1
- package/dist/omadaClient/client.js +12 -1
- package/dist/omadaClient/client.js.map +1 -1
- package/dist/omadaClient/diagnose.d.ts +68 -0
- package/dist/omadaClient/diagnose.js +164 -0
- package/dist/omadaClient/diagnose.js.map +1 -0
- package/dist/omadaClient/index.d.ts +9 -0
- package/dist/omadaClient/index.js +19 -0
- package/dist/omadaClient/index.js.map +1 -1
- package/dist/omadaClient/macGroups.d.ts +58 -0
- package/dist/omadaClient/macGroups.js +110 -0
- package/dist/omadaClient/macGroups.js.map +1 -0
- package/dist/omadaClient/serialize.d.ts +13 -0
- package/dist/omadaClient/serialize.js +29 -0
- package/dist/omadaClient/serialize.js.map +1 -0
- package/dist/omadaClient/timeRange.d.ts +41 -0
- package/dist/omadaClient/timeRange.js +109 -0
- package/dist/omadaClient/timeRange.js.map +1 -0
- package/dist/tools/infra/actions.d.ts +1 -1
- package/dist/tools/infra/actions.js +1 -1
- package/dist/tools/omada/accessControl.d.ts +151 -0
- package/dist/tools/omada/accessControl.js +158 -0
- package/dist/tools/omada/accessControl.js.map +1 -0
- package/dist/tools/omada/index.js +9 -0
- package/dist/tools/omada/index.js.map +1 -1
- package/dist/tools/omada/macGroups.d.ts +19 -0
- package/dist/tools/omada/macGroups.js +77 -0
- package/dist/tools/omada/macGroups.js.map +1 -0
- package/dist/tools/omada/namespace.js +16 -0
- package/dist/tools/omada/namespace.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/.codex/review-rules.md +0 -16
- package/.dockerignore +0 -8
- package/.gitattributes +0 -30
- package/.github/workflows/ci.yml +0 -193
- package/AGENTS.md +0 -389
- package/Dockerfile +0 -50
- package/docker-compose.yml +0 -23
- package/docs/CHANGELOG.md +0 -341
- package/docs/COVERAGE.md +0 -219
- package/docs/QUICK_START.md +0 -240
- package/eslint.config.js +0 -33
- package/n8n/ai-agent-safety-monitor.json +0 -355
- package/openapi.json +0 -1259
- package/scripts/check-version-sync.mjs +0 -69
- package/scripts/generate-jwt.ts +0 -121
- package/src/config.ts +0 -440
- package/src/haClient/automations.ts +0 -261
- package/src/haClient/calendars.ts +0 -94
- package/src/haClient/config.ts +0 -52
- package/src/haClient/devices.ts +0 -653
- package/src/haClient/entities.ts +0 -128
- package/src/haClient/events.ts +0 -689
- package/src/haClient/history.ts +0 -73
- package/src/haClient/index.ts +0 -336
- package/src/haClient/request.ts +0 -142
- package/src/haClient/services.ts +0 -45
- package/src/haClient/states.ts +0 -60
- package/src/haClient/updates.ts +0 -58
- package/src/haClient/wsCommand.ts +0 -44
- package/src/index.ts +0 -164
- package/src/localAI/index.ts +0 -88
- package/src/localAI/prompts.ts +0 -50
- package/src/localAI/providers/base.ts +0 -120
- package/src/localAI/providers/ollama.ts +0 -78
- package/src/localAI/providers/openai.ts +0 -86
- package/src/localAI/types.ts +0 -67
- package/src/omadaClient/auth.ts +0 -178
- package/src/omadaClient/client.ts +0 -337
- package/src/omadaClient/device.ts +0 -118
- package/src/omadaClient/index.ts +0 -310
- package/src/omadaClient/network.ts +0 -284
- package/src/omadaClient/request.ts +0 -365
- package/src/omadaClient/security.ts +0 -51
- package/src/omadaClient/site.ts +0 -344
- package/src/ownerQuestions/service.ts +0 -536
- package/src/permissions/index.ts +0 -143
- package/src/resources/index.ts +0 -213
- package/src/server/auth.ts +0 -175
- package/src/server/chatFace.ts +0 -789
- package/src/server/chatSlice.ts +0 -57
- package/src/server/common.ts +0 -170
- package/src/server/eventSubscription.ts +0 -343
- package/src/server/http.ts +0 -626
- package/src/server/instructions.ts +0 -97
- package/src/server/rateLimiter.ts +0 -227
- package/src/server/stdio.ts +0 -41
- package/src/server/stream.ts +0 -205
- package/src/tools/ai/analyzeSensors.ts +0 -33
- package/src/tools/ai/index.ts +0 -20
- package/src/tools/common.ts +0 -442
- package/src/tools/homeassistant/askOwner.ts +0 -133
- package/src/tools/homeassistant/automationControls.ts +0 -66
- package/src/tools/homeassistant/calendar.ts +0 -97
- package/src/tools/homeassistant/callService.ts +0 -25
- package/src/tools/homeassistant/controlClimate.ts +0 -68
- package/src/tools/homeassistant/controlCover.ts +0 -48
- package/src/tools/homeassistant/controlFan.ts +0 -44
- package/src/tools/homeassistant/controlLight.ts +0 -38
- package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
- package/src/tools/homeassistant/createAutomation.ts +0 -35
- package/src/tools/homeassistant/deleteAutomation.ts +0 -24
- package/src/tools/homeassistant/entityAction.ts +0 -35
- package/src/tools/homeassistant/entityGuard.ts +0 -28
- package/src/tools/homeassistant/getAllSensors.ts +0 -46
- package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
- package/src/tools/homeassistant/getDomainSummary.ts +0 -29
- package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
- package/src/tools/homeassistant/getHistory.ts +0 -24
- package/src/tools/homeassistant/getState.ts +0 -24
- package/src/tools/homeassistant/getStates.ts +0 -46
- package/src/tools/homeassistant/getVersion.ts +0 -21
- package/src/tools/homeassistant/index.ts +0 -121
- package/src/tools/homeassistant/listAutomations.ts +0 -35
- package/src/tools/homeassistant/listEntities.ts +0 -44
- package/src/tools/homeassistant/listPersons.ts +0 -46
- package/src/tools/homeassistant/sceneAndScript.ts +0 -53
- package/src/tools/homeassistant/searchEntities.ts +0 -58
- package/src/tools/homeassistant/sendNotification.ts +0 -179
- package/src/tools/homeassistant/systemTools.ts +0 -52
- package/src/tools/homeassistant/triggerAutomation.ts +0 -24
- package/src/tools/index.ts +0 -78
- package/src/tools/infra/actions.ts +0 -95
- package/src/tools/infra/index.ts +0 -138
- package/src/tools/omada/blockClient.ts +0 -28
- package/src/tools/omada/cyclePoePort.ts +0 -32
- package/src/tools/omada/deleteClient.ts +0 -27
- package/src/tools/omada/disableClientRateLimit.ts +0 -23
- package/src/tools/omada/getClient.ts +0 -14
- package/src/tools/omada/getDevice.ts +0 -14
- package/src/tools/omada/getFirewallSetting.ts +0 -14
- package/src/tools/omada/getInternetInfo.ts +0 -14
- package/src/tools/omada/getLanNetworkList.ts +0 -14
- package/src/tools/omada/getLanProfileList.ts +0 -14
- package/src/tools/omada/getPortForwardingStatus.ts +0 -26
- package/src/tools/omada/getRateLimitProfiles.ts +0 -14
- package/src/tools/omada/getSiteNtpStatus.ts +0 -14
- package/src/tools/omada/getSsidDetail.ts +0 -22
- package/src/tools/omada/getSsidList.ts +0 -21
- package/src/tools/omada/getSwitchStackDetail.ts +0 -14
- package/src/tools/omada/getThreatList.ts +0 -42
- package/src/tools/omada/getWlanGroupList.ts +0 -14
- package/src/tools/omada/graph.ts +0 -187
- package/src/tools/omada/index.ts +0 -153
- package/src/tools/omada/listBlockedClients.ts +0 -17
- package/src/tools/omada/listClients.ts +0 -14
- package/src/tools/omada/listClientsActivity.ts +0 -25
- package/src/tools/omada/listClientsPastConnections.ts +0 -44
- package/src/tools/omada/listDevices.ts +0 -14
- package/src/tools/omada/listDevicesStats.ts +0 -41
- package/src/tools/omada/listMostActiveClients.ts +0 -14
- package/src/tools/omada/listSites.ts +0 -18
- package/src/tools/omada/namespace.ts +0 -1164
- package/src/tools/omada/searchDevices.ts +0 -20
- package/src/tools/omada/setClientRateLimit.ts +0 -26
- package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
- package/src/tools/omada/setSiteNtpServers.ts +0 -36
- package/src/tools/omada/setSsidEnabled.ts +0 -38
- package/src/tools/omada/unblockClient.ts +0 -26
- package/src/tools/types.ts +0 -11
- package/src/tools/utils/pagination-schema.ts +0 -27
- package/src/types/ai/index.ts +0 -11
- package/src/types/ai/types.ts +0 -67
- package/src/types/homeassistant/automations.ts +0 -43
- package/src/types/homeassistant/calendars.ts +0 -26
- package/src/types/homeassistant/config.ts +0 -14
- package/src/types/homeassistant/entities.ts +0 -14
- package/src/types/homeassistant/errors.ts +0 -33
- package/src/types/homeassistant/haConfig.ts +0 -7
- package/src/types/homeassistant/history.ts +0 -13
- package/src/types/homeassistant/index.ts +0 -46
- package/src/types/homeassistant/request.ts +0 -6
- package/src/types/homeassistant/serviceCall.ts +0 -18
- package/src/types/homeassistant/states.ts +0 -58
- package/src/types/homeassistant/updates.ts +0 -28
- package/src/types/index.ts +0 -12
- package/src/types/omada/activeClientInfo.ts +0 -17
- package/src/types/omada/clientActivity.ts +0 -32
- package/src/types/omada/clientBlockStatus.ts +0 -11
- package/src/types/omada/clientPastConnection.ts +0 -82
- package/src/types/omada/index.ts +0 -14
- package/src/types/omada/omadaApiResponse.ts +0 -5
- package/src/types/omada/omadaClientInfo.ts +0 -11
- package/src/types/omada/omadaDeviceInfo.ts +0 -11
- package/src/types/omada/omadaDeviceStats.ts +0 -31
- package/src/types/omada/omadaSiteSummary.ts +0 -5
- package/src/types/omada/oswStackDetail.ts +0 -11
- package/src/types/omada/paginatedResult.ts +0 -6
- package/src/types/omada/rateLimitProfile.ts +0 -58
- package/src/types/omada/threatInfo.ts +0 -38
- package/src/types/omada/tokenResult.ts +0 -6
- package/src/types.ts +0 -2
- package/src/utils/config-validations.ts +0 -68
- package/src/utils/jwt.ts +0 -112
- package/src/utils/logger.ts +0 -121
- package/src/utils/plainText.ts +0 -48
- package/src/utils/redact.ts +0 -59
- package/src/utils/sanitizeError.ts +0 -18
- package/src/utils/tlsDispatcher.ts +0 -69
- package/src/version.ts +0 -5
- package/tests/bdd/automation-creation.spec.ts +0 -214
- package/tests/bdd/automation-management.spec.ts +0 -267
- package/tests/bdd/calendar.spec.ts +0 -190
- package/tests/bdd/configuration.spec.ts +0 -283
- package/tests/bdd/device-control.spec.ts +0 -344
- package/tests/bdd/entity-listing.spec.ts +0 -208
- package/tests/bdd/entity-search.spec.ts +0 -183
- package/tests/bdd/history-retrieval.spec.ts +0 -195
- package/tests/bdd/notifications.spec.ts +0 -238
- package/tests/bdd/service-calls.spec.ts +0 -144
- package/tests/bdd/state-retrieval.spec.ts +0 -204
- package/tests/bench/hotpaths.bench.ts +0 -68
- package/tests/config.test.ts +0 -355
- package/tests/features/automation-creation.feature +0 -36
- package/tests/features/automation-management.feature +0 -54
- package/tests/features/calendar.feature +0 -37
- package/tests/features/configuration.feature +0 -70
- package/tests/features/device-control.feature +0 -100
- package/tests/features/entity-listing.feature +0 -51
- package/tests/features/entity-search.feature +0 -44
- package/tests/features/history-retrieval.feature +0 -38
- package/tests/features/notifications.feature +0 -37
- package/tests/features/service-calls.feature +0 -35
- package/tests/features/state-retrieval.feature +0 -37
- package/tests/fixtures/openapi-default-slice.json +0 -290
- package/tests/haClient/automationTrace.test.ts +0 -59
- package/tests/haClient/automations.test.ts +0 -149
- package/tests/haClient/config.test.ts +0 -117
- package/tests/haClient/entities.test.ts +0 -188
- package/tests/haClient/events.test.ts +0 -455
- package/tests/haClient/eventsTls.test.ts +0 -52
- package/tests/haClient/history.test.ts +0 -228
- package/tests/haClient/index.test.ts +0 -243
- package/tests/haClient/request.test.ts +0 -217
- package/tests/haClient/services.test.ts +0 -120
- package/tests/haClient/states.test.ts +0 -119
- package/tests/haClient/updates.test.ts +0 -193
- package/tests/index.test.ts +0 -160
- package/tests/integration/homeassistant.test.ts +0 -144
- package/tests/integration/omada.test.ts +0 -167
- package/tests/integration/server.test.ts +0 -151
- package/tests/localAI/index.test.ts +0 -177
- package/tests/localAI/ollama.test.ts +0 -187
- package/tests/localAI/openai.test.ts +0 -257
- package/tests/omadaClient/actions.test.ts +0 -161
- package/tests/omadaClient/clientBlocks.test.ts +0 -170
- package/tests/omadaClient/ntp.test.ts +0 -381
- package/tests/omadaClient/site.test.ts +0 -37
- package/tests/ownerQuestions/service.test.ts +0 -499
- package/tests/ownerQuestions/tools.test.ts +0 -186
- package/tests/permissions/index.test.ts +0 -217
- package/tests/resources/index.test.ts +0 -288
- package/tests/scripts/generateJwt.test.ts +0 -44
- package/tests/security/accessControl.test.ts +0 -223
- package/tests/security/authentication.test.ts +0 -117
- package/tests/security/configuration.test.ts +0 -170
- package/tests/security/cryptoTransport.test.ts +0 -172
- package/tests/security/dependencies.test.ts +0 -152
- package/tests/security/injection.test.ts +0 -117
- package/tests/security/insecureDesign.test.ts +0 -121
- package/tests/security/logging.test.ts +0 -131
- package/tests/security/resourceConsumption.test.ts +0 -142
- package/tests/server/auth.test.ts +0 -249
- package/tests/server/chatFace.test.ts +0 -320
- package/tests/server/common.test.ts +0 -84
- package/tests/server/http.test.ts +0 -561
- package/tests/server/instructions.test.ts +0 -152
- package/tests/server/memoizeJsonSchema.test.ts +0 -71
- package/tests/server/stdio.test.ts +0 -99
- package/tests/server/stream.test.ts +0 -285
- package/tests/server/toolVisibility.http.test.ts +0 -111
- package/tests/server/toolVisibility.test.ts +0 -129
- package/tests/tools/ai/analyzeSensors.test.ts +0 -227
- package/tests/tools/common.test.ts +0 -123
- package/tests/tools/homeassistant/handlers.test.ts +0 -872
- package/tests/tools/infra/invokeAction.test.ts +0 -280
- package/tests/tools/omada/graph.test.ts +0 -385
- package/tests/tools/omada/handlers.test.ts +0 -897
- package/tests/tools/omada/namespace.test.ts +0 -483
- package/tests/tools/registry.test.ts +0 -168
- package/tests/utils/config-validations.test.ts +0 -352
- package/tests/utils/jwt.test.ts +0 -115
- package/tests/utils/logger.test.ts +0 -327
- package/tests/utils/redact.test.ts +0 -105
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -38
package/src/haClient/history.ts
DELETED
|
@@ -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
|
-
}
|
package/src/haClient/index.ts
DELETED
|
@@ -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';
|
package/src/haClient/request.ts
DELETED
|
@@ -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
|
-
}
|
package/src/haClient/services.ts
DELETED
|
@@ -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
|
-
}
|
package/src/haClient/states.ts
DELETED
|
@@ -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
|
-
}
|