carconnectivity-cli 0.1a1__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.
- carconnectivity_cli/__init__.py +0 -0
- carconnectivity_cli/_version.py +16 -0
- carconnectivity_cli/carconnectivity_cli_base.py +540 -0
- carconnectivity_cli-0.1a1.dist-info/LICENSE +21 -0
- carconnectivity_cli-0.1a1.dist-info/METADATA +92 -0
- carconnectivity_cli-0.1a1.dist-info/RECORD +9 -0
- carconnectivity_cli-0.1a1.dist-info/WHEEL +5 -0
- carconnectivity_cli-0.1a1.dist-info/entry_points.txt +2 -0
- carconnectivity_cli-0.1a1.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.1a1'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 1)
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
"""Module containing the commandline interface for the carconnectivity package."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
import argparse
|
|
9
|
+
import logging
|
|
10
|
+
import tempfile
|
|
11
|
+
import cmd
|
|
12
|
+
import json
|
|
13
|
+
import time
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
|
|
16
|
+
from carconnectivity import carconnectivity, errors, util, objects, attributes, observable
|
|
17
|
+
from carconnectivity._version import __version__ as __carconnectivity_version__
|
|
18
|
+
|
|
19
|
+
from carconnectivity_cli._version import __version__
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from typing import Literal, List
|
|
23
|
+
from carconnectivity.objects import GenericObject
|
|
24
|
+
from carconnectivity.attributes import GenericAttribute
|
|
25
|
+
|
|
26
|
+
LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
27
|
+
DEFAULT_LOG_LEVEL = "ERROR"
|
|
28
|
+
|
|
29
|
+
LOG = logging.getLogger("carconnectivity-cli")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Formats(Enum):
|
|
33
|
+
"""
|
|
34
|
+
Formats is an enumeration that defines the output formats supported by the application.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
STRING (str): Represents the string format.
|
|
38
|
+
JSON (str): Represents the JSON format.
|
|
39
|
+
"""
|
|
40
|
+
STRING = 'string'
|
|
41
|
+
JSON = 'json'
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
return self.value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main() -> None: # noqa: C901 # pylint: disable=too-many-statements,too-many-branches,too-many-locals
|
|
48
|
+
"""
|
|
49
|
+
Entry point for the carconnectivity-cli command-line interface.
|
|
50
|
+
|
|
51
|
+
This function sets up the argument parser, handles logging configuration, and processes commands
|
|
52
|
+
such as 'list', 'get', 'set', 'save', and 'shell'. It interacts with the CarConnectivity service
|
|
53
|
+
to perform various operations based on the provided arguments.
|
|
54
|
+
|
|
55
|
+
Commands:
|
|
56
|
+
- list: Lists available resource IDs and exits.
|
|
57
|
+
- get: Retrieves resources by ID and exits.
|
|
58
|
+
- set: Sets resources by ID and exits.
|
|
59
|
+
- save: Saves resources by ID to a file.
|
|
60
|
+
- shell: Starts the WeConnect shell.
|
|
61
|
+
|
|
62
|
+
Arguments:
|
|
63
|
+
--version: Displays the version of the CLI and CarConnectivity.
|
|
64
|
+
config: Path to the configuration file.
|
|
65
|
+
--tokenfile: File to store the token (default: system temp directory).
|
|
66
|
+
-v, --verbose: Increases logging verbosity.
|
|
67
|
+
--logging-format: Specifies the logging format (default: '%(asctime)s:%(levelname)s:%(message)s').
|
|
68
|
+
--logging-date-format: Specifies the logging date format (default: '%Y-%m-%dT%H:%M:%S%z').
|
|
69
|
+
--hide-repeated-log: Hides repeated log messages from the same module.
|
|
70
|
+
"""
|
|
71
|
+
parser = argparse.ArgumentParser(
|
|
72
|
+
prog='carconectivity-cli',
|
|
73
|
+
description='Commandline Interface to interact with Car Services of various brands')
|
|
74
|
+
parser.add_argument('--version', action='version',
|
|
75
|
+
version=f'%(prog)s {__version__} (using CarConnectivity {__carconnectivity_version__})')
|
|
76
|
+
parser.add_argument('config', help='Path to the configuration file')
|
|
77
|
+
|
|
78
|
+
default_temp = os.path.join(tempfile.gettempdir(), 'carconnectivity.token')
|
|
79
|
+
parser.add_argument('--tokenfile', help=f'file to store token (default: {default_temp})', default=default_temp)
|
|
80
|
+
default_cache_temp = os.path.join(tempfile.gettempdir(), 'carconnectivity.cache')
|
|
81
|
+
parser.add_argument('--cachefile', help=f'file to store cache (default: {default_cache_temp})', default=default_cache_temp)
|
|
82
|
+
|
|
83
|
+
logging_group = parser.add_argument_group('Logging')
|
|
84
|
+
logging_group.add_argument('-v', '--verbose', action="append_const", help='Logging level (verbosity)', const=-1,)
|
|
85
|
+
logging_group.add_argument('--logging-format', dest='logging_format', help='Logging format configured for python logging '
|
|
86
|
+
'(default: %%(asctime)s:%%(module)s:%%(message)s)', default='%(asctime)s:%(levelname)s:%(message)s')
|
|
87
|
+
logging_group.add_argument('--logging-date-format', dest='logging_date_format', help='Logging format configured for python logging '
|
|
88
|
+
'(default: %%Y-%%m-%%dT%%H:%%M:%%S%%z)', default='%Y-%m-%dT%H:%M:%S%z')
|
|
89
|
+
logging_group.add_argument('--hide-repeated-log', dest='hide_repeated_log', help='Hide repeated log messages from the same module', action='store_true')
|
|
90
|
+
|
|
91
|
+
parser.set_defaults(command='shell')
|
|
92
|
+
|
|
93
|
+
subparsers = parser.add_subparsers(title='commands', description='Valid commands',
|
|
94
|
+
help='The following commands can be used')
|
|
95
|
+
parser_list: argparse.ArgumentParser = subparsers.add_parser('list', aliases=['l'], help='List available ressource ids and exit')
|
|
96
|
+
parser_list.add_argument('-s', '--setters', help='List attributes that can be set', action='store_true')
|
|
97
|
+
parser_list.set_defaults(command='list')
|
|
98
|
+
parser_get: argparse.ArgumentParser = subparsers.add_parser('get', aliases=['g'], help='Get ressources by id and exit')
|
|
99
|
+
parser_get.add_argument('id', metavar='ID', type=str, help='Id to fetch')
|
|
100
|
+
parser_get.add_argument('--format', type=Formats, default=Formats.STRING, help='Output format', choices=list(Formats))
|
|
101
|
+
parser_get.set_defaults(command='get')
|
|
102
|
+
parser_set: argparse.ArgumentParser = subparsers.add_parser('set', aliases=['s'], help='Set ressources by id and exit')
|
|
103
|
+
parser_set.add_argument('id', metavar='ID', type=str, help='Id to set')
|
|
104
|
+
parser_set.add_argument('value', metavar='VALUE', type=str, help='Value to set')
|
|
105
|
+
parser_set.set_defaults(command='set')
|
|
106
|
+
parser_save: argparse.ArgumentParser = subparsers.add_parser('save', help='Save ressources by id to file')
|
|
107
|
+
parser_save.add_argument('id', metavar='ID', type=str, help='Id to save')
|
|
108
|
+
parser_save.add_argument('filename', metavar='FILENAME', type=str, help='File to save to')
|
|
109
|
+
parser_events = subparsers.add_parser(
|
|
110
|
+
'events', aliases=['e'], help='Continously retrieve events and show on console')
|
|
111
|
+
parser_events.set_defaults(command='events')
|
|
112
|
+
parser_shell: argparse.ArgumentParser = subparsers.add_parser(
|
|
113
|
+
'shell', aliases=['sh'], help='Start WeConnect shell')
|
|
114
|
+
parser_shell.set_defaults(command='shell')
|
|
115
|
+
|
|
116
|
+
args = parser.parse_args()
|
|
117
|
+
log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)
|
|
118
|
+
for adjustment in args.verbose or ():
|
|
119
|
+
log_level = min(len(LOG_LEVELS) - 1, max(log_level + adjustment, 0))
|
|
120
|
+
|
|
121
|
+
logging.basicConfig(level=LOG_LEVELS[log_level], format=args.logging_format, datefmt=args.logging_date_format)
|
|
122
|
+
if args.hide_repeated_log:
|
|
123
|
+
for handler in logging.root.handlers:
|
|
124
|
+
handler.addFilter(util.DuplicateFilter())
|
|
125
|
+
|
|
126
|
+
try: # pylint: disable=too-many-nested-blocks
|
|
127
|
+
with open(file=args.config, mode='r', encoding='utf-8') as config_file:
|
|
128
|
+
config_dict = json.load(config_file)
|
|
129
|
+
car_connectivity = carconnectivity.CarConnectivity(config=config_dict, tokenstore_file=args.tokenfile, cache_file=args.cachefile)
|
|
130
|
+
|
|
131
|
+
if args.command == 'shell':
|
|
132
|
+
try:
|
|
133
|
+
car_connectivity.startup()
|
|
134
|
+
CarConnectivityShell(car_connectivity).cmdloop()
|
|
135
|
+
except KeyboardInterrupt:
|
|
136
|
+
pass
|
|
137
|
+
elif args.command == 'list':
|
|
138
|
+
car_connectivity.fetch_all()
|
|
139
|
+
all_elements: List[GenericAttribute] = car_connectivity.get_attributes(recursive=True)
|
|
140
|
+
for element in all_elements:
|
|
141
|
+
if args.setters:
|
|
142
|
+
if isinstance(element, attributes.ChangeableAttribute):
|
|
143
|
+
print(element.get_absolute_path())
|
|
144
|
+
else:
|
|
145
|
+
print(element.get_absolute_path())
|
|
146
|
+
elif args.command == 'get':
|
|
147
|
+
car_connectivity.fetch_all()
|
|
148
|
+
element: carconnectivity.GenericObject | objects.GenericAttribute | bool = car_connectivity.get_by_path(args.id)
|
|
149
|
+
if element:
|
|
150
|
+
if args.format == Formats.STRING:
|
|
151
|
+
if isinstance(element, dict):
|
|
152
|
+
print('\n'.join([str(value) for value in element.values()]))
|
|
153
|
+
else:
|
|
154
|
+
print(element)
|
|
155
|
+
# elif args.format == Formats.JSON:
|
|
156
|
+
# print(element.toJSON())
|
|
157
|
+
else:
|
|
158
|
+
print('Unknown format')
|
|
159
|
+
sys.exit('Unknown format')
|
|
160
|
+
else:
|
|
161
|
+
print(f'id {args.id} not found', file=sys.stderr)
|
|
162
|
+
sys.exit('id not found')
|
|
163
|
+
elif args.command == 'set':
|
|
164
|
+
car_connectivity.fetch_all()
|
|
165
|
+
element: carconnectivity.GenericObject | objects.GenericAttribute | bool = car_connectivity.get_by_path(args.id)
|
|
166
|
+
if element:
|
|
167
|
+
try:
|
|
168
|
+
if isinstance(element, attributes.ChangeableAttribute):
|
|
169
|
+
element.value = args.value
|
|
170
|
+
else:
|
|
171
|
+
print(f'id {args.id} cannot be set. You can see all changeable entries with "list -s', file=sys.stderr)
|
|
172
|
+
except ValueError as value_error:
|
|
173
|
+
print(f'id {args.id} cannot be set: {value_error}', file=sys.stderr)
|
|
174
|
+
sys.exit('id cannot be set')
|
|
175
|
+
except NotImplementedError:
|
|
176
|
+
print(f'id {args.id} cannot be set. You can see all changeable entries with "list -s"', file=sys.stderr)
|
|
177
|
+
sys.exit('id cannot be set')
|
|
178
|
+
except errors.SetterError as err:
|
|
179
|
+
print(f'id {args.id} cannot be set: {err}', file=sys.stderr)
|
|
180
|
+
sys.exit('id cannot be set')
|
|
181
|
+
else:
|
|
182
|
+
print(f'id {args.id} not found', file=sys.stderr)
|
|
183
|
+
sys.exit('id not found')
|
|
184
|
+
elif args.command == 'events':
|
|
185
|
+
car_connectivity.startup()
|
|
186
|
+
def observer(element, flags):
|
|
187
|
+
if flags & observable.Observable.ObserverEvent.ENABLED:
|
|
188
|
+
print(str(datetime.now()) + ': ' + element.get_absolute_path() + ': new object created')
|
|
189
|
+
elif flags & observable.Observable.ObserverEvent.DISABLED:
|
|
190
|
+
print(str(datetime.now()) + ': ' + element.get_absolute_path() + ': object not available anymore')
|
|
191
|
+
elif flags & observable.Observable.ObserverEvent.VALUE_CHANGED:
|
|
192
|
+
print(str(datetime.now()) + ': ' + element.get_absolute_path() + ': new value: ' + str(element))
|
|
193
|
+
elif flags & observable.Observable.ObserverEvent.UPDATED_NEW_MEASUREMENT:
|
|
194
|
+
print(str(datetime.now()) + ': ' + element.get_absolute_path()
|
|
195
|
+
+ ': was updated from vehicle but did not change: ' + str(element))
|
|
196
|
+
elif flags & observable.Observable.ObserverEvent.UPDATED:
|
|
197
|
+
print(str(datetime.now()) + ': ' + element.get_absolute_path()
|
|
198
|
+
+ ': was updated from server but did not change: ' + str(element))
|
|
199
|
+
else:
|
|
200
|
+
print(str(datetime.now()) + ' (' + str(flags) + '): '
|
|
201
|
+
+ element.get_absolute_path() + ': ' + str(element))
|
|
202
|
+
car_connectivity.add_observer(observer, observable.Observable.ObserverEvent.ALL,
|
|
203
|
+
priority=observable.Observable.ObserverPriority.USER_MID)
|
|
204
|
+
try:
|
|
205
|
+
while True:
|
|
206
|
+
time.sleep(1)
|
|
207
|
+
except KeyboardInterrupt:
|
|
208
|
+
LOG.info('Keyboard interrupt received, shutting down...')
|
|
209
|
+
else:
|
|
210
|
+
LOG.error('command not implemented')
|
|
211
|
+
sys.exit('command not implemented')
|
|
212
|
+
|
|
213
|
+
car_connectivity.shutdown()
|
|
214
|
+
except json.JSONDecodeError as e:
|
|
215
|
+
LOG.critical('Could not load configuration file %s (%s)', args.config, e)
|
|
216
|
+
sys.exit('Could not load configuration file')
|
|
217
|
+
except errors.AuthenticationError as e:
|
|
218
|
+
LOG.critical('There was a problem when authenticating with one or multiple services: %s', e)
|
|
219
|
+
sys.exit('There was a problem when communicating with one or multiple services')
|
|
220
|
+
except errors.APICompatibilityError as e:
|
|
221
|
+
LOG.critical('There was a problem when communicating with one or multiple services.'
|
|
222
|
+
' If this problem persists please open a bug report: %s', e)
|
|
223
|
+
sys.exit('There was a problem when communicating with one or multiple services')
|
|
224
|
+
except errors.RetrievalError as e:
|
|
225
|
+
LOG.critical('There was a problem when communicating with one or multiple services: %s', e)
|
|
226
|
+
sys.exit('There was a problem when communicating with one or multiple services')
|
|
227
|
+
except errors.ConfigurationError as e:
|
|
228
|
+
LOG.critical('There was a problem with the configuration: %s', e)
|
|
229
|
+
sys.exit('There was a problem with the configuration')
|
|
230
|
+
except KeyboardInterrupt:
|
|
231
|
+
sys.exit("killed")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class CarConnectivityShell(cmd.Cmd):
|
|
235
|
+
"""
|
|
236
|
+
CarConnectivityShell is a command-line interface (CLI) shell for interacting with car connectivity data.
|
|
237
|
+
|
|
238
|
+
Attributes:
|
|
239
|
+
prompt (str): The command prompt string.
|
|
240
|
+
intro (str): The introductory message displayed when the shell starts.
|
|
241
|
+
car_connectivity (Any): The car connectivity object to interact with.
|
|
242
|
+
pwd (Any): The current working directory within the car connectivity data.
|
|
243
|
+
|
|
244
|
+
Methods:
|
|
245
|
+
__init__(car_connectivity): Initializes the shell with the given car connectivity object.
|
|
246
|
+
set_prompt(path): Sets the command prompt based on the given path.
|
|
247
|
+
help_exit(): Displays help information for the 'exit' command.
|
|
248
|
+
do_exit(arguments): Exits the shell.
|
|
249
|
+
help_cd(): Displays help information for the 'cd' command.
|
|
250
|
+
do_cd(arguments): Changes the current working directory.
|
|
251
|
+
complete_cd(text, line, begidx, endidx): Provides tab completion for the 'cd' command.
|
|
252
|
+
help_ls(): Displays help information for the 'ls' command.
|
|
253
|
+
do_ls(arguments): Lists subelements of the current path.
|
|
254
|
+
help_pwd(): Displays help information for the 'pwd' command.
|
|
255
|
+
do_pwd(arguments): Displays the current path.
|
|
256
|
+
help_update(): Displays help information for the 'update' command.
|
|
257
|
+
do_update(arguments): Updates the data from the server.
|
|
258
|
+
help_cat(): Displays help information for the 'cat' command.
|
|
259
|
+
do_cat(arguments): Prints the content of the current or specified element.
|
|
260
|
+
help_find(): Displays help information for the 'find' command.
|
|
261
|
+
do_find(arguments): Lists all elements recursively.
|
|
262
|
+
default(line): Handles unrecognized commands.
|
|
263
|
+
do_EOF(): Exits the shell on EOF (Ctrl-D).
|
|
264
|
+
help_EOF(): Displays help information for the EOF command.
|
|
265
|
+
"""
|
|
266
|
+
prompt = 'error'
|
|
267
|
+
intro = "Welcome! Type ? to list commands"
|
|
268
|
+
|
|
269
|
+
def __init__(self, car_connectivity: carconnectivity.CarConnectivity) -> None:
|
|
270
|
+
self.car_connectivity: carconnectivity.CarConnectivity = car_connectivity
|
|
271
|
+
self.pwd: GenericObject | GenericAttribute = car_connectivity
|
|
272
|
+
|
|
273
|
+
super().__init__()
|
|
274
|
+
# Set the prompt to the current path
|
|
275
|
+
self.set_prompt(self.car_connectivity.get_absolute_path())
|
|
276
|
+
|
|
277
|
+
def set_prompt(self, path) -> None:
|
|
278
|
+
"""
|
|
279
|
+
Sets the command prompt for the CarConnectivityShell.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
path (str): The current path to be displayed in the prompt. If an empty string is provided, it defaults to '/'.
|
|
283
|
+
|
|
284
|
+
"""
|
|
285
|
+
if path == '':
|
|
286
|
+
path = '/'
|
|
287
|
+
CarConnectivityShell.prompt = f'ccs:{path}$'
|
|
288
|
+
|
|
289
|
+
def help_exit(self) -> None:
|
|
290
|
+
"""
|
|
291
|
+
Prints the help message for the 'exit' command.
|
|
292
|
+
|
|
293
|
+
This method provides information on how to exit the application,
|
|
294
|
+
including the shorthand commands: 'x', 'q', and 'Ctrl-D'.
|
|
295
|
+
"""
|
|
296
|
+
print('exit the application. Shorthand: x q Ctrl-D.')
|
|
297
|
+
|
|
298
|
+
def do_exit(self, arguments) -> Literal[True]:
|
|
299
|
+
"""
|
|
300
|
+
Exits the CLI application.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
arguments: The arguments passed to the command. This parameter is not used.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
bool: Always returns True to indicate the command should exit.
|
|
307
|
+
"""
|
|
308
|
+
del arguments
|
|
309
|
+
print("Bye")
|
|
310
|
+
return True
|
|
311
|
+
|
|
312
|
+
def help_cd(self):
|
|
313
|
+
"""
|
|
314
|
+
Prints the help message for the 'cd' command.
|
|
315
|
+
|
|
316
|
+
This method is used to provide help information about changing the location
|
|
317
|
+
in the tree structure.
|
|
318
|
+
"""
|
|
319
|
+
print('change location in tree')
|
|
320
|
+
|
|
321
|
+
def do_cd(self, arguments):
|
|
322
|
+
"""
|
|
323
|
+
Change the current directory to the specified path.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
arguments (str): The path to change to. If None or an empty string is provided,
|
|
327
|
+
the path will default to '/'.
|
|
328
|
+
|
|
329
|
+
Behavior:
|
|
330
|
+
- If the provided path starts with '/', it is treated as an absolute path.
|
|
331
|
+
- If the provided path does not start with '/', it is treated as a relative path
|
|
332
|
+
from the current directory.
|
|
333
|
+
- If the specified path exists and is accessible, the current directory is updated
|
|
334
|
+
to the new path and the prompt is set accordingly.
|
|
335
|
+
- If the specified path does not exist or is not accessible, an error message is printed.
|
|
336
|
+
|
|
337
|
+
Example:
|
|
338
|
+
do_cd('/new/path')
|
|
339
|
+
do_cd('relative/path')
|
|
340
|
+
"""
|
|
341
|
+
# If no arguments are provided, set the path to '/'
|
|
342
|
+
if arguments is None or arguments == '':
|
|
343
|
+
arguments = '/'
|
|
344
|
+
# If the path starts with '/', treat it as an absolute path
|
|
345
|
+
if arguments.startswith('/'):
|
|
346
|
+
path = arguments
|
|
347
|
+
else:
|
|
348
|
+
path = f'{self.pwd.get_absolute_path()}/{arguments}'
|
|
349
|
+
# Get the object at the specified path
|
|
350
|
+
new_pwd = self.car_connectivity.get_by_path(path)
|
|
351
|
+
# If the object exists and is accessible, update the current directory and prompt
|
|
352
|
+
if new_pwd is not None and new_pwd is not False:
|
|
353
|
+
self.pwd = new_pwd
|
|
354
|
+
path = self.pwd.get_absolute_path()
|
|
355
|
+
if path == '':
|
|
356
|
+
path = '/'
|
|
357
|
+
self.set_prompt(path)
|
|
358
|
+
else:
|
|
359
|
+
# Print an error message if the path does not exist or is not accessible
|
|
360
|
+
print(f'*** {arguments} does not exist or is not accessible')
|
|
361
|
+
|
|
362
|
+
def complete_cd(self, text, line, begidx, endidx):
|
|
363
|
+
"""
|
|
364
|
+
Autocompletion method for the 'cd' command.
|
|
365
|
+
|
|
366
|
+
This method provides suggestions for directory names based on the current input text.
|
|
367
|
+
If the input text starts with '/', it suggests absolute paths from the root directory.
|
|
368
|
+
Otherwise, it suggests directory IDs from the current working directory.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
text (str): The current input text for the 'cd' command.
|
|
372
|
+
line (str): The entire input line (unused).
|
|
373
|
+
begidx (int): The beginning index of the input text (unused).
|
|
374
|
+
endidx (int): The ending index of the input text (unused).
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
list: A list of suggested directory names or paths.
|
|
378
|
+
"""
|
|
379
|
+
del line
|
|
380
|
+
del begidx
|
|
381
|
+
del endidx
|
|
382
|
+
if text.startswith('/'):
|
|
383
|
+
# Return absolute paths from the root directory
|
|
384
|
+
root: carconnectivity.GenericObject | objects.GenericAttribute = self.pwd.get_root()
|
|
385
|
+
if isinstance(root, objects.GenericObject):
|
|
386
|
+
return [child.get_absolute_path() for child in root.children if child.get_absolute_path().startswith(text)]
|
|
387
|
+
# Return directory IDs from the current working directory
|
|
388
|
+
if isinstance(self.pwd, objects.GenericObject):
|
|
389
|
+
return [child.id for child in self.pwd.children if child.id.startswith(text)]
|
|
390
|
+
return []
|
|
391
|
+
|
|
392
|
+
def help_ls(self):
|
|
393
|
+
"""
|
|
394
|
+
Prints the help message for the 'ls' command
|
|
395
|
+
|
|
396
|
+
This method is intended to provide help or guidance to the user about the 'ls' command,
|
|
397
|
+
which lists subelements of the current path.
|
|
398
|
+
"""
|
|
399
|
+
print('list subelements of current path')
|
|
400
|
+
|
|
401
|
+
def do_ls(self, arguments):
|
|
402
|
+
"""
|
|
403
|
+
Lists the contents of the current directory.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
arguments: Command-line arguments (not used).
|
|
407
|
+
|
|
408
|
+
Prints:
|
|
409
|
+
The parent directory indicator ('..') if the current directory has a parent.
|
|
410
|
+
The IDs of the children of the current directory.
|
|
411
|
+
"""
|
|
412
|
+
del arguments
|
|
413
|
+
if self.pwd.parent is not None:
|
|
414
|
+
print('..')
|
|
415
|
+
if isinstance(self.pwd, objects.GenericObject):
|
|
416
|
+
for child in self.pwd.children:
|
|
417
|
+
print(child.id)
|
|
418
|
+
|
|
419
|
+
def help_pwd(self):
|
|
420
|
+
"""
|
|
421
|
+
Prints the help message for the 'pwd' command.
|
|
422
|
+
|
|
423
|
+
This method is intended to provide help or guidance to the user about the 'pwd' command,
|
|
424
|
+
which displays the current path.
|
|
425
|
+
"""
|
|
426
|
+
print('show current path')
|
|
427
|
+
|
|
428
|
+
def do_pwd(self, arguments):
|
|
429
|
+
"""
|
|
430
|
+
Prints the current working directory.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
arguments: Command line arguments (not used).
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
None
|
|
437
|
+
"""
|
|
438
|
+
del arguments
|
|
439
|
+
path = self.pwd.get_absolute_path()
|
|
440
|
+
if path == '':
|
|
441
|
+
path = '/'
|
|
442
|
+
print(path)
|
|
443
|
+
|
|
444
|
+
def help_update(self):
|
|
445
|
+
"""
|
|
446
|
+
Prints the help message for the 'update' command.
|
|
447
|
+
|
|
448
|
+
This method provides a simple help message for the update command.
|
|
449
|
+
"""
|
|
450
|
+
print('update the data from the server')
|
|
451
|
+
|
|
452
|
+
def do_update(self, arguments):
|
|
453
|
+
"""
|
|
454
|
+
Updates the car connectivity data by fetching all available data.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
arguments: Command-line arguments passed to the update command.
|
|
458
|
+
"""
|
|
459
|
+
del arguments
|
|
460
|
+
self.car_connectivity.fetch_all()
|
|
461
|
+
print('update done')
|
|
462
|
+
|
|
463
|
+
def help_cat(self):
|
|
464
|
+
"""
|
|
465
|
+
Prints the help message for the 'cat' command.
|
|
466
|
+
|
|
467
|
+
This method is used to display the help information related to the cat command that prints content.
|
|
468
|
+
"""
|
|
469
|
+
print('Print content')
|
|
470
|
+
|
|
471
|
+
def do_cat(self, arguments):
|
|
472
|
+
"""
|
|
473
|
+
Display the content of a specified path.
|
|
474
|
+
|
|
475
|
+
If no arguments are provided, this method prints the content of the current working directory.
|
|
476
|
+
If a path is provided as an argument, it prints the content of the specified path
|
|
477
|
+
if it exists and is accessible. Otherwise, it prints an error message.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
arguments (str): The path to the element to be displayed. If None or empty,
|
|
481
|
+
the current working directory is displayed.
|
|
482
|
+
"""
|
|
483
|
+
if arguments is None or arguments.strip() == '':
|
|
484
|
+
print(str(self.pwd))
|
|
485
|
+
else:
|
|
486
|
+
element = self.pwd.get_by_path(arguments.strip())
|
|
487
|
+
if element is not None and element is not False:
|
|
488
|
+
print(str(element))
|
|
489
|
+
else:
|
|
490
|
+
print(f'*** {arguments} does not exist or is not accessible')
|
|
491
|
+
|
|
492
|
+
def help_find(self):
|
|
493
|
+
"""
|
|
494
|
+
Prints a message describing the functionality of the 'find' command.
|
|
495
|
+
|
|
496
|
+
This method provides a brief description of what the 'find' command does,
|
|
497
|
+
which is to list all elements recursively.
|
|
498
|
+
"""
|
|
499
|
+
print('Find lists all elements recursively')
|
|
500
|
+
|
|
501
|
+
def do_find(self, arguments):
|
|
502
|
+
"""
|
|
503
|
+
Finds and prints the absolute paths of attributes in the current working directory.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
arguments (str): Command line arguments. If '-s' is passed, only attributes
|
|
507
|
+
that are changeable will be printed.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
None
|
|
511
|
+
"""
|
|
512
|
+
setters = bool(arguments.strip() == '-s')
|
|
513
|
+
all_elements = self.pwd.get_attributes(recursive=True)
|
|
514
|
+
for element in all_elements:
|
|
515
|
+
if setters:
|
|
516
|
+
if isinstance(element, attributes.ChangeableAttribute):
|
|
517
|
+
print(element.get_absolute_path())
|
|
518
|
+
else:
|
|
519
|
+
print(element.get_absolute_path())
|
|
520
|
+
|
|
521
|
+
def default(self, line):
|
|
522
|
+
"""
|
|
523
|
+
Handle the default case for unrecognized commands.
|
|
524
|
+
|
|
525
|
+
If the input line is 'x' or 'q', it triggers the exit command.
|
|
526
|
+
Otherwise, it calls the default method of the superclass.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
line (str): The input command line.
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
The result of the exit command if 'x' or 'q' is input, otherwise the result of the superclass's default method.
|
|
533
|
+
"""
|
|
534
|
+
if line in ('x', 'q'):
|
|
535
|
+
self.do_exit(line)
|
|
536
|
+
return None
|
|
537
|
+
return super().default(line)
|
|
538
|
+
|
|
539
|
+
do_EOF = do_exit
|
|
540
|
+
help_EOF = help_exit
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Till Steinbach
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: carconnectivity-cli
|
|
3
|
+
Version: 0.1a1
|
|
4
|
+
Summary: Library for retrieving information from car connectivity services
|
|
5
|
+
Author: Till Steinbach
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2021 Till Steinbach
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: Development Status :: 3 - Alpha
|
|
29
|
+
Classifier: Environment :: Console
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
32
|
+
Classifier: Intended Audience :: System Administrators
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Topic :: Utilities
|
|
39
|
+
Classifier: Topic :: System :: Monitoring
|
|
40
|
+
Requires-Python: >=3.9
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Requires-Dist: argparse
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# CarConnectivity Command Line Interface
|
|
48
|
+
[](https://github.com/tillsteinbach/CarConnectivity-cli/)
|
|
49
|
+
[](https://github.com/tillsteinbach/CarConnectivity-cli/releases/latest)
|
|
50
|
+
[](https://github.com/tillsteinbach/CarConnectivity-cli/blob/master/LICENSE)
|
|
51
|
+
[](https://github.com/tillsteinbach/CarConnectivity-cli/issues)
|
|
52
|
+
[](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
|
|
53
|
+
[](https://github.com/sponsors/tillsteinbach)
|
|
54
|
+
|
|
55
|
+
## CarConnectivity will become the successor of [WeConnect-python](https://github.com/tillsteinbach/WeConnect-python) in 2025 with similar functionality but support for other brands beyond Volkswagen!
|
|
56
|
+
|
|
57
|
+
## Supported Car Brands
|
|
58
|
+
CarConenctivity uses a plugin architecture to enable access to the services of various brands. Currently known plugins are:
|
|
59
|
+
|
|
60
|
+
| Brand | Connector |
|
|
61
|
+
|------------|---------------------------------------------------------------------------------------------------------------|
|
|
62
|
+
| Skoda | [CarConnectivity-connector-skoda](https://github.com/tillsteinbach/CarConnectivity-connector-skoda) |
|
|
63
|
+
| Volkswagen | [CarConnectivity-connector-volkswagen](https://github.com/tillsteinbach/CarConnectivity-connector-volkswagen) |
|
|
64
|
+
|
|
65
|
+
If you know of a connector not listed here let me know and I will add it to the list.
|
|
66
|
+
If you are a python developer and willing to implement a connector for a brand not listed here, let me know and I try to support you as good as possible
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
In your carconnectivity.json configuration add a section for the connectors you like to use like this:
|
|
70
|
+
```
|
|
71
|
+
{
|
|
72
|
+
"carConnectivity": {
|
|
73
|
+
"connectors": [
|
|
74
|
+
{
|
|
75
|
+
"type": "volkswagen",
|
|
76
|
+
"config": {
|
|
77
|
+
"username": "test@test.de"
|
|
78
|
+
"password": "testpassword123"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"type": "skoda",
|
|
83
|
+
"config": {
|
|
84
|
+
"username": "test@test.de"
|
|
85
|
+
"password": "testpassword123"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
The detailed configuration options of the connectors can be found in their README files.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
carconnectivity_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
carconnectivity_cli/_version.py,sha256=ZWJN-Hz-vyH2zvnF8EJz3K-yUyljRo9TBKr3g0d3oik,408
|
|
3
|
+
carconnectivity_cli/carconnectivity_cli_base.py,sha256=FKlWpVoSOLF5vDauDk905FuBYSVISOa7CXUvqDLTzAU,24603
|
|
4
|
+
carconnectivity_cli-0.1a1.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
|
|
5
|
+
carconnectivity_cli-0.1a1.dist-info/METADATA,sha256=p-14gx_9KydBeArmfRS9iN2w6jv9HRVM_J4NWbg_2sg,4801
|
|
6
|
+
carconnectivity_cli-0.1a1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
7
|
+
carconnectivity_cli-0.1a1.dist-info/entry_points.txt,sha256=zUPLT-dwyZ605ra1ID7BA-ghgo7phWac5zPYRdQwe5I,90
|
|
8
|
+
carconnectivity_cli-0.1a1.dist-info/top_level.txt,sha256=Hz46d8yVvc8mHCP3Zbgk5Fk4BBS-VyWHdF1b03G7_cQ,20
|
|
9
|
+
carconnectivity_cli-0.1a1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
carconnectivity_cli
|