pySmartHashtag 0.12.0__tar.gz → 0.12.2__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 (98) hide show
  1. {pysmarthashtag-0.12.0/pySmartHashtag.egg-info → pysmarthashtag-0.12.2}/PKG-INFO +1 -1
  2. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2/pySmartHashtag.egg-info}/PKG-INFO +1 -1
  3. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pySmartHashtag.egg-info/SOURCES.txt +1 -0
  4. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pySmartHashtag.egg-info/scm_file_list.json +66 -65
  5. pysmarthashtag-0.12.2/pySmartHashtag.egg-info/scm_version.json +8 -0
  6. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/const.py +6 -0
  7. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_account.py +4 -2
  8. pysmarthashtag-0.12.2/pysmarthashtag/tests/test_charging_states.py +119 -0
  9. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_dc_charging.py +71 -3
  10. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/battery.py +59 -4
  11. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/vehicle.py +10 -4
  12. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/requirements-test.txt +2 -2
  13. pysmarthashtag-0.12.0/pySmartHashtag.egg-info/scm_version.json +0 -8
  14. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.devcontainer.json +0 -0
  15. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.github/copilot-instructions.md +0 -0
  16. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.github/dependabot.yml +0 -0
  17. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.github/workflows/python-package.yml +0 -0
  18. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.github/workflows/python-publish.yml +0 -0
  19. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.gitignore +0 -0
  20. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.pre-commit-config.yaml +0 -0
  21. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.vscode/launch.json +0 -0
  22. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/.vscode/settings.json +0 -0
  23. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/AUTHORS +0 -0
  24. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/CODE_OF_CONDUCT.md +0 -0
  25. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/CONTRIBUTING.md +0 -0
  26. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/ChangeLog +0 -0
  27. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/LICENSE +0 -0
  28. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/README.md +0 -0
  29. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pySmartHashtag.egg-info/dependency_links.txt +0 -0
  30. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pySmartHashtag.egg-info/requires.txt +0 -0
  31. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pySmartHashtag.egg-info/top_level.txt +0 -0
  32. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pyproject.toml +0 -0
  33. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/__init__.py +0 -0
  34. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/account.py +0 -0
  35. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/__init__.py +0 -0
  36. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/authentication.py +0 -0
  37. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/client.py +0 -0
  38. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/log_sanitizer.py +0 -0
  39. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/ssl_context.py +0 -0
  40. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/api/utils.py +0 -0
  41. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/cli.py +0 -0
  42. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/control/charging.py +0 -0
  43. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/control/climate.py +0 -0
  44. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/control/journal.py +0 -0
  45. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/models.py +0 -0
  46. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/__init__.py +0 -0
  47. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/common.py +0 -0
  48. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/conftest.py +0 -0
  49. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/Human_and_vehicle_relationship_does_not_exist.json +0 -0
  50. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/api_access.json +0 -0
  51. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/auth_context.url +0 -0
  52. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/auth_intermediate.url +0 -0
  53. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/auth_result.url +0 -0
  54. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/charging_success.json +0 -0
  55. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/climate_success.json +0 -0
  56. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/journal_response.json +0 -0
  57. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/journal_toggle_success.json +0 -0
  58. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/login_result.json +0 -0
  59. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/ota_response.json +0 -0
  60. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/soc_80.json +0 -0
  61. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/soc_90.json +0 -0
  62. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/token_expired.json +0 -0
  63. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/trackpoints_response.json +0 -0
  64. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/vehicle_info.json +0 -0
  65. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/vehicle_info2.json +0 -0
  66. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/vehicle_info_dc_charging.json +0 -0
  67. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/vehicle_response.json +0 -0
  68. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/replys/vehicle_result.json +0 -0
  69. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_actions.py +0 -0
  70. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_authentication_backoff.py +0 -0
  71. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_charging.py +0 -0
  72. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_endpoint_urls.py +0 -0
  73. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_journal.py +0 -0
  74. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_journal_control.py +0 -0
  75. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_journal_pagination.py +0 -0
  76. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_log_sanitizer.py +0 -0
  77. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_missing_fields.py +0 -0
  78. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_ssl_context.py +0 -0
  79. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_token_lifecycle.py +0 -0
  80. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_tracked_vins.py +0 -0
  81. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_trackpoints.py +0 -0
  82. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_vehicle_state.py +0 -0
  83. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/tests/test_vin_headers.py +0 -0
  84. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/__init__.py +0 -0
  85. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/climate.py +0 -0
  86. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/journal.py +0 -0
  87. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/maintenance.py +0 -0
  88. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/position.py +0 -0
  89. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/running.py +0 -0
  90. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/safety.py +0 -0
  91. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/tires.py +0 -0
  92. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/trackpoints.py +0 -0
  93. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/pysmarthashtag/vehicle/vehicle_state.py +0 -0
  94. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/requirements-cli.txt +0 -0
  95. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/requirements.txt +0 -0
  96. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/run.sh +0 -0
  97. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/setup.cfg +0 -0
  98. {pysmarthashtag-0.12.0 → pysmarthashtag-0.12.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pySmartHashtag
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: A python library to get information from Smart #1, #3 and #5 web API
5
5
  Home-page: https://github.com/dasBasti/pySmartHashtag
6
6
  Author: dasBasti
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pySmartHashtag
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: A python library to get information from Smart #1, #3 and #5 web API
5
5
  Home-page: https://github.com/dasBasti/pySmartHashtag
6
6
  Author: dasBasti
@@ -48,6 +48,7 @@ pysmarthashtag/tests/test_account.py
48
48
  pysmarthashtag/tests/test_actions.py
49
49
  pysmarthashtag/tests/test_authentication_backoff.py
50
50
  pysmarthashtag/tests/test_charging.py
51
+ pysmarthashtag/tests/test_charging_states.py
51
52
  pysmarthashtag/tests/test_dc_charging.py
52
53
  pysmarthashtag/tests/test_endpoint_urls.py
53
54
  pysmarthashtag/tests/test_journal.py
@@ -1,92 +1,93 @@
1
1
  {
2
2
  "files": [
3
- "requirements.txt",
4
- "requirements-test.txt",
3
+ ".devcontainer.json",
4
+ ".github/copilot-instructions.md",
5
+ ".github/dependabot.yml",
6
+ ".github/workflows/python-package.yml",
7
+ ".github/workflows/python-publish.yml",
8
+ ".gitignore",
5
9
  ".pre-commit-config.yaml",
10
+ ".vscode/launch.json",
11
+ ".vscode/settings.json",
12
+ "AUTHORS",
13
+ "CODE_OF_CONDUCT.md",
6
14
  "CONTRIBUTING.md",
7
- ".devcontainer.json",
8
- "requirements-cli.txt",
9
- "setup.py",
10
- "pyproject.toml",
11
- "setup.cfg",
12
15
  "ChangeLog",
13
- "run.sh",
14
- "CODE_OF_CONDUCT.md",
15
16
  "LICENSE",
16
- "AUTHORS",
17
17
  "README.md",
18
- ".gitignore",
19
- ".github/copilot-instructions.md",
20
- ".github/dependabot.yml",
21
- ".github/workflows/python-publish.yml",
22
- ".github/workflows/python-package.yml",
23
- "pysmarthashtag/account.py",
24
- "pysmarthashtag/const.py",
18
+ "pyproject.toml",
25
19
  "pysmarthashtag/__init__.py",
20
+ "pysmarthashtag/account.py",
21
+ "pysmarthashtag/api/__init__.py",
22
+ "pysmarthashtag/api/authentication.py",
23
+ "pysmarthashtag/api/client.py",
24
+ "pysmarthashtag/api/log_sanitizer.py",
25
+ "pysmarthashtag/api/ssl_context.py",
26
+ "pysmarthashtag/api/utils.py",
26
27
  "pysmarthashtag/cli.py",
27
- "pysmarthashtag/models.py",
28
+ "pysmarthashtag/const.py",
29
+ "pysmarthashtag/control/charging.py",
28
30
  "pysmarthashtag/control/climate.py",
29
31
  "pysmarthashtag/control/journal.py",
30
- "pysmarthashtag/control/charging.py",
31
- "pysmarthashtag/tests/test_vin_headers.py",
32
- "pysmarthashtag/tests/test_vehicle_state.py",
33
- "pysmarthashtag/tests/test_actions.py",
34
- "pysmarthashtag/tests/common.py",
35
- "pysmarthashtag/tests/test_trackpoints.py",
36
- "pysmarthashtag/tests/test_log_sanitizer.py",
37
- "pysmarthashtag/tests/test_tracked_vins.py",
38
- "pysmarthashtag/tests/test_charging.py",
39
- "pysmarthashtag/tests/test_journal.py",
40
- "pysmarthashtag/tests/test_token_lifecycle.py",
32
+ "pysmarthashtag/models.py",
41
33
  "pysmarthashtag/tests/__init__.py",
42
- "pysmarthashtag/tests/test_ssl_context.py",
43
- "pysmarthashtag/tests/test_journal_pagination.py",
44
- "pysmarthashtag/tests/test_journal_control.py",
45
- "pysmarthashtag/tests/test_authentication_backoff.py",
46
- "pysmarthashtag/tests/test_missing_fields.py",
47
- "pysmarthashtag/tests/test_dc_charging.py",
34
+ "pysmarthashtag/tests/common.py",
48
35
  "pysmarthashtag/tests/conftest.py",
49
- "pysmarthashtag/tests/test_endpoint_urls.py",
50
- "pysmarthashtag/tests/test_account.py",
51
- "pysmarthashtag/tests/replys/vehicle_response.json",
36
+ "pysmarthashtag/tests/replys/Human_and_vehicle_relationship_does_not_exist.json",
37
+ "pysmarthashtag/tests/replys/api_access.json",
38
+ "pysmarthashtag/tests/replys/auth_context.url",
39
+ "pysmarthashtag/tests/replys/auth_intermediate.url",
40
+ "pysmarthashtag/tests/replys/auth_result.url",
41
+ "pysmarthashtag/tests/replys/charging_success.json",
42
+ "pysmarthashtag/tests/replys/climate_success.json",
43
+ "pysmarthashtag/tests/replys/journal_response.json",
52
44
  "pysmarthashtag/tests/replys/journal_toggle_success.json",
53
- "pysmarthashtag/tests/replys/vehicle_info.json",
54
45
  "pysmarthashtag/tests/replys/login_result.json",
55
- "pysmarthashtag/tests/replys/charging_success.json",
56
- "pysmarthashtag/tests/replys/soc_80.json",
57
- "pysmarthashtag/tests/replys/auth_result.url",
58
- "pysmarthashtag/tests/replys/trackpoints_response.json",
59
- "pysmarthashtag/tests/replys/api_access.json",
60
46
  "pysmarthashtag/tests/replys/ota_response.json",
47
+ "pysmarthashtag/tests/replys/soc_80.json",
48
+ "pysmarthashtag/tests/replys/soc_90.json",
61
49
  "pysmarthashtag/tests/replys/token_expired.json",
62
- "pysmarthashtag/tests/replys/climate_success.json",
63
- "pysmarthashtag/tests/replys/Human_and_vehicle_relationship_does_not_exist.json",
50
+ "pysmarthashtag/tests/replys/trackpoints_response.json",
51
+ "pysmarthashtag/tests/replys/vehicle_info.json",
64
52
  "pysmarthashtag/tests/replys/vehicle_info2.json",
65
- "pysmarthashtag/tests/replys/soc_90.json",
66
- "pysmarthashtag/tests/replys/auth_intermediate.url",
67
53
  "pysmarthashtag/tests/replys/vehicle_info_dc_charging.json",
54
+ "pysmarthashtag/tests/replys/vehicle_response.json",
68
55
  "pysmarthashtag/tests/replys/vehicle_result.json",
69
- "pysmarthashtag/tests/replys/journal_response.json",
70
- "pysmarthashtag/tests/replys/auth_context.url",
56
+ "pysmarthashtag/tests/test_account.py",
57
+ "pysmarthashtag/tests/test_actions.py",
58
+ "pysmarthashtag/tests/test_authentication_backoff.py",
59
+ "pysmarthashtag/tests/test_charging.py",
60
+ "pysmarthashtag/tests/test_charging_states.py",
61
+ "pysmarthashtag/tests/test_dc_charging.py",
62
+ "pysmarthashtag/tests/test_endpoint_urls.py",
63
+ "pysmarthashtag/tests/test_journal.py",
64
+ "pysmarthashtag/tests/test_journal_control.py",
65
+ "pysmarthashtag/tests/test_journal_pagination.py",
66
+ "pysmarthashtag/tests/test_log_sanitizer.py",
67
+ "pysmarthashtag/tests/test_missing_fields.py",
68
+ "pysmarthashtag/tests/test_ssl_context.py",
69
+ "pysmarthashtag/tests/test_token_lifecycle.py",
70
+ "pysmarthashtag/tests/test_tracked_vins.py",
71
+ "pysmarthashtag/tests/test_trackpoints.py",
72
+ "pysmarthashtag/tests/test_vehicle_state.py",
73
+ "pysmarthashtag/tests/test_vin_headers.py",
74
+ "pysmarthashtag/vehicle/__init__.py",
75
+ "pysmarthashtag/vehicle/battery.py",
71
76
  "pysmarthashtag/vehicle/climate.py",
72
77
  "pysmarthashtag/vehicle/journal.py",
78
+ "pysmarthashtag/vehicle/maintenance.py",
79
+ "pysmarthashtag/vehicle/position.py",
73
80
  "pysmarthashtag/vehicle/running.py",
74
81
  "pysmarthashtag/vehicle/safety.py",
75
- "pysmarthashtag/vehicle/position.py",
76
- "pysmarthashtag/vehicle/vehicle_state.py",
77
- "pysmarthashtag/vehicle/battery.py",
78
- "pysmarthashtag/vehicle/__init__.py",
79
- "pysmarthashtag/vehicle/vehicle.py",
80
- "pysmarthashtag/vehicle/trackpoints.py",
81
82
  "pysmarthashtag/vehicle/tires.py",
82
- "pysmarthashtag/vehicle/maintenance.py",
83
- "pysmarthashtag/api/ssl_context.py",
84
- "pysmarthashtag/api/authentication.py",
85
- "pysmarthashtag/api/__init__.py",
86
- "pysmarthashtag/api/client.py",
87
- "pysmarthashtag/api/log_sanitizer.py",
88
- "pysmarthashtag/api/utils.py",
89
- ".vscode/launch.json",
90
- ".vscode/settings.json"
83
+ "pysmarthashtag/vehicle/trackpoints.py",
84
+ "pysmarthashtag/vehicle/vehicle.py",
85
+ "pysmarthashtag/vehicle/vehicle_state.py",
86
+ "requirements-cli.txt",
87
+ "requirements-test.txt",
88
+ "requirements.txt",
89
+ "run.sh",
90
+ "setup.cfg",
91
+ "setup.py"
91
92
  ]
92
93
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.12.2",
3
+ "distance": 0,
4
+ "node": "g800eaf2bed8475decf036d35ccdea3178897c327",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-15"
8
+ }
@@ -17,6 +17,12 @@ API_JOURNAL_TOGGLE_URL = "/remote-control/vehicle/status/journalLog/"
17
17
 
18
18
  OTA_SERVER_URL = "https://ota.srv.smart.com/"
19
19
 
20
+ # Prefixes of the "seriesCodeVs" field identifying the vehicle model. They select
21
+ # the API version to talk to and, for the #5, the scaling of some telemetry fields.
22
+ SERIES_CODE_PREFIX_SMART_1 = "HX"
23
+ SERIES_CODE_PREFIX_SMART_3 = "HC"
24
+ SERIES_CODE_PREFIX_SMART_5 = "HY"
25
+
20
26
  # Gigya socialize endpoint used to bootstrap a Gigya session (gmid + ucid)
21
27
  # before posting accounts.login. Smart's tenant rejects accounts.login from
22
28
  # a "fresh" client without these identifiers + the gig_bootstrap cookie.
@@ -137,9 +137,11 @@ async def test_get_vehicle_chargin_dc(smart_fixture: respx.Router):
137
137
  # TODO: missing 3-phase charging
138
138
  assert vehicles["TestVIN0000000002"].battery.charging_status == "DC_CHARGING"
139
139
  assert vehicles["TestVIN0000000002"].battery.is_charger_connected
140
- assert vehicles["TestVIN0000000002"].battery.charging_current == ValueWithUnit(value=102.6, unit="A")
140
+ # VIN ...002 has a Smart #5 series code, so the raw "dcChargeIAct" of -102.6 is
141
+ # read as deci-ampere and scaled down to 10.26 A (issue #459).
142
+ assert vehicles["TestVIN0000000002"].battery.charging_current == ValueWithUnit(value=10.26, unit="A")
141
143
  assert vehicles["TestVIN0000000002"].battery.charging_voltage == ValueWithUnit(value=429, unit="V")
142
- assert vehicles["TestVIN0000000002"].battery.charging_power == ValueWithUnit(value=44015, unit="W")
144
+ assert vehicles["TestVIN0000000002"].battery.charging_power == ValueWithUnit(value=4401, unit="W")
143
145
 
144
146
 
145
147
  @pytest.mark.asyncio
@@ -0,0 +1,119 @@
1
+ """Tests for the enumerated charging and charger connection states.
2
+
3
+ Consumers such as the Home Assistant integration need to declare every state a
4
+ sensor can take up front, otherwise the automation editor cannot offer them as
5
+ trigger values. See SmartHashtag issue #439.
6
+ """
7
+
8
+ import pytest
9
+
10
+ from pysmarthashtag.vehicle.battery import (
11
+ CHARGER_CONNECTION_STATES,
12
+ CHARGING_STATES,
13
+ Battery,
14
+ ChargerConnectionState,
15
+ ChargingState,
16
+ charger_connection_state_name,
17
+ )
18
+
19
+
20
+ def create_vehicle_data(charger_state: str, charger_connection: str) -> dict:
21
+ """Create minimal vehicle data carrying the two status fields."""
22
+ return {
23
+ "vehicleStatus": {
24
+ "updateTime": "1716485767970",
25
+ "additionalVehicleStatus": {
26
+ "electricVehicleStatus": {
27
+ "chargeLevel": "50",
28
+ "chargerState": charger_state,
29
+ "statusOfChargerConnection": charger_connection,
30
+ }
31
+ },
32
+ },
33
+ }
34
+
35
+
36
+ class TestChargingStates:
37
+ """Test the de-duplicated list of charging states."""
38
+
39
+ def test_contains_no_duplicates(self):
40
+ """The exported states are unique, unlike the index based ChargingState."""
41
+ assert len(CHARGING_STATES) == len(set(CHARGING_STATES))
42
+
43
+ def test_covers_every_indexed_state(self):
44
+ """Every state reachable through a chargerState code is exported."""
45
+ assert set(CHARGING_STATES) == set(ChargingState)
46
+
47
+ def test_keeps_definition_order(self):
48
+ """The first occurrence of each state defines its position."""
49
+ assert CHARGING_STATES[0] == "NOT_CHARGING"
50
+ assert CHARGING_STATES[-1] == "DC_CHARGING"
51
+
52
+ @pytest.mark.parametrize("charger_state", range(len(ChargingState)))
53
+ def test_every_code_reports_an_exported_state(self, charger_state: int):
54
+ """Parsing any known code yields a state that consumers can enumerate."""
55
+ battery = Battery.from_vehicle_data(create_vehicle_data(str(charger_state), "0"))
56
+
57
+ assert battery is not None
58
+ assert battery.charging_status in CHARGING_STATES
59
+
60
+ def test_unknown_code_falls_back_to_unknown(self):
61
+ """Codes beyond the known range do not leak into the state."""
62
+ battery = Battery.from_vehicle_data(create_vehicle_data("99", "0"))
63
+
64
+ assert battery is not None
65
+ assert battery.charging_status == "UNKNOWN"
66
+
67
+ def test_negative_code_falls_back_to_unknown(self):
68
+ """A negative code must not wrap around to the end of the list."""
69
+ battery = Battery.from_vehicle_data(create_vehicle_data("-1", "0"))
70
+
71
+ assert battery is not None
72
+ assert battery.charging_status == "UNKNOWN"
73
+
74
+
75
+ class TestChargerConnectionState:
76
+ """Test the named charger connection state."""
77
+
78
+ def test_states_are_lower_case_names(self):
79
+ """The exported names are usable as translation keys."""
80
+ assert CHARGER_CONNECTION_STATES == (
81
+ "not_connected",
82
+ "dc_connected",
83
+ "plugged_not_charging",
84
+ "charging",
85
+ "unknown",
86
+ )
87
+
88
+ @pytest.mark.parametrize("state", ChargerConnectionState)
89
+ def test_known_codes_map_to_exported_names(self, state: ChargerConnectionState):
90
+ """Every code of the enum maps to an exported name."""
91
+ assert charger_connection_state_name(state.value) in CHARGER_CONNECTION_STATES
92
+
93
+ def test_missing_code_stays_none(self):
94
+ """A vehicle that does not report the field keeps the state unset."""
95
+ assert charger_connection_state_name(None) is None
96
+
97
+ def test_unmapped_code_falls_back_to_unknown(self):
98
+ """Codes the API adds later do not break enumeration."""
99
+ assert charger_connection_state_name(42) == "unknown"
100
+
101
+ def test_parsed_from_vehicle_data(self):
102
+ """The raw code and the named state are both available after parsing."""
103
+ battery = Battery.from_vehicle_data(create_vehicle_data("2", "3"))
104
+
105
+ assert battery is not None
106
+ assert battery.charger_connection_status == 3
107
+ assert battery.charger_connection_state == "charging"
108
+
109
+ def test_absent_field_leaves_state_unset(self):
110
+ """No connection field means no state, not a wrong one."""
111
+ vehicle_data = create_vehicle_data("2", "0")
112
+ del vehicle_data["vehicleStatus"]["additionalVehicleStatus"]["electricVehicleStatus"][
113
+ "statusOfChargerConnection"
114
+ ]
115
+ battery = Battery.from_vehicle_data(vehicle_data)
116
+
117
+ assert battery is not None
118
+ assert battery.charger_connection_status is None
119
+ assert battery.charger_connection_state is None
@@ -6,13 +6,17 @@ from pysmarthashtag.models import ValueWithUnit
6
6
  from pysmarthashtag.vehicle.battery import Battery, ChargingState, DcChargingVoltLevels
7
7
 
8
8
 
9
- def create_vehicle_data_with_dc_charging(charge_level: int, dc_charge_current: float) -> dict:
9
+ def create_vehicle_data_with_dc_charging(
10
+ charge_level: int, dc_charge_current: float, series_code: str = "HX11"
11
+ ) -> dict:
10
12
  """Create test vehicle data with DC charging parameters.
11
13
 
12
14
  Args:
13
15
  ----
14
16
  charge_level: Battery charge level percentage (0-100)
15
- dc_charge_current: DC charging current in Amps (negative for charging)
17
+ dc_charge_current: DC charging current as reported by the API
18
+ (negative for charging, deci-ampere on the Smart #5)
19
+ series_code: Value of the "seriesCodeVs" field selecting the vehicle model
16
20
 
17
21
  Returns:
18
22
  -------
@@ -20,6 +24,7 @@ def create_vehicle_data_with_dc_charging(charge_level: int, dc_charge_current: f
20
24
 
21
25
  """
22
26
  return {
27
+ "seriesCodeVs": series_code,
23
28
  "vehicleStatus": {
24
29
  "updateTime": "1716485767970",
25
30
  "additionalVehicleStatus": {
@@ -36,7 +41,7 @@ def create_vehicle_data_with_dc_charging(charge_level: int, dc_charge_current: f
36
41
  "averPowerConsumption": "-102.3",
37
42
  }
38
43
  },
39
- }
44
+ },
40
45
  }
