@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/LICENSE +21 -0
- package/README.md +183 -0
- package/commands/cli.ts +74 -0
- package/commands/slash.ts +160 -0
- package/config.ts +39 -0
- package/index.ts +107 -0
- package/openclaw.plugin.json +22 -0
- package/package.json +46 -0
- package/platform.ts +361 -0
- package/skill.md +737 -0
- package/telemetry.ts +245 -0
- package/tools/capabilities.ts +341 -0
- package/tools/charge.ts +141 -0
- package/tools/climate.ts +134 -0
- package/tools/navigation.ts +199 -0
- package/tools/security.ts +207 -0
- package/tools/status.ts +47 -0
- package/tools/system.ts +67 -0
- package/tools/triggers.ts +311 -0
- package/tools/trunk.ts +67 -0
- package/tools/vehicle.ts +115 -0
- package/types/openclaw.d.ts +108 -0
package/telemetry.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry event type definitions for the tescmd node.
|
|
3
|
+
*
|
|
4
|
+
* Documents all event types that the tescmd node emits to the OpenClaw
|
|
5
|
+
* Gateway via `req:agent` frames. These are derived from the Tesla Fleet
|
|
6
|
+
* Telemetry stream and transformed by the EventEmitter
|
|
7
|
+
* (src/tescmd/openclaw/emitter.py).
|
|
8
|
+
*
|
|
9
|
+
* Event envelope format:
|
|
10
|
+
* ```json
|
|
11
|
+
* {
|
|
12
|
+
* "method": "req:agent",
|
|
13
|
+
* "params": {
|
|
14
|
+
* "event_type": "<type>",
|
|
15
|
+
* "source": "<client_id>",
|
|
16
|
+
* "vin": "<vehicle_vin>",
|
|
17
|
+
* "timestamp": "<ISO 8601>",
|
|
18
|
+
* "data": { ... }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Data event types (11)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
export interface LocationEvent {
|
|
29
|
+
event_type: "location";
|
|
30
|
+
data: {
|
|
31
|
+
/** Latitude in decimal degrees. */
|
|
32
|
+
latitude: number;
|
|
33
|
+
/** Longitude in decimal degrees. */
|
|
34
|
+
longitude: number;
|
|
35
|
+
/** Heading in degrees (0-360). */
|
|
36
|
+
heading: number;
|
|
37
|
+
/** Speed in mph. */
|
|
38
|
+
speed: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface BatteryEvent {
|
|
43
|
+
event_type: "battery";
|
|
44
|
+
data: {
|
|
45
|
+
/** State of charge as a percentage (0-100). */
|
|
46
|
+
battery_level?: number;
|
|
47
|
+
/** Estimated range in miles. */
|
|
48
|
+
range_miles?: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface InsideTempEvent {
|
|
53
|
+
event_type: "inside_temp";
|
|
54
|
+
data: {
|
|
55
|
+
/** Inside cabin temperature in Fahrenheit. */
|
|
56
|
+
inside_temp_f: number;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface OutsideTempEvent {
|
|
61
|
+
event_type: "outside_temp";
|
|
62
|
+
data: {
|
|
63
|
+
/** Outside ambient temperature in Fahrenheit. */
|
|
64
|
+
outside_temp_f: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface SpeedEvent {
|
|
69
|
+
event_type: "speed";
|
|
70
|
+
data: {
|
|
71
|
+
/** Vehicle speed in mph. */
|
|
72
|
+
speed_mph: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ChargeStartedEvent {
|
|
77
|
+
event_type: "charge_started";
|
|
78
|
+
data: {
|
|
79
|
+
/** Raw charge state string from the vehicle. */
|
|
80
|
+
state: string;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ChargeCompleteEvent {
|
|
85
|
+
event_type: "charge_complete";
|
|
86
|
+
data: {
|
|
87
|
+
state: string;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ChargeStoppedEvent {
|
|
92
|
+
event_type: "charge_stopped";
|
|
93
|
+
data: {
|
|
94
|
+
state: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ChargeStateChangedEvent {
|
|
99
|
+
event_type: "charge_state_changed";
|
|
100
|
+
data: {
|
|
101
|
+
/** Raw charge state string (for transitions that don't match start/complete/stopped). */
|
|
102
|
+
state: string;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface SecurityChangedEvent {
|
|
107
|
+
event_type: "security_changed";
|
|
108
|
+
data: {
|
|
109
|
+
/** Which field changed: "locked" or "sentrymode". */
|
|
110
|
+
field: string;
|
|
111
|
+
/** New value of the field. */
|
|
112
|
+
value: unknown;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface GearChangedEvent {
|
|
117
|
+
event_type: "gear_changed";
|
|
118
|
+
data: {
|
|
119
|
+
/** Current gear: "P", "R", "N", "D", or other. */
|
|
120
|
+
gear: string;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Lifecycle event types (3)
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export interface NodeConnectedEvent {
|
|
129
|
+
event_type: "node.connected";
|
|
130
|
+
data: {
|
|
131
|
+
client_id: string;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface NodeDisconnectingEvent {
|
|
136
|
+
event_type: "node.disconnecting";
|
|
137
|
+
data: {
|
|
138
|
+
client_id: string;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface TriggerFiredEvent {
|
|
143
|
+
event_type: "trigger.fired";
|
|
144
|
+
data: {
|
|
145
|
+
trigger_id: string;
|
|
146
|
+
field: string;
|
|
147
|
+
value: unknown;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Union type
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
/** All possible telemetry event types emitted by the tescmd node. */
|
|
156
|
+
export type TescmdTelemetryEvent =
|
|
157
|
+
| LocationEvent
|
|
158
|
+
| BatteryEvent
|
|
159
|
+
| InsideTempEvent
|
|
160
|
+
| OutsideTempEvent
|
|
161
|
+
| SpeedEvent
|
|
162
|
+
| ChargeStartedEvent
|
|
163
|
+
| ChargeCompleteEvent
|
|
164
|
+
| ChargeStoppedEvent
|
|
165
|
+
| ChargeStateChangedEvent
|
|
166
|
+
| SecurityChangedEvent
|
|
167
|
+
| GearChangedEvent
|
|
168
|
+
| NodeConnectedEvent
|
|
169
|
+
| NodeDisconnectingEvent
|
|
170
|
+
| TriggerFiredEvent;
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Event type constants (for use in hooks, filters, etc.)
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/** All data event type names. */
|
|
177
|
+
export const DATA_EVENT_TYPES = [
|
|
178
|
+
"location",
|
|
179
|
+
"battery",
|
|
180
|
+
"inside_temp",
|
|
181
|
+
"outside_temp",
|
|
182
|
+
"speed",
|
|
183
|
+
"charge_started",
|
|
184
|
+
"charge_complete",
|
|
185
|
+
"charge_stopped",
|
|
186
|
+
"charge_state_changed",
|
|
187
|
+
"security_changed",
|
|
188
|
+
"gear_changed",
|
|
189
|
+
] as const;
|
|
190
|
+
|
|
191
|
+
/** All lifecycle event type names. */
|
|
192
|
+
export const LIFECYCLE_EVENT_TYPES = [
|
|
193
|
+
"node.connected",
|
|
194
|
+
"node.disconnecting",
|
|
195
|
+
"trigger.fired",
|
|
196
|
+
] as const;
|
|
197
|
+
|
|
198
|
+
/** All event type names combined. */
|
|
199
|
+
export const ALL_EVENT_TYPES = [...DATA_EVENT_TYPES, ...LIFECYCLE_EVENT_TYPES] as const;
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Telemetry field documentation
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Maps Tesla Fleet Telemetry field names to their OpenClaw event types.
|
|
207
|
+
* Used for documentation and filtering configuration.
|
|
208
|
+
*/
|
|
209
|
+
export const TELEMETRY_FIELD_MAP: Record<string, string> = {
|
|
210
|
+
Location: "location",
|
|
211
|
+
Soc: "battery",
|
|
212
|
+
BatteryLevel: "battery",
|
|
213
|
+
EstBatteryRange: "battery",
|
|
214
|
+
InsideTemp: "inside_temp",
|
|
215
|
+
OutsideTemp: "outside_temp",
|
|
216
|
+
VehicleSpeed: "speed",
|
|
217
|
+
ChargeState: "charge_started | charge_complete | charge_stopped",
|
|
218
|
+
DetailedChargeState: "charge_state_changed",
|
|
219
|
+
Locked: "security_changed",
|
|
220
|
+
SentryMode: "security_changed",
|
|
221
|
+
Gear: "gear_changed",
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Default filter thresholds for telemetry emission.
|
|
226
|
+
* Matches the tescmd bridge defaults in BridgeConfig.
|
|
227
|
+
*/
|
|
228
|
+
export const DEFAULT_FILTER_CONFIG: Record<
|
|
229
|
+
string,
|
|
230
|
+
{ granularity: number; throttle_seconds: number }
|
|
231
|
+
> = {
|
|
232
|
+
Location: { granularity: 50.0, throttle_seconds: 1.0 },
|
|
233
|
+
Soc: { granularity: 5.0, throttle_seconds: 10.0 },
|
|
234
|
+
InsideTemp: { granularity: 5.0, throttle_seconds: 30.0 },
|
|
235
|
+
OutsideTemp: { granularity: 5.0, throttle_seconds: 30.0 },
|
|
236
|
+
VehicleSpeed: { granularity: 5.0, throttle_seconds: 2.0 },
|
|
237
|
+
ChargeState: { granularity: 0.0, throttle_seconds: 0.0 },
|
|
238
|
+
DetailedChargeState: { granularity: 0.0, throttle_seconds: 0.0 },
|
|
239
|
+
Locked: { granularity: 0.0, throttle_seconds: 0.0 },
|
|
240
|
+
SentryMode: { granularity: 0.0, throttle_seconds: 0.0 },
|
|
241
|
+
BatteryLevel: { granularity: 1.0, throttle_seconds: 10.0 },
|
|
242
|
+
EstBatteryRange: { granularity: 5.0, throttle_seconds: 30.0 },
|
|
243
|
+
Odometer: { granularity: 1.0, throttle_seconds: 60.0 },
|
|
244
|
+
Gear: { granularity: 0.0, throttle_seconds: 0.0 },
|
|
245
|
+
};
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capabilities meta-tool — on-demand help for the agent.
|
|
3
|
+
*
|
|
4
|
+
* Provides a structured overview of all available Tesla vehicle tools,
|
|
5
|
+
* common workflows, telemetry event types, and usage examples. The agent
|
|
6
|
+
* can call this tool to understand what's available before taking action.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Type } from "@sinclair/typebox";
|
|
10
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
11
|
+
|
|
12
|
+
const CAPABILITIES_TEXT = `# Tesla Vehicle Tools (tescmd)
|
|
13
|
+
|
|
14
|
+
You have access to a Tesla vehicle through the tescmd node. Here's what you can do and when to use each tool.
|
|
15
|
+
|
|
16
|
+
**Source repositories:**
|
|
17
|
+
- tescmd node: https://github.com/oceanswave/tescmd
|
|
18
|
+
- OpenClaw plugin: https://github.com/oceanswave/openclaw-tescmd
|
|
19
|
+
|
|
20
|
+
## Quick Reference
|
|
21
|
+
|
|
22
|
+
### Reading Vehicle State (no side effects, safe to call anytime)
|
|
23
|
+
| Tool | Use When... |
|
|
24
|
+
|------|------------|
|
|
25
|
+
| tescmd_get_location | User asks where the car is, needs GPS coordinates, or you need to check proximity |
|
|
26
|
+
| tescmd_get_battery | User asks about charge level, range, or you need to decide if charging is needed |
|
|
27
|
+
| tescmd_get_speed | User asks if the car is moving, or you need to check if it's parked before taking action |
|
|
28
|
+
| tescmd_get_charge_state | User asks about charging status — is it plugged in, charging, complete? |
|
|
29
|
+
| tescmd_get_temperature | User asks about cabin/outside temp, or you need to decide about climate control |
|
|
30
|
+
| tescmd_get_security | User asks if the car is locked, or you need to check sentry mode status |
|
|
31
|
+
|
|
32
|
+
### Controlling the Vehicle (side effects — confirm intent with user when appropriate)
|
|
33
|
+
| Tool | Use When... |
|
|
34
|
+
|------|------------|
|
|
35
|
+
| tescmd_lock_doors / tescmd_unlock_doors | User wants to lock/unlock. Unlock is sensitive — confirm first |
|
|
36
|
+
| tescmd_climate_on / tescmd_climate_off | User wants to precondition the cabin or stop climate |
|
|
37
|
+
| tescmd_set_climate_temp | User specifies a desired temperature (in °F) |
|
|
38
|
+
| tescmd_start_charge / tescmd_stop_charge | User wants to start/stop charging. Check charge_state first |
|
|
39
|
+
| tescmd_set_charge_limit | User wants to change the charge limit (50-100%) |
|
|
40
|
+
| tescmd_open_trunk / tescmd_open_frunk | User needs trunk/frunk opened. Frunk can't be closed remotely |
|
|
41
|
+
| tescmd_flash_lights / tescmd_honk_horn | User wants to locate the car or signal |
|
|
42
|
+
| tescmd_sentry_on / tescmd_sentry_off | User wants camera monitoring on/off. Note: uses ~1-2% battery/hour |
|
|
43
|
+
|
|
44
|
+
### Navigation (send destinations to the vehicle)
|
|
45
|
+
| Tool | Use When... |
|
|
46
|
+
|------|------------|
|
|
47
|
+
| tescmd_nav_send | User says 'navigate to [place]' or 'send directions to the car'. Pass a full address or place name |
|
|
48
|
+
| tescmd_nav_gps | User provides lat/lon coordinates, or you have coordinates from a location lookup |
|
|
49
|
+
| tescmd_nav_supercharger | User says 'find a Supercharger' or battery is low. Pair with tescmd_get_battery first |
|
|
50
|
+
| tescmd_nav_waypoints | User wants a multi-stop road trip using Google Place IDs as waypoints |
|
|
51
|
+
| tescmd_homelink | User says 'open the garage'. Get location first with tescmd_get_location, then pass lat/lon |
|
|
52
|
+
|
|
53
|
+
### Triggers (subscribe to telemetry conditions)
|
|
54
|
+
| Tool | Use When... |
|
|
55
|
+
|------|------------|
|
|
56
|
+
| tescmd_create_trigger | User wants alerts on conditions (low battery, high temp, geofence) |
|
|
57
|
+
| tescmd_battery_trigger | Shortcut: alert on battery level (e.g., below 20%) |
|
|
58
|
+
| tescmd_cabin_temp_trigger | Shortcut: alert on cabin temp (e.g., above 100°F for hot car alert) |
|
|
59
|
+
| tescmd_outside_temp_trigger | Shortcut: alert on outside temp (e.g., below freezing) |
|
|
60
|
+
| tescmd_location_trigger | Shortcut: geofence alert (enter/leave an area) |
|
|
61
|
+
| tescmd_list_triggers | Check what triggers are already active |
|
|
62
|
+
| tescmd_poll_triggers | Check if any triggers have fired recently |
|
|
63
|
+
| tescmd_delete_trigger | Remove a trigger that's no longer needed |
|
|
64
|
+
|
|
65
|
+
### Advanced
|
|
66
|
+
| Tool | Use When... |
|
|
67
|
+
|------|------------|
|
|
68
|
+
| tescmd_run_command | You need a command not covered by a dedicated tool, or the method name is dynamic |
|
|
69
|
+
|
|
70
|
+
## Common Workflows
|
|
71
|
+
|
|
72
|
+
### "Is my car ready to go?"
|
|
73
|
+
1. tescmd_get_battery → check charge level and range
|
|
74
|
+
2. tescmd_get_charge_state → check if still charging
|
|
75
|
+
3. tescmd_get_temperature → check cabin comfort
|
|
76
|
+
4. tescmd_get_security → verify doors are locked
|
|
77
|
+
5. Summarize: battery %, range, charging status, cabin temp, lock state
|
|
78
|
+
|
|
79
|
+
### "Precondition the cabin"
|
|
80
|
+
1. tescmd_get_temperature → check current cabin temp
|
|
81
|
+
2. tescmd_climate_on → start climate control
|
|
82
|
+
3. Optionally: tescmd_set_climate_temp → set specific temperature
|
|
83
|
+
4. Confirm: "Climate control is on, targeting X°F"
|
|
84
|
+
|
|
85
|
+
### "Set up for overnight charging"
|
|
86
|
+
1. tescmd_get_charge_state → verify cable is connected
|
|
87
|
+
2. tescmd_set_charge_limit → set to user's desired limit (typically 80%)
|
|
88
|
+
3. tescmd_start_charge → begin charging
|
|
89
|
+
4. Optionally: tescmd_battery_trigger → alert when charge reaches target
|
|
90
|
+
|
|
91
|
+
### "Keep an eye on the car" (parking/security)
|
|
92
|
+
1. tescmd_lock_doors → ensure locked
|
|
93
|
+
2. tescmd_sentry_on → enable camera monitoring
|
|
94
|
+
3. tescmd_location_trigger → geofence alert if car moves
|
|
95
|
+
4. Confirm setup
|
|
96
|
+
|
|
97
|
+
### "Navigate somewhere"
|
|
98
|
+
1. tescmd_nav_send → pass the address or place name
|
|
99
|
+
2. Or: tescmd_nav_gps → if you have exact lat/lon coordinates
|
|
100
|
+
3. For multi-stop: tescmd_nav_waypoints → comma-separated Place IDs
|
|
101
|
+
|
|
102
|
+
### "Open the garage"
|
|
103
|
+
1. tescmd_get_location → get the vehicle's current GPS
|
|
104
|
+
2. tescmd_homelink → pass lat/lon to trigger HomeLink
|
|
105
|
+
|
|
106
|
+
### "Find my car"
|
|
107
|
+
1. tescmd_get_location → get GPS coordinates
|
|
108
|
+
2. tescmd_flash_lights → visual signal
|
|
109
|
+
3. Optionally: tescmd_honk_horn → audible signal
|
|
110
|
+
|
|
111
|
+
### "Hot car alert"
|
|
112
|
+
1. tescmd_cabin_temp_trigger with operator='gt', value=100 → alert over 100°F
|
|
113
|
+
2. When triggered: tescmd_climate_on → auto-start climate
|
|
114
|
+
3. tescmd_get_temperature → verify cooling
|
|
115
|
+
|
|
116
|
+
## Error Handling
|
|
117
|
+
|
|
118
|
+
### Node Not Connected
|
|
119
|
+
If commands fail with "no node connected" or similar errors:
|
|
120
|
+
1. Call **tescmd_node_status** to confirm the node is offline
|
|
121
|
+
2. Tell the user: "No tescmd node is connected. Start one with:"
|
|
122
|
+
\`tescmd serve <VIN> --openclaw <gateway_url> --openclaw-token <token>\`
|
|
123
|
+
3. Call **tescmd_help** with topic='setup' for full setup instructions if needed
|
|
124
|
+
|
|
125
|
+
### Vehicle Asleep
|
|
126
|
+
Write commands auto-wake the vehicle, but this is a **billable API call**.
|
|
127
|
+
- If a read returns \`{pending: true}\`, the vehicle data is being fetched. Wait 3-5 seconds and retry.
|
|
128
|
+
- If the wake fails (rare), suggest the user wake the vehicle from the Tesla app (free).
|
|
129
|
+
- Avoid unnecessary write commands to sleeping vehicles — check state with reads first.
|
|
130
|
+
|
|
131
|
+
### Authentication Expired
|
|
132
|
+
If commands fail with auth/token errors:
|
|
133
|
+
- The tescmd node handles token refresh automatically
|
|
134
|
+
- If refresh fails, the user needs to re-authenticate: \`tescmd auth login\`
|
|
135
|
+
- Check auth status: \`tescmd auth status\`
|
|
136
|
+
|
|
137
|
+
### Rate Limiting
|
|
138
|
+
Tesla's API has rate limits (429 responses). tescmd caches read responses to minimize API calls:
|
|
139
|
+
- Static data (specs, warranty): cached 1 hour
|
|
140
|
+
- Fleet lists: cached 5 minutes
|
|
141
|
+
- Standard queries: cached 1 minute
|
|
142
|
+
- Location/speed: cached 30 seconds
|
|
143
|
+
- If you get rate limit errors, wait and avoid rapid repeated calls
|
|
144
|
+
|
|
145
|
+
### Command Failures
|
|
146
|
+
If a write command fails:
|
|
147
|
+
- Check if the vehicle is in a valid state (e.g., charge cable connected for charge.start)
|
|
148
|
+
- Check if the vehicle is online (tescmd_get_speed returns non-null when driving)
|
|
149
|
+
- Try tescmd_run_command as a fallback — it accepts both dot-notation and Fleet API names
|
|
150
|
+
|
|
151
|
+
### Common Error Patterns
|
|
152
|
+
| Error | Cause | Resolution |
|
|
153
|
+
|-------|-------|------------|
|
|
154
|
+
| "no handler configured" | Node has no dispatcher | Restart node with full serve command |
|
|
155
|
+
| "unknown command: X" | Command not in whitelist | Check tescmd_help for valid commands |
|
|
156
|
+
| "handler timeout (30s)" | Vehicle/API unresponsive | Retry after brief wait |
|
|
157
|
+
| "Triggers not available" | Node started without telemetry | Restart with telemetry enabled |
|
|
158
|
+
| {pending: true} | Data being fetched from API | Retry in 3-5 seconds |
|
|
159
|
+
|
|
160
|
+
## Important Notes
|
|
161
|
+
|
|
162
|
+
- **Auto-wake:** Write commands automatically wake the vehicle if it's asleep. This is a billable API call — avoid unnecessary wake-ups.
|
|
163
|
+
- **Pending responses:** If a read returns {pending: true}, the data is being fetched from the API. Wait briefly and retry.
|
|
164
|
+
- **Telemetry vs API:** When the telemetry stream is active, reads are instant (from memory). When telemetry is offline, reads hit the Fleet API (slower, may need wake).
|
|
165
|
+
- **Signed commands:** Security-sensitive commands (lock, unlock, trunk) use cryptographically signed commands via the Vehicle Command Protocol when available.
|
|
166
|
+
- **Trigger operators:** lt, gt, lte, gte, eq, neq (numeric), changed (any change), enter/leave (geofence).
|
|
167
|
+
- **Temperature units:** climate.set_temp takes Fahrenheit. temperature.get returns Celsius. Telemetry events emit Fahrenheit for temp.
|
|
168
|
+
- **Node status:** Always call tescmd_node_status before your first vehicle command in a conversation to verify the node is online.
|
|
169
|
+
|
|
170
|
+
## Telemetry Events (streamed automatically)
|
|
171
|
+
|
|
172
|
+
The tescmd node streams these events to the Gateway in real-time:
|
|
173
|
+
- **location** — GPS updates (filtered: 50m movement threshold)
|
|
174
|
+
- **battery** — charge level changes (filtered: 5% threshold, 10s throttle)
|
|
175
|
+
- **inside_temp / outside_temp** — temperature changes (5° threshold, 30s throttle)
|
|
176
|
+
- **speed** — speed changes (5mph threshold, 2s throttle)
|
|
177
|
+
- **charge_started / charge_complete / charge_stopped** — charging state transitions (immediate)
|
|
178
|
+
- **security_changed** — lock or sentry mode changed (immediate)
|
|
179
|
+
- **gear_changed** — gear shift (immediate)
|
|
180
|
+
- **trigger.fired** — a trigger condition was met
|
|
181
|
+
|
|
182
|
+
## Starting the tescmd Node
|
|
183
|
+
|
|
184
|
+
If no tescmd node is currently connected, you can spawn one. The node is a Python CLI that bridges the Tesla Fleet API to the OpenClaw Gateway.
|
|
185
|
+
|
|
186
|
+
### Prerequisites
|
|
187
|
+
- **Python 3.11+** (required)
|
|
188
|
+
- **pip** (required — ships with Python)
|
|
189
|
+
- **Tesla account** linked to a vehicle (required)
|
|
190
|
+
- **Git** (required — for key hosting setup)
|
|
191
|
+
- **GitHub CLI** (\`gh\`) (recommended — auto-creates key hosting via GitHub Pages)
|
|
192
|
+
- **Tailscale** (recommended — provides public HTTPS for telemetry streaming with zero infra)
|
|
193
|
+
|
|
194
|
+
### Installation
|
|
195
|
+
\`\`\`bash
|
|
196
|
+
pip install tescmd
|
|
197
|
+
\`\`\`
|
|
198
|
+
|
|
199
|
+
### First-Time Setup
|
|
200
|
+
Run the interactive setup wizard (only needed once):
|
|
201
|
+
\`\`\`bash
|
|
202
|
+
tescmd setup
|
|
203
|
+
\`\`\`
|
|
204
|
+
This walks through: Tesla Developer app creation, EC key generation, public key hosting (GitHub Pages or Tailscale Funnel), Fleet API registration, OAuth2 login, and key enrollment on the vehicle.
|
|
205
|
+
|
|
206
|
+
### Launching the Node
|
|
207
|
+
\`\`\`bash
|
|
208
|
+
# Full mode: MCP server + telemetry streaming + OpenClaw bridge
|
|
209
|
+
tescmd serve <VIN> --openclaw <gateway_ws_url> --openclaw-token <gateway_token>
|
|
210
|
+
|
|
211
|
+
# Example:
|
|
212
|
+
tescmd serve 5YJ3E1EA1NF000000 --openclaw ws://gateway.example.com:18789 --openclaw-token my-token
|
|
213
|
+
|
|
214
|
+
# With explicit MCP credentials:
|
|
215
|
+
tescmd serve <VIN> --client-id my-agent --client-secret my-secret --openclaw <url> --openclaw-token <token>
|
|
216
|
+
|
|
217
|
+
# OpenClaw bridge only (no MCP server):
|
|
218
|
+
tescmd serve <VIN> --no-mcp --openclaw <url> --openclaw-token <token>
|
|
219
|
+
|
|
220
|
+
# Telemetry + OpenClaw without MCP:
|
|
221
|
+
tescmd serve <VIN> --no-mcp --openclaw <url>
|
|
222
|
+
|
|
223
|
+
# Standalone OpenClaw bridge (alternative command):
|
|
224
|
+
tescmd openclaw bridge --gateway <url> --token <token>
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
### Serve Command Key Flags
|
|
228
|
+
| Flag | Description |
|
|
229
|
+
|------|-------------|
|
|
230
|
+
| \`<VIN>\` | Vehicle Identification Number (positional argument) |
|
|
231
|
+
| \`--openclaw <ws_url>\` | OpenClaw Gateway WebSocket URL (e.g. ws://host:18789) |
|
|
232
|
+
| \`--openclaw-token <token>\` | Authentication token for the Gateway |
|
|
233
|
+
| \`--openclaw-config <path>\` | Path to bridge config JSON (default: ~/.config/tescmd/bridge.json) |
|
|
234
|
+
| \`--transport <type>\` | MCP transport: streamable-http (default), stdio |
|
|
235
|
+
| \`--port <num>\` | MCP HTTP port (default: 8080) |
|
|
236
|
+
| \`--host <addr>\` | MCP bind address (default: 127.0.0.1) |
|
|
237
|
+
| \`--telemetry-port <num>\` | Telemetry WebSocket port (default: 4443) |
|
|
238
|
+
| \`--fields <preset>\` | Telemetry field preset: driving, charging, all |
|
|
239
|
+
| \`--interval <sec>\` | Telemetry polling interval in seconds |
|
|
240
|
+
| \`--no-telemetry\` | Disable telemetry streaming (MCP only) |
|
|
241
|
+
| \`--no-mcp\` | Disable MCP server (telemetry + OpenClaw only) |
|
|
242
|
+
| \`--no-log\` | Disable CSV telemetry logging |
|
|
243
|
+
| \`--dry-run\` | Log events as JSONL without connecting to Gateway |
|
|
244
|
+
| \`--tailscale\` | Expose MCP via Tailscale Funnel |
|
|
245
|
+
| \`--client-id <id>\` | MCP OAuth client ID |
|
|
246
|
+
| \`--client-secret <secret>\` | MCP OAuth client secret |
|
|
247
|
+
|
|
248
|
+
### Environment Variables
|
|
249
|
+
Instead of CLI flags, you can set these in \`~/.config/tescmd/.env\`:
|
|
250
|
+
\`\`\`bash
|
|
251
|
+
TESLA_CLIENT_ID=your-client-id
|
|
252
|
+
TESLA_CLIENT_SECRET=your-client-secret
|
|
253
|
+
TESLA_VIN=5YJ3E1EA1NF000000
|
|
254
|
+
TESLA_REGION=na # na, eu, or cn
|
|
255
|
+
OPENCLAW_GATEWAY_URL=ws://gateway.example.com:18789
|
|
256
|
+
OPENCLAW_GATEWAY_TOKEN=your-token
|
|
257
|
+
TESLA_COMMAND_PROTOCOL=auto # auto, signed, or unsigned
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
### tescmd CLI Quick Reference
|
|
261
|
+
Beyond the serve command, tescmd offers 100+ CLI commands for direct vehicle interaction:
|
|
262
|
+
\`\`\`bash
|
|
263
|
+
tescmd charge status # Check battery and charging state
|
|
264
|
+
tescmd charge start --wake # Start charging (wakes vehicle)
|
|
265
|
+
tescmd charge limit 80 # Set charge limit to 80%
|
|
266
|
+
tescmd climate on --wake # Turn on climate
|
|
267
|
+
tescmd climate set 72 --wake # Set temperature to 72°F
|
|
268
|
+
tescmd security lock --wake # Lock doors
|
|
269
|
+
tescmd security status # Check lock/sentry state
|
|
270
|
+
tescmd vehicle location # Get GPS coordinates
|
|
271
|
+
tescmd vehicle info # Full vehicle data snapshot
|
|
272
|
+
tescmd vehicle list # List all vehicles on account
|
|
273
|
+
tescmd trunk open --wake # Open rear trunk
|
|
274
|
+
tescmd trunk frunk --wake # Open frunk
|
|
275
|
+
tescmd security flash --wake # Flash headlights
|
|
276
|
+
tescmd security honk --wake # Honk horn
|
|
277
|
+
tescmd security sentry on --wake # Enable Sentry Mode
|
|
278
|
+
tescmd cache status # Check cache stats
|
|
279
|
+
tescmd auth status # Check auth token status
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
All read commands are cached (tiered TTLs: 30s-1h). Write commands auto-invalidate the cache. The \`--wake\` flag is required for commands that need the vehicle awake (billable API call). Use \`--format json\` for structured output.
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
export function registerCapabilitiesTool(api: OpenClawPluginApi): void {
|
|
286
|
+
api.registerTool(
|
|
287
|
+
{
|
|
288
|
+
name: "tescmd_help",
|
|
289
|
+
label: "Tesla Tools Help",
|
|
290
|
+
description:
|
|
291
|
+
"Get a comprehensive guide to all available Tesla vehicle tools, " +
|
|
292
|
+
"common workflows, and usage examples. Call this tool when you need " +
|
|
293
|
+
"to understand what Tesla vehicle capabilities are available, how to " +
|
|
294
|
+
"chain tools together for common tasks, or when you're unsure which " +
|
|
295
|
+
"tool to use for a vehicle-related request. Returns a structured " +
|
|
296
|
+
"reference with tool descriptions, workflow recipes, and important notes.",
|
|
297
|
+
parameters: Type.Object({
|
|
298
|
+
topic: Type.Optional(
|
|
299
|
+
Type.String({
|
|
300
|
+
description:
|
|
301
|
+
"Optional topic to focus on: 'tools', 'workflows', 'triggers', " +
|
|
302
|
+
"'errors', 'telemetry', 'setup', 'cli', or 'all' (default: 'all')",
|
|
303
|
+
}),
|
|
304
|
+
),
|
|
305
|
+
}),
|
|
306
|
+
async execute(_toolCallId: string, params: Record<string, unknown>) {
|
|
307
|
+
const topic = (params.topic as string) ?? "all";
|
|
308
|
+
|
|
309
|
+
let text = CAPABILITIES_TEXT;
|
|
310
|
+
|
|
311
|
+
// Filter to specific section if requested
|
|
312
|
+
if (topic !== "all") {
|
|
313
|
+
const sectionMap: Record<string, string> = {
|
|
314
|
+
tools: "## Quick Reference",
|
|
315
|
+
workflows: "## Common Workflows",
|
|
316
|
+
triggers: "### Triggers",
|
|
317
|
+
errors: "## Error Handling",
|
|
318
|
+
telemetry: "## Telemetry Events",
|
|
319
|
+
setup: "## Starting the tescmd Node",
|
|
320
|
+
cli: "### tescmd CLI Quick Reference",
|
|
321
|
+
};
|
|
322
|
+
const header = sectionMap[topic];
|
|
323
|
+
if (header) {
|
|
324
|
+
const start = text.indexOf(header);
|
|
325
|
+
if (start !== -1) {
|
|
326
|
+
// Find the next ## heading after the section
|
|
327
|
+
const nextSection = text.indexOf("\n## ", start + header.length);
|
|
328
|
+
text = nextSection !== -1 ? text.slice(start, nextSection) : text.slice(start);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
content: [{ type: "text" as const, text: text.trim() }],
|
|
335
|
+
details: { topic },
|
|
336
|
+
};
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{ name: "tescmd_help" },
|
|
340
|
+
);
|
|
341
|
+
}
|