ha-opencode 0.1.1 → 0.2.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.
@@ -1,191 +0,0 @@
1
- blueprint:
2
- name: OpenCode State Notifications
3
- description: >
4
- Send notifications to your mobile device when OpenCode sessions need attention.
5
- Notifies when:
6
- - A task completes (idle after working)
7
- - Permission is required (with approve/reject buttons)
8
- - An error occurs
9
-
10
- Works with both iOS and Android via the Home Assistant Companion app.
11
-
12
- Requires the Permission Response Handler blueprint to handle approve/reject actions.
13
- domain: automation
14
- author: ha-opencode
15
- source_url: https://github.com/your-repo/ha-opencode/blob/main/blueprints/opencode_state_notifications.yaml
16
- input:
17
- notify_service:
18
- name: Notification Service
19
- description: >
20
- The notification service to use (e.g., notify.mobile_app_your_phone).
21
- selector:
22
- text:
23
- default: "notify.mobile_app_phone"
24
-
25
- notify_on_complete:
26
- name: Notify on Task Complete
27
- description: Send a notification when a task completes (idle after working).
28
- default: true
29
- selector:
30
- boolean:
31
-
32
- notify_on_permission:
33
- name: Notify on Permission Required
34
- description: Send a notification with approve/reject buttons when permission is needed.
35
- default: true
36
- selector:
37
- boolean:
38
-
39
- notify_on_error:
40
- name: Notify on Error
41
- description: Send a notification when an error occurs.
42
- default: true
43
- selector:
44
- boolean:
45
-
46
- notification_channel:
47
- name: Notification Channel (Android)
48
- description: >
49
- Android notification channel name.
50
- You can create channels in the Companion app settings.
51
- default: "OpenCode"
52
- selector:
53
- text:
54
-
55
- dashboard_path:
56
- name: Dashboard Path
57
- description: >
58
- Path to your OpenCode dashboard for click action (e.g., /lovelace/opencode).
59
- Leave empty to use default behavior.
60
- default: "/lovelace/opencode"
61
- selector:
62
- text:
63
-
64
- mode: parallel
65
- max_exceeded: silent
66
-
67
- trigger:
68
- - platform: mqtt
69
- topic: "opencode/+/state"
70
- id: state_change
71
-
72
- variables:
73
- # The new state from the MQTT payload
74
- new_state: "{{ trigger.payload }}"
75
-
76
- # Extract device_id from topic: opencode/{device_id}/state
77
- device_id: "{{ trigger.topic.split('/')[1] }}"
78
-
79
- # Build the state_topic_base for entity lookup
80
- state_topic_base: "opencode/{{ device_id }}"
81
-
82
- # Find the device_id entity that has this state_topic_base in its attributes
83
- device_id_entity: >
84
- {% set ns = namespace(found='') %}
85
- {% for entity in states.sensor %}
86
- {% if entity.attributes.get('state_topic_base', '') == state_topic_base %}
87
- {% set ns.found = entity.entity_id %}
88
- {% endif %}
89
- {% endfor %}
90
- {{ ns.found }}
91
-
92
- # Check if we found the entity
93
- entity_found: "{{ device_id_entity != '' }}"
94
-
95
- # Derive other entity IDs from the device_id entity (they share the same base)
96
- entity_base: "{{ device_id_entity | replace('_device_id', '') if entity_found else '' }}"
97
- state_entity: "{{ (entity_base ~ '_state') if entity_found else '' }}"
98
- session_entity: "{{ (entity_base ~ '_session_title') if entity_found else '' }}"
99
-
100
- # Get values from entities (with defaults)
101
- previous_state: "{{ state_attr(state_entity, 'previous_state') | default('unknown') if entity_found else 'unknown' }}"
102
- project_name: "{{ state_attr(device_id_entity, 'device_name') | default('OpenCode') if entity_found else 'OpenCode' }}"
103
- command_topic: "{{ state_attr(device_id_entity, 'command_topic') | default('') if entity_found else '' }}"
104
- permission_id: "{{ state_attr(state_entity, 'permission_id') | default('') if entity_found else '' }}"
105
- permission_title: "{{ state_attr(state_entity, 'permission_title') | default('Permission needed') if entity_found else 'Permission needed' }}"
106
- error_message: "{{ state_attr(state_entity, 'error_message') | default('An error occurred') if entity_found else 'An error occurred' }}"
107
- session_title: "{{ states(session_entity) | default('Task finished') if entity_found else 'Task finished' }}"
108
-
109
- # Determine if this is a transition we care about
110
- was_working: "{{ previous_state == 'working' }}"
111
-
112
- action:
113
- # Log for debugging (can be removed later)
114
- - service: system_log.write
115
- data:
116
- message: >
117
- OpenCode notification trigger: new_state={{ new_state }}, previous_state={{ previous_state }},
118
- device_id={{ device_id }}, entity_found={{ entity_found }}, was_working={{ was_working }}
119
- level: info
120
- logger: ha-opencode.blueprint
121
-
122
- - choose:
123
- # Permission Required (always notify, regardless of previous state)
124
- - conditions:
125
- - condition: template
126
- value_template: "{{ new_state == 'waiting_permission' }}"
127
- - condition: template
128
- value_template: !input notify_on_permission
129
- sequence:
130
- # Small delay to let permission attributes arrive
131
- - delay:
132
- milliseconds: 500
133
- - service: !input notify_service
134
- data:
135
- title: "OpenCode: Permission Required"
136
- message: "{{ project_name }}: {{ permission_title }}"
137
- data:
138
- tag: "opencode-permission-{{ device_id }}"
139
- channel: !input notification_channel
140
- importance: high
141
- priority: high
142
- ttl: 0
143
- clickAction: !input dashboard_path
144
- url: !input dashboard_path
145
- actions:
146
- - action: "OPENCODE_APPROVE_{{ device_id }}"
147
- title: "Approve"
148
- - action: "OPENCODE_REJECT_{{ device_id }}"
149
- title: "Reject"
150
- # Pass data needed for permission response
151
- action_data:
152
- device_id: "{{ device_id }}"
153
- permission_id: "{{ permission_id }}"
154
- command_topic: "{{ command_topic }}"
155
-
156
- # Error Occurred (only after working)
157
- - conditions:
158
- - condition: template
159
- value_template: "{{ new_state == 'error' and was_working }}"
160
- - condition: template
161
- value_template: !input notify_on_error
162
- sequence:
163
- - service: !input notify_service
164
- data:
165
- title: "OpenCode: Error"
166
- message: "{{ project_name }}: {{ error_message }}"
167
- data:
168
- tag: "opencode-error-{{ device_id }}"
169
- channel: !input notification_channel
170
- importance: high
171
- priority: high
172
- ttl: 0
173
- clickAction: !input dashboard_path
174
- url: !input dashboard_path
175
-
176
- # Work Complete (idle after working)
177
- - conditions:
178
- - condition: template
179
- value_template: "{{ new_state == 'idle' and was_working }}"
180
- - condition: template
181
- value_template: !input notify_on_complete
182
- sequence:
183
- - service: !input notify_service
184
- data:
185
- title: "OpenCode: Task Complete"
186
- message: "{{ project_name }}: {{ session_title }}"
187
- data:
188
- tag: "opencode-complete-{{ device_id }}"
189
- channel: !input notification_channel
190
- clickAction: !input dashboard_path
191
- url: !input dashboard_path
package/dist/cleanup.d.ts DELETED
@@ -1,30 +0,0 @@
1
- import type { MqttWrapper } from "./mqtt.js";
2
- import type { HaConfig } from "./config.js";
3
- export interface CleanupConfig {
4
- maxAgeDays: number;
5
- haConfig: HaConfig;
6
- }
7
- export interface CleanupResult {
8
- sessionsRemoved: number;
9
- sessionIds: string[];
10
- }
11
- /**
12
- * Clean up stale OpenCode sessions from Home Assistant.
13
- * Removes entities for sessions that haven't been active in `maxAgeDays` days.
14
- *
15
- * @param mqtt - MQTT client wrapper
16
- * @param config - Cleanup configuration
17
- * @returns Result with count and IDs of removed sessions
18
- */
19
- export declare function cleanupStaleSessions(mqtt: MqttWrapper, config: CleanupConfig): Promise<CleanupResult>;
20
- /**
21
- * Manually trigger cleanup and publish results to MQTT.
22
- * Called via the cleanup_stale_sessions command.
23
- */
24
- export declare function cleanupStaleSessionsManual(mqtt: MqttWrapper, config: CleanupConfig): Promise<void>;
25
- /**
26
- * Run cleanup in the background (non-blocking).
27
- * Logs results but doesn't throw on error.
28
- */
29
- export declare function runCleanupInBackground(mqtt: MqttWrapper, config: CleanupConfig): void;
30
- //# sourceMappingURL=cleanup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AA+ED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,aAAa,CAAC,CA8BxB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,aAAa,GACpB,IAAI,CAYN"}
package/dist/cleanup.js DELETED
@@ -1,125 +0,0 @@
1
- import { Discovery } from "./discovery.js";
2
- const CLEANUP_RESPONSE_TOPIC = "opencode/cleanup/response";
3
- /**
4
- * Find all OpenCode sessions by subscribing to last_activity topics.
5
- * Returns a map of deviceId -> last activity timestamp.
6
- */
7
- async function discoverSessions(mqtt, timeoutMs = 5000) {
8
- const sessions = new Map();
9
- return new Promise((resolve) => {
10
- const topic = "opencode/+/last_activity";
11
- let timeoutId;
12
- const handleMessage = (_topic, payload) => {
13
- // Extract device ID from topic: opencode/{deviceId}/last_activity
14
- const match = _topic.match(/^opencode\/([^/]+)\/last_activity$/);
15
- if (match) {
16
- const deviceId = match[1];
17
- const timestamp = new Date(payload);
18
- if (!isNaN(timestamp.getTime())) {
19
- sessions.set(deviceId, timestamp);
20
- }
21
- }
22
- };
23
- // Subscribe and collect retained messages
24
- mqtt.subscribe(topic, handleMessage).then(() => {
25
- // Wait for retained messages to arrive, then resolve
26
- timeoutId = setTimeout(async () => {
27
- try {
28
- await mqtt.unsubscribe(topic);
29
- }
30
- catch {
31
- // Ignore unsubscribe errors
32
- }
33
- resolve(sessions);
34
- }, timeoutMs);
35
- }).catch(() => {
36
- clearTimeout(timeoutId);
37
- resolve(sessions);
38
- });
39
- });
40
- }
41
- /**
42
- * Remove a stale session's entities from Home Assistant.
43
- */
44
- async function removeSession(mqtt, deviceId, haConfig) {
45
- const entityKeys = Discovery.getEntityKeys();
46
- const discoveryPrefix = haConfig.discoveryPrefix;
47
- // Publish empty config to remove each entity from HA
48
- for (const key of entityKeys) {
49
- const configTopic = `${discoveryPrefix}/sensor/${deviceId}/${key}/config`;
50
- await mqtt.publish(configTopic, "", true);
51
- }
52
- // Clear retained state messages
53
- const stateTopicBase = `opencode/${deviceId}`;
54
- for (const key of entityKeys) {
55
- await mqtt.publish(`${stateTopicBase}/${key}`, "", true);
56
- // Also clear attributes topics for entities that have them
57
- if (key === "device_id" || key === "state" || key === "permission") {
58
- await mqtt.publish(`${stateTopicBase}/${key}/attributes`, "", true);
59
- }
60
- }
61
- // Clear availability topic
62
- await mqtt.publish(`${stateTopicBase}/availability`, "", true);
63
- }
64
- /**
65
- * Clean up stale OpenCode sessions from Home Assistant.
66
- * Removes entities for sessions that haven't been active in `maxAgeDays` days.
67
- *
68
- * @param mqtt - MQTT client wrapper
69
- * @param config - Cleanup configuration
70
- * @returns Result with count and IDs of removed sessions
71
- */
72
- export async function cleanupStaleSessions(mqtt, config) {
73
- const maxAgeMs = config.maxAgeDays * 24 * 60 * 60 * 1000;
74
- const cutoffDate = new Date(Date.now() - maxAgeMs);
75
- // Discover all sessions
76
- const sessions = await discoverSessions(mqtt);
77
- const removedSessionIds = [];
78
- for (const [deviceId, lastActivity] of sessions) {
79
- if (lastActivity < cutoffDate) {
80
- try {
81
- await removeSession(mqtt, deviceId, config.haConfig);
82
- removedSessionIds.push(deviceId);
83
- console.log(`[ha-opencode] Cleaned up stale session: ${deviceId} (last active: ${lastActivity.toISOString()})`);
84
- }
85
- catch (err) {
86
- console.error(`[ha-opencode] Failed to clean up session ${deviceId}:`, err);
87
- }
88
- }
89
- }
90
- return {
91
- sessionsRemoved: removedSessionIds.length,
92
- sessionIds: removedSessionIds,
93
- };
94
- }
95
- /**
96
- * Manually trigger cleanup and publish results to MQTT.
97
- * Called via the cleanup_stale_sessions command.
98
- */
99
- export async function cleanupStaleSessionsManual(mqtt, config) {
100
- const result = await cleanupStaleSessions(mqtt, config);
101
- // Publish result to global cleanup response topic
102
- await mqtt.publish(CLEANUP_RESPONSE_TOPIC, {
103
- type: "cleanup_result",
104
- sessions_removed: result.sessionsRemoved,
105
- session_ids: result.sessionIds,
106
- max_age_days: config.maxAgeDays,
107
- timestamp: new Date().toISOString(),
108
- }, false);
109
- }
110
- /**
111
- * Run cleanup in the background (non-blocking).
112
- * Logs results but doesn't throw on error.
113
- */
114
- export function runCleanupInBackground(mqtt, config) {
115
- cleanupStaleSessions(mqtt, config)
116
- .then((result) => {
117
- if (result.sessionsRemoved > 0) {
118
- console.log(`[ha-opencode] Cleanup complete: removed ${result.sessionsRemoved} stale session(s)`);
119
- }
120
- })
121
- .catch((err) => {
122
- console.error("[ha-opencode] Background cleanup failed:", err);
123
- });
124
- }
125
- //# sourceMappingURL=cleanup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAY3C,MAAM,sBAAsB,GAAG,2BAA2B,CAAC;AAE3D;;;GAGG;AACH,KAAK,UAAU,gBAAgB,CAC7B,IAAiB,EACjB,YAAoB,IAAI;IAExB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,0BAA0B,CAAC;QACzC,IAAI,SAAyB,CAAC;QAE9B,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,OAAe,EAAE,EAAE;YACxD,kEAAkE;YAClE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACjE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;gBACpC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;oBAChC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,0CAA0C;QAC1C,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YAC7C,qDAAqD;YACrD,SAAS,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;gBAChC,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;gBAChC,CAAC;gBAAC,MAAM,CAAC;oBACP,4BAA4B;gBAC9B,CAAC;gBACD,OAAO,CAAC,QAAQ,CAAC,CAAC;YACpB,CAAC,EAAE,SAAS,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACZ,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAC1B,IAAiB,EACjB,QAAgB,EAChB,QAAkB;IAElB,MAAM,UAAU,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC;IAC7C,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;IAEjD,qDAAqD;IACrD,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,GAAG,eAAe,WAAW,QAAQ,IAAI,GAAG,SAAS,CAAC;QAC1E,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,gCAAgC;IAChC,MAAM,cAAc,GAAG,YAAY,QAAQ,EAAE,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,cAAc,IAAI,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACzD,2DAA2D;QAC3D,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACnE,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,cAAc,IAAI,GAAG,aAAa,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,cAAc,eAAe,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAiB,EACjB,MAAqB;IAErB,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC;IAEnD,wBAAwB;IACxB,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,iBAAiB,GAAa,EAAE,CAAC;IAEvC,KAAK,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,QAAQ,EAAE,CAAC;QAChD,IAAI,YAAY,GAAG,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACrD,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACjC,OAAO,CAAC,GAAG,CACT,2CAA2C,QAAQ,kBAAkB,YAAY,CAAC,WAAW,EAAE,GAAG,CACnG,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CACX,4CAA4C,QAAQ,GAAG,EACvD,GAAG,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,eAAe,EAAE,iBAAiB,CAAC,MAAM;QACzC,UAAU,EAAE,iBAAiB;KAC9B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAAiB,EACjB,MAAqB;IAErB,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAExD,kDAAkD;IAClD,MAAM,IAAI,CAAC,OAAO,CAChB,sBAAsB,EACtB;QACE,IAAI,EAAE,gBAAgB;QACtB,gBAAgB,EAAE,MAAM,CAAC,eAAe;QACxC,WAAW,EAAE,MAAM,CAAC,UAAU;QAC9B,YAAY,EAAE,MAAM,CAAC,UAAU;QAC/B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,EACD,KAAK,CACN,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAiB,EACjB,MAAqB;IAErB,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC;SAC/B,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QACf,IAAI,MAAM,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CACT,2CAA2C,MAAM,CAAC,eAAe,mBAAmB,CACrF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACP,CAAC"}
package/dist/config.d.ts DELETED
@@ -1,28 +0,0 @@
1
- export interface MqttConfig {
2
- host: string;
3
- port: number;
4
- username?: string;
5
- password?: string;
6
- clientId: string;
7
- }
8
- export interface HaConfig {
9
- discoveryPrefix: string;
10
- }
11
- export interface PluginConfig {
12
- mqtt: MqttConfig;
13
- ha: HaConfig;
14
- }
15
- export interface JsonConfig {
16
- mqtt?: {
17
- host?: string;
18
- port?: number;
19
- username?: string;
20
- password?: string;
21
- clientId?: string;
22
- };
23
- ha?: {
24
- discoveryPrefix?: string;
25
- };
26
- }
27
- export declare function loadConfig(jsonConfig?: JsonConfig): PluginConfig;
28
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,QAAQ,CAAC;CACd;AAGD,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,EAAE,CAAC,EAAE;QACH,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,UAAU,GAAG,YAAY,CAiBhE"}
package/dist/config.js DELETED
@@ -1,18 +0,0 @@
1
- export function loadConfig(jsonConfig) {
2
- const hostname = process.env.HOSTNAME || "unknown";
3
- const timestamp = Date.now().toString(36);
4
- // Env vars take precedence over JSON config
5
- return {
6
- mqtt: {
7
- host: process.env.OPENCODE_MQTT_HOST || jsonConfig?.mqtt?.host || "localhost",
8
- port: parseInt(process.env.OPENCODE_MQTT_PORT || String(jsonConfig?.mqtt?.port || 1883), 10),
9
- username: process.env.OPENCODE_MQTT_USERNAME || jsonConfig?.mqtt?.username,
10
- password: process.env.OPENCODE_MQTT_PASSWORD || jsonConfig?.mqtt?.password,
11
- clientId: process.env.OPENCODE_MQTT_CLIENT_ID || jsonConfig?.mqtt?.clientId || `opencode-${hostname}-${timestamp}`,
12
- },
13
- ha: {
14
- discoveryPrefix: process.env.OPENCODE_HA_DISCOVERY_PREFIX || jsonConfig?.ha?.discoveryPrefix || "homeassistant",
15
- },
16
- };
17
- }
18
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA+BA,MAAM,UAAU,UAAU,CAAC,UAAuB;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,SAAS,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAE1C,4CAA4C;IAC5C,OAAO;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,UAAU,EAAE,IAAI,EAAE,IAAI,IAAI,WAAW;YAC7E,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YAC5F,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,UAAU,EAAE,IAAI,EAAE,QAAQ;YAC1E,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,UAAU,EAAE,IAAI,EAAE,QAAQ;YAC1E,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,UAAU,EAAE,IAAI,EAAE,QAAQ,IAAI,YAAY,QAAQ,IAAI,SAAS,EAAE;SACnH;QACD,EAAE,EAAE;YACF,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,UAAU,EAAE,EAAE,EAAE,eAAe,IAAI,eAAe;SAChH;KACF,CAAC;AACJ,CAAC"}
@@ -1,129 +0,0 @@
1
- import type { MqttWrapper, MqttWillConfig } from "./mqtt.js";
2
- import type { HaConfig } from "./config.js";
3
- /**
4
- * Extract the unique portion of a session ID by stripping the "ses_" prefix.
5
- */
6
- export declare function extractSessionIdPart(sessionId: string): string;
7
- /**
8
- * Generate device ID from session ID.
9
- */
10
- export declare function getDeviceIdForSession(sessionId: string): string;
11
- /**
12
- * Generate the availability topic for LWT configuration.
13
- * This can be called before Discovery is instantiated.
14
- */
15
- export declare function getAvailabilityTopicForSession(sessionId: string): string;
16
- /**
17
- * Create MQTT LWT (Last Will and Testament) config for availability tracking.
18
- */
19
- export declare function createWillConfig(sessionId: string): MqttWillConfig;
20
- export interface DeviceInfo {
21
- identifiers: string[];
22
- name: string;
23
- manufacturer: string;
24
- model: string;
25
- sw_version: string;
26
- }
27
- declare const ENTITY_DEFINITIONS: readonly [{
28
- readonly key: "device_id";
29
- readonly name: "Device ID";
30
- readonly icon: "mdi:identifier";
31
- readonly hasAttributes: true;
32
- }, {
33
- readonly key: "state";
34
- readonly name: "State";
35
- readonly icon: "mdi:state-machine";
36
- readonly hasAttributes: true;
37
- }, {
38
- readonly key: "session_title";
39
- readonly name: "Session";
40
- readonly icon: "mdi:message-text";
41
- }, {
42
- readonly key: "model";
43
- readonly name: "Model";
44
- readonly icon: "mdi:brain";
45
- }, {
46
- readonly key: "current_tool";
47
- readonly name: "Current Tool";
48
- readonly icon: "mdi:tools";
49
- }, {
50
- readonly key: "tokens_input";
51
- readonly name: "Input Tokens";
52
- readonly icon: "mdi:arrow-right-bold";
53
- readonly unit: "tokens";
54
- readonly state_class: "measurement";
55
- }, {
56
- readonly key: "tokens_output";
57
- readonly name: "Output Tokens";
58
- readonly icon: "mdi:arrow-left-bold";
59
- readonly unit: "tokens";
60
- readonly state_class: "measurement";
61
- }, {
62
- readonly key: "cost";
63
- readonly name: "Cost";
64
- readonly icon: "mdi:currency-usd";
65
- readonly unit: "USD";
66
- readonly state_class: "total_increasing";
67
- }, {
68
- readonly key: "last_activity";
69
- readonly name: "Last Activity";
70
- readonly icon: "mdi:clock-outline";
71
- readonly device_class: "timestamp";
72
- }, {
73
- readonly key: "permission";
74
- readonly name: "Permission Request";
75
- readonly icon: "mdi:shield-alert";
76
- readonly hasAttributes: true;
77
- }];
78
- export type EntityKey = (typeof ENTITY_DEFINITIONS)[number]["key"];
79
- export interface PermissionInfo {
80
- id: string;
81
- type: string;
82
- title: string;
83
- sessionID: string;
84
- messageID: string;
85
- callID?: string;
86
- pattern?: string | string[];
87
- metadata: Record<string, unknown>;
88
- }
89
- export declare class Discovery {
90
- private readonly mqtt;
91
- private readonly haConfig;
92
- readonly deviceId: string;
93
- private device;
94
- private readonly stateTopicBase;
95
- private readonly sessionId;
96
- private readonly projectName;
97
- constructor(mqtt: MqttWrapper, haConfig: HaConfig, sessionId: string, projectName: string);
98
- /**
99
- * Update the device friendly name when session title becomes available.
100
- * This re-publishes all entity configs with the updated device name.
101
- */
102
- updateDeviceName(title: string): Promise<void>;
103
- registerDevice(): Promise<void>;
104
- private registerEntity;
105
- getStateTopic(key: EntityKey): string;
106
- getAttributesTopic(key: EntityKey): string;
107
- getCommandTopic(): string;
108
- getResponseTopic(): string;
109
- getAvailabilityTopic(): string;
110
- /**
111
- * Publish online status. Call this after device registration.
112
- */
113
- publishAvailable(): Promise<void>;
114
- /**
115
- * Publish offline status. Call this before graceful shutdown.
116
- */
117
- publishUnavailable(): Promise<void>;
118
- publishState(key: EntityKey, value: string | number): Promise<void>;
119
- publishAttributes(key: EntityKey, attributes: Record<string, unknown>): Promise<void>;
120
- publishDeviceInfo(): Promise<void>;
121
- publishPermission(permission: PermissionInfo | null): Promise<void>;
122
- unregisterDevice(): Promise<void>;
123
- /**
124
- * Get the list of all entity keys for cleanup purposes.
125
- */
126
- static getEntityKeys(): readonly EntityKey[];
127
- }
128
- export {};
129
- //# sourceMappingURL=discovery.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}