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))
def test_parsing_attributes(self): """Test parsing different attributes of the vehicle. Just make sure parsing that no exception is raised. """ backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): backend_mock.setup_default_vehicles() account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_COUNTRY) account.update_vehicle_states() for vehicle in account.vehicles: print('testing vehicle {}'.format(vehicle.name)) state = vehicle.state self.assertIsNotNone(state.lids) self.assertIsNotNone(state.is_vehicle_tracking_enabled) self.assertIsNotNone(state.windows) self.assertIsNotNone(state.condition_based_services) if vehicle.vin != F32_VIN: # these values are not available in the F32 self.assertIsNotNone(state.door_lock_state) self.assertIsNotNone(state.timestamp) self.assertIsNotNone(state.mileage) self.assertIsNotNone(state.remaining_fuel) self.assertIsNotNone(state.all_windows_closed)
def send_poi_from_address(args) -> None: """Create Point of Interest from OSM Nominatim and send to car.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) address = [(str(' '.join(args.address)))] try: response = requests.get("https://nominatim.openstreetmap.org", params={ "q": address, "format": "json", "addressdetails": 1, "limit": 1 }).json()[0] except IndexError: print('\nAddress not found') sys.exit(1) address = response.get("address", {}) city = address.get("city") town = address.get("town") poi_data = dict( lat=response["lat"], lon=response["lon"], name=args.name, street=address.get("road"), city=town if city is None and town is not None else None, postal_code=address.get("postcode"), country=address.get("country") ) vehicle.remote_services.trigger_send_poi(poi_data)
def send_poi(args) -> None: """Send Point Of Interest to car.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) poi = PointOfInterest(args.latitude, args.longitude, name=args.name, street=args.street, city=args.city, postalCode=args.postalcode, country=args.country) vehicle.send_poi(poi)
def test_parsing_attributes(self): """Test parsing different attributes of the vehicle. Just make sure parsing that no exception is raised. """ backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): backend_mock.setup_default_vehicles() account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION) account.update_vehicle_states() for vehicle in account.vehicles: print('testing vehicle {}'.format(vehicle.name)) state = vehicle.state self.assertIsNotNone(state.lids) self.assertIsNotNone(state.is_vehicle_tracking_enabled) self.assertIsNotNone(state.windows) self.assertIsNotNone(state.condition_based_services) self.assertIsNotNone(state.door_lock_state) self.assertIsNotNone(state.timestamp) self.assertGreater(state.mileage, 0) self.assertGreater(state.remaining_range_total, 0) self.assertIsNotNone(state.remaining_fuel) self.assertIsNotNone(state.all_windows_closed) self.assertEqual(0, len(state.check_control_messages)) self.assertFalse(state.has_check_control_messages) for attrib in vehicle.drive_train_attributes: print(attrib, getattr(state, attrib)) # charging_time is only set when charging if attrib != 'charging_time_remaining': self.assertIsNotNone(getattr(state, attrib), attrib)
def get_status(args) -> None: """Get the vehicle status.""" if args.json: for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) if args.lat and args.lng: for vehicle in account.vehicles: vehicle.set_observer_position(args.lat, args.lng) account.update_vehicle_states() if args.json: print(to_json(account.vehicles)) else: print('Found {} vehicles: {}'.format( len(account.vehicles), ','.join([v.name for v in account.vehicles]))) for vehicle in account.vehicles: print('VIN: {}'.format(vehicle.vin)) print('Mileage: {}'.format(vehicle.status.mileage)) print('Vehicle data:') print(to_json(vehicle, indent=4))
def test_invalid_send_response(self): """Test parsing the results of an invalid request""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, Regions.REST_OF_WORLD) with self.assertRaises(IOError): account.send_request('invalid_url')
def send_message(args) -> None: """Send a message to car.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) msg_data = dict(text=args.text, subject=args.subject) vehicle.remote_services.trigger_send_message(msg_data)
def test_us_header(self): """Test if the host is set correctly in the request.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, Regions.NORTH_AMERICA) request = [r for r in backend_mock.last_request if 'oauth' in r.url][0] self.assertEqual('b2vapi.bmwgroup.us', request.headers['Host'])
def __init__(self, username: str, password: str, country: str, name: str) -> None: """Constructor.""" from bimmer_connected.account import ConnectedDriveAccount self.account = ConnectedDriveAccount(username, password, country) self.name = name self._update_listeners = []
def test_parsing_of_lsc_type(self): """Test parsing the lsc type field.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION) for vehicle in account.vehicles: self.assertIsNotNone(vehicle.lsc_type)
def __init__(self, username: str, password: str, region_str: str, name: str, read_only) -> None: """Initialize account.""" region = get_region_from_name(region_str) self.read_only = read_only self.account = ConnectedDriveAccount(username, password, region) self.name = name self._update_listeners = []
def image(args) -> None: """Download a rendered image of the vehicle.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) with open('image.png', 'wb') as output_file: image_data = vehicle.get_vehicle_image(400, 400, VehicleViewDirection.FRONT) output_file.write(image_data) print('vehicle image saved to image.png')
def test_update_data_error(self): """Test with server returning an error.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_COUNTRY) vehicle = account.get_vehicle(G31_VIN) with self.assertRaises(IOError): vehicle.update_state()
def get_status(args) -> None: """Get the vehicle status.""" _LOGGER = logging.getLogger(__name__) for rtime in [1, 1, 1, 5, 30, 60, None]: try: account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) except IOError as e: if rtime is not None: _LOGGER.debug('Request failed. Retry in {} seconds.'.format(rtime)) time.sleep(rtime) continue else: raise break if args.lat and args.lng: for vehicle in account.vehicles: vehicle.set_observer_position(args.lat, args.lng) for rtime in [1, 1, 1, 5, 30, 60, None]: try: account.update_vehicle_states() except IOError as e: if rtime is not None: _LOGGER.debug('Request failed. Retry in {} seconds.'.format(rtime)) time.sleep(rtime) continue else: raise break dict_resp = [] for vehicle in account.vehicles: for rtime in [1, 1, 1, 5, 30, 60, None]: try: vehicle.last_trip.update_data() except IOError as e: if rtime is not None: _LOGGER.debug('Request failed. Retry in {} seconds.'.format(rtime)) time.sleep(rtime) continue else: raise break dict_resp.append({ 'vin': vehicle.vin, 'mileage': vehicle.state.mileage, 'properties': vehicle.attributes, 'status': vehicle.state.attributes, 'last_trip': vehicle.last_trip.attributes }) print(json.dumps(dict_resp, indent=4))
def test_invalid_password_china(self): """Test parsing the results of an invalid password.""" with requests_mock.Mocker(adapter=get_base_adapter()) as mock: mock.post( "/eadrax-coas/v1/login/pwd", json=load_response(RESPONSE_DIR / "auth" / "auth_cn_login_error.json"), status_code=422, ) with self.assertRaises(HTTPError): ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, get_region_from_name("china"))
def light_flash(args) -> None: """Trigger the vehicle to flash its lights.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) if not vehicle: valid_vins = ", ".join(v.vin for v in account.vehicles) print('Error: Could not find vehicle for VIN "{}". Valid VINs are: {}'.format(args.vin, valid_vins)) return status = vehicle.remote_services.trigger_remote_light_flash() print(status.state)
def test_invalid_password(self): """Test parsing the results of an invalid password.""" with requests_mock.Mocker(adapter=get_base_adapter()) as mock: mock.post( "/gcdm/oauth/authenticate", json=load_response(RESPONSE_DIR / "auth" / "auth_error_wrong_password.json"), status_code=401, ) with self.assertRaises(HTTPError): ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION)
def test_token_vehicles(self): """Test getting backend token and vehicle list.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_COUNTRY) self.assertIsNotNone(account._oauth_token) self.assertEqual(5, len(account.vehicles)) vin = 'G31_NBTEvo_VIN' vehicle = account.get_vehicle(vin) self.assertEqual(vehicle.vin, vin)
def test_vehicle_search_case(self): """Check if the search for the vehicle by VIN is NOT case sensitive.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION) vin = account.vehicles[1].vin self.assertEqual(vin, account.get_vehicle(vin).vin) self.assertEqual(vin, account.get_vehicle(vin.lower()).vin) self.assertEqual(vin, account.get_vehicle(vin.upper()).vin)
def test_server_error(self): """Test parsing the results of a server error.""" with requests_mock.Mocker(adapter=get_base_adapter()) as mock: mock.post( "/gcdm/oauth/authenticate", text=load_response(RESPONSE_DIR / "auth" / "auth_error_internal_error.txt"), status_code=500, ) with self.assertRaises(HTTPError): ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION)
def test_statistics_available(self): """Test parsing the statistics_available field.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION) g31 = account.get_vehicle(G31_VIN) f45 = account.get_vehicle(F45_VIN) self.assertFalse(g31.statistics_available) self.assertTrue(f45.statistics_available)
def test_set_observer_value(self): """Test set_observer_position with valid arguments.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, Regions.REST_OF_WORLD) account.set_observer_position(1.0, 2.0) for vehicle in account.vehicles: self.assertEqual(vehicle.observer_latitude, 1.0) self.assertEqual(vehicle.observer_longitude, 2.0)
def test_token_vehicles(self): """Test getting backend token and vehicle list.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, Regions.REST_OF_WORLD) self.assertIsNotNone(account._oauth_token) self.assertEqual(5, len(account.vehicles)) vehicle = account.get_vehicle(G31_VIN) self.assertEqual(G31_VIN, vehicle.vin) self.assertIsNone(account.get_vehicle('invalid_vin'))
def __init__(self, username: str, password: str, region_str: str, name: str) -> None: """Constructor.""" from bimmer_connected.account import ConnectedDriveAccount from bimmer_connected.country_selector import get_region_from_name region = get_region_from_name(region_str) self.account = ConnectedDriveAccount(username, password, region) self.name = name self._update_listeners = []
def test_parsing_attributes(self): """Test parsing different attributes of the vehicle.""" backend_mock = BackendMock() with mock.patch('bimmer_connected.account.requests', new=backend_mock): account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_COUNTRY) for vehicle in account.vehicles: self.assertIsNotNone(vehicle.drive_train) self.assertIsNotNone(vehicle.name) self.assertIsNotNone(vehicle.has_rex)
def get_all_trips(args) -> None: """Downlad statistics of all trips""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) if not vehicle: valid_vins = ", ".join(v.vin for v in account.vehicles) print('Error: Could not find vehicle for VIN "{}". Valid VINs are: {}'. format(args.vin, valid_vins)) return print(json.dumps(vehicle.get_vehicle_alltrips(), indent=4))
def get_vehicle_rangemap(args) -> None: """Get a set of lat/lon points defining a polygon bounding vehicle range""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) if not vehicle: valid_vins = ", ".join(v.vin for v in account.vehicles) print('Error: Could not find vehicle for VIN "{}". Valid VINs are: {}'. format(args.vin, valid_vins)) return print(json.dumps(vehicle.get_vehicle_rangemap(), indent=4))
def get_vehicle_destinations(args) -> None: """Shows the destinations you've previously sent to the car.""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) if not vehicle: valid_vins = ", ".join(v.vin for v in account.vehicles) print('Error: Could not find vehicle for VIN "{}". Valid VINs are: {}'. format(args.vin, valid_vins)) return print(json.dumps(vehicle.get_vehicle_destinations(), indent=4))
def get_vehicle_charging_profile(args) -> None: """Download one-time and weekly charging schedules and settings""" account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region)) vehicle = account.get_vehicle(args.vin) if not vehicle: valid_vins = ", ".join(v.vin for v in account.vehicles) print('Error: Could not find vehicle for VIN "{}". Valid VINs are: {}'. format(args.vin, valid_vins)) return print(json.dumps(vehicle.get_vehicle_charging_profile(), indent=4))