remotivelabs-cli 0.0.1a3__tar.gz → 0.0.13__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.
Files changed (51) hide show
  1. remotivelabs_cli-0.0.13/LICENSE +17 -0
  2. remotivelabs_cli-0.0.13/PKG-INFO +33 -0
  3. remotivelabs_cli-0.0.13/README.md +7 -0
  4. {remotivelabs_cli-0.0.1a3 → remotivelabs_cli-0.0.13}/cli/__about__.py +1 -1
  5. remotivelabs_cli-0.0.13/cli/__init__.py +0 -0
  6. {remotivelabs_cli-0.0.1a3/cli → remotivelabs_cli-0.0.13/cli/broker}/brokers.py +27 -29
  7. remotivelabs_cli-0.0.13/cli/broker/export.py +110 -0
  8. remotivelabs_cli-0.0.13/cli/broker/files.py +119 -0
  9. {remotivelabs_cli-0.0.1a3/cli → remotivelabs_cli-0.0.13/cli/broker}/lib/__about__.py +1 -1
  10. remotivelabs_cli-0.0.13/cli/broker/lib/broker.py +522 -0
  11. remotivelabs_cli-0.0.13/cli/broker/playback.py +137 -0
  12. remotivelabs_cli-0.0.13/cli/broker/record.py +41 -0
  13. remotivelabs_cli-0.0.13/cli/broker/scripting.py +119 -0
  14. remotivelabs_cli-0.0.13/cli/broker/signals.py +189 -0
  15. {remotivelabs_cli-0.0.1a3 → remotivelabs_cli-0.0.13}/cli/cloud/auth.py +57 -54
  16. remotivelabs_cli-0.0.13/cli/cloud/auth_tokens.py +116 -0
  17. remotivelabs_cli-0.0.13/cli/cloud/brokers.py +121 -0
  18. remotivelabs_cli-0.0.13/cli/cloud/cloud_cli.py +39 -0
  19. {remotivelabs_cli-0.0.1a3 → remotivelabs_cli-0.0.13}/cli/cloud/configs.py +45 -11
  20. remotivelabs_cli-0.0.13/cli/cloud/projects.py +40 -0
  21. remotivelabs_cli-0.0.13/cli/cloud/recordings.py +487 -0
  22. remotivelabs_cli-0.0.13/cli/cloud/rest_helper.py +194 -0
  23. remotivelabs_cli-0.0.13/cli/cloud/sample_recordings.py +24 -0
  24. remotivelabs_cli-0.0.13/cli/cloud/service_account_tokens.py +67 -0
  25. remotivelabs_cli-0.0.13/cli/cloud/service_accounts.py +43 -0
  26. remotivelabs_cli-0.0.13/cli/connect/__init__.py +1 -0
  27. remotivelabs_cli-0.0.13/cli/connect/connect.py +106 -0
  28. remotivelabs_cli-0.0.13/cli/connect/protopie/protopie.py +173 -0
  29. remotivelabs_cli-0.0.13/cli/errors.py +36 -0
  30. remotivelabs_cli-0.0.13/cli/remotive.py +64 -0
  31. {remotivelabs_cli-0.0.1a3 → remotivelabs_cli-0.0.13}/cli/requirements.txt +1 -1
  32. remotivelabs_cli-0.0.13/cli/settings.py +25 -0
  33. remotivelabs_cli-0.0.13/cli/tools/__init__.py +1 -0
  34. remotivelabs_cli-0.0.13/cli/tools/can/__init__.py +1 -0
  35. remotivelabs_cli-0.0.13/cli/tools/can/can.py +79 -0
  36. remotivelabs_cli-0.0.13/cli/tools/tools.py +10 -0
  37. remotivelabs_cli-0.0.13/pyproject.toml +39 -0
  38. remotivelabs_cli-0.0.1a3/PKG-INFO +0 -46
  39. remotivelabs_cli-0.0.1a3/README.md +0 -26
  40. remotivelabs_cli-0.0.1a3/cli/__init__.py +0 -1
  41. remotivelabs_cli-0.0.1a3/cli/cloud/brokers.py +0 -57
  42. remotivelabs_cli-0.0.1a3/cli/cloud/cloud_cli.py +0 -49
  43. remotivelabs_cli-0.0.1a3/cli/cloud/recordings.py +0 -164
  44. remotivelabs_cli-0.0.1a3/cli/cloud/rest_helper.py +0 -99
  45. remotivelabs_cli-0.0.1a3/cli/cloud/service_account_keys.py +0 -43
  46. remotivelabs_cli-0.0.1a3/cli/cloud/service_accounts.py +0 -36
  47. remotivelabs_cli-0.0.1a3/cli/lib/broker.py +0 -138
  48. remotivelabs_cli-0.0.1a3/cli/remotive.py +0 -8
  49. remotivelabs_cli-0.0.1a3/cli/test/test_simple.py +0 -5
  50. remotivelabs_cli-0.0.1a3/pyproject.toml +0 -25
  51. {remotivelabs_cli-0.0.1a3 → remotivelabs_cli-0.0.13}/cli/cloud/__init__.py +0 -0
