@oceanswave/openclaw-tescmd 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/platform.ts ADDED
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Tesla platform registration and command whitelist.
3
+ *
4
+ * Registers the "tesla" platform with the OpenClaw Gateway and whitelists
5
+ * all 28 commands that the tescmd node can handle. Without this whitelist,
6
+ * the Gateway will reject command dispatch requests to tescmd nodes.
7
+ *
8
+ * Commands are derived from the tescmd `NodeCapabilities` class
9
+ * (src/tescmd/openclaw/config.py) and verified against the
10
+ * `CommandDispatcher` handler map (src/tescmd/openclaw/dispatcher.py).
11
+ */
12
+
13
+ import type { OpenClawPluginApi, PlatformCommand } from "openclaw/plugin-sdk";
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Read commands (8)
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const READ_COMMANDS: PlatformCommand[] = [
20
+ {
21
+ method: "location.get",
22
+ label: "Get Location",
23
+ description:
24
+ "Get the vehicle's current GPS coordinates, heading, and speed. " +
25
+ "Returns {latitude, longitude, heading, speed}. Checks real-time " +
26
+ "telemetry first, falls back to Fleet API drive_state.",
27
+ direction: "read",
28
+ },
29
+ {
30
+ method: "battery.get",
31
+ label: "Get Battery",
32
+ description:
33
+ "Get the vehicle's battery level (percentage) and estimated range in miles. " +
34
+ "Returns {battery_level, range_miles}. Sources from Soc/BatteryLevel " +
35
+ "telemetry fields, falls back to Fleet API charge_state.",
36
+ direction: "read",
37
+ },
38
+ {
39
+ method: "temperature.get",
40
+ label: "Get Temperature",
41
+ description:
42
+ "Get the vehicle's inside cabin temperature and outside ambient temperature " +
43
+ "in Celsius. Returns {inside_temp_c, outside_temp_c}. Sources from " +
44
+ "InsideTemp/OutsideTemp telemetry, falls back to Fleet API climate_state.",
45
+ direction: "read",
46
+ },
47
+ {
48
+ method: "speed.get",
49
+ label: "Get Speed",
50
+ description:
51
+ "Get the vehicle's current speed in mph. Returns {speed_mph}. " +
52
+ "Sources from VehicleSpeed telemetry, falls back to Fleet API drive_state.",
53
+ direction: "read",
54
+ },
55
+ {
56
+ method: "charge_state.get",
57
+ label: "Get Charge State",
58
+ description:
59
+ "Get the vehicle's current charging status. Returns {charge_state} which " +
60
+ "may be 'Charging', 'Complete', 'Stopped', 'Disconnected', etc. " +
61
+ "Sources from ChargeState/DetailedChargeState telemetry, falls back to Fleet API.",
62
+ direction: "read",
63
+ },
64
+ {
65
+ method: "security.get",
66
+ label: "Get Security",
67
+ description:
68
+ "Get the vehicle's lock state and sentry mode status. " +
69
+ "Returns {locked: boolean, sentry_mode: boolean}. Sources from " +
70
+ "Locked/SentryMode telemetry, falls back to Fleet API vehicle_state.",
71
+ direction: "read",
72
+ },
73
+ {
74
+ method: "trigger.list",
75
+ label: "List Triggers",
76
+ description:
77
+ "List all active trigger subscriptions on the node. Returns " +
78
+ "{triggers: [{id, field, operator, value, once, cooldown_seconds}]}. " +
79
+ "Triggers fire when telemetry fields match specified conditions.",
80
+ direction: "read",
81
+ },
82
+ {
83
+ method: "trigger.poll",
84
+ label: "Poll Triggers",
85
+ description:
86
+ "Poll for trigger notifications that have fired since the last poll. " +
87
+ "Returns {notifications: []} — each notification contains the trigger ID, " +
88
+ "field name, current value, and timestamp. Drains the pending queue.",
89
+ direction: "read",
90
+ },
91
+ ];
92
+
93
+ // ---------------------------------------------------------------------------
94
+ // Write commands (26)
95
+ // ---------------------------------------------------------------------------
96
+
97
+ const WRITE_COMMANDS: PlatformCommand[] = [
98
+ // -- Door/Security --
99
+ {
100
+ method: "door.lock",
101
+ label: "Lock Doors",
102
+ description:
103
+ "Lock all vehicle doors. Requires the vehicle to be awake " +
104
+ "(auto-wakes if asleep). Returns {result: true, reason: 'ok'}.",
105
+ direction: "write",
106
+ },
107
+ {
108
+ method: "door.unlock",
109
+ label: "Unlock Doors",
110
+ description:
111
+ "Unlock all vehicle doors. Requires the vehicle to be awake " +
112
+ "(auto-wakes if asleep). Returns {result: true, reason: 'ok'}.",
113
+ direction: "write",
114
+ },
115
+
116
+ // -- Climate --
117
+ {
118
+ method: "climate.on",
119
+ label: "Climate On",
120
+ description:
121
+ "Turn on the vehicle's climate control (HVAC). Starts auto-conditioning. " +
122
+ "Useful for pre-conditioning the cabin before driving.",
123
+ direction: "write",
124
+ },
125
+ {
126
+ method: "climate.off",
127
+ label: "Climate Off",
128
+ description: "Turn off the vehicle's climate control (HVAC). Stops auto-conditioning.",
129
+ direction: "write",
130
+ },
131
+ {
132
+ method: "climate.set_temp",
133
+ label: "Set Climate Temperature",
134
+ description:
135
+ "Set the cabin temperature for climate control. Requires a 'temp' parameter " +
136
+ "in Fahrenheit. Sets both driver and passenger to the same temperature.",
137
+ direction: "write",
138
+ },
139
+
140
+ // -- Charging --
141
+ {
142
+ method: "charge.start",
143
+ label: "Start Charging",
144
+ description:
145
+ "Start charging the vehicle. The charge cable must be connected. " +
146
+ "Returns {result: true, reason: 'ok'} on success.",
147
+ direction: "write",
148
+ },
149
+ {
150
+ method: "charge.stop",
151
+ label: "Stop Charging",
152
+ description: "Stop an active charging session. Returns {result: true, reason: 'ok'}.",
153
+ direction: "write",
154
+ },
155
+ {
156
+ method: "charge.set_limit",
157
+ label: "Set Charge Limit",
158
+ description:
159
+ "Set the battery charge limit as a percentage (50-100). Requires a 'percent' " +
160
+ "parameter. Returns {result: true, reason: 'ok'}.",
161
+ direction: "write",
162
+ },
163
+
164
+ // -- Trunk/Frunk --
165
+ {
166
+ method: "trunk.open",
167
+ label: "Open Trunk",
168
+ description:
169
+ "Open or close the rear trunk. Actuates the trunk — if closed it opens, " +
170
+ "if open it closes (on supported models).",
171
+ direction: "write",
172
+ },
173
+ {
174
+ method: "frunk.open",
175
+ label: "Open Frunk",
176
+ description:
177
+ "Open the front trunk (frunk). Note: the frunk cannot be closed remotely " +
178
+ "and must be physically pushed down.",
179
+ direction: "write",
180
+ },
181
+
182
+ // -- Alerts --
183
+ {
184
+ method: "flash_lights",
185
+ label: "Flash Lights",
186
+ description: "Flash the vehicle's headlights briefly. Useful for locating the car.",
187
+ direction: "write",
188
+ },
189
+ {
190
+ method: "honk_horn",
191
+ label: "Honk Horn",
192
+ description: "Honk the vehicle's horn briefly. Useful for locating the car.",
193
+ direction: "write",
194
+ },
195
+
196
+ // -- Sentry --
197
+ {
198
+ method: "sentry.on",
199
+ label: "Sentry Mode On",
200
+ description:
201
+ "Enable Sentry Mode. The vehicle's cameras will actively monitor " +
202
+ "surroundings and record events when threats are detected.",
203
+ direction: "write",
204
+ },
205
+ {
206
+ method: "sentry.off",
207
+ label: "Sentry Mode Off",
208
+ description: "Disable Sentry Mode. Stops active camera monitoring.",
209
+ direction: "write",
210
+ },
211
+
212
+ // -- Triggers --
213
+ {
214
+ method: "trigger.create",
215
+ label: "Create Trigger",
216
+ description:
217
+ "Create a new telemetry trigger subscription. Required parameters: " +
218
+ "'field' (telemetry field name, e.g. 'Soc', 'InsideTemp', 'Location'), " +
219
+ "'operator' (lt|gt|lte|gte|eq|neq|changed|enter|leave), " +
220
+ "'value' (threshold). Optional: 'once' (boolean, default false), " +
221
+ "'cooldown_seconds' (minimum time between fires, default 60).",
222
+ direction: "write",
223
+ },
224
+ {
225
+ method: "trigger.delete",
226
+ label: "Delete Trigger",
227
+ description:
228
+ "Delete an existing trigger by its ID. Requires 'id' parameter. " +
229
+ "Returns {deleted: boolean, id: string}.",
230
+ direction: "write",
231
+ },
232
+
233
+ // -- Convenience trigger aliases --
234
+ {
235
+ method: "cabin_temp.trigger",
236
+ label: "Cabin Temp Trigger",
237
+ description:
238
+ "Create a trigger on cabin temperature (InsideTemp field). Shortcut that " +
239
+ "pre-fills 'field'. Parameters: 'operator', 'value', optional 'once', 'cooldown_seconds'. " +
240
+ "Example: operator='gt', value=90 fires when cabin exceeds 90°F.",
241
+ direction: "write",
242
+ },
243
+ {
244
+ method: "outside_temp.trigger",
245
+ label: "Outside Temp Trigger",
246
+ description:
247
+ "Create a trigger on outside/ambient temperature (OutsideTemp field). " +
248
+ "Shortcut that pre-fills 'field'. Parameters: 'operator', 'value', " +
249
+ "optional 'once', 'cooldown_seconds'.",
250
+ direction: "write",
251
+ },
252
+ {
253
+ method: "battery.trigger",
254
+ label: "Battery Trigger",
255
+ description:
256
+ "Create a trigger on battery level (BatteryLevel field). Shortcut that " +
257
+ "pre-fills 'field'. Parameters: 'operator', 'value', optional 'once', " +
258
+ "'cooldown_seconds'. Example: operator='lt', value=20 fires when battery drops below 20%.",
259
+ direction: "write",
260
+ },
261
+ {
262
+ method: "location.trigger",
263
+ label: "Location Trigger",
264
+ description:
265
+ "Create a geofence trigger on vehicle location (Location field). " +
266
+ "Uses 'enter' or 'leave' operators with lat/lon/radius parameters. " +
267
+ "Optional: 'once', 'cooldown_seconds'.",
268
+ direction: "write",
269
+ },
270
+
271
+ // -- Navigation --
272
+ {
273
+ method: "nav.send",
274
+ label: "Send Destination",
275
+ description:
276
+ "Send a street address or place name to the vehicle's navigation. " +
277
+ "Destination appears on the touchscreen ready to route. Alias: Fleet API 'share'.",
278
+ direction: "write",
279
+ },
280
+ {
281
+ method: "nav.gps",
282
+ label: "Navigate to GPS",
283
+ description:
284
+ "Send GPS coordinates (lat/lon) to the vehicle's navigation. " +
285
+ "Optional 'order' parameter for multi-stop routing.",
286
+ direction: "write",
287
+ },
288
+ {
289
+ method: "nav.supercharger",
290
+ label: "Navigate to Supercharger",
291
+ description: "Route the vehicle to the nearest Tesla Supercharger station.",
292
+ direction: "write",
293
+ },
294
+ {
295
+ method: "nav.waypoints",
296
+ label: "Send Waypoints",
297
+ description:
298
+ "Send a multi-stop route using comma-separated waypoint reference IDs " +
299
+ "(Google Place IDs in refId format).",
300
+ direction: "write",
301
+ },
302
+ {
303
+ method: "homelink.trigger",
304
+ label: "Trigger HomeLink",
305
+ description:
306
+ "Trigger the vehicle's HomeLink to open/close a garage door. " +
307
+ "Requires lat/lon to verify the vehicle is near the programmed device.",
308
+ direction: "write",
309
+ },
310
+
311
+ // -- Meta-dispatch --
312
+ {
313
+ method: "system.run",
314
+ label: "Run Command",
315
+ description:
316
+ "Meta-dispatch: execute any registered command by name. Accepts both " +
317
+ "OpenClaw dot-notation (e.g. 'door.lock') and Tesla Fleet API snake_case " +
318
+ "names (e.g. 'door_lock'). Parameters: 'method' (required), 'params' (optional object). " +
319
+ "Cannot invoke itself. Useful for running commands not exposed as dedicated tools.",
320
+ direction: "write",
321
+ },
322
+ ];
323
+
324
+ // ---------------------------------------------------------------------------
325
+ // All commands combined
326
+ // ---------------------------------------------------------------------------
327
+
328
+ export const ALL_COMMANDS: PlatformCommand[] = [...READ_COMMANDS, ...WRITE_COMMANDS];
329
+
330
+ // ---------------------------------------------------------------------------
331
+ // Registration
332
+ // ---------------------------------------------------------------------------
333
+
334
+ /**
335
+ * Register the Tesla platform with the OpenClaw Gateway.
336
+ *
337
+ * This whitelists all 34 commands so the Gateway will dispatch
338
+ * `node.invoke.request` events to connected tescmd nodes.
339
+ */
340
+ export function registerPlatform(api: OpenClawPluginApi): void {
341
+ api.registerPlatform({
342
+ id: "tesla",
343
+ label: "Tesla Vehicle",
344
+ description:
345
+ "Tesla vehicle control and real-time telemetry via tescmd. " +
346
+ "Supports 34 commands across vehicle status, charging, climate, " +
347
+ "security, trunk, sentry mode, navigation, and trigger subscriptions. " +
348
+ "The tescmd node connects to the Tesla Fleet API and streams " +
349
+ "telemetry data including GPS location, battery, temperature, " +
350
+ "speed, charge state, and security events.",
351
+ nodeRole: "node",
352
+ scopes: ["node.telemetry", "node.command"],
353
+ commands: ALL_COMMANDS,
354
+ });
355
+
356
+ api.logger.info(
357
+ `Registered tesla platform with ${ALL_COMMANDS.length} whitelisted commands ` +
358
+ `(${READ_COMMANDS.length} reads, ${WRITE_COMMANDS.length} writes, ` +
359
+ `including system.run meta-dispatch)`,
360
+ );
361
+ }