tyta-cli 0.1.0__tar.gz
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.
- tyta_cli-0.1.0/PKG-INFO +267 -0
- tyta_cli-0.1.0/README.md +245 -0
- tyta_cli-0.1.0/pyproject.toml +43 -0
- tyta_cli-0.1.0/tyta/__init__.py +3 -0
- tyta_cli-0.1.0/tyta/cli/__init__.py +1 -0
- tyta_cli-0.1.0/tyta/cli/auth.py +140 -0
- tyta_cli-0.1.0/tyta/cli/climate.py +323 -0
- tyta_cli-0.1.0/tyta/cli/electric.py +347 -0
- tyta_cli-0.1.0/tyta/cli/main.py +111 -0
- tyta_cli-0.1.0/tyta/cli/remote.py +224 -0
- tyta_cli-0.1.0/tyta/cli/status.py +110 -0
- tyta_cli-0.1.0/tyta/cli/trips.py +257 -0
- tyta_cli-0.1.0/tyta/cli/vehicles.py +160 -0
- tyta_cli-0.1.0/tyta/core/__init__.py +1 -0
- tyta_cli-0.1.0/tyta/core/auth.py +482 -0
- tyta_cli-0.1.0/tyta/core/client.py +235 -0
- tyta_cli-0.1.0/tyta/core/climate.py +147 -0
- tyta_cli-0.1.0/tyta/core/config.py +62 -0
- tyta_cli-0.1.0/tyta/core/electric.py +226 -0
- tyta_cli-0.1.0/tyta/core/envelope.py +43 -0
- tyta_cli-0.1.0/tyta/core/remote.py +57 -0
- tyta_cli-0.1.0/tyta/core/status.py +64 -0
- tyta_cli-0.1.0/tyta/core/trips.py +117 -0
- tyta_cli-0.1.0/tyta/core/vehicles.py +191 -0
tyta_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tyta-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for Toyota Connected Services (EU)
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Stepan Bolotnikov
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Requires-Dist: httpx (>=0.27)
|
|
16
|
+
Requires-Dist: loguru (>=0.7)
|
|
17
|
+
Requires-Dist: pydantic (>=2.0)
|
|
18
|
+
Requires-Dist: pyjwt (>=2.8)
|
|
19
|
+
Requires-Dist: typer[all] (>=0.9)
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# tyta-cli
|
|
23
|
+
|
|
24
|
+
A command-line interface for Toyota Connected Services (EU). Communicate with your Toyota vehicle
|
|
25
|
+
directly from the terminal or from automation scripts.
|
|
26
|
+
|
|
27
|
+
> **Note:** EU region only. Requires a Toyota Connected Services account.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pipx install tyta-cli
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or for development:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pipx install --editable .
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Authenticate
|
|
45
|
+
tyta auth login
|
|
46
|
+
|
|
47
|
+
# Check auth status
|
|
48
|
+
tyta auth status
|
|
49
|
+
|
|
50
|
+
# List your vehicles
|
|
51
|
+
tyta vehicles list
|
|
52
|
+
|
|
53
|
+
# Set a default vehicle (so you don't need --vin on every command)
|
|
54
|
+
tyta vehicles use JTDKB3FU803012345
|
|
55
|
+
|
|
56
|
+
# Show vehicle status
|
|
57
|
+
tyta status
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Global Options
|
|
61
|
+
|
|
62
|
+
These options are accepted by every command:
|
|
63
|
+
|
|
64
|
+
| Option | Env Var | Default | Description |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `--auth-file PATH` | `TOYOTA_AUTH_FILE` | `~/.config/tyta-cli/auth.json` | Path to auth token file |
|
|
67
|
+
| `--config-file PATH` | `TOYOTA_CONFIG_FILE` | `~/.config/tyta-cli/config.json` | Path to config file |
|
|
68
|
+
| `--debug` | — | off | Enable debug logs to stderr; show tracebacks on error |
|
|
69
|
+
|
|
70
|
+
## Environment Variables
|
|
71
|
+
|
|
72
|
+
| Variable | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `TOYOTA_USERNAME` | Toyota account email. Used for non-interactive login and headless re-auth. |
|
|
75
|
+
| `TOYOTA_PASSWORD` | Toyota account password. Used for non-interactive login and headless re-auth. |
|
|
76
|
+
| `TOYOTA_AUTH_FILE` | Override path to the auth token file. |
|
|
77
|
+
| `TOYOTA_CONFIG_FILE` | Override path to the config file. |
|
|
78
|
+
| `TOYOTA_VIN` | Default VIN for per-vehicle commands. Overridden by `--vin` flag; overrides config default. |
|
|
79
|
+
|
|
80
|
+
### Headless / CI environments
|
|
81
|
+
|
|
82
|
+
If `TOYOTA_USERNAME` and `TOYOTA_PASSWORD` are both set, the CLI will re-authenticate
|
|
83
|
+
automatically whenever tokens expire — no manual intervention needed.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
export TOYOTA_USERNAME="user@example.com"
|
|
87
|
+
export TOYOTA_PASSWORD="secret"
|
|
88
|
+
tyta status # re-auths silently if tokens have expired
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
### Auth
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
tyta auth login # Authenticate and save tokens
|
|
97
|
+
tyta auth logout # Delete saved tokens
|
|
98
|
+
tyta auth status # Show authentication state and token expiry
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`tyta auth login` supports non-interactive mode via `TOYOTA_USERNAME` and `TOYOTA_PASSWORD`
|
|
102
|
+
environment variables.
|
|
103
|
+
|
|
104
|
+
### Vehicles
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
tyta vehicles list # List all vehicles linked to the account
|
|
108
|
+
tyta vehicles use <VIN> # Set the default VIN (validates against API)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Status
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
tyta status [--vin VIN]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Returns dashboard (odometer, fuel level), location, lock status, and vehicle health.
|
|
118
|
+
|
|
119
|
+
### Electric
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
tyta electric status [--vin VIN]
|
|
123
|
+
tyta electric refresh [--vin VIN] # Real-time refresh (warns about 12V drain)
|
|
124
|
+
tyta electric charge-now [--vin VIN]
|
|
125
|
+
|
|
126
|
+
tyta electric reserve-charge \
|
|
127
|
+
--day MONDAY --start 07:00 [--end 08:00] [--charge-type startOnly|startEnd] \
|
|
128
|
+
[--vin VIN]
|
|
129
|
+
|
|
130
|
+
tyta electric set-charging-time \
|
|
131
|
+
--day MONDAY --start 07:00 [--end 08:00] [--charge-type startOnly|startEnd] \
|
|
132
|
+
[--vin VIN]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`--day` accepts uppercase weekday names: `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`,
|
|
136
|
+
`SATURDAY`, `SUNDAY`.
|
|
137
|
+
|
|
138
|
+
`--start` / `--end` accept 24-hour time as `HH:MM` (e.g. `07:30`).
|
|
139
|
+
|
|
140
|
+
`--charge-type` defaults to `startOnly`; use `startEnd` when `--end` is provided.
|
|
141
|
+
|
|
142
|
+
### Climate
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
tyta climate status [--vin VIN]
|
|
146
|
+
tyta climate settings [--vin VIN]
|
|
147
|
+
tyta climate set [--temperature FLOAT] [--unit C|F] [--on | --off] [--vin VIN]
|
|
148
|
+
tyta climate on [--vin VIN]
|
|
149
|
+
tyta climate off [--vin VIN]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`tyta climate set` performs a read-modify-write: it fetches current settings, applies your
|
|
153
|
+
changes, and POSTs them back. All flags are optional; omitting `--on`/`--off` leaves the on/off
|
|
154
|
+
state unchanged.
|
|
155
|
+
|
|
156
|
+
### Trips
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
tyta trips list [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--routes] [--vin VIN]
|
|
160
|
+
tyta trips summary [--period day|week|month|year] [--vin VIN]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`trips list` defaults to the last 30 days. `--routes` includes per-trip route geometry.
|
|
164
|
+
|
|
165
|
+
`trips summary` defaults to the current month. Other periods:
|
|
166
|
+
- `day` — today only
|
|
167
|
+
- `week` — Monday through Sunday of the current week
|
|
168
|
+
- `month` — first to last day of the current month
|
|
169
|
+
- `year` — 1 January to 31 December of the current year
|
|
170
|
+
|
|
171
|
+
### Remote
|
|
172
|
+
|
|
173
|
+
All remote commands are fire-and-forget. The API acknowledges immediately; vehicle execution is
|
|
174
|
+
asynchronous.
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
tyta remote lock [--vin VIN]
|
|
178
|
+
tyta remote unlock [--vin VIN]
|
|
179
|
+
tyta remote horn [--vin VIN]
|
|
180
|
+
tyta remote find [--vin VIN] # Flash lights + sound horn
|
|
181
|
+
tyta remote headlights on [--vin VIN]
|
|
182
|
+
tyta remote headlights off [--vin VIN]
|
|
183
|
+
tyta remote hazard on [--vin VIN]
|
|
184
|
+
tyta remote hazard off [--vin VIN]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## VIN Resolution
|
|
188
|
+
|
|
189
|
+
For all per-vehicle commands, the VIN is resolved in this order:
|
|
190
|
+
|
|
191
|
+
1. `--vin` flag on the command
|
|
192
|
+
2. `TOYOTA_VIN` environment variable
|
|
193
|
+
3. `default_vin` from config file (set via `tyta vehicles use <vin>`)
|
|
194
|
+
4. Auto-select if the account has exactly one vehicle (emits a warning)
|
|
195
|
+
|
|
196
|
+
## Output Format
|
|
197
|
+
|
|
198
|
+
All output is **JSON** printed to stdout. Scripts and LLMs always receive parseable output.
|
|
199
|
+
|
|
200
|
+
### Success
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"ok": true,
|
|
205
|
+
"warnings": [],
|
|
206
|
+
"data": { ... }
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Success with warnings
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"ok": true,
|
|
215
|
+
"warnings": ["No default vehicle set, using only available vehicle: VIN XXXXXXX"],
|
|
216
|
+
"data": { ... }
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Error
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"ok": false,
|
|
225
|
+
"error": {
|
|
226
|
+
"code": "not_authenticated",
|
|
227
|
+
"message": "Not authenticated. Run `tyta auth login`."
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Error Codes
|
|
233
|
+
|
|
234
|
+
| Code | Meaning |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `not_authenticated` | No token file found |
|
|
237
|
+
| `token_expired` | Tokens expired and refresh failed |
|
|
238
|
+
| `api_error` | Toyota API returned a non-2xx response |
|
|
239
|
+
| `vehicle_not_found` | Specified VIN not found in account |
|
|
240
|
+
| `no_vehicles` | Account has no linked vehicles |
|
|
241
|
+
| `invalid_config` | Config or token file is malformed |
|
|
242
|
+
| `invalid_input` | User-supplied input is invalid |
|
|
243
|
+
| `unknown_error` | Unexpected exception |
|
|
244
|
+
|
|
245
|
+
### Exit Codes
|
|
246
|
+
|
|
247
|
+
| Code | Meaning |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `0` | Success |
|
|
250
|
+
| `1` | Operational error (`ok: false`) |
|
|
251
|
+
| `2` | Usage error (bad arguments) |
|
|
252
|
+
|
|
253
|
+
## Debugging
|
|
254
|
+
|
|
255
|
+
Pass `--debug` to enable verbose logs on stderr and print tracebacks on error:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
tyta --debug status
|
|
259
|
+
tyta --debug auth login
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Logs go to **stderr**; JSON output always goes to **stdout**.
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT
|
|
267
|
+
|
tyta_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# tyta-cli
|
|
2
|
+
|
|
3
|
+
A command-line interface for Toyota Connected Services (EU). Communicate with your Toyota vehicle
|
|
4
|
+
directly from the terminal or from automation scripts.
|
|
5
|
+
|
|
6
|
+
> **Note:** EU region only. Requires a Toyota Connected Services account.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pipx install tyta-cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or for development:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pipx install --editable .
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Authenticate
|
|
24
|
+
tyta auth login
|
|
25
|
+
|
|
26
|
+
# Check auth status
|
|
27
|
+
tyta auth status
|
|
28
|
+
|
|
29
|
+
# List your vehicles
|
|
30
|
+
tyta vehicles list
|
|
31
|
+
|
|
32
|
+
# Set a default vehicle (so you don't need --vin on every command)
|
|
33
|
+
tyta vehicles use JTDKB3FU803012345
|
|
34
|
+
|
|
35
|
+
# Show vehicle status
|
|
36
|
+
tyta status
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Global Options
|
|
40
|
+
|
|
41
|
+
These options are accepted by every command:
|
|
42
|
+
|
|
43
|
+
| Option | Env Var | Default | Description |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| `--auth-file PATH` | `TOYOTA_AUTH_FILE` | `~/.config/tyta-cli/auth.json` | Path to auth token file |
|
|
46
|
+
| `--config-file PATH` | `TOYOTA_CONFIG_FILE` | `~/.config/tyta-cli/config.json` | Path to config file |
|
|
47
|
+
| `--debug` | — | off | Enable debug logs to stderr; show tracebacks on error |
|
|
48
|
+
|
|
49
|
+
## Environment Variables
|
|
50
|
+
|
|
51
|
+
| Variable | Description |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `TOYOTA_USERNAME` | Toyota account email. Used for non-interactive login and headless re-auth. |
|
|
54
|
+
| `TOYOTA_PASSWORD` | Toyota account password. Used for non-interactive login and headless re-auth. |
|
|
55
|
+
| `TOYOTA_AUTH_FILE` | Override path to the auth token file. |
|
|
56
|
+
| `TOYOTA_CONFIG_FILE` | Override path to the config file. |
|
|
57
|
+
| `TOYOTA_VIN` | Default VIN for per-vehicle commands. Overridden by `--vin` flag; overrides config default. |
|
|
58
|
+
|
|
59
|
+
### Headless / CI environments
|
|
60
|
+
|
|
61
|
+
If `TOYOTA_USERNAME` and `TOYOTA_PASSWORD` are both set, the CLI will re-authenticate
|
|
62
|
+
automatically whenever tokens expire — no manual intervention needed.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export TOYOTA_USERNAME="user@example.com"
|
|
66
|
+
export TOYOTA_PASSWORD="secret"
|
|
67
|
+
tyta status # re-auths silently if tokens have expired
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
### Auth
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
tyta auth login # Authenticate and save tokens
|
|
76
|
+
tyta auth logout # Delete saved tokens
|
|
77
|
+
tyta auth status # Show authentication state and token expiry
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`tyta auth login` supports non-interactive mode via `TOYOTA_USERNAME` and `TOYOTA_PASSWORD`
|
|
81
|
+
environment variables.
|
|
82
|
+
|
|
83
|
+
### Vehicles
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
tyta vehicles list # List all vehicles linked to the account
|
|
87
|
+
tyta vehicles use <VIN> # Set the default VIN (validates against API)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Status
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
tyta status [--vin VIN]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Returns dashboard (odometer, fuel level), location, lock status, and vehicle health.
|
|
97
|
+
|
|
98
|
+
### Electric
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
tyta electric status [--vin VIN]
|
|
102
|
+
tyta electric refresh [--vin VIN] # Real-time refresh (warns about 12V drain)
|
|
103
|
+
tyta electric charge-now [--vin VIN]
|
|
104
|
+
|
|
105
|
+
tyta electric reserve-charge \
|
|
106
|
+
--day MONDAY --start 07:00 [--end 08:00] [--charge-type startOnly|startEnd] \
|
|
107
|
+
[--vin VIN]
|
|
108
|
+
|
|
109
|
+
tyta electric set-charging-time \
|
|
110
|
+
--day MONDAY --start 07:00 [--end 08:00] [--charge-type startOnly|startEnd] \
|
|
111
|
+
[--vin VIN]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`--day` accepts uppercase weekday names: `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`,
|
|
115
|
+
`SATURDAY`, `SUNDAY`.
|
|
116
|
+
|
|
117
|
+
`--start` / `--end` accept 24-hour time as `HH:MM` (e.g. `07:30`).
|
|
118
|
+
|
|
119
|
+
`--charge-type` defaults to `startOnly`; use `startEnd` when `--end` is provided.
|
|
120
|
+
|
|
121
|
+
### Climate
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
tyta climate status [--vin VIN]
|
|
125
|
+
tyta climate settings [--vin VIN]
|
|
126
|
+
tyta climate set [--temperature FLOAT] [--unit C|F] [--on | --off] [--vin VIN]
|
|
127
|
+
tyta climate on [--vin VIN]
|
|
128
|
+
tyta climate off [--vin VIN]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`tyta climate set` performs a read-modify-write: it fetches current settings, applies your
|
|
132
|
+
changes, and POSTs them back. All flags are optional; omitting `--on`/`--off` leaves the on/off
|
|
133
|
+
state unchanged.
|
|
134
|
+
|
|
135
|
+
### Trips
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
tyta trips list [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--routes] [--vin VIN]
|
|
139
|
+
tyta trips summary [--period day|week|month|year] [--vin VIN]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`trips list` defaults to the last 30 days. `--routes` includes per-trip route geometry.
|
|
143
|
+
|
|
144
|
+
`trips summary` defaults to the current month. Other periods:
|
|
145
|
+
- `day` — today only
|
|
146
|
+
- `week` — Monday through Sunday of the current week
|
|
147
|
+
- `month` — first to last day of the current month
|
|
148
|
+
- `year` — 1 January to 31 December of the current year
|
|
149
|
+
|
|
150
|
+
### Remote
|
|
151
|
+
|
|
152
|
+
All remote commands are fire-and-forget. The API acknowledges immediately; vehicle execution is
|
|
153
|
+
asynchronous.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
tyta remote lock [--vin VIN]
|
|
157
|
+
tyta remote unlock [--vin VIN]
|
|
158
|
+
tyta remote horn [--vin VIN]
|
|
159
|
+
tyta remote find [--vin VIN] # Flash lights + sound horn
|
|
160
|
+
tyta remote headlights on [--vin VIN]
|
|
161
|
+
tyta remote headlights off [--vin VIN]
|
|
162
|
+
tyta remote hazard on [--vin VIN]
|
|
163
|
+
tyta remote hazard off [--vin VIN]
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## VIN Resolution
|
|
167
|
+
|
|
168
|
+
For all per-vehicle commands, the VIN is resolved in this order:
|
|
169
|
+
|
|
170
|
+
1. `--vin` flag on the command
|
|
171
|
+
2. `TOYOTA_VIN` environment variable
|
|
172
|
+
3. `default_vin` from config file (set via `tyta vehicles use <vin>`)
|
|
173
|
+
4. Auto-select if the account has exactly one vehicle (emits a warning)
|
|
174
|
+
|
|
175
|
+
## Output Format
|
|
176
|
+
|
|
177
|
+
All output is **JSON** printed to stdout. Scripts and LLMs always receive parseable output.
|
|
178
|
+
|
|
179
|
+
### Success
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"ok": true,
|
|
184
|
+
"warnings": [],
|
|
185
|
+
"data": { ... }
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Success with warnings
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"ok": true,
|
|
194
|
+
"warnings": ["No default vehicle set, using only available vehicle: VIN XXXXXXX"],
|
|
195
|
+
"data": { ... }
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Error
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"ok": false,
|
|
204
|
+
"error": {
|
|
205
|
+
"code": "not_authenticated",
|
|
206
|
+
"message": "Not authenticated. Run `tyta auth login`."
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Error Codes
|
|
212
|
+
|
|
213
|
+
| Code | Meaning |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `not_authenticated` | No token file found |
|
|
216
|
+
| `token_expired` | Tokens expired and refresh failed |
|
|
217
|
+
| `api_error` | Toyota API returned a non-2xx response |
|
|
218
|
+
| `vehicle_not_found` | Specified VIN not found in account |
|
|
219
|
+
| `no_vehicles` | Account has no linked vehicles |
|
|
220
|
+
| `invalid_config` | Config or token file is malformed |
|
|
221
|
+
| `invalid_input` | User-supplied input is invalid |
|
|
222
|
+
| `unknown_error` | Unexpected exception |
|
|
223
|
+
|
|
224
|
+
### Exit Codes
|
|
225
|
+
|
|
226
|
+
| Code | Meaning |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `0` | Success |
|
|
229
|
+
| `1` | Operational error (`ok: false`) |
|
|
230
|
+
| `2` | Usage error (bad arguments) |
|
|
231
|
+
|
|
232
|
+
## Debugging
|
|
233
|
+
|
|
234
|
+
Pass `--debug` to enable verbose logs on stderr and print tracebacks on error:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
tyta --debug status
|
|
238
|
+
tyta --debug auth login
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Logs go to **stderr**; JSON output always goes to **stdout**.
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tyta-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "CLI tool for Toyota Connected Services (EU)"
|
|
5
|
+
authors = [{ name = "Stepan Bolotnikov" }]
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
requires-python = ">=3.10,<4.0"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"typer[all]>=0.9",
|
|
11
|
+
"httpx>=0.27",
|
|
12
|
+
"pyjwt>=2.8",
|
|
13
|
+
"pydantic>=2.0",
|
|
14
|
+
"loguru>=0.7",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
tyta = "tyta.cli.main:app"
|
|
19
|
+
|
|
20
|
+
[tool.poetry]
|
|
21
|
+
packages = [{ include = "tyta" }]
|
|
22
|
+
|
|
23
|
+
[tool.poetry.group.dev.dependencies]
|
|
24
|
+
pytest = ">=8.0"
|
|
25
|
+
pytest-asyncio = ">=0.23"
|
|
26
|
+
pytest-cov = ">=5.0"
|
|
27
|
+
respx = ">=0.21"
|
|
28
|
+
ruff = ">=0.9"
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
asyncio_mode = "auto"
|
|
32
|
+
|
|
33
|
+
[tool.ruff]
|
|
34
|
+
line-length = 100
|
|
35
|
+
target-version = "py310"
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
39
|
+
ignore = ["E501"]
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["poetry-core"]
|
|
43
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI layer for tyta-cli."""
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""tyta auth subcommands: login, logout, status."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import getpass
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from loguru import logger
|
|
13
|
+
|
|
14
|
+
from tyta.core import auth as core_auth
|
|
15
|
+
from tyta.core import envelope as env
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
name="auth",
|
|
19
|
+
help="Manage Toyota account authentication.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_debug() -> bool:
|
|
25
|
+
from tyta.cli.main import _get_state
|
|
26
|
+
|
|
27
|
+
return _get_state().get("debug", False)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_auth_path_override() -> str | None:
|
|
31
|
+
from tyta.cli.main import _get_state
|
|
32
|
+
|
|
33
|
+
return _get_state().get("auth_file")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _emit_and_exit(data: dict, exit_code: int = 0) -> None:
|
|
37
|
+
"""Print JSON envelope and raise SystemExit directly."""
|
|
38
|
+
print(json.dumps(data, indent=2))
|
|
39
|
+
sys.exit(exit_code)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@app.command("login")
|
|
43
|
+
def login() -> None:
|
|
44
|
+
"""Authenticate with Toyota and save credentials."""
|
|
45
|
+
debug = _get_debug()
|
|
46
|
+
auth_path = core_auth.get_auth_path(_get_auth_path_override())
|
|
47
|
+
|
|
48
|
+
# Collect credentials — use env vars if available
|
|
49
|
+
username = os.environ.get("TOYOTA_USERNAME")
|
|
50
|
+
password = os.environ.get("TOYOTA_PASSWORD")
|
|
51
|
+
|
|
52
|
+
if not username:
|
|
53
|
+
username = typer.prompt("Toyota username (email)")
|
|
54
|
+
if not password:
|
|
55
|
+
password = getpass.getpass("Toyota password: ")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
token_data = asyncio.run(core_auth.login(username, password, auth_path))
|
|
59
|
+
except core_auth.InvalidUsernameError as exc:
|
|
60
|
+
if debug:
|
|
61
|
+
import traceback
|
|
62
|
+
|
|
63
|
+
traceback.print_exc(file=sys.stderr)
|
|
64
|
+
_emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
|
|
65
|
+
return
|
|
66
|
+
except core_auth.AuthError as exc:
|
|
67
|
+
if debug:
|
|
68
|
+
import traceback
|
|
69
|
+
|
|
70
|
+
traceback.print_exc(file=sys.stderr)
|
|
71
|
+
_emit_and_exit(env.error(exc.code, exc.message), exit_code=1)
|
|
72
|
+
return
|
|
73
|
+
except json.JSONDecodeError:
|
|
74
|
+
if debug:
|
|
75
|
+
import traceback
|
|
76
|
+
|
|
77
|
+
traceback.print_exc(file=sys.stderr)
|
|
78
|
+
_emit_and_exit(
|
|
79
|
+
env.error("invalid_config", "Received malformed JSON from Toyota API."), exit_code=1
|
|
80
|
+
)
|
|
81
|
+
return
|
|
82
|
+
except Exception as exc:
|
|
83
|
+
if debug:
|
|
84
|
+
import traceback
|
|
85
|
+
|
|
86
|
+
traceback.print_exc(file=sys.stderr)
|
|
87
|
+
logger.debug(f"Unexpected error during login: {exc}")
|
|
88
|
+
_emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
_emit_and_exit(
|
|
92
|
+
env.ok(
|
|
93
|
+
{
|
|
94
|
+
"message": "Logged in successfully.",
|
|
95
|
+
"username": token_data["username"],
|
|
96
|
+
"expires_at": token_data["expires_at"],
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command("logout")
|
|
103
|
+
def logout() -> None:
|
|
104
|
+
"""Delete saved credentials."""
|
|
105
|
+
debug = _get_debug()
|
|
106
|
+
auth_path = core_auth.get_auth_path(_get_auth_path_override())
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
deleted = core_auth.logout(auth_path)
|
|
110
|
+
except Exception as exc:
|
|
111
|
+
if debug:
|
|
112
|
+
import traceback
|
|
113
|
+
|
|
114
|
+
traceback.print_exc(file=sys.stderr)
|
|
115
|
+
_emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
if deleted:
|
|
119
|
+
_emit_and_exit(env.ok({"message": "Logged out successfully."}))
|
|
120
|
+
else:
|
|
121
|
+
_emit_and_exit(env.ok({"message": "Already logged out (no token file found)."}))
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@app.command("status")
|
|
125
|
+
def auth_status() -> None:
|
|
126
|
+
"""Show current authentication status."""
|
|
127
|
+
debug = _get_debug()
|
|
128
|
+
auth_path = core_auth.get_auth_path(_get_auth_path_override())
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
result = core_auth.status(auth_path)
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
if debug:
|
|
134
|
+
import traceback
|
|
135
|
+
|
|
136
|
+
traceback.print_exc(file=sys.stderr)
|
|
137
|
+
_emit_and_exit(env.error("unknown_error", str(exc)), exit_code=1)
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
_emit_and_exit(env.ok(result))
|