remotivelabs-cli 0.2.3__tar.gz → 0.3.1__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.
Potentially problematic release.
This version of remotivelabs-cli might be problematic. Click here for more details.
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/PKG-INFO +3 -2
- remotivelabs_cli-0.3.1/cli/broker/__init__.py +36 -0
- remotivelabs_cli-0.3.1/cli/broker/discovery.py +43 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/export.py +6 -36
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/files.py +12 -12
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/lib/broker.py +132 -106
- remotivelabs_cli-0.3.1/cli/broker/lib/client.py +224 -0
- remotivelabs_cli-0.3.1/cli/broker/lib/helper.py +277 -0
- remotivelabs_cli-0.3.1/cli/broker/lib/signalcreator.py +196 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/license_flows.py +11 -13
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/playback.py +10 -10
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/record.py +4 -4
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/scripting.py +6 -9
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/signals.py +17 -19
- remotivelabs_cli-0.2.3/cli/cloud/cloud_cli.py → remotivelabs_cli-0.3.1/cli/cloud/__init__.py +2 -14
- remotivelabs_cli-0.3.1/cli/cloud/auth/cmd.py +129 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/auth/login.py +42 -54
- remotivelabs_cli-0.3.1/cli/cloud/auth_tokens.py +139 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/brokers.py +5 -9
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/configs.py +4 -17
- remotivelabs_cli-0.3.1/cli/cloud/licenses/cmd.py +14 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/organisations.py +12 -17
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/projects.py +3 -3
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/recordings.py +35 -61
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/recordings_playback.py +22 -22
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/resumable_upload.py +6 -6
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/service_account_tokens.py +4 -3
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/storage/cmd.py +2 -3
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/storage/copy.py +2 -1
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/connect/connect.py +4 -4
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/connect/protopie/protopie.py +22 -30
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/remotive.py +16 -26
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/__init__.py +1 -2
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/config_file.py +2 -0
- remotivelabs_cli-0.3.1/cli/settings/core.py +322 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_config_file.py +13 -6
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/migration_tools.py +6 -4
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/state_file.py +12 -4
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/tools/can/can.py +4 -7
- remotivelabs_cli-0.3.1/cli/topology/__init__.py +3 -0
- remotivelabs_cli-0.3.1/cli/topology/cmd.py +79 -0
- remotivelabs_cli-0.3.1/cli/topology/start_trial.py +105 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/typer/typer_utils.py +3 -6
- remotivelabs_cli-0.3.1/cli/utils/console.py +61 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/utils/rest_helper.py +33 -31
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/utils/versions.py +7 -19
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/pyproject.toml +3 -2
- remotivelabs_cli-0.2.3/cli/broker/brokers.py +0 -93
- remotivelabs_cli-0.2.3/cli/cloud/auth/cmd.py +0 -88
- remotivelabs_cli-0.2.3/cli/cloud/auth_tokens.py +0 -346
- remotivelabs_cli-0.2.3/cli/errors.py +0 -44
- remotivelabs_cli-0.2.3/cli/settings/core.py +0 -322
- remotivelabs_cli-0.2.3/cli/topology/cmd.py +0 -102
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/LICENSE +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/README.md +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/.DS_Store +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/api/cloud/tokens.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/lib/__about__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/broker/licenses.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/auth/__init__.py +0 -0
- {remotivelabs_cli-0.2.3/cli/cloud → remotivelabs_cli-0.3.1/cli/cloud/licenses}/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/sample_recordings.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/service_accounts.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/storage/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/storage/uri_or_path.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/cloud/uri.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/connect/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_all_token_files.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_legacy_dirs.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_token_file.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/settings/token_file.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/tools/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/tools/can/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/tools/tools.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/typer/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/utils/__init__.py +0 -0
- {remotivelabs_cli-0.2.3 → remotivelabs_cli-0.3.1}/cli/utils/time.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: remotivelabs-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: CLI for operating RemotiveCloud and RemotiveBroker
|
|
5
5
|
Author: Johan Rask
|
|
6
6
|
Author-email: johan.rask@remotivelabs.com
|
|
@@ -20,7 +20,8 @@ Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
|
|
20
20
|
Requires-Dist: pyjwt (>=2.6,<3.0)
|
|
21
21
|
Requires-Dist: python-can (>=4.3.1)
|
|
22
22
|
Requires-Dist: python-socketio (>=4.6.1)
|
|
23
|
-
Requires-Dist: remotivelabs-broker (>=0.1
|
|
23
|
+
Requires-Dist: remotivelabs-broker (>=0.9.1,<0.10.0)
|
|
24
|
+
Requires-Dist: requests (>=2.32.4,<3.0.0)
|
|
24
25
|
Requires-Dist: rich (>=13.7.0,<13.8.0)
|
|
25
26
|
Requires-Dist: trogon (>=0.5.0)
|
|
26
27
|
Requires-Dist: typer (==0.12.5)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from cli.broker.discovery import discover as discover_cmd
|
|
8
|
+
from cli.typer import typer_utils
|
|
9
|
+
|
|
10
|
+
from . import export, files, licenses, playback, record, scripting, signals
|
|
11
|
+
|
|
12
|
+
app = typer_utils.create_typer(rich_markup_mode="rich")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# TODO: remove this?
|
|
16
|
+
def cb(url: str = typer.Option(None, is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL")) -> None:
|
|
17
|
+
# This can be used to override the --url per command, lets see if this is a better approach
|
|
18
|
+
if url is not None:
|
|
19
|
+
os.environ["REMOTIVE_BROKER_URL"] = url
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# TODO: move broker commands to subcommand instead?
|
|
23
|
+
app.command(help="Discover brokers on this network")(discover_cmd)
|
|
24
|
+
app.callback()(cb)
|
|
25
|
+
|
|
26
|
+
# subcommands
|
|
27
|
+
app.add_typer(playback.app, name="playback", help="Manage playing recordings")
|
|
28
|
+
app.add_typer(record.app, name="record", help="Record data on buses")
|
|
29
|
+
app.add_typer(files.app, name="files", help="Upload/Download configurations and recordings")
|
|
30
|
+
app.add_typer(signals.app, name="signals", help="Find and subscribe to signals")
|
|
31
|
+
app.add_typer(export.app, name="export", help="Export to external formats")
|
|
32
|
+
app.add_typer(scripting.app, name="scripting", help="LUA scripting utilities")
|
|
33
|
+
app.add_typer(licenses.app, name="license", help="View and request license to broker")
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
app()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from time import sleep
|
|
4
|
+
|
|
5
|
+
from zeroconf import IPVersion, ServiceBrowser, ServiceStateChange, Zeroconf
|
|
6
|
+
|
|
7
|
+
from cli.utils.console import print_generic_message, print_newline
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def discover() -> None:
|
|
11
|
+
zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
|
|
12
|
+
services = ["_remotivebroker._tcp.local."]
|
|
13
|
+
|
|
14
|
+
print_generic_message("Looking for RemotiveBrokers on your network, press Ctrl-C to exit...")
|
|
15
|
+
ServiceBrowser(zeroconf, services, handlers=[on_service_state_change])
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
while True:
|
|
19
|
+
sleep(0.1)
|
|
20
|
+
except KeyboardInterrupt:
|
|
21
|
+
pass
|
|
22
|
+
finally:
|
|
23
|
+
zeroconf.close()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def on_service_state_change(zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange) -> None:
|
|
27
|
+
"""TODO: use log instead of print for debug information?"""
|
|
28
|
+
if state_change is ServiceStateChange.Removed:
|
|
29
|
+
print_generic_message(f"Service {name} was removed")
|
|
30
|
+
|
|
31
|
+
if state_change is ServiceStateChange.Updated:
|
|
32
|
+
print_generic_message(f"Service {name} was updated")
|
|
33
|
+
|
|
34
|
+
if state_change is ServiceStateChange.Added:
|
|
35
|
+
print_generic_message(f"[ {name} ]")
|
|
36
|
+
info = zeroconf.get_service_info(service_type, name)
|
|
37
|
+
if info:
|
|
38
|
+
for addr in info.parsed_scoped_addresses():
|
|
39
|
+
print_generic_message(f"RemotiveBrokerApp: http://{addr}:8080")
|
|
40
|
+
print_generic_message(f"RemotiveBroker http://{addr}:50051")
|
|
41
|
+
else:
|
|
42
|
+
print_generic_message(" No info")
|
|
43
|
+
print_newline()
|
|
@@ -7,7 +7,7 @@ from typing import Any, List
|
|
|
7
7
|
import grpc
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
|
-
from cli.
|
|
10
|
+
from cli.utils.console import print_generic_message, print_grpc_error, print_hint
|
|
11
11
|
|
|
12
12
|
from ..typer import typer_utils
|
|
13
13
|
from .lib.broker import Broker, SubscribableSignal
|
|
@@ -26,7 +26,6 @@ def influxdb(
|
|
|
26
26
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
|
27
27
|
api_key: str = typer.Option(None, help="Cloud Broker API-KEY", envvar="REMOTIVE_BROKER_API_KEY"),
|
|
28
28
|
signal: List[str] = typer.Option(..., help="List of signal names to subscribe to in format namespace:signal_name"),
|
|
29
|
-
# namespace: str = typer.Option(..., help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
|
30
29
|
on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
|
|
31
30
|
output: str = typer.Option(None, help="Write results to file, defaults to stdout"),
|
|
32
31
|
) -> None:
|
|
@@ -51,6 +50,7 @@ def influxdb(
|
|
|
51
50
|
|
|
52
51
|
"""
|
|
53
52
|
|
|
53
|
+
# TODO: file is not properly opened using a context manager
|
|
54
54
|
if output is not None:
|
|
55
55
|
f = open(output, "w")
|
|
56
56
|
|
|
@@ -65,53 +65,23 @@ def influxdb(
|
|
|
65
65
|
return
|
|
66
66
|
sig: str = signals[0]["name"].rpartition(".")[-1]
|
|
67
67
|
frame = signals[0]["name"].rsplit(".", 1)[0]
|
|
68
|
-
# frame_name = signals[0]["name"].split(".")[1]
|
|
69
68
|
namespace = signals[0]["namespace"]
|
|
70
69
|
signals_str = ",".join(list(map(lambda s: f"{sig}={s['value']}", signals)))
|
|
71
70
|
influx_lp = f"{frame},namespace={namespace} {signals_str} {round(signals[0]['timestamp_us'] * 1000)}"
|
|
72
71
|
if output is not None:
|
|
73
72
|
f.write(f"{influx_lp}\n")
|
|
74
73
|
else:
|
|
75
|
-
print
|
|
76
|
-
|
|
77
|
-
# TODO - support for csv
|
|
78
|
-
# def csv(x):
|
|
79
|
-
# list = list(x)
|
|
80
|
-
# print(x)
|
|
81
|
-
# l = list(x)
|
|
82
|
-
# print(l)
|
|
83
|
-
# ll=list(map(lambda s : s, l))
|
|
84
|
-
# for s in l:
|
|
85
|
-
# dt = datetime.fromtimestamp(s["timestamp_nanos"] / 1000000)
|
|
86
|
-
# t=datetime.isoformat(dt)
|
|
87
|
-
# t=rfc3339.format_millisecond(dt)
|
|
88
|
-
# rich_rprint(len(l))
|
|
89
|
-
# lat = (l[0])
|
|
90
|
-
# lon = l[1]
|
|
91
|
-
# dt = datetime.fromtimestamp(lat["timestamp_nanos"] / 1000000)
|
|
92
|
-
# t=datetime.isoformat(dt)
|
|
93
|
-
# t = rfc3339.format_millisecond(dt)
|
|
94
|
-
# name = s["name"]
|
|
95
|
-
# value = s["value"]
|
|
96
|
-
# if output is not None:
|
|
97
|
-
# f.write(f'coord,{lat["value"]},{lon["value"]},{t}\n')
|
|
98
|
-
# else:
|
|
99
|
-
# print(f'coord,{lat["value"]},{lon["value"]},{t}')
|
|
100
|
-
# if output is not None:
|
|
101
|
-
# f.flush()
|
|
102
|
-
# print(x["timestamp_nanos"])
|
|
103
|
-
# rich_rprint(json.dumps(list(x)))
|
|
74
|
+
# TODO: Use log instead of print for debug information?
|
|
75
|
+
print_generic_message(f"{influx_lp}")
|
|
104
76
|
|
|
105
77
|
os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
|
|
106
78
|
|
|
107
|
-
# print(namespace)
|
|
108
|
-
# signals2 = list(map( lambda s: s['signal'], broker.list_signal_names2(namespace)))
|
|
109
79
|
try:
|
|
110
80
|
|
|
111
81
|
def to_subscribable_signal(sig: str) -> SubscribableSignal:
|
|
112
82
|
arr = sig.split(":")
|
|
113
83
|
if len(arr) != 2:
|
|
114
|
-
|
|
84
|
+
print_hint(f"--signal must have format namespace:signal ({sig})")
|
|
115
85
|
sys.exit(1)
|
|
116
86
|
|
|
117
87
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
|
@@ -120,4 +90,4 @@ def influxdb(
|
|
|
120
90
|
broker = Broker(url, api_key)
|
|
121
91
|
broker.long_name_subscribe(signals_to_subscribe_to, per_frame_influx_line_protocol, on_change_only)
|
|
122
92
|
except grpc.RpcError as rpc_error:
|
|
123
|
-
|
|
93
|
+
print_grpc_error(rpc_error)
|
|
@@ -7,8 +7,8 @@ import grpc
|
|
|
7
7
|
import typer
|
|
8
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
9
|
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
10
|
from cli.typer import typer_utils
|
|
11
|
+
from cli.utils.console import print_generic_error, print_grpc_error, print_success
|
|
12
12
|
|
|
13
13
|
from .lib.broker import Broker
|
|
14
14
|
|
|
@@ -23,9 +23,9 @@ def reload_configuration(
|
|
|
23
23
|
try:
|
|
24
24
|
broker = Broker(url, api_key)
|
|
25
25
|
broker.reload_config()
|
|
26
|
-
|
|
26
|
+
print_success("Configuration reloaded")
|
|
27
27
|
except grpc.RpcError as err:
|
|
28
|
-
|
|
28
|
+
print_grpc_error(err)
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@app.command()
|
|
@@ -42,12 +42,12 @@ def delete(
|
|
|
42
42
|
broker = Broker(url, api_key)
|
|
43
43
|
|
|
44
44
|
if len(path) == 0:
|
|
45
|
-
|
|
45
|
+
print_generic_error("At least one path must be suppled")
|
|
46
46
|
raise typer.Exit(1)
|
|
47
47
|
|
|
48
48
|
broker.delete_files(path, exit_on_failure)
|
|
49
49
|
except grpc.RpcError as err:
|
|
50
|
-
|
|
50
|
+
print_grpc_error(err)
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
@app.command()
|
|
@@ -72,12 +72,12 @@ def download(
|
|
|
72
72
|
if output != "":
|
|
73
73
|
output_file = output
|
|
74
74
|
if os.path.exists(output_file):
|
|
75
|
-
|
|
75
|
+
print_generic_error(f"File already exist {output_file}, please use another output file name")
|
|
76
76
|
else:
|
|
77
77
|
broker.download(path, output_file)
|
|
78
|
-
|
|
78
|
+
print_success(f"{output_file} saved")
|
|
79
79
|
except grpc.RpcError as err:
|
|
80
|
-
|
|
80
|
+
print_grpc_error(err)
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
@app.command()
|
|
@@ -102,19 +102,19 @@ def upload(
|
|
|
102
102
|
path = "./" ## Does not work otherwise
|
|
103
103
|
|
|
104
104
|
if not os.path.exists(path):
|
|
105
|
-
|
|
105
|
+
print_generic_error(f"File {path} does not exist")
|
|
106
106
|
raise typer.Exit(1)
|
|
107
107
|
|
|
108
108
|
broker = Broker(url, api_key)
|
|
109
109
|
|
|
110
110
|
if os.path.isdir(path):
|
|
111
111
|
broker.upload_folder(path)
|
|
112
|
-
|
|
112
|
+
print_success(f"{path} uploaded")
|
|
113
113
|
else:
|
|
114
114
|
output_file = os.path.basename(path)
|
|
115
115
|
if output != "":
|
|
116
116
|
output_file = output
|
|
117
117
|
broker.upload(path, output_file)
|
|
118
|
-
|
|
118
|
+
print_success(f"{path} uploaded")
|
|
119
119
|
except grpc.RpcError as err:
|
|
120
|
-
|
|
120
|
+
print_grpc_error(err)
|