41
46
 
42
47
 
@@ -150,6 +155,69 @@ class TestDcChargingDataHandling:
150
155
  assert battery.remaining_range_at_full_charge == ValueWithUnit(value=427, unit="km")
151
156
 
152
157
 
158
+ class TestDcChargingSmart5Scaling:
159
+ """Test that the Smart #5 deci-ampere DC current is scaled down (issue #459)."""
160
+
161
+ def test_smart_5_dc_current_is_divided_by_ten(self):
162
+ """Test that a #5 reporting 1650 deci-ampere yields 165 A instead of 1650 A."""
163
+ vehicle_data = create_vehicle_data_with_dc_charging(
164
+ charge_level=55, dc_charge_current=-1650.0, series_code="HY11"
165
+ )
166
+ battery = Battery.from_vehicle_data(vehicle_data)
167
+
168
+ assert battery is not None
169
+ assert battery.charging_status == "DC_CHARGING"
170
+ assert battery.charging_current == ValueWithUnit(value=165.0, unit="A")
171
+ # Voltage is unaffected by the scaling
172
+ assert battery.charging_voltage == ValueWithUnit(value=DcChargingVoltLevels[55], unit="V")
173
+ expected_power = math.floor(165.0 * DcChargingVoltLevels[55])
174
+ assert battery.charging_power == ValueWithUnit(value=expected_power, unit="W")
175
+
176
+ def test_smart_1_dc_current_is_not_scaled(self):
177
+ """Test that the #1 keeps reporting DC current in ampere."""
178
+ vehicle_data = create_vehicle_data_with_dc_charging(
179
+ charge_level=55, dc_charge_current=-165.0, series_code="HX11"
180
+ )
181
+ battery = Battery.from_vehicle_data(vehicle_data)
182
+
183
+ assert battery is not None
184
+ assert battery.charging_current == ValueWithUnit(value=165.0, unit="A")
185
+
186
+ def test_smart_3_dc_current_is_not_scaled(self):
187
+ """Test that the #3 keeps reporting DC current in ampere."""
188
+ vehicle_data = create_vehicle_data_with_dc_charging(
189
+ charge_level=55, dc_charge_current=-165.0, series_code="HC11"
190
+ )
191
+ battery = Battery.from_vehicle_data(vehicle_data)
192
+
193
+ assert battery is not None
194
+ assert battery.charging_current == ValueWithUnit(value=165.0, unit="A")
195
+
196
+ def test_missing_series_code_is_not_scaled(self):
197
+ """Test that data without a series code falls back to the unscaled V1 behaviour."""
198
+ vehicle_data = create_vehicle_data_with_dc_charging(charge_level=55, dc_charge_current=-165.0)
199
+ del vehicle_data["seriesCodeVs"]
200
+ battery = Battery.from_vehicle_data(vehicle_data)
201
+
202
+ assert battery is not None
203
+ assert battery.charging_current == ValueWithUnit(value=165.0, unit="A")
204
+
205
+ def test_smart_5_ac_current_is_not_scaled(self):
206
+ """Test that AC charging on a #5 is passed through unscaled."""
207
+ vehicle_data = create_vehicle_data_with_dc_charging(charge_level=55, dc_charge_current=0.0, series_code="HY11")
208
+ evStatus = vehicle_data["vehicleStatus"]["additionalVehicleStatus"]["electricVehicleStatus"]
209
+ evStatus["chargerState"] = "2" # CHARGING (AC)
210
+ evStatus["chargeUAct"] = "230.0"
211
+ evStatus["chargeIAct"] = "16.000"
212
+ battery = Battery.from_vehicle_data(vehicle_data)
213
+
214
+ assert battery is not None
215
+ assert battery.charging_status == "CHARGING"
216
+ assert battery.charging_current == ValueWithUnit(value=16.0, unit="A")
217
+ assert battery.charging_voltage == ValueWithUnit(value=230.0, unit="V")
218
+ assert battery.charging_power == ValueWithUnit(value=230.0 * 16.0, unit="W")
219
+
220
+
153
221
  class TestChargingStateEnum:
