pySmartHashtag 0.14.2__tar.gz → 0.14.4__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.
- {pysmarthashtag-0.14.2/pySmartHashtag.egg-info → pysmarthashtag-0.14.4}/PKG-INFO +1 -1
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4/pySmartHashtag.egg-info}/PKG-INFO +1 -1
- pysmarthashtag-0.14.4/pySmartHashtag.egg-info/scm_version.json +8 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/climate.py +35 -4
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_charging_states.py +25 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_climate_zaf.py +50 -3
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/battery.py +8 -0
- pysmarthashtag-0.14.2/pySmartHashtag.egg-info/scm_version.json +0 -8
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.devcontainer.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.github/copilot-instructions.md +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.github/dependabot.yml +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.github/workflows/python-package.yml +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.github/workflows/python-publish.yml +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.gitignore +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.pre-commit-config.yaml +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.vscode/launch.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/.vscode/settings.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/AUTHORS +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/CODE_OF_CONDUCT.md +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/CONTRIBUTING.md +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/ChangeLog +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/LICENSE +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/README.md +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/SOURCES.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/dependency_links.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/requires.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/scm_file_list.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/top_level.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pyproject.toml +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/__init__.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/account.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/__init__.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/authentication.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/client.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/log_sanitizer.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/ssl_context.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/api/utils.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/cli.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/const.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/charging.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/horn_light.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/journal.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/lock.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/telematics.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/control/windows.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/models.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/__init__.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/common.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/conftest.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/Human_and_vehicle_relationship_does_not_exist.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/api_access.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/auth_context.url +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/auth_intermediate.url +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/auth_result.url +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/charging_success.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/climate_success.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/dc_charge_info.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/journal_response.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/journal_toggle_success.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/login_result.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/ota_response.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/soc_80.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/soc_90.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/token_expired.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/trackpoints_response.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_info.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_info2.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_info_dc_charging.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_response.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_result.json +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_account.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_actions.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_authentication_backoff.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_charging.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_charging_limit.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_cli.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_climate_status.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_dc_charge_info.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_dc_charging.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_endpoint_urls.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_horn_light_control.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_journal.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_journal_control.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_journal_pagination.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_lock_control.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_log_sanitizer.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_missing_fields.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_ota_info.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_send_destination.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_ssl_context.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_token_lifecycle.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_tracked_vins.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_trackpoints.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_vehicle_state.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_vin_headers.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_window_control.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/__init__.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/climate.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/journal.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/maintenance.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/position.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/running.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/safety.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/tires.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/trackpoints.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/vehicle.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/vehicle/vehicle_state.py +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/requirements-cli.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/requirements-test.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/requirements.txt +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/run.sh +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/setup.cfg +0 -0
- {pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/setup.py +0 -0
|
@@ -47,7 +47,7 @@ class RearHeatingLocation(str, Enum):
|
|
|
47
47
|
# ZAF service parameter keys of the Smart #5 comfort functions
|
|
48
48
|
ZAF_HEATING_KEYS = {
|
|
49
49
|
HeatingLocation.DRIVER_SEAT: "SH.11",
|
|
50
|
-
HeatingLocation.PASSENGER_SEAT: "SH.
|
|
50
|
+
HeatingLocation.PASSENGER_SEAT: "SH.12",
|
|
51
51
|
HeatingLocation.STEERING_WHEEL: "SW",
|
|
52
52
|
}
|
|
53
53
|
ZAF_VENTILATION_KEYS = {
|
|
@@ -56,9 +56,10 @@ ZAF_VENTILATION_KEYS = {
|
|
|
56
56
|
}
|
|
57
57
|
ZAF_REAR_HEATING_KEYS = {
|
|
58
58
|
RearHeatingLocation.REAR_LEFT: "SH.21",
|
|
59
|
-
RearHeatingLocation.REAR_RIGHT: "SH.
|
|
59
|
+
RearHeatingLocation.REAR_RIGHT: "SH.22",
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
ZAF_SEAT_HEATING_DURATION = "15"
|
|
62
|
+
ZAF_ALL_COMFORT_KEYS = ("AC", "SH.11", "SH.12", "SH.21", "SH.22", "SW", "DF", "SV.11", "SV.19")
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
class ClimateControll:
|
|
@@ -122,6 +123,10 @@ class ClimateControll:
|
|
|
122
123
|
else:
|
|
123
124
|
for key in ZAF_ALL_COMFORT_KEYS:
|
|
124
125
|
params[key] = "false"
|
|
126
|
+
return self._build_zaf_payload(params)
|
|
127
|
+
|
|
128
|
+
def _build_zaf_payload(self, params: dict[str, str]) -> str:
|
|
129
|
+
"""Wrap ZAF service parameters in the request body. Start and stop both use ``command: start``."""
|
|
125
130
|
payload = {
|
|
126
131
|
"serviceId": "ZAF",
|
|
127
132
|
"command": "start",
|
|
@@ -140,6 +145,13 @@ class ClimateControll:
|
|
|
140
145
|
}
|
|
141
146
|
return json.dumps(payload).replace(" ", "")
|
|
142
147
|
|
|
148
|
+
async def _send_zaf_command(self, params: str) -> bool:
|
|
149
|
+
"""Send a ZAF request and log what was sent and what the cloud answered."""
|
|
150
|
+
_LOGGER.debug("Sending ZAF command: %s", params)
|
|
151
|
+
result = await send_telematics_command(self.account, self.vin, params)
|
|
152
|
+
_LOGGER.debug("ZAF command result: %s", result)
|
|
153
|
+
return result is True
|
|
154
|
+
|
|
143
155
|
def set_ventilation_level(self, location: VentilationLocation, level: int):
|
|
144
156
|
"""Set the (Smart #5) seat ventilation level; selecting one clears the seat's heating."""
|
|
145
157
|
if not self.use_zaf:
|
|
@@ -214,7 +226,7 @@ class ClimateControll:
|
|
|
214
226
|
|
|
215
227
|
_LOGGER.debug("Setting climate conditioning: active=%s, temp=%s", active, temp)
|
|
216
228
|
if self.use_zaf:
|
|
217
|
-
return
|
|
229
|
+
return await self._send_zaf_command(self._get_zaf_payload(active))
|
|
218
230
|
return await self._send_rce_command(self._get_payload(active))
|
|
219
231
|
|
|
220
232
|
def _get_heating_payload(self, active: bool, levels: dict[HeatingLocation, int]) -> str:
|
|
@@ -256,8 +268,24 @@ class ClimateControll:
|
|
|
256
268
|
raise ValueError("Heating level must be between 1 and 3 to start heating.")
|
|
257
269
|
|
|
258
270
|
_LOGGER.debug("Setting heating: active=%s, levels=%s", active, levels)
|
|
271
|
+
if self.use_zaf:
|
|
272
|
+
return await self._send_zaf_command(self._get_zaf_heating_payload(active, levels))
|
|
259
273
|
return await self._send_rce_command(self._get_heating_payload(active, levels))
|
|
260
274
|
|
|
275
|
+
def _get_zaf_heating_payload(self, active: bool, levels: dict[HeatingLocation, int]) -> str:
|
|
276
|
+
"""Build the Smart #5 ZAF request that only touches the given seats / steering wheel."""
|
|
277
|
+
params: dict[str, str] = {"operation": "4"}
|
|
278
|
+
for loc, level in levels.items():
|
|
279
|
+
key = ZAF_HEATING_KEYS[loc]
|
|
280
|
+
if active:
|
|
281
|
+
params[key] = "true"
|
|
282
|
+
params[f"{key}.level"] = str(level)
|
|
283
|
+
if loc != HeatingLocation.STEERING_WHEEL:
|
|
284
|
+
params[f"{key}.duration"] = ZAF_SEAT_HEATING_DURATION
|
|
285
|
+
else:
|
|
286
|
+
params[key] = "false"
|
|
287
|
+
return self._build_zaf_payload(params)
|
|
288
|
+
|
|
261
289
|
def _get_defrost_payload(self, active: bool) -> str:
|
|
262
290
|
_payload = self.BASE_PAYLOAD_TEMPLATE.copy()
|
|
263
291
|
_payload["command"] = "start" if active else "stop"
|
|
@@ -277,6 +305,9 @@ class ClimateControll:
|
|
|
277
305
|
if not isinstance(active, bool):
|
|
278
306
|
raise TypeError("Defrost state must be a boolean")
|
|
279
307
|
_LOGGER.debug("Setting front windscreen defrost: active=%s", active)
|
|
308
|
+
if self.use_zaf:
|
|
309
|
+
params = {"operation": "4", "DF": "true" if active else "false"}
|
|
310
|
+
return await self._send_zaf_command(self._build_zaf_payload(params))
|
|
280
311
|
return await self._send_rce_command(self._get_defrost_payload(active))
|
|
281
312
|
|
|
282
313
|
async def _send_rce_command(self, params: str) -> bool:
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_charging_states.py
RENAMED
|
@@ -117,3 +117,28 @@ class TestChargerConnectionState:
|
|
|
117
117
|
assert battery is not None
|
|
118
118
|
assert battery.charger_connection_status is None
|
|
119
119
|
assert battery.charger_connection_state is None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class TestIsChargerConnected:
|
|
123
|
+
"""The plug state must not depend on the car currently drawing power (issue #441)."""
|
|
124
|
+
|
|
125
|
+
def test_plugged_not_charging(self):
|
|
126
|
+
"""Plugged in with chargerState NOT_CHARGING still counts as connected."""
|
|
127
|
+
battery = Battery.from_vehicle_data(create_vehicle_data("0", "2"))
|
|
128
|
+
|
|
129
|
+
assert battery is not None
|
|
130
|
+
assert battery.is_charger_connected is True
|
|
131
|
+
|
|
132
|
+
def test_not_connected(self):
|
|
133
|
+
"""Unplugged stays disconnected."""
|
|
134
|
+
battery = Battery.from_vehicle_data(create_vehicle_data("0", "0"))
|
|
135
|
+
|
|
136
|
+
assert battery is not None
|
|
137
|
+
assert battery.is_charger_connected is False
|
|
138
|
+
|
|
139
|
+
def test_unknown_connection_code_does_not_connect(self):
|
|
140
|
+
"""Codes the API adds later do not flip the plug state on their own."""
|
|
141
|
+
battery = Battery.from_vehicle_data(create_vehicle_data("0", "42"))
|
|
142
|
+
|
|
143
|
+
assert battery is not None
|
|
144
|
+
assert battery.is_charger_connected is False
|
|
@@ -31,7 +31,7 @@ def test_start_with_ventilation():
|
|
|
31
31
|
assert params["operation"] == "4"
|
|
32
32
|
assert params["AC"] == "true" and params["AC.temp"] == "21.0"
|
|
33
33
|
assert params["SV.11"] == "true" and params["SV.11.level"] == "2"
|
|
34
|
-
assert params["SH.
|
|
34
|
+
assert params["SH.12"] == "true" and params["SH.12.level"] == "3"
|
|
35
35
|
assert params["SH.11"] == "false" and "SH.11.level" not in params
|
|
36
36
|
assert params["SV.19"] == "false"
|
|
37
37
|
|
|
@@ -51,7 +51,7 @@ def test_stop_is_all_off():
|
|
|
51
51
|
params = _params(ctrl._get_zaf_payload(False))
|
|
52
52
|
assert json.loads(ctrl._get_zaf_payload(False))["command"] == "start"
|
|
53
53
|
assert params["operation"] == "4"
|
|
54
|
-
for key in ("AC", "SH.11", "SH.
|
|
54
|
+
for key in ("AC", "SH.11", "SH.12", "SH.21", "SH.22", "SW", "DF", "SV.11", "SV.19"):
|
|
55
55
|
assert params[key] == "false"
|
|
56
56
|
|
|
57
57
|
|
|
@@ -70,7 +70,7 @@ def test_rear_heating_and_all_keys_string_values():
|
|
|
70
70
|
ctrl.set_rear_heating_level(RearHeatingLocation.REAR_LEFT, 2)
|
|
71
71
|
params = _params(ctrl._get_zaf_payload(True))
|
|
72
72
|
assert params["SH.21"] == "true" and params["SH.21.level"] == "2"
|
|
73
|
-
assert params["SH.
|
|
73
|
+
assert params["SH.22"] == "false" and "SH.22.level" not in params
|
|
74
74
|
with pytest.raises(ValueError):
|
|
75
75
|
ctrl.set_rear_heating_level(RearHeatingLocation.REAR_LEFT, 4)
|
|
76
76
|
with pytest.raises(NotImplementedError):
|
|
@@ -92,3 +92,50 @@ async def test_stop_works_with_corrupt_temperature(monkeypatch):
|
|
|
92
92
|
with pytest.raises(TypeError):
|
|
93
93
|
await ctrl.set_climate_conditioning("garbage", True)
|
|
94
94
|
assert len(sent) == 1
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pytest.mark.asyncio
|
|
98
|
+
async def test_set_heating_on_smart5_uses_zaf_without_ac(monkeypatch):
|
|
99
|
+
sent = []
|
|
100
|
+
|
|
101
|
+
async def fake_send(account, vin, payload):
|
|
102
|
+
sent.append(payload)
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
monkeypatch.setattr("pysmarthashtag.control.climate.send_telematics_command", fake_send)
|
|
106
|
+
ctrl = _ctrl()
|
|
107
|
+
levels = {HeatingLocation.DRIVER_SEAT: 3, HeatingLocation.PASSENGER_SEAT: 2}
|
|
108
|
+
assert await ctrl.set_heating(True, levels) is True
|
|
109
|
+
payload = json.loads(sent[0])
|
|
110
|
+
assert payload["serviceId"] == "ZAF"
|
|
111
|
+
params = _params(sent[0])
|
|
112
|
+
assert params == {
|
|
113
|
+
"operation": "4",
|
|
114
|
+
"SH.11": "true",
|
|
115
|
+
"SH.11.level": "3",
|
|
116
|
+
"SH.11.duration": "15",
|
|
117
|
+
"SH.12": "true",
|
|
118
|
+
"SH.12.level": "2",
|
|
119
|
+
"SH.12.duration": "15",
|
|
120
|
+
}
|
|
121
|
+
assert ctrl.heating_levels[HeatingLocation.DRIVER_SEAT] == 0
|
|
122
|
+
|
|
123
|
+
assert await ctrl.set_heating(False, {HeatingLocation.STEERING_WHEEL: 0}) is True
|
|
124
|
+
assert _params(sent[1]) == {"operation": "4", "SW": "false"}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@pytest.mark.asyncio
|
|
128
|
+
async def test_set_defrost_on_smart5_uses_zaf(monkeypatch):
|
|
129
|
+
sent = []
|
|
130
|
+
|
|
131
|
+
async def fake_send(account, vin, payload):
|
|
132
|
+
sent.append(payload)
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
monkeypatch.setattr("pysmarthashtag.control.climate.send_telematics_command", fake_send)
|
|
136
|
+
ctrl = _ctrl()
|
|
137
|
+
assert await ctrl.set_defrost(True) is True
|
|
138
|
+
assert json.loads(sent[0])["serviceId"] == "ZAF"
|
|
139
|
+
assert _params(sent[0]) == {"operation": "4", "DF": "true"}
|
|
140
|
+
await ctrl.set_defrost(False)
|
|
141
|
+
assert _params(sent[1]) == {"operation": "4", "DF": "false"}
|
|
@@ -304,6 +304,14 @@ class Battery(VehicleDataBase):
|
|
|
304
304
|
if charger_conn is not None:
|
|
305
305
|
retval["charger_connection_status"] = charger_conn
|
|
306
306
|
retval["charger_connection_state"] = charger_connection_state_name(charger_conn)
|
|
307
|
+
# chargerState reports NOT_CHARGING for a plugged-in car that is not drawing power,
|
|
308
|
+
# so the connection code is the only reliable plug signal. See SmartHashtag issue #441.
|
|
309
|
+
if charger_conn in (
|
|
310
|
+
ChargerConnectionState.DC_CONNECTED,
|
|
311
|
+
ChargerConnectionState.PLUGGED_NOT_CHARGING,
|
|
312
|
+
ChargerConnectionState.CHARGING,
|
|
313
|
+
):
|
|
314
|
+
retval["is_charger_connected"] = True
|
|
307
315
|
|
|
308
316
|
charging_status = retval.get("charging_status")
|
|
309
317
|
battery_percent = retval.get("remaining_battery_percent")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pySmartHashtag.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/auth_context.url
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/auth_intermediate.url
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/charging_success.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/climate_success.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/dc_charge_info.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/journal_response.json
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/login_result.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/ota_response.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/token_expired.json
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_info.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_info2.json
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_response.json
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/replys/vehicle_result.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_authentication_backoff.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_horn_light_control.py
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_journal_control.py
RENAMED
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_journal_pagination.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_send_destination.py
RENAMED
|
File without changes
|
|
File without changes
|
{pysmarthashtag-0.14.2 → pysmarthashtag-0.14.4}/pysmarthashtag/tests/test_token_lifecycle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|