@@ -0,0 +1,17 @@
1
+ Apache-2.0
2
+
3
+ RemotiveLabs CLI
4
+ Copyright 2022 RemotiveLabs <hello@remotivelabs.com>
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.1
2
+ Name: remotivelabs-cli
3
+ Version: 0.0.13
4
+ Summary: CLI for operating RemotiveCloud and RemotiveBroker
5
+ Author: Johan Rask
6
+ Author-email: johan.rask@remotivelabs.com
7
+ Requires-Python: >=3.8,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: plotext (>=5.2,<6.0)
15
+ Requires-Dist: pyjwt (>=2.6,<3.0)
16
+ Requires-Dist: python-can (>=4.3.1)
17
+ Requires-Dist: python-socketio (>=4.6.1)
18
+ Requires-Dist: remotivelabs-broker (>=0.1.17)
19
+ Requires-Dist: rich (>=13.7.0,<13.8.0)
20
+ Requires-Dist: trogon (>=0.5.0)
21
+ Requires-Dist: typer (>=0.9.0,<0.10.0)
22
+ Requires-Dist: websocket-client (>=1.6,<2.0)
23
+ Requires-Dist: zeroconf (>=0.127.0,<0.128.0)
24
+ Description-Content-Type: text/markdown
25
+
26
+ # RemotiveLabs - CLI
27
+ [![PyPI - Version](https://img.shields.io/pypi/v/remotivelabs-cli.svg)](https://pypi.org/project/remotivelabs-cli)
28
+
29
+ Use this CLI with our cloud and broker as a compliment to code and web tools.
30
+
31
+ Read more at https://docs.remotivelabs.com/docs/remotive-cli
32
+
33
+
@@ -0,0 +1,7 @@
1
+ # RemotiveLabs - CLI
2
+ [![PyPI - Version](https://img.shields.io/pypi/v/remotivelabs-cli.svg)](https://pypi.org/project/remotivelabs-cli)
3
+
4
+ Use this CLI with our cloud and broker as a compliment to code and web tools.
5
+
6
+ Read more at https://docs.remotivelabs.com/docs/remotive-cli
7
+
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2022-present U.N. Owen <void@some.where>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = '0.0.1'
4
+ __version__ = "0.0.1"
File without changes
@@ -1,4 +1,6 @@
1
- import json
1
+ from __future__ import annotations
2
+
3
+ import os
2
4
  from time import sleep
3
5
 
4
6
  import typer
@@ -9,30 +11,20 @@ from zeroconf import (
9
11
  Zeroconf,
10
12
  )
11
13
 
12
- from .lib.broker import Broker
14
+ from . import export, files, playback, record, scripting, signals
13
15
 
14
- app = typer.Typer()
16
+ app = typer.Typer(rich_markup_mode="rich")
15
17
 
16
18
 
17
- @app.command(help="List signals on broker")
18
- def signals(
19
- url: str = typer.Option(..., help="Broker URL", envvar='REMOTIVE_BROKER_URL'),
20
- api_key: str = typer.Option("offline", help="Cloud Broker API-KEY", envvar='REMOTIVE_BROKER_API_KEY')
19
+ @app.callback()
20
+ def main(
21
+ url: str = typer.Option(None, is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
21
22
  ):
22
- broker = Broker(url, api_key)
23
- # print("Listing available signals")
24
- available_signals = broker.list_signal_names()
25
- print(json.dumps(available_signals))
26
-
27
-
28
- @app.command(help="List namespaces on broker")
29
- def namespaces(
30
- url: str = typer.Option(..., help="Broker URL", envvar='REMOTIVE_BROKER_URL'),
31
- api_key: str = typer.Option("offline", help="Cloud Broker API-KEY", envvar='REMOTIVE_BROKER_API_KEY')
32
- ):
33
- broker = Broker(url, api_key)
34
- namespaces_json = broker.list_namespaces()
35
- print(json.dumps(namespaces_json))
23
+ # This can be used to override the --url per command, lets see if this is a better approach
24
+ if url is not None:
25
+ os.environ["REMOTIVE_BROKER_URL"] = url
26
+ # Do other global stuff, handle other global options here
27
+ return
36
28
 
37
29
 
38
30
  @app.command(help="Discover brokers on this network")
@@ -41,11 +33,11 @@ def discover():
41
33
 
42
34
  zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
43
35
 
44
- services = ["_remotivebroker._tcp.local.", "_googlecast._tcp.local."]
36
+ services = ["_remotivebroker._tcp.local."]
45
37
  # services = list(ZeroconfServiceTypes.find(zc=zeroconf))
46
38
 
47
39
  print("\nLooking for RemotiveBrokers on your network, press Ctrl-C to exit...\n")
48
- browser = ServiceBrowser(zeroconf, services, handlers=[on_service_state_change])
40
+ ServiceBrowser(zeroconf, services, handlers=[on_service_state_change])
49
41
 
50
42
  try:
51
43
  while True:
@@ -56,9 +48,7 @@ def discover():
56
48
  zeroconf.close()
57
49
 
58
50
 
59
- def on_service_state_change(
60
- zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange
61
- ) -> None:
51
+ def on_service_state_change(zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange) -> None:
62
52
  # print(f"Service {name} state changed: {state_change}")
63
53
 
64
54
  if state_change is ServiceStateChange.Removed:
@@ -68,14 +58,15 @@ def on_service_state_change(
68
58
  print(f"Service {name} was updated")
69
59
 
70
60
  if state_change is ServiceStateChange.Added:
71
- print(f"Discovered {name} ")
61
+ print(f"[ {name} ]")
72
62
  info = zeroconf.get_service_info(service_type, name)
73
63
  # print("Info from zeroconf.get_service_info: %r" % (info))
74
64
 
75
65
  if info:
76
66
  # addresses = ["%s:%d" % (addr, cast(int, info.port)) for addr in info.parsed_scoped_addresses()]
77
67
  for addr in info.parsed_scoped_addresses():
78
- print(addr)
68
+ print(f"RemotiveBrokerApp: http://{addr}:8080")
69
+ print(f"RemotiveBroker http://{addr}:50051")
79
70
  # print(" Weight: %d, priority: %d" % (info.weight, info.priority))
80
71
  # print(f" Server: {info.server}")
81
72
  # if info.properties:
@@ -86,8 +77,15 @@ def on_service_state_change(
86
77
  # print(" No properties")
87
78
  else:
88
79
  print(" No info")
89
- print('\n')
80
+ print("\n")
81
+
90
82
 
83
+ app.add_typer(playback.app, name="playback", help="Manage playing recordings")
84
+ app.add_typer(record.app, name="record", help="Record data on buses")
85
+ app.add_typer(files.app, name="files", help="Upload/Download configurations and recordings")
86
+ app.add_typer(signals.app, name="signals", help="Find and subscribe to signals")
87
+ app.add_typer(export.app, name="export", help="Export to external formats")
88
+ app.add_typer(scripting.app, name="scripting")
91
89
 
92
90
  if __name__ == "__main__":
93
91
  app()
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import signal as os_signal
5
+ from typing import List
6
+
7
+ import grpc
8
+ import typer
9
+
10
+ from cli.errors import ErrorPrinter
11
+
12
+ from .lib.broker import Broker
13
+
14
+ app = typer.Typer(
15
+ rich_markup_mode="rich",
16
+ help="""
17
+ Export subscribed signals to different formats, currently only InfluxDB line protocol
18
+ but more formats will come soon
19
+ """,
20
+ )
21
+
22
+
23
+ @app.command()
24
+ def influxdb(
25
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
26
+ api_key: str = typer.Option(None, help="Cloud Broker API-KEY", envvar="REMOTIVE_BROKER_API_KEY"),
27
+ signal: List[str] = typer.Option(..., help="List of signal names to subscribe to"),
28
+ namespace: str = typer.Option(..., help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
29
+ on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
30
+ output: str = typer.Option(None, help="Write results to file, defaults to stdout"),
31
+ ):
32
+ """
33
+ Exports subscribed signals to InfluxDB line-protocol, really useful to dump some signals into
34
+ influxdb for offline analysis and insights.
35
+
36
+ This is a sample for exporting and importing to InfluxDB using remotive-cli and influx-cli
37
+
38
+ Export:
39
+ remotive broker export influxdb --url [URL] --output signals.influx --namespace VehicleBus \\
40
+ --signal Control.SteeringWheel_Position --signal Control.Accelerator_PedalPosition \\
41
+ --signal GpsPosition.GPS_Longitude --signal GpsPosition.GPS_Latitude
42
+
43
+ Output:
44
+ Control, SteeringWheel_Position=1.0,Accelerator_PedalPosition=0,Speed=0 1664787032944374000
45
+ GpsPosition, GPS_Longitude=12.982076,GPS_Latitude=55.618748 1664787032948256000
46
+
47
+ Import:
48
+ influx write --org myorg -b my-bucket -p ns --format=lp -f signals.influx
49
+
50
+
51
+ """
52
+
53
+ if output is not None:
54
+ f = open(output, "w")
55
+
56
+ def exit_on_ctrlc(sig, frame):
57
+ if output is not None:
58
+ f.close()
59
+ os._exit(0)
60
+
61
+ def per_frame_influx_line_protocol(x):
62
+ signals = list(x)
63
+ if len(signals) == 0:
64
+ return
65
+
66
+ frame_name = signals[0]["name"].split(".")[0]
67
+ signals_str = ",".join(list(map(lambda s: f'{s["name"].split(".")[1]}={s["value"]}', signals)))
68
+ influx_lp = f"{frame_name},namespace={namespace} {signals_str} {round(signals[0]['timestamp_us'] * 1000)}"
69
+ if output is not None:
70
+ f.write(f"{influx_lp}\n")
71
+ else:
72
+ print(f"{influx_lp}")
73
+
74
+ # TODO - support for csv
75
+ # def csv(x):
76
+ # list = list(x)
77
+ # print(x)
78
+ # l = list(x)
79
+ # print(l)
80
+ # ll=list(map(lambda s : s, l))
81
+ # for s in l:
82
+ # dt = datetime.fromtimestamp(s["timestamp_nanos"] / 1000000)
83
+ # t=datetime.isoformat(dt)
84
+ # t=rfc3339.format_millisecond(dt)
85
+ # rich_rprint(len(l))
86
+ # lat = (l[0])
87
+ # lon = l[1]
88
+ # dt = datetime.fromtimestamp(lat["timestamp_nanos"] / 1000000)
89
+ # t=datetime.isoformat(dt)
90
+ # t = rfc3339.format_millisecond(dt)
91
+ # name = s["name"]
92
+ # value = s["value"]
93
+ # if output is not None:
94
+ # f.write(f'coord,{lat["value"]},{lon["value"]},{t}\n')
95
+ # else:
96
+ # print(f'coord,{lat["value"]},{lon["value"]},{t}')
97
+ # if output is not None:
98
+ # f.flush()
99
+ # print(x["timestamp_nanos"])
100
+ # rich_rprint(json.dumps(list(x)))
101
+
102
+ os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
103
+
104
+ # print(namespace)
105
+ # signals2 = list(map( lambda s: s['signal'], broker.list_signal_names2(namespace)))
106
+ try:
107
+ broker = Broker(url, api_key)
108
+ broker.subscribe(signal, namespace, per_frame_influx_line_protocol, on_change_only)
109
+ except grpc.RpcError as rpc_error:
110
+ ErrorPrinter.print_grpc_error(rpc_error)
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import List
5
+
6
+ import grpc
7
+ import typer
8
+ from rich.progress import Progress, SpinnerColumn, TextColumn
9
+
10
+ from cli.errors import ErrorPrinter
11
+
12
+ from .lib.broker import Broker
13
+
14
+ app = typer.Typer(help=help)
15
+
16
+
17
+ @app.command()
18
+ def reload_configuration(
19
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
20
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
21
+ ):
22
+ try:
23
+ broker = Broker(url, api_key)
24
+ broker.reload_config()
25
+ print("Configuration successfully reloaded")
26
+ except grpc.RpcError as err:
27
+ ErrorPrinter.print_grpc_error(err)
28
+
29
+
30
+ @app.command()
31
+ def delete(
32
+ path: List[str] = typer.Argument(..., help="Paths to files on broker to delete"),
33
+ exit_on_failure: bool = typer.Option(False, help="Exits if there was a problem deleting a file"),
34
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
35
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
36
+ ):
37
+ """
38
+ Deletes the specified files from the broker
39
+ """
40
+ try:
41
+ broker = Broker(url, api_key)
42
+
43
+ if len(path) == 0:
44
+ print("At least one path must be suppled")
45
+ raise typer.Exit(1)
46
+
47
+ broker.delete_files(path, exit_on_failure)
48
+ except grpc.RpcError as err:
49
+ ErrorPrinter.print_grpc_error(err)
50
+
51
+
52
+ @app.command()
53
+ def download(
54
+ path: str = typer.Argument(..., help="Path to file on broker to download"),
55
+ output: str = typer.Option("", help="Optional output file name"),
56
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
57
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
58
+ ):
59
+ """
60
+ Downloads a file from a broker
61
+ """
62
+ try:
63
+ with Progress(
64
+ SpinnerColumn(),
65
+ TextColumn("[progress.description]{task.description}"),
66
+ transient=True,
67
+ ) as progress:
68
+ progress.add_task(description=f"Downloading {path}...", total=None)
69
+ broker = Broker(url, api_key)
70
+ output_file = os.path.basename(path)
71
+ if output != "":
72
+ output_file = output
73
+ if os.path.exists(output_file):
74
+ print(f"File already exist {output_file}, please use another output file name")
75
+ else:
76
+ broker.download(path, output_file)
77
+ print(f"Successfully saved {output_file}")
78
+ except grpc.RpcError as err:
79
+ ErrorPrinter.print_grpc_error(err)
80
+
81
+
82
+ @app.command()
83
+ def upload(
84
+ path: str = typer.Argument(..., help="Path to local file to upload"),
85
+ output: str = typer.Option("", help="Optional output path on broker"),
86
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
87
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
88
+ ):
89
+ """
90
+ Uploads a file to a broker - physical or in cloud.
91
+ """
92
+ try:
93
+ with Progress(
94
+ SpinnerColumn(),
95
+ TextColumn("[progress.description]{task.description}"),
96
+ transient=True,
97
+ ) as progress:
98
+ progress.add_task(description=f"Uploading {path}...", total=None)
99
+
100
+ if path == ".":
101
+ path = "./" ## Does not work otherwise
102
+
103
+ if not os.path.exists(path):
104
+ print(f"File {path} does not exist")
105
+ raise typer.Exit(1)
106
+
107
+ broker = Broker(url, api_key)
108
+
109
+ if os.path.isdir(path):
110
+ broker.upload_folder(path)
111
+ print(f"Successfully uploaded {path}")
112
+ else:
113
+ output_file = os.path.basename(path)
114
+ if output != "":
115
+ output_file = output
116
+ broker.upload(path, output_file)
117
+ print(f"Successfully uploaded {path}")
118
+ except grpc.RpcError as err:
119
+ ErrorPrinter.print_grpc_error(err)
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2022-present remotiveLabs <support@remotivelabs.com>
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
- __version__ = '0.0.1'
4
+ __version__ = "0.0.1"