carconnectivity-cli 0.1__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.
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.1'
16
+ __version_tuple__ = version_tuple = (0, 1)
@@ -0,0 +1,546 @@
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, timezone
15
+
16
+ from json_minify import json_minify
17
+
18
+ from carconnectivity import carconnectivity, errors, util, objects, observable
19
+ from carconnectivity.attributes import GenericAttribute
20
+ from carconnectivity._version import __version__ as __carconnectivity_version__
21
+
22
+ from carconnectivity_cli._version import __version__
23
+
24
+ if TYPE_CHECKING:
25
+ from typing import Literal, List
26
+ from carconnectivity.objects import GenericObject
27
+
28
+ LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
29
+ DEFAULT_LOG_LEVEL = "ERROR"
30
+
31
+ LOG = logging.getLogger("carconnectivity-cli")
32
+
33
+
34
+ class Formats(Enum):
35
+ """
36
+ Formats is an enumeration that defines the output formats supported by the application.
37
+
38
+ Attributes:
39
+ STRING (str): Represents the string format.
40
+ JSON (str): Represents the JSON format.
41
+ """
42
+ STRING = 'string'
43
+ JSON = 'json'
44
+
45
+ def __str__(self) -> str:
46
+ return self.value
47
+
48
+
49
+ def main() -> None: # noqa: C901 # pylint: disable=too-many-statements,too-many-branches,too-many-locals
50
+ """
51
+ Entry point for the carconnectivity-cli command-line interface.
52
+
53
+ This function sets up the argument parser, handles logging configuration, and processes commands
54
+ such as 'list', 'get', 'set', 'save', and 'shell'. It interacts with the CarConnectivity service
55
+ to perform various operations based on the provided arguments.
56
+
57
+ Commands:
58
+ - list: Lists available resource IDs and exits.
59
+ - get: Retrieves resources by ID and exits.
60
+ - set: Sets resources by ID and exits.
61
+ - save: Saves resources by ID to a file.
62
+ - shell: Starts the WeConnect shell.
63
+
64
+ Arguments:
65
+ --version: Displays the version of the CLI and CarConnectivity.
66
+ config: Path to the configuration file.
67
+ --tokenfile: File to store the token (default: system temp directory).
68
+ -v, --verbose: Increases logging verbosity.
69
+ --logging-format: Specifies the logging format (default: '%(asctime)s:%(levelname)s:%(message)s').
70
+ --logging-date-format: Specifies the logging date format (default: '%Y-%m-%dT%H:%M:%S%z').
71
+ --hide-repeated-log: Hides repeated log messages from the same module.
72
+ """
73
+ parser = argparse.ArgumentParser(
74
+ prog='carconectivity-cli',
75
+ description='Commandline Interface to interact with Car Services of various brands')
76
+ parser.add_argument('--version', action='version',
77
+ version=f'%(prog)s {__version__} (using CarConnectivity {__carconnectivity_version__})')
78
+ parser.add_argument('config', help='Path to the configuration file')
79
+
80
+ default_temp = os.path.join(tempfile.gettempdir(), 'carconnectivity.token')
81
+ parser.add_argument('--tokenfile', help=f'file to store token (default: {default_temp})', default=default_temp)
82
+ default_cache_temp = os.path.join(tempfile.gettempdir(), 'carconnectivity.cache')
83
+ parser.add_argument('--cachefile', help=f'file to store cache (default: {default_cache_temp})', default=default_cache_temp)
84
+
85
+ logging_group = parser.add_argument_group('Logging')
86
+ logging_group.add_argument('-v', '--verbose', action="append_const", help='Logging level (verbosity)', const=-1,)
87
+ logging_group.add_argument('--logging-format', dest='logging_format', help='Logging format configured for python logging '
88
+ '(default: %%(asctime)s:%%(module)s:%%(message)s)', default='%(asctime)s:%(levelname)s:%(message)s')
89
+ logging_group.add_argument('--logging-date-format', dest='logging_date_format', help='Logging format configured for python logging '
90
+ '(default: %%Y-%%m-%%dT%%H:%%M:%%S%%z)', default='%Y-%m-%dT%H:%M:%S%z')
91
+ logging_group.add_argument('--hide-repeated-log', dest='hide_repeated_log', help='Hide repeated log messages from the same module', action='store_true')
92
+
93
+ parser.set_defaults(command='shell')
94
+
95
+ subparsers = parser.add_subparsers(title='commands', description='Valid commands',
96
+ help='The following commands can be used')
97
+ parser_list: argparse.ArgumentParser = subparsers.add_parser('list', aliases=['l'], help='List available ressource ids and exit')
98
+ parser_list.add_argument('-s', '--setters', help='List attributes that can be set', action='store_true')
99
+ parser_list.set_defaults(command='list')
100
+ parser_get: argparse.ArgumentParser = subparsers.add_parser('get', aliases=['g'], help='Get ressources by id and exit')
101
+ parser_get.add_argument('id', metavar='ID', type=str, help='Id to fetch')
102
+ parser_get.add_argument('--format', type=Formats, default=Formats.STRING, help='Output format', choices=list(Formats))
103
+ parser_get.set_defaults(command='get')
104
+ parser_set: argparse.ArgumentParser = subparsers.add_parser('set', aliases=['s'], help='Set ressources by id and exit')
105
+ parser_set.add_argument('id', metavar='ID', type=str, help='Id to set')
106
+ parser_set.add_argument('value', metavar='VALUE', type=str, help='Value to set')
107
+ parser_set.set_defaults(command='set')
108
+ parser_save: argparse.ArgumentParser = subparsers.add_parser('save', help='Save ressources by id to file')
109
+ parser_save.add_argument('id', metavar='ID', type=str, help='Id to save')
110
+ parser_save.add_argument('filename', metavar='FILENAME', type=str, help='File to save to')
111
+ parser_events = subparsers.add_parser(
112
+ 'events', aliases=['e'], help='Continously retrieve events and show on console')
113
+ parser_events.set_defaults(command='events')
114
+ parser_shell: argparse.ArgumentParser = subparsers.add_parser(
115
+ 'shell', aliases=['sh'], help='Start WeConnect shell')
116
+ parser_shell.set_defaults(command='shell')
117
+
118
+ args = parser.parse_args()
119
+ log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)
120
+ for adjustment in args.verbose or ():
121
+ log_level = min(len(LOG_LEVELS) - 1, max(log_level + adjustment, 0))
122
+
123
+ logging.basicConfig(level=LOG_LEVELS[log_level], format=args.logging_format, datefmt=args.logging_date_format)
124
+ if args.hide_repeated_log:
125
+ for handler in logging.root.handlers:
126
+ handler.addFilter(util.DuplicateFilter())
127
+
128
+ try: # pylint: disable=too-many-nested-blocks
129
+ try:
130
+ with open(file=args.config, mode='r', encoding='utf-8') as config_file:
131
+ config_dict = json.loads(json_minify(config_file.read(), strip_space=False))
132
+ car_connectivity = carconnectivity.CarConnectivity(config=config_dict, tokenstore_file=args.tokenfile, cache_file=args.cachefile)
133
+
134
+ if args.command == 'shell':
135
+ try:
136
+ car_connectivity.startup()
137
+ CarConnectivityShell(car_connectivity).cmdloop()
138
+ except KeyboardInterrupt:
139
+ pass
140
+ elif args.command == 'list':
141
+ car_connectivity.fetch_all()
142
+ all_elements: List[GenericAttribute] = car_connectivity.get_attributes(recursive=True)
143
+ for element in all_elements:
144
+ if args.setters:
145
+ if element.is_changeable:
146
+ print(element.get_absolute_path())
147
+ else:
148
+ print(element.get_absolute_path())
149
+ elif args.command == 'get':
150
+ car_connectivity.fetch_all()
151
+ element: carconnectivity.GenericObject | GenericAttribute | bool = car_connectivity.get_by_path(args.id)
152
+ if element:
153
+ if args.format == Formats.STRING:
154
+ if isinstance(element, dict):
155
+ print('\n'.join([str(value) for value in element.values()]))
156
+ else:
157
+ print(element)
158
+ # elif args.format == Formats.JSON:
159
+ # print(element.toJSON())
160
+ else:
161
+ print('Unknown format')
162
+ sys.exit('Unknown format')
163
+ else:
164
+ print(f'id {args.id} not found', file=sys.stderr)
165
+ sys.exit('id not found')
166
+ elif args.command == 'set':
167
+ car_connectivity.fetch_all()
168
+ element: GenericObject | GenericAttribute | bool = car_connectivity.get_by_path(args.id)
169
+ if element:
170
+ try:
171
+ if isinstance(element, GenericAttribute) and element.is_changeable:
172
+ element.value = args.value
173
+ else:
174
+ print(f'id {args.id} cannot be set. You can see all changeable entries with "list -s', file=sys.stderr)
175
+ except ValueError as value_error:
176
+ print(f'id {args.id} cannot be set: {value_error}', file=sys.stderr)
177
+ sys.exit('id cannot be set')
178
+ except NotImplementedError:
179
+ print(f'id {args.id} cannot be set. You can see all changeable entries with "list -s"', file=sys.stderr)
180
+ sys.exit('id cannot be set')
181
+ except errors.SetterError as err:
182
+ print(f'id {args.id} cannot be set: {err}', file=sys.stderr)
183
+ sys.exit('id cannot be set')
184
+ else:
185
+ print(f'id {args.id} not found', file=sys.stderr)
186
+ sys.exit('id not found')
187
+ elif args.command == 'events':
188
+ car_connectivity.startup()
189
+
190
+ def observer(element, flags):
191
+ if flags & observable.Observable.ObserverEvent.ENABLED:
192
+ print(str(datetime.now(tz=timezone.utc)) + ': ' + element.get_absolute_path() + ': new object created')
193
+ elif flags & observable.Observable.ObserverEvent.DISABLED:
194
+ print(str(datetime.now(tz=timezone.utc)) + ': ' + element.get_absolute_path() + ': object not available anymore')
195
+ elif flags & observable.Observable.ObserverEvent.VALUE_CHANGED:
196
+ print(str(datetime.now(tz=timezone.utc)) + ': ' + element.get_absolute_path() + ': new value: ' + str(element))
197
+ elif flags & observable.Observable.ObserverEvent.UPDATED_NEW_MEASUREMENT:
198
+ print(str(datetime.now(tz=timezone.utc)) + ': ' + element.get_absolute_path()
199
+ + ': was updated from vehicle but did not change: ' + str(element))
200
+ elif flags & observable.Observable.ObserverEvent.UPDATED:
201
+ print(str(datetime.now(tz=timezone.utc)) + ': ' + element.get_absolute_path()
202
+ + ': was updated from server but did not change: ' + str(element))
203
+ else:
204
+ print(str(datetime.now(tz=timezone.utc)) + ' (' + str(flags) + '): '
205
+ + element.get_absolute_path() + ': ' + str(element))
206
+ car_connectivity.add_observer(observer, observable.Observable.ObserverEvent.ALL,
207
+ priority=observable.Observable.ObserverPriority.USER_MID)
208
+ try:
209
+ while True:
210
+ time.sleep(1)
211
+ except KeyboardInterrupt:
212
+ LOG.info('Keyboard interrupt received, shutting down...')
213
+ else:
214
+ LOG.error('command not implemented')
215
+ sys.exit('command not implemented')
216
+ car_connectivity.shutdown()
217
+ except FileNotFoundError as e:
218
+ LOG.critical('Could not find configuration file %s (%s)', args.config, e)
219
+ sys.exit('Could not find configuration file')
220
+ except json.JSONDecodeError as e:
221
+ LOG.critical('Could not load configuration file %s (%s)', args.config, e)
222
+ sys.exit('Could not load configuration file')
223
+ except errors.AuthenticationError as e:
224
+ LOG.critical('There was a problem when authenticating with one or multiple services: %s', e)
225
+ sys.exit('There was a problem when communicating with one or multiple services')
226
+ except errors.APICompatibilityError as e:
227
+ LOG.critical('There was a problem when communicating with one or multiple services.'
228
+ ' If this problem persists please open a bug report: %s', e)
229
+ sys.exit('There was a problem when communicating with one or multiple services')
230
+ except errors.RetrievalError as e:
231
+ LOG.critical('There was a problem when communicating with one or multiple services: %s', e)
232
+ sys.exit('There was a problem when communicating with one or multiple services')
233
+ except errors.ConfigurationError as e:
234
+ LOG.critical('There was a problem with the configuration: %s', e)
235
+ sys.exit('There was a problem with the configuration')
236
+ except KeyboardInterrupt:
237
+ sys.exit("killed")
238
+
239
+
240
+ class CarConnectivityShell(cmd.Cmd):
241
+ """
242
+ CarConnectivityShell is a command-line interface (CLI) shell for interacting with car connectivity data.
243
+
244
+ Attributes:
245
+ prompt (str): The command prompt string.
246
+ intro (str): The introductory message displayed when the shell starts.
247
+ car_connectivity (Any): The car connectivity object to interact with.
248
+ pwd (Any): The current working directory within the car connectivity data.
249
+
250
+ Methods:
251
+ __init__(car_connectivity): Initializes the shell with the given car connectivity object.
252
+ set_prompt(path): Sets the command prompt based on the given path.
253
+ help_exit(): Displays help information for the 'exit' command.
254
+ do_exit(arguments): Exits the shell.
255
+ help_cd(): Displays help information for the 'cd' command.
256
+ do_cd(arguments): Changes the current working directory.
257
+ complete_cd(text, line, begidx, endidx): Provides tab completion for the 'cd' command.
258
+ help_ls(): Displays help information for the 'ls' command.
259
+ do_ls(arguments): Lists subelements of the current path.
260
+ help_pwd(): Displays help information for the 'pwd' command.
261
+ do_pwd(arguments): Displays the current path.
262
+ help_update(): Displays help information for the 'update' command.
263
+ do_update(arguments): Updates the data from the server.
264
+ help_cat(): Displays help information for the 'cat' command.
265
+ do_cat(arguments): Prints the content of the current or specified element.
266
+ help_find(): Displays help information for the 'find' command.
267
+ do_find(arguments): Lists all elements recursively.
268
+ default(line): Handles unrecognized commands.
269
+ do_EOF(): Exits the shell on EOF (Ctrl-D).
270
+ help_EOF(): Displays help information for the EOF command.
271
+ """
272
+ prompt = 'error'
273
+ intro = "Welcome! Type ? to list commands"
274
+
275
+ def __init__(self, car_connectivity: carconnectivity.CarConnectivity) -> None:
276
+ self.car_connectivity: carconnectivity.CarConnectivity = car_connectivity
277
+ self.pwd: GenericObject | GenericAttribute = car_connectivity
278
+
279
+ super().__init__()
280
+ # Set the prompt to the current path
281
+ self.set_prompt(self.car_connectivity.get_absolute_path())
282
+
283
+ def set_prompt(self, path) -> None:
284
+ """
285
+ Sets the command prompt for the CarConnectivityShell.
286
+
287
+ Args:
288
+ path (str): The current path to be displayed in the prompt. If an empty string is provided, it defaults to '/'.
289
+
290
+ """
291
+ if path == '':
292
+ path = '/'
293
+ CarConnectivityShell.prompt = f'ccs:{path}$'
294
+
295
+ def help_exit(self) -> None:
296
+ """
297
+ Prints the help message for the 'exit' command.
298
+
299
+ This method provides information on how to exit the application,
300
+ including the shorthand commands: 'x', 'q', and 'Ctrl-D'.
301
+ """
302
+ print('exit the application. Shorthand: x q Ctrl-D.')
303
+
304
+ def do_exit(self, arguments) -> Literal[True]:
305
+ """
306
+ Exits the CLI application.
307
+
308
+ Args:
309
+ arguments: The arguments passed to the command. This parameter is not used.
310
+
311
+ Returns:
312
+ bool: Always returns True to indicate the command should exit.
313
+ """
314
+ del arguments
315
+ print("Bye")
316
+ return True
317
+
318
+ def help_cd(self):
319
+ """
320
+ Prints the help message for the 'cd' command.
321
+
322
+ This method is used to provide help information about changing the location
323
+ in the tree structure.
324
+ """
325
+ print('change location in tree')
326
+
327
+ def do_cd(self, arguments):
328
+ """
329
+ Change the current directory to the specified path.
330
+
331
+ Args:
332
+ arguments (str): The path to change to. If None or an empty string is provided,
333
+ the path will default to '/'.
334
+
335
+ Behavior:
336
+ - If the provided path starts with '/', it is treated as an absolute path.
337
+ - If the provided path does not start with '/', it is treated as a relative path
338
+ from the current directory.
339
+ - If the specified path exists and is accessible, the current directory is updated
340
+ to the new path and the prompt is set accordingly.
341
+ - If the specified path does not exist or is not accessible, an error message is printed.
342
+
343
+ Example:
344
+ do_cd('/new/path')
345
+ do_cd('relative/path')
346
+ """
347
+ # If no arguments are provided, set the path to '/'
348
+ if arguments is None or arguments == '':
349
+ arguments = '/'
350
+ # If the path starts with '/', treat it as an absolute path
351
+ if arguments.startswith('/'):
352
+ path = arguments
353
+ else:
354
+ path = f'{self.pwd.get_absolute_path()}/{arguments}'
355
+ # Get the object at the specified path
356
+ new_pwd = self.car_connectivity.get_by_path(path)
357
+ # If the object exists and is accessible, update the current directory and prompt
358
+ if new_pwd is not None and new_pwd is not False:
359
+ self.pwd = new_pwd
360
+ path = self.pwd.get_absolute_path()
361
+ if path == '':
362
+ path = '/'
363
+ self.set_prompt(path)
364
+ else:
365
+ # Print an error message if the path does not exist or is not accessible
366
+ print(f'*** {arguments} does not exist or is not accessible')
367
+
368
+ def complete_cd(self, text, line, begidx, endidx):
369
+ """
370
+ Autocompletion method for the 'cd' command.
371
+
372
+ This method provides suggestions for directory names based on the current input text.
373
+ If the input text starts with '/', it suggests absolute paths from the root directory.
374
+ Otherwise, it suggests directory IDs from the current working directory.
375
+
376
+ Args:
377
+ text (str): The current input text for the 'cd' command.
378
+ line (str): The entire input line (unused).
379
+ begidx (int): The beginning index of the input text (unused).
380
+ endidx (int): The ending index of the input text (unused).
381
+
382
+ Returns:
383
+ list: A list of suggested directory names or paths.
384
+ """
385
+ del line
386
+ del begidx
387
+ del endidx
388
+ if text.startswith('/'):
389
+ # Return absolute paths from the root directory
390
+ root: carconnectivity.GenericObject | objects.GenericAttribute = self.pwd.get_root()
391
+ if isinstance(root, objects.GenericObject):
392
+ return [child.get_absolute_path() for child in root.children if child.get_absolute_path().startswith(text)]
393
+ # Return directory IDs from the current working directory
394
+ if isinstance(self.pwd, objects.GenericObject):
395
+ return [child.id for child in self.pwd.children if child.id.startswith(text)]
396
+ return []
397
+
398
+ def help_ls(self):
399
+ """
400
+ Prints the help message for the 'ls' command
401
+
402
+ This method is intended to provide help or guidance to the user about the 'ls' command,
403
+ which lists subelements of the current path.
404
+ """
405
+ print('list subelements of current path')
406
+
407
+ def do_ls(self, arguments):
408
+ """
409
+ Lists the contents of the current directory.
410
+
411
+ Args:
412
+ arguments: Command-line arguments (not used).
413
+
414
+ Prints:
415
+ The parent directory indicator ('..') if the current directory has a parent.
416
+ The IDs of the children of the current directory.
417
+ """
418
+ del arguments
419
+ if self.pwd.parent is not None:
420
+ print('..')
421
+ if isinstance(self.pwd, objects.GenericObject):
422
+ for child in self.pwd.children:
423
+ print(child.id)
424
+
425
+ def help_pwd(self):
426
+ """
427
+ Prints the help message for the 'pwd' command.
428
+
429
+ This method is intended to provide help or guidance to the user about the 'pwd' command,
430
+ which displays the current path.
431
+ """
432
+ print('show current path')
433
+
434
+ def do_pwd(self, arguments):
435
+ """
436
+ Prints the current working directory.
437
+
438
+ Args:
439
+ arguments: Command line arguments (not used).
440
+
441
+ Returns:
442
+ None
443
+ """
444
+ del arguments
445
+ path = self.pwd.get_absolute_path()
446
+ if path == '':
447
+ path = '/'
448
+ print(path)
449
+
450
+ def help_update(self):
451
+ """
452
+ Prints the help message for the 'update' command.
453
+
454
+ This method provides a simple help message for the update command.
455
+ """
456
+ print('update the data from the server')
457
+
458
+ def do_update(self, arguments):
459
+ """
460
+ Updates the car connectivity data by fetching all available data.
461
+
462
+ Args:
463
+ arguments: Command-line arguments passed to the update command.
464
+ """
465
+ del arguments
466
+ self.car_connectivity.fetch_all()
467
+ print('update done')
468
+
469
+ def help_cat(self):
470
+ """
471
+ Prints the help message for the 'cat' command.
472
+
473
+ This method is used to display the help information related to the cat command that prints content.
474
+ """
475
+ print('Print content')
476
+
477
+ def do_cat(self, arguments):
478
+ """
479
+ Display the content of a specified path.
480
+
481
+ If no arguments are provided, this method prints the content of the current working directory.
482
+ If a path is provided as an argument, it prints the content of the specified path
483
+ if it exists and is accessible. Otherwise, it prints an error message.
484
+
485
+ Args:
486
+ arguments (str): The path to the element to be displayed. If None or empty,
487
+ the current working directory is displayed.
488
+ """
489
+ if arguments is None or arguments.strip() == '':
490
+ print(str(self.pwd))
491
+ else:
492
+ element = self.pwd.get_by_path(arguments.strip())
493
+ if element is not None and element is not False:
494
+ print(str(element))
495
+ else:
496
+ print(f'*** {arguments} does not exist or is not accessible')
497
+
498
+ def help_find(self):
499
+ """
500
+ Prints a message describing the functionality of the 'find' command.
501
+
502
+ This method provides a brief description of what the 'find' command does,
503
+ which is to list all elements recursively.
504
+ """
505
+ print('Find lists all elements recursively')
506
+
507
+ def do_find(self, arguments):
508
+ """
509
+ Finds and prints the absolute paths of attributes in the current working directory.
510
+
511
+ Args:
512
+ arguments (str): Command line arguments. If '-s' is passed, only attributes
513
+ that are changeable will be printed.
514
+
515
+ Returns:
516
+ None
517
+ """
518
+ setters = bool(arguments.strip() == '-s')
519
+ all_elements = self.pwd.get_attributes(recursive=True)
520
+ for element in all_elements:
521
+ if setters:
522
+ if isinstance(element, GenericAttribute) and element.is_changeable:
523
+ print(element.get_absolute_path())
524
+ else:
525
+ print(element.get_absolute_path())
526
+
527
+ def default(self, line):
528
+ """
529
+ Handle the default case for unrecognized commands.
530
+
531
+ If the input line is 'x' or 'q', it triggers the exit command.
532
+ Otherwise, it calls the default method of the superclass.
533
+
534
+ Args:
535
+ line (str): The input command line.
536
+
537
+ Returns:
538
+ The result of the exit command if 'x' or 'q' is input, otherwise the result of the superclass's default method.
539
+ """
540
+ if line in ('x', 'q'):
541
+ self.do_exit(line)
542
+ return None
543
+ return super().default(line)
544
+
545
+ do_EOF = do_exit
546
+ 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,203 @@
1
+ Metadata-Version: 2.2
2
+ Name: carconnectivity-cli
3
+ Version: 0.1
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: carconnectivity~=0.1.0
44
+ Requires-Dist: argparse
45
+ Requires-Dist: JSON_minify~=0.3.0
46
+ Provides-Extra: connectors
47
+ Requires-Dist: carconnectivity-connector-volkswagen; extra == "connectors"
48
+ Requires-Dist: carconnectivity-connector-skoda; extra == "connectors"
49
+ Provides-Extra: plugins
50
+ Requires-Dist: carconnectivity-plugin-abrp; extra == "plugins"
51
+ Requires-Dist: carconnectivity-plugin-homekit; extra == "plugins"
52
+
53
+
54
+
55
+ # CarConnectivity Command Line Interface
56
+ [![GitHub sourcecode](https://img.shields.io/badge/Source-GitHub-green)](https://github.com/tillsteinbach/CarConnectivity-cli/)
57
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/tillsteinbach/CarConnectivity-cli)](https://github.com/tillsteinbach/CarConnectivity-cli/releases/latest)
58
+ [![GitHub](https://img.shields.io/github/license/tillsteinbach/CarConnectivity-cli)](https://github.com/tillsteinbach/CarConnectivity-cli/blob/master/LICENSE)
59
+ [![GitHub issues](https://img.shields.io/github/issues/tillsteinbach/CarConnectivity-cli)](https://github.com/tillsteinbach/CarConnectivity-cli/issues)
60
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/carconnectivity-cli?label=PyPI%20Downloads)](https://pypi.org/project/carconnectivity-cli/)
61
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/carconnectivity-cli)](https://pypi.org/project/carconnectivity-cli/)
62
+ [![Donate at PayPal](https://img.shields.io/badge/Donate-PayPal-2997d8)](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
63
+ [![Sponsor at Github](https://img.shields.io/badge/Sponsor-GitHub-28a745)](https://github.com/sponsors/tillsteinbach)
64
+
65
+ ## 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!
66
+
67
+ ## Supported Car Brands
68
+ CarConenctivity uses a plugin architecture to enable access to the services of various brands. Currently known plugins are:
69
+
70
+ | Brand | Connector |
71
+ |------------|---------------------------------------------------------------------------------------------------------------|
72
+ | Skoda | [CarConnectivity-connector-skoda](https://github.com/tillsteinbach/CarConnectivity-connector-skoda) |
73
+ | Volkswagen | [CarConnectivity-connector-volkswagen](https://github.com/tillsteinbach/CarConnectivity-connector-volkswagen) |
74
+
75
+ If you know of a connector not listed here let me know and I will add it to the list.
76
+ 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
77
+
78
+ ## How to use
79
+ Start by creating a carconnectivity.json configuration file
80
+
81
+ ### Configuration
82
+ In your carconnectivity.json configuration add a section for the connectors you like to use like this:
83
+ ```
84
+ {
85
+ "carConnectivity": {
86
+ "connectors": [
87
+ {
88
+ "type": "volkswagen",
89
+ "config": {
90
+ "username": "test@test.de"
91
+ "password": "testpassword123"
92
+ }
93
+ },
94
+ {
95
+ "type": "skoda",
96
+ "config": {
97
+ "username": "test@test.de"
98
+ "password": "testpassword123"
99
+ }
100
+ }
101
+ ]
102
+ }
103
+ }
104
+ ```
105
+ The detailed configuration options of the connectors can be found in their README files.
106
+
107
+ ### How to use the commandline interface
108
+ Start carconnectivity-cli from the commandline, by default you will enter the interactive shell:
109
+ ```bash
110
+ carconnectivity-cli mycarconnectivity_config.json
111
+ ```
112
+ You get all the usage information by using the --help command
113
+ ```bash
114
+ carconnectivity-cli mycarconnectivity_config.json --help
115
+ ```
116
+ With the "list" command you can get a list of all available information you can query (use "list -s" if you want to see which attributes can be changed)
117
+ ```bash
118
+ carconnectivity-cli mycarconnectivity_config.json list
119
+ /garage/WVWABCE1ZSD057394
120
+ /garage/WVWABCE1ZSD057394/vin
121
+ /garage/WVWABCE1ZSD057394/type
122
+ /garage/WVWABCE1ZSD057394/odometer
123
+ /garage/WVWABCE1ZSD057394/model
124
+ /garage/WVWABCE1ZSD057394/name
125
+ ...
126
+ ```
127
+ You can then pass the addresses to the "get" command:
128
+ ```bash
129
+ carconnectivity-cli mycarconnectivity_config.json get /garage/WVWABCE1ZSD057394/model
130
+ ID.3
131
+ ```
132
+ or the "set" command:
133
+ ```bash
134
+ carconnectivity-cli mycarconnectivity_config.json /garage/WVWABCE1ZSD057394/climatisation/command stop
135
+ ```
136
+ The "events" command allows you to monitor what is happening on the WeConnect Interface:
137
+ ```bash
138
+ carconnectivity-cli mycarconnectivity_config.json events
139
+ 2021-05-26 16:49:58.698570: /garage/WVWABCE1ZSD057394/doors/lock_state: new value: unlocked
140
+ 2021-05-26 16:49:58.698751: /garage/WVWABCE1ZSD057394/doors/bonnet/lock_state: new value: unknown lock state
141
+ 2021-05-26 16:49:58.698800: /garage/WVWABCE1ZSD057394/doors/bonnet/open_state: new value: closed
142
+ 2021-05-26 16:49:58.698980: /garage/WVWABCE1ZSD057394/doors/frontLeft/lock_state: new value: unlocked
143
+ 2021-05-26 16:49:58.699056: /garage/WVWABCE1ZSD057394/doors/frontLeft/open_state: new value: closed
144
+ ```
145
+
146
+ ### S-PIN
147
+ For some commands (e.g. locking/unlocking supported on some cars) you need in addition to your login the so called S-PIN, you can provide it with the spin config option:
148
+
149
+ ### Interactive Shell
150
+ You can also use an interactive shell:
151
+ ```
152
+ carconnectivity-cli --username user@mail.de --password test123 shell
153
+ Welcome! Type ? to list commands
154
+ user@mail.de@weconnect-sh:/$update
155
+ update done
156
+ user@mail.de@weconnect-sh:/$cd garage
157
+ user@mail.de@weconnect-sh:/garage$ ls
158
+ ..
159
+ WVWABCE1ZSD057394
160
+ WVWABCE13SD057505
161
+ user@mail.de@weconnect-sh:/garage$ cd /garage/WVWABCE13SD057505/status/parkingPosition
162
+ user@mail.de@weconnect-sh:/garage/WVWABCE13SD057505/status/parkingPosition$ cat
163
+ [parkingPosition] (last captured 2021-06-01T19:05:04+00:00)
164
+ Latitude: 51.674535
165
+ Longitude: 16.154376
166
+ user@mail.de@weconnect-sh:/garage/WVWABCE13SD057505/parking/parkingPosition$ exit
167
+ Bye
168
+ ```
169
+ ### Caching
170
+ By default carconnectivity-cli will cache (store) the data for 300 seconds before retrieving new data from the servers. This makes carconnectivity-cli more responsive and at the same time does not cause unneccessary requests to the vw servers. If you want to increase the cache duration use max_age config option. If you do not want to cache use no_cache option. Please use the no_cache option with care. You are generating traffic with subsequent requests. If you request too often you might be blocked for some time until you can generate requests again.
171
+
172
+ ### Credentials
173
+ If you do not want to provide your username or password all the time you have to create a ".netrc" file at the appropriate location (usually this is your home folder):
174
+ ```
175
+ machine volkswagen.de
176
+ login test@test.de
177
+ password testpassword123
178
+ ```
179
+ You can also provide the location of the netrc file in the configuration.
180
+
181
+ The optional S-PIN needed for some commands can be provided in the account section:
182
+ ```
183
+ # For WeConnect
184
+ machine volkswagen.de
185
+ login test@test.de
186
+ password testpassword123
187
+ account 1234
188
+ ```
189
+
190
+ ## Tested with
191
+ - Volkswagen ID.3 Modelyear 2021
192
+ - Volkswagen Passat GTE Modelyear 2021
193
+ - Skoda Enyaq RS Modelyear 2025
194
+
195
+ ## Reporting Issues
196
+ Please feel free to open an issue at [GitHub Issue page](https://github.com/tillsteinbach/carconnectivity-cli/issues) to report problems you found.
197
+
198
+ ### Known Issues
199
+ - The Tool is in alpha state and may change unexpectedly at any time!
200
+
201
+ ## Related Projects:
202
+ - [WeConnect-MQTT](https://github.com/tillsteinbach/WeConnect-mqtt): MQTT Client that publishes data from CarConnectivity to any MQTT broker.
203
+ - [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity): The underlying python API behind CarConnectivity-cli. If you are a developer and want to implement an application or service with vehicle telemetry data you can use [CarConnectivity-Library](https://github.com/tillsteinbach/CarConnectivity).
@@ -0,0 +1,9 @@
1
+ carconnectivity_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ carconnectivity_cli/_version.py,sha256=7V2ukxSfzXlOjRj21NN9XMWrUwTZJIQZggSzFRx5qs8,406
3
+ carconnectivity_cli/carconnectivity_cli_base.py,sha256=LUe0vQpsJXAlJxSOLqmaA6fG2xe5OZXgXze2eVOdt0s,24934
4
+ carconnectivity_cli-0.1.dist-info/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
5
+ carconnectivity_cli-0.1.dist-info/METADATA,sha256=tZVYGqupRlQmd4-tSF-cqiWg2iQwkyaFoP3OkwHUSm8,10058
6
+ carconnectivity_cli-0.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
7
+ carconnectivity_cli-0.1.dist-info/entry_points.txt,sha256=zUPLT-dwyZ605ra1ID7BA-ghgo7phWac5zPYRdQwe5I,90
8
+ carconnectivity_cli-0.1.dist-info/top_level.txt,sha256=Hz46d8yVvc8mHCP3Zbgk5Fk4BBS-VyWHdF1b03G7_cQ,20
9
+ carconnectivity_cli-0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ carconnectivity-cli = carconnectivity_cli.carconnectivity_cli_base:main
@@ -0,0 +1 @@
1
+ carconnectivity_cli