From 233589ccac203ae9f93479ce701e8c7533d3d570 Mon Sep 17 00:00:00 2001 From: Gerard Date: Sun, 25 Oct 2020 12:57:36 +0100 Subject: [PATCH] Select available state services from vehicles list (#217) Co-authored-by: rikroe --- bimmer_connected/state.py | 2 +- bimmer_connected/vehicle.py | 45 ++++++++++++++++++++++++++++++++++++- test/__init__.py | 7 ++++++ test/test_vehicle.py | 33 ++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/bimmer_connected/state.py b/bimmer_connected/state.py index bcd4493e..75c49175 100644 --- a/bimmer_connected/state.py +++ b/bimmer_connected/state.py @@ -94,7 +94,7 @@ def update_data(self) -> None: 'dlon': self._vehicle.observer_longitude, } - for service in self._url: + for service in self._vehicle.available_state_services: try: response = self._account.send_request( self._url[service].format(server=self._account.server_url, vin=self._vehicle.vin), diff --git a/bimmer_connected/vehicle.py b/bimmer_connected/vehicle.py index bbc99d7b..0b3e258c 100644 --- a/bimmer_connected/vehicle.py +++ b/bimmer_connected/vehicle.py @@ -6,7 +6,9 @@ from bimmer_connected.state import VehicleState from bimmer_connected.vehicle_status import WINDOWS, LIDS from bimmer_connected.remote_services import RemoteServices -from bimmer_connected.const import VEHICLE_IMAGE_URL +from bimmer_connected.const import VEHICLE_IMAGE_URL, SERVICE_ALL_TRIPS, SERVICE_CHARGING_PROFILE, \ + SERVICE_DESTINATIONS, SERVICE_EFFICIENCY, SERVICE_LAST_TRIP, SERVICE_NAVIGATION, \ + SERVICE_RANGEMAP, SERVICE_STATUS _LOGGER = logging.getLogger(__name__) @@ -107,6 +109,26 @@ def has_internal_combustion_engine(self) -> bool: In this case we can get the state of the gas tank.""" return self.drive_train in COMBUSTION_ENGINE_DRIVE_TRAINS + @property + def has_statistics_service(self) -> bool: + """Return True if statistics are available.""" + return self.attributes.get('statisticsAvailable') + + @property + def has_weekly_planner_service(self) -> bool: + """Return True if charging control (weekly planner) is available.""" + return self.attributes.get('chargingControl') == "WEEKLY_PLANNER" + + @property + def has_destination_service(self) -> bool: + """Return True if destinations are available.""" + return self.attributes.get('lastDestinations') == "SUPPORTED" + + @property + def has_rangemap_service(self) -> bool: + """Return True if rangemap (range circle) is available.""" + return self.attributes.get('rangeMap') == "RANGE_CIRCLE" + @property def drive_train_attributes(self) -> List[str]: """Get list of attributes available for the drive train of the vehicle. @@ -156,6 +178,27 @@ def available_attributes(self) -> List[str]: result += ['lights_parking', 'lids', 'windows'] return result + @property + def available_state_services(self) -> List: + """Get the list of all available state services for this vehicle.""" + result = [SERVICE_STATUS] + if self.has_statistics_service: + result += [SERVICE_LAST_TRIP, SERVICE_ALL_TRIPS] + + if self.has_weekly_planner_service: + result += [SERVICE_CHARGING_PROFILE] + + if self.has_destination_service: + result += [SERVICE_DESTINATIONS] + + if self.has_rangemap_service: + result += [SERVICE_RANGEMAP] + + if self.drive_train != DriveTrainType.CONVENTIONAL: + result += [SERVICE_EFFICIENCY, SERVICE_NAVIGATION] + + return result + def get_vehicle_image(self, width: int, height: int, direction: VehicleViewDirection) -> bytes: """Get a rendered image of the vehicle. diff --git a/test/__init__.py b/test/__init__.py index 2b97d3d3..88b555cf 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -88,6 +88,13 @@ 'vehicleCountry', # para not available in all vehicles ] +AVAILABLE_STATES_MAPPING = { + "statisticsAvailable": {True: ["LAST_TRIP", "ALL_TRIPS"]}, + "chargingControl": {"WEEKLY_PLANNER": ["CHARGING_PROFILE"]}, + "lastDestinations": {"SUPPORTED": ["DESTINATIONS"]}, + "rangeMap": {"RANGE_CIRCLE": ["RANGEMAP"]} +} + POI_DATA = { "lat": 37.4028943, "lon": -121.9700289, diff --git a/test/test_vehicle.py b/test/test_vehicle.py index 0e786832..693d4412 100644 --- a/test/test_vehicle.py +++ b/test/test_vehicle.py @@ -3,7 +3,7 @@ from unittest import mock from test import load_response_json, BackendMock, TEST_USERNAME, TEST_PASSWORD, TEST_REGION, \ G31_VIN, F48_VIN, I01_VIN, I01_NOREX_VIN, F15_VIN, F45_VIN, F31_VIN, TEST_VEHICLE_DATA, \ - ATTRIBUTE_MAPPING, MISSING_ATTRIBUTES, ADDITIONAL_ATTRIBUTES, G30_PHEV_OS7_VIN + ATTRIBUTE_MAPPING, MISSING_ATTRIBUTES, ADDITIONAL_ATTRIBUTES, G30_PHEV_OS7_VIN, AVAILABLE_STATES_MAPPING from bimmer_connected.vehicle import ConnectedDriveVehicle, DriveTrainType from bimmer_connected.account import ConnectedDriveAccount @@ -34,6 +34,10 @@ def test_parsing_attributes(self): self.assertIsNotNone(vehicle.has_internal_combustion_engine) self.assertIsNotNone(vehicle.has_hv_battery) self.assertIsNotNone(vehicle.drive_train_attributes) + self.assertIsNotNone(vehicle.has_statistics_service) + self.assertIsNotNone(vehicle.has_weekly_planner_service) + self.assertIsNotNone(vehicle.has_destination_service) + self.assertIsNotNone(vehicle.has_rangemap_service) def test_drive_train_attributes(self): """Test parsing different attributes of the vehicle.""" @@ -73,3 +77,30 @@ def test_available_attributes(self): if a not in MISSING_ATTRIBUTES]) expected_attributes = sorted([a for a in vehicle.available_attributes if a not in ADDITIONAL_ATTRIBUTES]) self.assertListEqual(existing_attributes, expected_attributes) + + def test_available_state_services(self): + """Check that available_attributes returns exactly the arguments we have in our test data.""" + backend_mock = BackendMock() + with mock.patch('bimmer_connected.account.requests', new=backend_mock): + account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION) + + vehicles = load_response_json('vehicles.json') + + for test_vehicle in vehicles['vehicles']: + vehicle = account.get_vehicle(test_vehicle['vin']) + print(vehicle.name) + + services_to_check = { + k: v + for k, v in test_vehicle.items() + if k in list(AVAILABLE_STATES_MAPPING) + } + + available_services = ['STATUS'] + for key, value in services_to_check.items(): + if AVAILABLE_STATES_MAPPING[key].get(value): + available_services += AVAILABLE_STATES_MAPPING[key][value] + if vehicle.drive_train != DriveTrainType.CONVENTIONAL: + available_services += ['EFFICIENCY', 'NAVIGATION'] + + self.assertListEqual(sorted(vehicle.available_state_services), sorted(available_services))