Skip to content

Commit

Permalink
Add charge start/stop remote service
Browse files Browse the repository at this point in the history
  • Loading branch information
rikroe committed Feb 19, 2023
1 parent 9914701 commit 94765dd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 8 deletions.
1 change: 1 addition & 0 deletions bimmer_connected/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Regions(str, Enum):
VEHICLE_CHARGING_BASE_URL = "/eadrax-crccs/v1/vehicles/{vin}"
VEHICLE_CHARGING_SETTINGS_SET_URL = VEHICLE_CHARGING_BASE_URL + "/charging-settings"
VEHICLE_CHARGING_PROFILE_SET_URL = VEHICLE_CHARGING_BASE_URL + "/charging-profile"
VEHICLE_CHARGING_START_STOP_URL = VEHICLE_CHARGING_BASE_URL + "/{service_type}"

VEHICLE_IMAGE_URL = "/eadrax-ics/v3/presentation/vehicles/{vin}/images?carView={view}"
VEHICLE_POI_URL = "/eadrax-dcs/v1/send-to-car/send-to-car"
Expand Down
33 changes: 30 additions & 3 deletions bimmer_connected/vehicle/remote_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
REMOTE_SERVICE_URL,
VEHICLE_CHARGING_PROFILE_SET_URL,
VEHICLE_CHARGING_SETTINGS_SET_URL,
VEHICLE_CHARGING_START_STOP_URL,
VEHICLE_POI_URL,
)
from bimmer_connected.models import ChargingSettings, PointOfInterest, StrEnum
Expand All @@ -23,6 +24,7 @@
ChargingMode,
ChargingPreferences,
)
from bimmer_connected.vehicle.fuel_and_battery import ChargingState

if TYPE_CHECKING:
from bimmer_connected.vehicle import MyBMWVehicle
Expand All @@ -46,6 +48,7 @@ class ExecutionState(StrEnum):
DELIVERED = "DELIVERED"
EXECUTED = "EXECUTED"
ERROR = "ERROR"
IGNORED = "IGNORED"
UNKNOWN = "UNKNOWN"


Expand All @@ -58,7 +61,8 @@ class Services(StrEnum):
DOOR_UNLOCK = "door-unlock"
HORN = "horn-blow"
AIR_CONDITIONING = "climate-now"
CHARGE_NOW = "CHARGE_NOW"
CHARGE_START = "start-charging"
CHARGE_STOP = "stop-charging"
CHARGING_SETTINGS = "CHARGING_SETTINGS"
CHARGING_PROFILE = "CHARGING_PROFILE"
SEND_POI = "SEND_POI"
Expand All @@ -69,6 +73,8 @@ class Services(StrEnum):
Services.CHARGING_SETTINGS: VEHICLE_CHARGING_SETTINGS_SET_URL,
Services.CHARGING_PROFILE: VEHICLE_CHARGING_PROFILE_SET_URL,
Services.SEND_POI: VEHICLE_POI_URL,
Services.CHARGE_START: VEHICLE_CHARGING_START_STOP_URL,
Services.CHARGE_STOP: VEHICLE_CHARGING_START_STOP_URL,
}

