tescmd 0.1.2__py3-none-any.whl
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.
- tescmd/__init__.py +3 -0
- tescmd/__main__.py +5 -0
- tescmd/_internal/__init__.py +0 -0
- tescmd/_internal/async_utils.py +25 -0
- tescmd/_internal/permissions.py +43 -0
- tescmd/_internal/vin.py +44 -0
- tescmd/api/__init__.py +1 -0
- tescmd/api/charging.py +102 -0
- tescmd/api/client.py +189 -0
- tescmd/api/command.py +540 -0
- tescmd/api/energy.py +146 -0
- tescmd/api/errors.py +76 -0
- tescmd/api/partner.py +40 -0
- tescmd/api/sharing.py +65 -0
- tescmd/api/signed_command.py +277 -0
- tescmd/api/user.py +38 -0
- tescmd/api/vehicle.py +150 -0
- tescmd/auth/__init__.py +1 -0
- tescmd/auth/oauth.py +312 -0
- tescmd/auth/server.py +108 -0
- tescmd/auth/token_store.py +273 -0
- tescmd/ble/__init__.py +0 -0
- tescmd/cache/__init__.py +6 -0
- tescmd/cache/keys.py +51 -0
- tescmd/cache/response_cache.py +213 -0
- tescmd/cli/__init__.py +0 -0
- tescmd/cli/_client.py +603 -0
- tescmd/cli/_options.py +126 -0
- tescmd/cli/auth.py +682 -0
- tescmd/cli/billing.py +240 -0
- tescmd/cli/cache.py +85 -0
- tescmd/cli/charge.py +610 -0
- tescmd/cli/climate.py +501 -0
- tescmd/cli/energy.py +385 -0
- tescmd/cli/key.py +611 -0
- tescmd/cli/main.py +601 -0
- tescmd/cli/media.py +146 -0
- tescmd/cli/nav.py +242 -0
- tescmd/cli/partner.py +112 -0
- tescmd/cli/raw.py +75 -0
- tescmd/cli/security.py +495 -0
- tescmd/cli/setup.py +786 -0
- tescmd/cli/sharing.py +188 -0
- tescmd/cli/software.py +81 -0
- tescmd/cli/status.py +106 -0
- tescmd/cli/trunk.py +240 -0
- tescmd/cli/user.py +145 -0
- tescmd/cli/vehicle.py +837 -0
- tescmd/config/__init__.py +0 -0
- tescmd/crypto/__init__.py +19 -0
- tescmd/crypto/ecdh.py +46 -0
- tescmd/crypto/keys.py +122 -0
- tescmd/deploy/__init__.py +0 -0
- tescmd/deploy/github_pages.py +268 -0
- tescmd/models/__init__.py +85 -0
- tescmd/models/auth.py +108 -0
- tescmd/models/command.py +18 -0
- tescmd/models/config.py +63 -0
- tescmd/models/energy.py +56 -0
- tescmd/models/sharing.py +26 -0
- tescmd/models/user.py +37 -0
- tescmd/models/vehicle.py +185 -0
- tescmd/output/__init__.py +5 -0
- tescmd/output/formatter.py +132 -0
- tescmd/output/json_output.py +83 -0
- tescmd/output/rich_output.py +809 -0
- tescmd/protocol/__init__.py +23 -0
- tescmd/protocol/commands.py +175 -0
- tescmd/protocol/encoder.py +122 -0
- tescmd/protocol/metadata.py +116 -0
- tescmd/protocol/payloads.py +621 -0
- tescmd/protocol/protobuf/__init__.py +6 -0
- tescmd/protocol/protobuf/messages.py +564 -0
- tescmd/protocol/session.py +318 -0
- tescmd/protocol/signer.py +84 -0
- tescmd/py.typed +0 -0
- tescmd-0.1.2.dist-info/METADATA +458 -0
- tescmd-0.1.2.dist-info/RECORD +81 -0
- tescmd-0.1.2.dist-info/WHEEL +4 -0
- tescmd-0.1.2.dist-info/entry_points.txt +2 -0
- tescmd-0.1.2.dist-info/licenses/LICENSE +21 -0
tescmd/cli/user.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""CLI commands for user account operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from tescmd._internal.async_utils import run_async
|
|
10
|
+
from tescmd.cli._client import TTL_DEFAULT, TTL_STATIC, cached_api_call, get_user_api
|
|
11
|
+
from tescmd.cli._options import global_options
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from tescmd.cli.main import AppContext
|
|
15
|
+
|
|
16
|
+
user_group = click.Group("user", help="User account commands")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@user_group.command("me")
|
|
20
|
+
@global_options
|
|
21
|
+
def me_cmd(app_ctx: AppContext) -> None:
|
|
22
|
+
"""Show account information."""
|
|
23
|
+
run_async(_cmd_me(app_ctx))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def _cmd_me(app_ctx: AppContext) -> None:
|
|
27
|
+
formatter = app_ctx.formatter
|
|
28
|
+
client, api = get_user_api(app_ctx)
|
|
29
|
+
try:
|
|
30
|
+
data = await cached_api_call(
|
|
31
|
+
app_ctx,
|
|
32
|
+
scope="account",
|
|
33
|
+
identifier="global",
|
|
34
|
+
endpoint="user.me",
|
|
35
|
+
fetch=lambda: api.me(),
|
|
36
|
+
ttl=TTL_STATIC,
|
|
37
|
+
)
|
|
38
|
+
finally:
|
|
39
|
+
await client.close()
|
|
40
|
+
|
|
41
|
+
if formatter.format == "json":
|
|
42
|
+
formatter.output(data, command="user.me")
|
|
43
|
+
else:
|
|
44
|
+
formatter.rich.user_info(data)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@user_group.command("region")
|
|
48
|
+
@global_options
|
|
49
|
+
def region_cmd(app_ctx: AppContext) -> None:
|
|
50
|
+
"""Show regional Fleet API endpoint."""
|
|
51
|
+
run_async(_cmd_region(app_ctx))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def _cmd_region(app_ctx: AppContext) -> None:
|
|
55
|
+
formatter = app_ctx.formatter
|
|
56
|
+
client, api = get_user_api(app_ctx)
|
|
57
|
+
try:
|
|
58
|
+
data = await cached_api_call(
|
|
59
|
+
app_ctx,
|
|
60
|
+
scope="account",
|
|
61
|
+
identifier="global",
|
|
62
|
+
endpoint="user.region",
|
|
63
|
+
fetch=lambda: api.region(),
|
|
64
|
+
ttl=TTL_STATIC,
|
|
65
|
+
)
|
|
66
|
+
finally:
|
|
67
|
+
await client.close()
|
|
68
|
+
|
|
69
|
+
if formatter.format == "json":
|
|
70
|
+
formatter.output(data, command="user.region")
|
|
71
|
+
else:
|
|
72
|
+
formatter.rich.user_region(data)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@user_group.command("orders")
|
|
76
|
+
@global_options
|
|
77
|
+
def orders_cmd(app_ctx: AppContext) -> None:
|
|
78
|
+
"""Show vehicle orders."""
|
|
79
|
+
run_async(_cmd_orders(app_ctx))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def _cmd_orders(app_ctx: AppContext) -> None:
|
|
83
|
+
formatter = app_ctx.formatter
|
|
84
|
+
client, api = get_user_api(app_ctx)
|
|
85
|
+
try:
|
|
86
|
+
data = await cached_api_call(
|
|
87
|
+
app_ctx,
|
|
88
|
+
scope="account",
|
|
89
|
+
identifier="global",
|
|
90
|
+
endpoint="user.orders",
|
|
91
|
+
fetch=lambda: api.orders(),
|
|
92
|
+
ttl=TTL_DEFAULT,
|
|
93
|
+
)
|
|
94
|
+
finally:
|
|
95
|
+
await client.close()
|
|
96
|
+
|
|
97
|
+
if formatter.format == "json":
|
|
98
|
+
formatter.output(data, command="user.orders")
|
|
99
|
+
else:
|
|
100
|
+
if data:
|
|
101
|
+
for order in data:
|
|
102
|
+
if isinstance(order, dict):
|
|
103
|
+
oid = order.get("order_id") or "?"
|
|
104
|
+
model = order.get("model") or ""
|
|
105
|
+
status = order.get("status") or ""
|
|
106
|
+
else:
|
|
107
|
+
oid = order.order_id or "?"
|
|
108
|
+
model = order.model or ""
|
|
109
|
+
status = order.status or ""
|
|
110
|
+
formatter.rich.info(f" {oid}: {model} [{status}]")
|
|
111
|
+
else:
|
|
112
|
+
formatter.rich.info("[dim]No orders found.[/dim]")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@user_group.command("features")
|
|
116
|
+
@global_options
|
|
117
|
+
def features_cmd(app_ctx: AppContext) -> None:
|
|
118
|
+
"""Show feature flags."""
|
|
119
|
+
run_async(_cmd_features(app_ctx))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def _cmd_features(app_ctx: AppContext) -> None:
|
|
123
|
+
formatter = app_ctx.formatter
|
|
124
|
+
client, api = get_user_api(app_ctx)
|
|
125
|
+
try:
|
|
126
|
+
data = await cached_api_call(
|
|
127
|
+
app_ctx,
|
|
128
|
+
scope="account",
|
|
129
|
+
identifier="global",
|
|
130
|
+
endpoint="user.features",
|
|
131
|
+
fetch=lambda: api.feature_config(),
|
|
132
|
+
ttl=TTL_STATIC,
|
|
133
|
+
)
|
|
134
|
+
finally:
|
|
135
|
+
await client.close()
|
|
136
|
+
|
|
137
|
+
if formatter.format == "json":
|
|
138
|
+
formatter.output(data, command="user.features")
|
|
139
|
+
else:
|
|
140
|
+
dumped = data.model_dump(exclude_none=True) if hasattr(data, "model_dump") else data
|
|
141
|
+
if dumped:
|
|
142
|
+
for key, val in sorted(dumped.items()):
|
|
143
|
+
formatter.rich.info(f" {key}: {val}")
|
|
144
|
+
else:
|
|
145
|
+
formatter.rich.info("[dim]No feature flags available.[/dim]")
|