154
222
  """Test charging state handling."""
155
223
 
@@ -4,12 +4,18 @@ import logging
4
4
  import math
5
5
  from dataclasses import dataclass
6
6
  from datetime import datetime
7
+ from enum import IntEnum
7
8
  from typing import Any, Optional
8
9
 
10
+ from pysmarthashtag.const import SERIES_CODE_PREFIX_SMART_5
9
11
  from pysmarthashtag.models import ValueWithUnit, VehicleDataBase, get_field_as_type
10
12
 
11
13
  _LOGGER = logging.getLogger(__name__)
12
14
 
15
+ """The V2 API used by the Smart #5 reports "dcChargeIAct" in deci-ampere, while the V1
16
+ API of the #1/#3 reports it in ampere. See SmartHashtag issue #459."""
17
+ DC_CHARGE_CURRENT_DIVIDER_SMART_5 = 10
18
+
13
19
  """Charging state of electric vehicle."""
14
20
  ChargingState = [
15
21
  "NOT_CHARGING",
@@ -30,6 +36,46 @@ ChargingState = [
30
36
  "DC_CHARGING",
31
37
  ]
32
38
 
39
+ """Every distinct value :attr:`Battery.charging_status` can take, in the order the API
40
+ codes are defined.
41
+
42
+ ``ChargingState`` is indexed by the raw ``chargerState`` code and therefore repeats
43
+ ``UNKNOWN`` for the codes with no known meaning. Consumers that need to enumerate the
44
+ possible states up front should use this de-duplicated tuple instead of hard coding a
45
+ subset: Home Assistant, for example, only offers a sensor's states to the automation
46
+ editor when the entity declares all of them in advance. See SmartHashtag issue #439."""
47
+ CHARGING_STATES: tuple = tuple(dict.fromkeys(ChargingState))
48
+
49
+
50
+ class ChargerConnectionState(IntEnum):
51
+ """Connection state of the charger, as reported in ``statusOfChargerConnection``."""
52
+
53
+ NOT_CONNECTED = 0
54
+ DC_CONNECTED = 1
55
+ PLUGGED_NOT_CHARGING = 2
56
+ CHARGING = 3
57
+
58
+
59
+ """Every value :attr:`Battery.charger_connection_state` can take, including the
60
+ ``unknown`` fallback used for codes the API adds later."""
61
+ CHARGER_CONNECTION_STATES: tuple = tuple(state.name.lower() for state in ChargerConnectionState) + ("unknown",)
62
+
63
+
64
+ def charger_connection_state_name(status: Optional[int]) -> Optional[str]:
65
+ """Return the stable, lower case name for a raw ``statusOfChargerConnection`` code.
66
+
67
+ Returns ``None`` if the vehicle did not report a code and ``"unknown"`` for codes
68
+ that are not part of :class:`ChargerConnectionState`.
69
+ """
70
+ if status is None:
71
+ return None
72
+ try:
73
+ return ChargerConnectionState(status).name.lower()
74
+ except ValueError:
75
+ _LOGGER.debug("Unknown charger connection status %s", status)
76
+ return "unknown"
77
+
78
+
33
79
  DcChargingVoltLevels = [
34
80
  370,
35
81
  374,
@@ -155,7 +201,12 @@ class Battery(VehicleDataBase):
155
201
  """Charging status of the vehicle."""
156
202
 
157
203
  charger_connection_status: Optional[int] = None
158
- """Charger connection status of the vehicle."""
204
+ """Charger connection status of the vehicle as raw API code."""
205
+
206
+ charger_connection_state: Optional[str] = None
207
+ """Charger connection status of the vehicle as stable, lower case name.
208
+
209
+ Always one of :data:`CHARGER_CONNECTION_STATES` once the vehicle reported a code."""
159
210
 
160
211
  is_charger_connected: bool = False
161
212
  """Is the charger connected to the vehicle."""
@@ -210,7 +261,7 @@ class Battery(VehicleDataBase):
210
261
  charger_state = get_field_as_type(evStatus, "chargerState", int, log_missing=False)
211
262
  if charger_state is not None:
212
263
  retval["charging_status"] = (
213
- ChargingState[charger_state] if charger_state < len(ChargingState) else "UNKNOWN"
264
+ ChargingState[charger_state] if 0 <= charger_state < len(ChargingState) else "UNKNOWN"
214
265
  )
215
266
  retval["charging_status_raw"] = charger_state
216
267
  retval["is_charger_connected"] = (
@@ -223,6 +274,7 @@ class Battery(VehicleDataBase):
223
274
  charger_conn = get_field_as_type(evStatus, "statusOfChargerConnection", int, log_missing=False)
224
275
  if charger_conn is not None:
225
276
  retval["charger_connection_status"] = charger_conn
277
+ retval["charger_connection_state"] = charger_connection_state_name(charger_conn)
226
278
 
227
279
  charging_status = retval.get("charging_status")
228
280
  battery_percent = retval.get("remaining_battery_percent")
@@ -236,10 +288,13 @@ class Battery(VehicleDataBase):
236
288
  # Clamp to valid index range: 0 to len(DcChargingVoltLevels) - 1
237
289
  max_index = len(DcChargingVoltLevels) - 1
238
290
  battery_value = max(0, min(battery_value, max_index))
291
+ dc_charge_current = abs(dc_charge_i)
292
+ if str(vehicle_data.get("seriesCodeVs", "")).startswith(SERIES_CODE_PREFIX_SMART_5):
293
+ dc_charge_current /= DC_CHARGE_CURRENT_DIVIDER_SMART_5
239
294
  retval["charging_voltage"] = ValueWithUnit(DcChargingVoltLevels[battery_value], "V")
240
- retval["charging_current"] = ValueWithUnit(abs(dc_charge_i), "A")
295
+ retval["charging_current"] = ValueWithUnit(dc_charge_current, "A")
241
296
  retval["charging_power"] = ValueWithUnit(
242
- math.floor(abs(dc_charge_i) * DcChargingVoltLevels[battery_value]),
297
+ math.floor(dc_charge_current * DcChargingVoltLevels[battery_value]),
243
298
  "W",
244
299
  )
245
300
  else:
@@ -4,7 +4,13 @@ import datetime
4
4
  import logging
5
5
  from typing import Optional
6
6
 
7
- from pysmarthashtag.const import API_BASE_URL, API_BASE_URL_V2
7
+ from pysmarthashtag.const import (
8
+ API_BASE_URL,
9
+ API_BASE_URL_V2,
10
+ SERIES_CODE_PREFIX_SMART_1,
11
+ SERIES_CODE_PREFIX_SMART_3,
12
+ SERIES_CODE_PREFIX_SMART_5,
13
+ )
8
14
  from pysmarthashtag.models import ValueWithUnit, get_element_from_dict_maybe
9
15
  from pysmarthashtag.vehicle.battery import Battery
10
16
  from pysmarthashtag.vehicle.climate import Climate
@@ -90,13 +96,13 @@ class SmartVehicle:
90
96
  self.account = account
91
97
  self.data = {}
92
98
  self.combine_data(vehicle_base, vehicle_state, charging_settings, None, fetched_at)
93
- if self.data["seriesCodeVs"].startswith("HX"):
99
+ if self.data["seriesCodeVs"].startswith(SERIES_CODE_PREFIX_SMART_1):
94
100
  _LOGGER.debug("Selected Vehicle is Smart #1 use V1 API")
95
101
  self.base_url = API_BASE_URL
96
- elif self.data["seriesCodeVs"].startswith("HC"):
102
+ elif self.data["seriesCodeVs"].startswith(SERIES_CODE_PREFIX_SMART_3):
97
103
  _LOGGER.debug("Selected Vehicle is Smart #3 use V1 API")
98
104
  self.base_url = API_BASE_URL
99
- elif self.data["seriesCodeVs"].startswith("HY"):
105
+ elif self.data["seriesCodeVs"].startswith(SERIES_CODE_PREFIX_SMART_5):
100
106
  _LOGGER.debug("Selected Vehicle is Smart #5 use V2 API")
101
107
  self.base_url = API_BASE_URL_V2
102
108
  else:
@@ -7,7 +7,7 @@ black
7
7
  mypy
8
8
  types-setuptools
9
9
  pbr
10
- time_machine<3.3.0
10
+ time_machine<3.4.0
11
11
  pre-commit
12
12
  backports.zoneinfo;python_version<"3.9"
13
- ruff==0.15.22
13
+ ruff==0.16.1
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "0.12.0",
3
- "distance": 0,
4
- "node": "g5cb4e0529b5c1262227b520f2b68320ed5182ced",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-07-25"
8
- }
File without changes
File without changes
File without changes