CHARGING_MODE_TO_CHARGING_PREFERENCE = {
Expand Down Expand Up @@ -179,9 +185,30 @@ async def trigger_remote_horn(self) -> RemoteServiceStatus:
raise ValueError(f"Vehicle does not support remote service {repr(Services.HORN)}.")
return await self.trigger_remote_service(Services.HORN)

async def trigger_charge_now(self) -> RemoteServiceStatus:
async def trigger_charge_start(self) -> RemoteServiceStatus:
"""Trigger the vehicle to start charging."""
return await self.trigger_remote_service(Services.CHARGE_NOW, refresh=True)
if not self._vehicle.is_remote_charge_start_enabled:
raise ValueError(f"Vehicle does not support remote service {repr(Services.CHARGE_START)}.")

if not self._vehicle.fuel_and_battery.is_charger_connected:
_LOGGER.warning("Charger not connected, cannot start charging.")
return RemoteServiceStatus({"eventStatus": "IGNORED"})

return await self.trigger_remote_service(Services.CHARGE_START, refresh=True)

async def trigger_charge_stop(self) -> RemoteServiceStatus:
"""Trigger the vehicle to start charging."""
if not self._vehicle.is_remote_charge_stop_enabled:
raise ValueError(f"Vehicle does not support remote service {repr(Services.CHARGE_STOP)}.")

if not self._vehicle.fuel_and_battery.is_charger_connected:
_LOGGER.warning("Charger not connected, cannot stop charging.")
return RemoteServiceStatus({"eventStatus": "IGNORED"})
if self._vehicle.fuel_and_battery.charging_status != ChargingState.CHARGING:
_LOGGER.warning("Vehicle not charging, cannot stop charging.")
return RemoteServiceStatus({"eventStatus": "IGNORED"})

return await self.trigger_remote_service(Services.CHARGE_STOP, refresh=True)

async def trigger_remote_air_conditioning(self) -> RemoteServiceStatus:
"""Trigger the air conditioning to start."""
Expand Down
51 changes: 46 additions & 5 deletions test/test_remote_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from bimmer_connected.vehicle import remote_services
from bimmer_connected.vehicle.remote_services import ExecutionState, RemoteServiceStatus

from . import RESPONSE_DIR, VIN_F31, VIN_G01, VIN_G26, VIN_I01_NOREX, load_response
from . import RESPONSE_DIR, VIN_F31, VIN_G01, VIN_G26, VIN_I01_NOREX, VIN_I20, load_response
from .test_account import account_mock, get_mocked_account

_RESPONSE_INITIATED = RESPONSE_DIR / "remote_services" / "eadrax_service_initiated.json"
Expand Down Expand Up @@ -101,6 +101,9 @@ def remote_services_mock():
router.post(path__regex=r"/eadrax-vrccs/v3/presentation/remote-commands/.+/.+$").mock(
side_effect=service_trigger_sideeffect
)
router.post(path__regex=r"/eadrax-crccs/v1/vehicles/.+/(start|stop)-charging$").mock(
side_effect=service_trigger_sideeffect
)
router.post(path__regex=r"/eadrax-crccs/v1/vehicles/.+/charging-settings$").mock(
side_effect=service_trigger_sideeffect
)
Expand Down Expand Up @@ -140,7 +143,8 @@ def test_states():
"VEHICLE_FINDER": {"call": "trigger_remote_vehicle_finder", "refresh": False},
"HORN_BLOW": {"call": "trigger_remote_horn", "refresh": False},
"SEND_POI": {"call": "trigger_send_poi", "refresh": False, "args": [POI_DATA]},
"CHARGE_NOW": {"call": "trigger_charge_now", "refresh": True},
"CHARGE_START": {"call": "trigger_charge_start", "refresh": True},
"CHARGE_STOP": {"call": "trigger_charge_stop", "refresh": True},
"CHARGING_SETTINGS": {"call": "trigger_charging_settings_update", "refresh": True, "kwargs": CHARGING_SETTINGS},
}

Expand All @@ -152,7 +156,7 @@ async def test_trigger_remote_services():
"""Test executing a remote light flash."""

account = await get_mocked_account()
vehicle = account.get_vehicle(VIN_G26)
vehicle = account.get_vehicle(VIN_I20)

for service in ALL_SERVICES.values():
with mock.patch(
Expand Down Expand Up @@ -253,7 +257,7 @@ async def test_set_charging_profile():

@remote_services_mock()
@pytest.mark.asyncio
async def test_vehicles_without_enabled():
async def test_vehicles_without_enabled_services():
"""Test setting the charging profile on a car."""

account = await get_mocked_account()
Expand All @@ -265,12 +269,49 @@ async def test_vehicles_without_enabled():

for service in ALL_SERVICES.values():
with pytest.raises(ValueError):

await getattr(vehicle.remote_services, service["call"])(
*service.get("args", []), **service.get("kwargs", {})
)


@remote_services_mock()
@pytest.mark.asyncio
async def test_trigger_charge_start_stop_warnings(caplog):
"""Test if warnings are produced correctly with the charge start/stop services."""

account = await get_mocked_account()
vehicle = account.get_vehicle(VIN_I20)

fixture_not_connected = {
**vehicle.data["state"]["electricChargingState"],
"chargingStatus": "INVALID",
"isChargerConnected": False,
}
vehicle.update_state(vehicle.data, {"state": {"electricChargingState": fixture_not_connected}})

result = await vehicle.remote_services.trigger_charge_start()
assert result.state == ExecutionState.IGNORED
assert len([r for r in caplog.records if r.levelname == "WARNING" and "Charger not connected" in r.message]) == 1
caplog.clear()

result = await vehicle.remote_services.trigger_charge_stop()
assert result.state == ExecutionState.IGNORED
assert len([r for r in caplog.records if r.levelname == "WARNING" and "Charger not connected" in r.message]) == 1
caplog.clear()

fixture_connected_not_charging = {
**vehicle.data["state"]["electricChargingState"],
"chargingStatus": "WAITING_FOR_CHARGING",
"isChargerConnected": True,
}
vehicle.update_state(vehicle.data, {"state": {"electricChargingState": fixture_connected_not_charging}})

result = await vehicle.remote_services.trigger_charge_stop()
assert result.state == ExecutionState.IGNORED
assert len([r for r in caplog.records if r.levelname == "WARNING" and "Vehicle not charging" in r.message]) == 1
caplog.clear()


@remote_services_mock()
@pytest.mark.asyncio
async def test_get_remote_position():
Expand Down

0 comments on commit 94765dd

Please sign in to comment.