@shepherdjerred/home-assistant 0.1.0-dev.14474
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/CHANGELOG.md +8 -0
- package/LICENSE +674 -0
- package/README.md +126 -0
- package/demos/codegen-fixture.cast +40 -0
- package/demos/home-assistant-fixture.json +33 -0
- package/demos/record-codegen-demo.sh +119 -0
- package/dist/codegen/cli.d.ts +3 -0
- package/dist/codegen/cli.d.ts.map +1 -0
- package/dist/codegen/cli.js +118 -0
- package/dist/codegen/cli.js.map +1 -0
- package/dist/codegen/emit.d.ts +8 -0
- package/dist/codegen/emit.d.ts.map +1 -0
- package/dist/codegen/emit.js +143 -0
- package/dist/codegen/emit.js.map +1 -0
- package/dist/codegen/introspect.d.ts +193 -0
- package/dist/codegen/introspect.d.ts.map +1 -0
- package/dist/codegen/introspect.js +131 -0
- package/dist/codegen/introspect.js.map +1 -0
- package/dist/codegen/known-events.d.ts +10 -0
- package/dist/codegen/known-events.d.ts.map +1 -0
- package/dist/codegen/known-events.js +25 -0
- package/dist/codegen/known-events.js.map +1 -0
- package/dist/codegen/selector-to-ts.d.ts +15 -0
- package/dist/codegen/selector-to-ts.d.ts.map +1 -0
- package/dist/codegen/selector-to-ts.js +110 -0
- package/dist/codegen/selector-to-ts.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/rest/client.d.ts +34 -0
- package/dist/rest/client.d.ts.map +1 -0
- package/dist/rest/client.js +112 -0
- package/dist/rest/client.js.map +1 -0
- package/dist/rest/errors.d.ts +12 -0
- package/dist/rest/errors.d.ts.map +1 -0
- package/dist/rest/errors.js +23 -0
- package/dist/rest/errors.js.map +1 -0
- package/dist/rest/schemas.d.ts +124 -0
- package/dist/rest/schemas.d.ts.map +1 -0
- package/dist/rest/schemas.js +64 -0
- package/dist/rest/schemas.js.map +1 -0
- package/dist/schema/types.d.ts +138 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +12 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/shared/config.d.ts +6 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +4 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/ws/client.d.ts +58 -0
- package/dist/ws/client.d.ts.map +1 -0
- package/dist/ws/client.js +343 -0
- package/dist/ws/client.js.map +1 -0
- package/dist/ws/errors.d.ts +14 -0
- package/dist/ws/errors.d.ts.map +1 -0
- package/dist/ws/errors.js +27 -0
- package/dist/ws/errors.js.map +1 -0
- package/dist/ws/messages.d.ts +130 -0
- package/dist/ws/messages.d.ts.map +1 -0
- package/dist/ws/messages.js +79 -0
- package/dist/ws/messages.js.map +1 -0
- package/dist/ws/socket-helpers.d.ts +23 -0
- package/dist/ws/socket-helpers.d.ts.map +1 -0
- package/dist/ws/socket-helpers.js +73 -0
- package/dist/ws/socket-helpers.js.map +1 -0
- package/dist/ws/subscriptions.d.ts +37 -0
- package/dist/ws/subscriptions.d.ts.map +1 -0
- package/dist/ws/subscriptions.js +58 -0
- package/dist/ws/subscriptions.js.map +1 -0
- package/examples/basic-client.mjs +18 -0
- package/examples/codegen-fixture.mjs +95 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @shepherdjerred/home-assistant
|
|
2
|
+
|
|
3
|
+
Type-safe access to the [Home Assistant](https://www.home-assistant.io/) REST
|
|
4
|
+
and WebSocket APIs for Node.js and Bun. It validates wire data with Zod and can
|
|
5
|
+
generate a schema for one Home Assistant instance, so entity IDs, services, and
|
|
6
|
+
event types are checked before deployment.
|
|
7
|
+
|
|
8
|
+
Requires Node.js 24 or newer, or Bun.
|
|
9
|
+
|
|
10
|
+
## Why use this?
|
|
11
|
+
|
|
12
|
+
Home Assistant integrations tend to put entity IDs, service names, and payloads
|
|
13
|
+
in string literals. Those mistakes otherwise appear only when an automation
|
|
14
|
+
reaches a real home. This package validates API responses at runtime and can
|
|
15
|
+
generate a schema from one instance, moving those mistakes into the editor and
|
|
16
|
+
TypeScript before deployment.
|
|
17
|
+
|
|
18
|
+
Use it when you are building:
|
|
19
|
+
|
|
20
|
+
- A Node.js service that reads states or calls services with explicit error
|
|
21
|
+
types for API, authentication, and WebSocket failures.
|
|
22
|
+
- A typed automation codebase where `light.kitchen`, `light.turn_on`, and its
|
|
23
|
+
payload must match the Home Assistant instance it will control.
|
|
24
|
+
- A real-time integration that subscribes to Home Assistant events over the
|
|
25
|
+
WebSocket API instead of polling REST endpoints.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Install the package and provide a Home Assistant long-lived access token through
|
|
30
|
+
your environment:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun add @shepherdjerred/home-assistant
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { HomeAssistantRestClient } from "@shepherdjerred/home-assistant";
|
|
38
|
+
|
|
39
|
+
const { HA_TOKEN: token, HA_URL: baseUrl } = process.env;
|
|
40
|
+
if (baseUrl === undefined || token === undefined) {
|
|
41
|
+
throw new Error("Set HA_URL and HA_TOKEN before running this program.");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const client = new HomeAssistantRestClient({
|
|
45
|
+
baseUrl,
|
|
46
|
+
token,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const state = await client.getState("light.kitchen");
|
|
50
|
+
console.log(state.state);
|
|
51
|
+
|
|
52
|
+
await client.callService("light", "turn_on", {
|
|
53
|
+
entity_id: "light.kitchen",
|
|
54
|
+
brightness: 200,
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The runnable [`examples/basic-client.mjs`](examples/basic-client.mjs) requires
|
|
59
|
+
`HA_URL`, `HA_TOKEN`, and `HA_ENTITY_ID`; it never prints the token.
|
|
60
|
+
|
|
61
|
+
## Generate an instance schema
|
|
62
|
+
|
|
63
|
+
`ha-codegen` reads Home Assistant's states, services, events, and configuration
|
|
64
|
+
endpoints, then writes a TypeScript module that describes that instance.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ha-codegen \
|
|
68
|
+
--url "$HA_URL" \
|
|
69
|
+
--out src/generated/ha-schema.ts \
|
|
70
|
+
--name MyHomeSchema
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Do not pass credentials on the command line. The generated module includes
|
|
74
|
+
private entity IDs and service metadata, so add it to `.gitignore` and
|
|
75
|
+
regenerate it when your instance changes.
|
|
76
|
+
|
|
77
|
+
Use the generated schema as the client type parameter:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { HomeAssistantRestClient } from "@shepherdjerred/home-assistant";
|
|
81
|
+
import type { MyHomeSchema } from "./generated/ha-schema.js";
|
|
82
|
+
|
|
83
|
+
const { HA_TOKEN: token, HA_URL: baseUrl } = process.env;
|
|
84
|
+
if (baseUrl === undefined || token === undefined) {
|
|
85
|
+
throw new Error("Set HA_URL and HA_TOKEN before running this program.");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const client = new HomeAssistantRestClient<MyHomeSchema>({
|
|
89
|
+
baseUrl,
|
|
90
|
+
token,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## API reference
|
|
95
|
+
|
|
96
|
+
- `HomeAssistantRestClient` reads state and calls REST services.
|
|
97
|
+
- `HomeAssistantEventClient` connects to the WebSocket API and supports event
|
|
98
|
+
subscriptions, triggers, service calls, and state reads.
|
|
99
|
+
- `HaApiError`, `HaAuthError`, and `HaWebSocketError` distinguish API,
|
|
100
|
+
authentication, and socket failures.
|
|
101
|
+
- `ha-codegen --help` lists the command's supported options.
|
|
102
|
+
|
|
103
|
+
Both clients use the runtime's standard `fetch` and `WebSocket` implementations;
|
|
104
|
+
there is no Bun-only runtime dependency.
|
|
105
|
+
|
|
106
|
+
## Demo recording
|
|
107
|
+
|
|
108
|
+
The package includes a replayable asciicast generated from a synthetic local
|
|
109
|
+
Home Assistant API fixture—no live instance data or credentials are recorded.
|
|
110
|
+
It walks from the API data through the generated module to the compile-time
|
|
111
|
+
checks that catch an invalid entity, domain, service, or payload:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
asciinema play node_modules/@shepherdjerred/home-assistant/demos/codegen-fixture.cast
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Security and compatibility
|
|
118
|
+
|
|
119
|
+
Use a dedicated long-lived token with only the access your integration needs.
|
|
120
|
+
Keep `HA_TOKEN` in your environment or secret manager, never in shell history,
|
|
121
|
+
source code, or a generated schema. The package targets Node.js 24+ and Bun;
|
|
122
|
+
earlier Node versions are not supported.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
GPL-3.0-only.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{"version":3,"term":{"cols":100,"rows":28},"timestamp":1788642698,"idle_time_limit":12.0,"command":"./demos/record-codegen-demo.sh","title":"Home Assistant: API data to compiler-checked client","env":{"SHELL":"/opt/homebrew/bin/fish"}}
|
|
2
|
+
[0.016, "o", "\u001b[2J\u001b[H\u001b[1;36m━━━ Home Assistant -> a typed client ━━━\u001b[0m\r\n\r\n"]
|
|
3
|
+
[2.010, "o", "\u001b[2J\u001b[H\u001b[1;36m━━━ 1/4 Start with runtime API data ━━━\u001b[0m\r\nEntity IDs, service fields, and events begin as JSON from Home Assistant.\r\n"]
|
|
4
|
+
[5.018, "o", "\u001b[2m$ jq -C <selected fields> demos/home-assistant-fixture.json\u001b[0m\r\n\u001b[0;34m{\u001b[0m\r\n \u001b[1;35m\"entity_id\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32m\"light.demo_lamp\"\u001b[0m\u001b[0;34m,\u001b[0m\r\n \u001b[1;35m\"state\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32m\"off\"\u001b[0m\u001b[0;34m,\u001b[0m\r\n \u001b[1;35m\"service\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32m\"light.turn_on\"\u001b[0m\u001b[0;34m,\u001b[0m\r\n \u001b[1;35m\"field\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m{\u001b[0m\r\n \u001b[1;35m\"brightness\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32m\"number (optional)\"\u001b[0m\r\n \u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m\r\n \u001b[1;35m\"event\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32m\"state_changed\"\u001b[0m\r\n\u001b[0;34m}\u001b[0m\r\n"]
|
|
5
|
+
[4.008, "o", "\u001b[2J\u001b[H\u001b[1;36m━━━ 2/4 Generate the instance schema ━━━\u001b[0m\r\nha-codegen reads that API and emits a normal TypeScript module.\r\n"]
|
|
6
|
+
[4.008, "o", "\u001b[2m$ node examples/codegen-fixture.mjs | bat --language ts\u001b[0m\r\n"]
|
|
7
|
+
[0.161, "o", "\u001b[38;2;136;57;239mexport\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mentities\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;23;146;153m=\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\r\n"]
|
|
8
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mlight.demo_lamp\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mdomain\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mlight\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mattributes\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mfriendly_name\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\r\n"]
|
|
9
|
+
[0.001, "o", "\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mas\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n\r\n"]
|
|
10
|
+
[0.001, "o", "\u001b[38;2;136;57;239mexport\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mservices\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;23;146;153m=\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\r\n"]
|
|
11
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mlight\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\r\n"]
|
|
12
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mturn_on\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mfields\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\r\n"]
|
|
13
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mbrightness\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mtype\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mnumber\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mrequired\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;254;100;11mfalse\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\r\n"]
|
|
14
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\r\n\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\r\n"]
|
|
15
|
+
[0.000, "o", "\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mas\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
16
|
+
[0.000, "o", "\r\n"]
|
|
17
|
+
[0.000, "o", "\u001b[38;2;136;57;239mexport\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mevents\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;23;146;153m=\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105m[\u001b[0m\r\n"]
|
|
18
|
+
[0.000, "o", "\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mstate_changed\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\r\n"]
|
|
19
|
+
[0.000, "o", "\u001b[38;2;76;79;105m]\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mas\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n\r\n"]
|
|
20
|
+
[0.001, "o", "\u001b[38;2;136;57;239mexport\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;136;57;239mtype\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;223;142;29mEventData\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;23;146;153m=\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\r\n"]
|
|
21
|
+
[0.001, "o", "\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mstate_changed\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;76;79;105mentity_id\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;136;57;239mstring\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;76;79;105mold_state\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;136;57;239munknown\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;76;79;105mnew_state\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;136;57;239munknown\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
22
|
+
[0.000, "o", "\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
23
|
+
[12.012, "o", "\u001b[2J\u001b[H\u001b[1;36m━━━ 3/4 See the compiler reject a mistyped entity ━━━\u001b[0m\r\nThis temporary client imports the emitted HaSchema and deliberately misspells the entity ID.\r\n"]
|
|
24
|
+
[6.141, "o", "\u001b[2m$ bat invalid.ts\u001b[0m\r\n"]
|
|
25
|
+
[0.019, "o", "\u001b[38;2;136;57;239mimport\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mtype\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mHomeAssistantRestClient\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mfrom\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43m<package>/dist/index.js\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
26
|
+
[0.000, "o", "\u001b[38;2;136;57;239mimport\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mtype\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m{\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mHaSchema\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;124;127;147m}\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mfrom\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43m./ha-schema.js\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
27
|
+
[0.000, "o", "\r\n"]
|
|
28
|
+
[0.002, "o", "\u001b[38;2;136;57;239mdeclare\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mconst\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mclient\u001b[0m\u001b[38;2;23;146;153m:\u001b[0m\u001b[38;2;223;142;29m \u001b[0m\u001b[38;2;223;142;29mHomeAssistantRestClient\u001b[0m\u001b[38;2;4;165;229m<\u001b[0m\u001b[38;2;223;142;29mHaSchema\u001b[0m\u001b[38;2;4;165;229m>\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
29
|
+
[0.000, "o", "\u001b[38;2;76;79;105mclient\u001b[0m\u001b[38;2;23;146;153m.\u001b[0m\u001b[38;2;30;102;245mgetState\u001b[0m\u001b[38;2;76;79;105m(\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;64;160;43mlight.demo_lmp\u001b[0m\u001b[38;2;64;160;43m\"\u001b[0m\u001b[38;2;76;79;105m)\u001b[0m\u001b[38;2;124;127;147m;\u001b[0m\r\n"]
|
|
30
|
+
[5.012, "o", "\u001b[2m$ tsc --noEmit invalid.ts\u001b[0m\r\n"]
|
|
31
|
+
[0.260, "o", "\u001b[38;2;76;79;105minvalid\u001b[0m\u001b[38;2;23;146;153m.\u001b[0m\u001b[38;2;30;102;245mts\u001b[0m\u001b[38;2;76;79;105m(\u001b[0m\u001b[38;2;254;100;11m5\u001b[0m\u001b[38;2;124;127;147m,\u001b[0m\u001b[38;2;254;100;11m17\u001b[0m\u001b[38;2;76;79;105m)\u001b[0m\u001b[38;2;76;79;105m: \u001b[0m\u001b[38;2;76;79;105merror\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;23;146;153mTS2345\u001b[0m\u001b[38;2;124;127;147m:\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mArgument\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mof\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mtype\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m'\u001b[0m\u001b[38;2;64;160;43m\"light.demo_lmp\"\u001b[0m\u001b[38;2;64;160;43m'\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mis\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mnot\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105massignable\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mto\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mparameter\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;136;57;239mof\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;76;79;105mtype\u001b[0m\u001b[38;2;76;79;105m \u001b[0m\u001b[38;2;64;160;43m'\u001b[0m\u001b[38;2;64;160;43m\"light.demo_lamp\"\u001b[0m\u001b[38;2;64;160;43m'\u001b[0m\u001b[38;2;23;146;153m.\u001b[0m\r\n"]
|
|
32
|
+
[4.011, "o", "\u001b[2J\u001b[H\u001b[1;36m━━━ 4/4 Correct the entity before the request runs ━━━\u001b[0m\r\nThe generated schema gives the editor and CI the same precise feedback.\r\n"]
|
|
33
|
+
[5.009, "o", "\u001b[2m$ delta --color-only --paging never\u001b[0m\r\n"]
|
|
34
|
+
[0.016, "o", "diff --git a/client.ts b/client.ts\r\n--- a/client.ts\r\n+++ b/client.ts\r\n"]
|
|
35
|
+
[0.001, "o", "@@ -3,3 +3,3 @@\r\n"]
|
|
36
|
+
[0.007, "o", "\u001b[2m declare const client: HomeAssistantRestClient<HaSchema>;\u001b[0m\r\n"]
|
|
37
|
+
[0.000, "o", "\u001b[1;31m-client.getState(\"light.demo_lmp\");\u001b[0m\r\n\u001b[1;32m+client.getState(\"light.demo_lamp\");\u001b[0m\r\n\u001b[2m \u001b[0m\r\n"]
|
|
38
|
+
[0.000, "o", "\u001b[1;31m-// The typo would become a failing API request.\u001b[0m\r\n\u001b[1;32m+// The typo is corrected before the request can run.\u001b[0m\r\n"]
|
|
39
|
+
[10.013, "o", "\r\n\u001b[1;32m✓ The generated schema catches the typo before runtime.\u001b[0m\r\n"]
|
|
40
|
+
[0.003, "x", "0"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"states": [
|
|
3
|
+
{
|
|
4
|
+
"entity_id": "light.demo_lamp",
|
|
5
|
+
"state": "off",
|
|
6
|
+
"attributes": {
|
|
7
|
+
"friendly_name": "Demo lamp"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"services": [
|
|
12
|
+
{
|
|
13
|
+
"domain": "light",
|
|
14
|
+
"services": {
|
|
15
|
+
"turn_on": {
|
|
16
|
+
"fields": {
|
|
17
|
+
"brightness": {
|
|
18
|
+
"required": false,
|
|
19
|
+
"selector": {
|
|
20
|
+
"number": {}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"events": [
|
|
29
|
+
{
|
|
30
|
+
"event": "state_changed"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
cd "$(dirname "$0")/.."
|
|
5
|
+
|
|
6
|
+
pause_for() {
|
|
7
|
+
word_count="$(printf '%s' "$1" | wc -w | tr -d '[:space:]')"
|
|
8
|
+
seconds=$(((word_count + 3) / 4))
|
|
9
|
+
|
|
10
|
+
if ((seconds < 2)); then
|
|
11
|
+
seconds=2
|
|
12
|
+
elif ((seconds > 12)); then
|
|
13
|
+
seconds=12
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
sleep "$seconds"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
section() {
|
|
20
|
+
printf '\033[2J\033[H\033[1;36m━━━ %s ━━━\033[0m\n%s\n' "$1" "$2"
|
|
21
|
+
pause_for "$1 $2"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
section \
|
|
25
|
+
"Home Assistant -> a typed client" \
|
|
26
|
+
""
|
|
27
|
+
|
|
28
|
+
section \
|
|
29
|
+
"1/4 Start with runtime API data" \
|
|
30
|
+
"Entity IDs, service fields, and events begin as JSON from Home Assistant."
|
|
31
|
+
fixture_summary="$(jq -C '{
|
|
32
|
+
entity_id: .states[0].entity_id,
|
|
33
|
+
state: .states[0].state,
|
|
34
|
+
service: "light.turn_on",
|
|
35
|
+
field: { brightness: "number (optional)" },
|
|
36
|
+
event: .events[0].event
|
|
37
|
+
}' demos/home-assistant-fixture.json)"
|
|
38
|
+
printf '\033[2m$ jq -C <selected fields> demos/home-assistant-fixture.json\033[0m\n%s\n' "$fixture_summary"
|
|
39
|
+
pause_for "$fixture_summary"
|
|
40
|
+
|
|
41
|
+
section \
|
|
42
|
+
"2/4 Generate the instance schema" \
|
|
43
|
+
"ha-codegen reads that API and emits a normal TypeScript module."
|
|
44
|
+
printf '\033[2m$ node examples/codegen-fixture.mjs | bat --language ts\033[0m\n'
|
|
45
|
+
generated_schema_preview="$(node examples/codegen-fixture.mjs | sed -n '6,25p')"
|
|
46
|
+
printf '%s\n' "$generated_schema_preview" \
|
|
47
|
+
| bat --color=always --language=typescript --paging=never --plain --style=numbers \
|
|
48
|
+
--line-range 1:20
|
|
49
|
+
pause_for "$generated_schema_preview"
|
|
50
|
+
|
|
51
|
+
section \
|
|
52
|
+
"3/4 See the compiler reject a mistyped entity" \
|
|
53
|
+
"This temporary client imports the emitted HaSchema and deliberately misspells the entity ID."
|
|
54
|
+
demo_directory="$(mktemp -d -t home-assistant-demo)"
|
|
55
|
+
trap 'rm -r -- "$demo_directory"' EXIT
|
|
56
|
+
package_directory="$(pwd)"
|
|
57
|
+
generated_schema="$demo_directory/ha-schema.ts"
|
|
58
|
+
invalid_client="$demo_directory/invalid.ts"
|
|
59
|
+
diagnostics="$demo_directory/diagnostics.txt"
|
|
60
|
+
node examples/codegen-fixture.mjs >"$generated_schema"
|
|
61
|
+
printf '%s\n' \
|
|
62
|
+
"import type { HomeAssistantRestClient } from \"${package_directory}/dist/index.js\";" \
|
|
63
|
+
'import type { HaSchema } from "./ha-schema.js";' \
|
|
64
|
+
'' \
|
|
65
|
+
'declare const client: HomeAssistantRestClient<HaSchema>;' \
|
|
66
|
+
'client.getState("light.demo_lmp");' \
|
|
67
|
+
>"$invalid_client"
|
|
68
|
+
printf '\033[2m$ bat invalid.ts\033[0m\n'
|
|
69
|
+
invalid_client_preview="$(sed "s#${package_directory}#<package>#g" "$invalid_client")"
|
|
70
|
+
printf '%s\n' "$invalid_client_preview" \
|
|
71
|
+
| bat --color=always --language=typescript --paging=never --plain --style=numbers
|
|
72
|
+
pause_for "$invalid_client_preview"
|
|
73
|
+
printf '\033[2m$ tsc --noEmit invalid.ts\033[0m\n'
|
|
74
|
+
if (
|
|
75
|
+
cd "$demo_directory"
|
|
76
|
+
PATH="$package_directory/node_modules/@typescript/native/bin:$PATH" tsc \
|
|
77
|
+
--noEmit \
|
|
78
|
+
--ignoreConfig \
|
|
79
|
+
--strict \
|
|
80
|
+
--module NodeNext \
|
|
81
|
+
--moduleResolution NodeNext \
|
|
82
|
+
--target ES2024 \
|
|
83
|
+
invalid.ts
|
|
84
|
+
) >"$diagnostics" 2>&1; then
|
|
85
|
+
printf '\033[1;31mExpected the invalid entity ID to fail type checking.\033[0m\n' >&2
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
diagnostic_preview="$(<"$diagnostics")"
|
|
89
|
+
printf '%s\n' "$diagnostic_preview" \
|
|
90
|
+
| bat --color=always --language=typescript --paging=never --plain --style=numbers
|
|
91
|
+
pause_for "$diagnostic_preview"
|
|
92
|
+
|
|
93
|
+
section \
|
|
94
|
+
"4/4 Correct the entity before the request runs" \
|
|
95
|
+
"The generated schema gives the editor and CI the same precise feedback."
|
|
96
|
+
printf '\033[2m$ delta --color-only --paging never\033[0m\n'
|
|
97
|
+
diff_preview="$(printf '%s\n' \
|
|
98
|
+
'diff --git a/client.ts b/client.ts' \
|
|
99
|
+
'--- a/client.ts' \
|
|
100
|
+
'+++ b/client.ts' \
|
|
101
|
+
'@@ -3,3 +3,3 @@' \
|
|
102
|
+
' declare const client: HomeAssistantRestClient<HaSchema>;' \
|
|
103
|
+
'-client.getState("light.demo_lmp");' \
|
|
104
|
+
'+client.getState("light.demo_lamp");' \
|
|
105
|
+
' ' \
|
|
106
|
+
'-// The typo would become a failing API request.' \
|
|
107
|
+
'+// The typo is corrected before the request can run.')"
|
|
108
|
+
printf '%s\n' "$diff_preview" \
|
|
109
|
+
| delta \
|
|
110
|
+
--color-only \
|
|
111
|
+
--paging=never \
|
|
112
|
+
--syntax-theme Dracula \
|
|
113
|
+
--minus-style 'red bold' \
|
|
114
|
+
--minus-emph-style 'red bold' \
|
|
115
|
+
--plus-style 'green bold' \
|
|
116
|
+
--plus-emph-style 'green bold' \
|
|
117
|
+
--zero-style dim
|
|
118
|
+
pause_for "$diff_preview"
|
|
119
|
+
printf '\n\033[1;32m✓ The generated schema catches the typo before runtime.\033[0m\n'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/codegen/cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { emitSchemaModule } from "./emit.js";
|
|
5
|
+
import { introspect } from "./introspect.js";
|
|
6
|
+
const HELP_TEXT = `
|
|
7
|
+
ha-codegen - Generate a typed Home Assistant schema from a live instance
|
|
8
|
+
|
|
9
|
+
USAGE:
|
|
10
|
+
HA_TOKEN=<long-lived-access-token> ha-codegen --url <base-url> --out <path> [options]
|
|
11
|
+
|
|
12
|
+
OPTIONS:
|
|
13
|
+
--url <base-url> Home Assistant base URL (defaults to HA_URL)
|
|
14
|
+
--out <path> Generated TypeScript module path (required)
|
|
15
|
+
--name <schema-name> Generated schema type name (default: HaSchema)
|
|
16
|
+
--help, -h Show this help message
|
|
17
|
+
|
|
18
|
+
The generated module contains entity IDs and service definitions from your
|
|
19
|
+
instance. Treat it as private: gitignore it and regenerate it when needed.
|
|
20
|
+
`;
|
|
21
|
+
function parseArgs(argv) {
|
|
22
|
+
const map = new Map();
|
|
23
|
+
let help = false;
|
|
24
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
25
|
+
const arg = argv[i];
|
|
26
|
+
if (arg === "--help" || arg === "-h") {
|
|
27
|
+
help = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const key = parseOptionKey(arg);
|
|
31
|
+
const next = argv[i + 1];
|
|
32
|
+
if (next?.startsWith("--") !== false) {
|
|
33
|
+
map.set(key, "true");
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
map.set(key, next);
|
|
37
|
+
i += 1;
|
|
38
|
+
}
|
|
39
|
+
const url = map.get("url") ?? environmentValue("HA_URL");
|
|
40
|
+
const token = environmentValue("HA_TOKEN");
|
|
41
|
+
const out = map.get("out");
|
|
42
|
+
const name = map.get("name") ?? "HaSchema";
|
|
43
|
+
if (help) {
|
|
44
|
+
return { url: "", token: "", out: "", name: "HaSchema", help: true };
|
|
45
|
+
}
|
|
46
|
+
if (url === undefined || url === "") {
|
|
47
|
+
fail("Missing --url (or HA_URL env var)");
|
|
48
|
+
}
|
|
49
|
+
if (token === undefined || token === "") {
|
|
50
|
+
fail("Missing HA_TOKEN environment variable");
|
|
51
|
+
}
|
|
52
|
+
if (out === undefined || out === "") {
|
|
53
|
+
fail("Missing --out <path>");
|
|
54
|
+
}
|
|
55
|
+
return { url, token, out, name, help: false };
|
|
56
|
+
}
|
|
57
|
+
function parseOptionKey(arg) {
|
|
58
|
+
if (arg?.startsWith("--") !== true) {
|
|
59
|
+
fail(`Unexpected argument: ${arg ?? ""}`);
|
|
60
|
+
}
|
|
61
|
+
const key = arg.slice(2);
|
|
62
|
+
if (key !== "url" && key !== "out" && key !== "name") {
|
|
63
|
+
fail(`Unknown option: ${arg}`);
|
|
64
|
+
}
|
|
65
|
+
return key;
|
|
66
|
+
}
|
|
67
|
+
function environmentValue(name) {
|
|
68
|
+
return globalThis.process.env[name];
|
|
69
|
+
}
|
|
70
|
+
function fail(message) {
|
|
71
|
+
console.error(`ha-codegen: ${message}`);
|
|
72
|
+
console.error("Usage: ha-codegen --url <base-url> --out <path> [--name <SchemaName>]");
|
|
73
|
+
process.exit(2);
|
|
74
|
+
}
|
|
75
|
+
async function main() {
|
|
76
|
+
const args = parseArgs(process.argv.slice(2));
|
|
77
|
+
if (args.help) {
|
|
78
|
+
process.stdout.write(HELP_TEXT);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const intro = await introspect(args.url, args.token);
|
|
82
|
+
const sourceHost = safeHost(args.url);
|
|
83
|
+
const module = emitSchemaModule(intro, {
|
|
84
|
+
schemaName: args.name,
|
|
85
|
+
sourceHost,
|
|
86
|
+
generatedAt: new Date().toISOString(),
|
|
87
|
+
});
|
|
88
|
+
const outPath = path.resolve(args.out);
|
|
89
|
+
await mkdir(path.dirname(outPath), { recursive: true });
|
|
90
|
+
await writeFile(outPath, module, "utf8");
|
|
91
|
+
console.warn(`ha-codegen: wrote ${String(intro.states.length)} entities, ` +
|
|
92
|
+
`${String(totalServices(intro))} services, ` +
|
|
93
|
+
`${String(intro.events.length)} event types → ${outPath}`);
|
|
94
|
+
}
|
|
95
|
+
function totalServices(intro) {
|
|
96
|
+
let n = 0;
|
|
97
|
+
for (const entry of intro.services) {
|
|
98
|
+
n += Object.keys(entry.services).length;
|
|
99
|
+
}
|
|
100
|
+
return n;
|
|
101
|
+
}
|
|
102
|
+
function safeHost(url) {
|
|
103
|
+
try {
|
|
104
|
+
return new URL(url).host;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return "<unparsed>";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await main();
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
115
|
+
console.error(`ha-codegen: ${message}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/codegen/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HaIntrospection } from "./introspect.js";
|
|
2
|
+
export type EmitOptions = {
|
|
3
|
+
schemaName: string;
|
|
4
|
+
sourceHost: string;
|
|
5
|
+
generatedAt: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function emitSchemaModule(intro: HaIntrospection, options: EmitOptions): string;
|
|
8
|
+
//# sourceMappingURL=emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/codegen/emit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAQvD,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,WAAW,GACnB,MAAM,CAkBR"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { KNOWN_EVENT_DATA } from "./known-events.js";
|
|
2
|
+
import { fieldMetaFromSpec, targetDomainFromSpec, } from "./selector-to-ts.js";
|
|
3
|
+
export function emitSchemaModule(intro, options) {
|
|
4
|
+
const entitiesLiteral = emitEntities(intro);
|
|
5
|
+
const servicesLiteral = emitServices(intro);
|
|
6
|
+
const eventsLiteral = emitEvents(intro);
|
|
7
|
+
const eventDataLiteral = emitEventData(intro);
|
|
8
|
+
return [
|
|
9
|
+
header(options),
|
|
10
|
+
entitiesLiteral,
|
|
11
|
+
"",
|
|
12
|
+
servicesLiteral,
|
|
13
|
+
"",
|
|
14
|
+
eventsLiteral,
|
|
15
|
+
"",
|
|
16
|
+
eventDataLiteral,
|
|
17
|
+
"",
|
|
18
|
+
emitSchemaType(options.schemaName),
|
|
19
|
+
"",
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
function header(options) {
|
|
23
|
+
return [
|
|
24
|
+
`// AUTO-GENERATED by @shepherdjerred/home-assistant ha-codegen — do not edit, do not commit.`,
|
|
25
|
+
`// Source host: ${options.sourceHost}`,
|
|
26
|
+
`// Generated at: ${options.generatedAt}`,
|
|
27
|
+
`/* eslint-disable */`,
|
|
28
|
+
``,
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
function emitEntities(intro) {
|
|
32
|
+
const entries = [];
|
|
33
|
+
const sorted = intro.states.toSorted((a, b) => a.entity_id.localeCompare(b.entity_id));
|
|
34
|
+
for (const state of sorted) {
|
|
35
|
+
const domain = state.entity_id.includes(".")
|
|
36
|
+
? state.entity_id.slice(0, state.entity_id.indexOf("."))
|
|
37
|
+
: "";
|
|
38
|
+
const attributes = emitAttributeSample(state.attributes);
|
|
39
|
+
entries.push(` ${JSON.stringify(state.entity_id)}: { domain: ${JSON.stringify(domain)}, attributes: ${attributes} },`);
|
|
40
|
+
}
|
|
41
|
+
return `export const entities = {\n${entries.join("\n")}\n} as const;`;
|
|
42
|
+
}
|
|
43
|
+
function emitAttributeSample(attributes) {
|
|
44
|
+
const props = [];
|
|
45
|
+
const sortedKeys = Object.keys(attributes).toSorted((a, b) => a.localeCompare(b));
|
|
46
|
+
for (const key of sortedKeys) {
|
|
47
|
+
const value = attributes[key];
|
|
48
|
+
const sample = sampleValueLiteral(value);
|
|
49
|
+
if (sample !== undefined) {
|
|
50
|
+
props.push(`${JSON.stringify(key)}: ${sample}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return props.length === 0 ? `{}` : `{ ${props.join(", ")} }`;
|
|
54
|
+
}
|
|
55
|
+
function sampleValueLiteral(value) {
|
|
56
|
+
if (typeof value === "string") {
|
|
57
|
+
return `""`;
|
|
58
|
+
}
|
|
59
|
+
if (typeof value === "number") {
|
|
60
|
+
return `0`;
|
|
61
|
+
}
|
|
62
|
+
if (typeof value === "boolean") {
|
|
63
|
+
return `false`;
|
|
64
|
+
}
|
|
65
|
+
if (value === null) {
|
|
66
|
+
return `null`;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return `[] as readonly unknown[]`;
|
|
70
|
+
}
|
|
71
|
+
if (typeof value === "object") {
|
|
72
|
+
return `{}`;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
function emitServices(intro) {
|
|
77
|
+
const domains = [];
|
|
78
|
+
const sorted = intro.services.toSorted((a, b) => a.domain.localeCompare(b.domain));
|
|
79
|
+
for (const entry of sorted) {
|
|
80
|
+
const services = [];
|
|
81
|
+
const serviceEntries = Object.entries(entry.services).toSorted(([a], [b]) => a.localeCompare(b));
|
|
82
|
+
for (const [serviceName, spec] of serviceEntries) {
|
|
83
|
+
const fields = Object.entries(spec.fields ?? {})
|
|
84
|
+
.toSorted(([a], [b]) => a.localeCompare(b))
|
|
85
|
+
.map(([fieldName, fieldSpec]) => {
|
|
86
|
+
const meta = fieldMetaFromSpec(fieldSpec);
|
|
87
|
+
return ` ${JSON.stringify(fieldName)}: ${emitFieldMeta(meta)}`;
|
|
88
|
+
})
|
|
89
|
+
.join(",\n");
|
|
90
|
+
const targetDomain = targetDomainFromSpec(spec.target);
|
|
91
|
+
const targetLiteral = targetDomain === undefined
|
|
92
|
+
? ""
|
|
93
|
+
: `, target: { domain: ${JSON.stringify(targetDomain)} }`;
|
|
94
|
+
const fieldsBlock = fields === "" ? `{}` : `{\n${fields}\n }`;
|
|
95
|
+
services.push(` ${JSON.stringify(serviceName)}: { fields: ${fieldsBlock}${targetLiteral} },`);
|
|
96
|
+
}
|
|
97
|
+
domains.push(` ${JSON.stringify(entry.domain)}: {\n${services.join("\n")}\n },`);
|
|
98
|
+
}
|
|
99
|
+
return `export const services = {\n${domains.join("\n")}\n} as const;`;
|
|
100
|
+
}
|
|
101
|
+
function emitFieldMeta(meta) {
|
|
102
|
+
const parts = [`type: ${JSON.stringify(meta.type)}`];
|
|
103
|
+
parts.push(`required: ${meta.required ? "true" : "false"}`);
|
|
104
|
+
if (meta.domain !== undefined) {
|
|
105
|
+
parts.push(`domain: ${JSON.stringify(meta.domain)}`);
|
|
106
|
+
}
|
|
107
|
+
if (meta.options !== undefined) {
|
|
108
|
+
const opts = meta.options.map((v) => JSON.stringify(v)).join(", ");
|
|
109
|
+
parts.push(`options: [${opts}] as const`);
|
|
110
|
+
}
|
|
111
|
+
return `{ ${parts.join(", ")} }`;
|
|
112
|
+
}
|
|
113
|
+
function emitEvents(intro) {
|
|
114
|
+
const names = intro.events
|
|
115
|
+
.map((e) => e.event)
|
|
116
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
117
|
+
const literal = names.length === 0
|
|
118
|
+
? "[]"
|
|
119
|
+
: `[\n${names.map((n) => ` ${JSON.stringify(n)},`).join("\n")}\n]`;
|
|
120
|
+
return `export const events = ${literal} as const;`;
|
|
121
|
+
}
|
|
122
|
+
function emitEventData(intro) {
|
|
123
|
+
const lines = [];
|
|
124
|
+
const names = new Set(intro.events.map((e) => e.event));
|
|
125
|
+
for (const [name, shape] of Object.entries(KNOWN_EVENT_DATA).toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
126
|
+
if (!names.has(name)) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
lines.push(` ${JSON.stringify(name)}: ${shape};`);
|
|
130
|
+
}
|
|
131
|
+
return `export type EventData = {\n${lines.join("\n")}\n};`;
|
|
132
|
+
}
|
|
133
|
+
function emitSchemaType(name) {
|
|
134
|
+
return [
|
|
135
|
+
`export type ${name} = {`,
|
|
136
|
+
` entities: typeof entities;`,
|
|
137
|
+
` services: typeof services;`,
|
|
138
|
+
` events: (typeof events)[number];`,
|
|
139
|
+
` eventData: EventData;`,
|
|
140
|
+
`};`,
|
|
141
|
+
].join("\n");
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=emit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../../src/codegen/emit.ts"],"names":[],"mappings":"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"}
|