Пример #1
0
class BMWConnectedDriveAccount:
    """Representation of a BMW vehicle."""

    def __init__(self, username: str, password: str, region_str: str,
                 name: str, read_only) -> 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.read_only = read_only
        self.account = ConnectedDriveAccount(username, password, region)
        self.name = name
        self._update_listeners = []

    def update(self, *_):
        """Update the state of all vehicles.

        Notify all listeners about the update.
        """
        _LOGGER.debug('Updating vehicle state for account %s, '
                      'notifying %d listeners',
                      self.name, len(self._update_listeners))
        try:
            self.account.update_vehicle_states()
            for listener in self._update_listeners:
                listener()
        except IOError as exception:
            _LOGGER.error('Error updating the vehicle state.')
            _LOGGER.exception(exception)

    def add_update_listener(self, listener):
        """Add a listener for update notifications."""
        self._update_listeners.append(listener)
    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 = []
Пример #3
0
    def test_check_control_messages(self):
        """Test handling of check control messages.

        F48 is the only vehicle with active Check Control Messages, so we only expect to get something there.
        """
        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(vehicle.name, vehicle.vin)
                if vehicle.vin == F48_VIN:
                    self.assertTrue(vehicle.state.has_check_control_messages)
                else:
                    self.assertFalse(vehicle.state.has_check_control_messages)
Пример #4
0
    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 test_trigger_remote_services(self):
        """Test executing a remote light flash."""
        remote_services._POLLING_CYCLE = 0
        remote_services._UPDATE_AFTER_REMOTE_SERVICE_DELAY = 0

        services = [('RLF', 'trigger_remote_light_flash', False),
                    ('RDL', 'trigger_remote_door_lock', True),
                    ('RDU', 'trigger_remote_door_unlock', True),
                    ('RCN', 'trigger_remote_air_conditioning', True),
                    ('RHB', 'trigger_remote_horn', False)]

        for service, call, triggers_update in services:
            backend_mock = BackendMock()
            backend_mock.setup_default_vehicles()

            backend_mock.add_response(
                r'.*/api/vehicle/remoteservices/v1/{vin}/{service}'.format(
                    vin=G31_VIN, service=service),
                data_files=['G31_NBTevo/RLF_INITIAL_RESPONSE.json'])

            backend_mock.add_response(
                '.*/api/vehicle/remoteservices/v1/{vin}/state/execution'.
                format(vin=G31_VIN),
                data_files=[
                    'G31_NBTevo/RLF_PENDING.json',
                    'G31_NBTevo/RLF_DELIVERED.json',
                    'G31_NBTevo/RLF_EXECUTED.json'
                ])

            with mock.patch('bimmer_connected.account.requests',
                            new=backend_mock):
                account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD,
                                                TEST_COUNTRY)
                mock_listener = mock.Mock(return_value=None)
                account.add_update_listener(mock_listener)
                vehicle = account.get_vehicle(G31_VIN)

                response = getattr(vehicle.remote_services, call)()
                self.assertEqual(ExecutionState.EXECUTED, response.state)

                if triggers_update:
                    mock_listener.assert_called_once_with()
                else:
                    mock_listener.assert_not_called()
Пример #6
0
 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'])
Пример #7
0
 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)
Пример #8
0
 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"))
Пример #9
0
 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_trigger_remote_services(self):
        """Test executing a remote light flash."""
        remote_services._POLLING_CYCLE = 0
        remote_services._UPDATE_AFTER_REMOTE_SERVICE_DELAY = 0

        services = [
            ('LIGHT_FLASH', 'trigger_remote_light_flash', False),
            ('DOOR_LOCK', 'trigger_remote_door_lock', True),
            ('DOOR_UNLOCK', 'trigger_remote_door_unlock', True),
            ('CLIMATE_NOW', 'trigger_remote_air_conditioning', True),
            ('HORN_BLOW', 'trigger_remote_horn', False)
        ]

        for service, call, triggers_update in services:
            backend_mock = BackendMock()
            backend_mock.setup_default_vehicles()

            backend_mock.add_response('https://.+/webapi/v1/user/vehicles/{vin}/executeService'.format(vin=G31_VIN),
                                      data_files=[_RESPONSE_INITIATED])

            backend_mock.add_response(
                r'https://.+/webapi/v1/user/vehicles/{vin}/serviceExecutionStatus\?serviceType={service_type}'.format(
                    vin=G31_VIN, service_type=service),
                data_files=[
                    _RESPONSE_PENDING,
                    _RESPONSE_PENDING,
                    _RESPONSE_DELIVERED,
                    _RESPONSE_DELIVERED,
                    _RESPONSE_EXECUTED])

            with mock.patch('bimmer_connected.account.requests', new=backend_mock):
                account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD, TEST_REGION)
                mock_listener = mock.Mock(return_value=None)
                account.add_update_listener(mock_listener)
                vehicle = account.get_vehicle(G31_VIN)

                response = getattr(vehicle.remote_services, call)()
                self.assertEqual(ExecutionState.EXECUTED, response.state)

                if triggers_update:
                    mock_listener.assert_called_once_with()
                else:
                    mock_listener.assert_not_called()
Пример #11
0
def get_status(args) -> None:
    """Get the vehicle status."""
    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()

    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.state.mileage))
        print('vehicle properties:')
        print(json.dumps(vehicle.attributes, indent=4))
        print('vehicle status:')
        print(json.dumps(vehicle.state.attributes, indent=4))
Пример #12
0
    def test_get_remote_service_status(self):
        """Test get_remove_service_status method."""
        backend_mock = BackendMock()

        with mock.patch('bimmer_connected.account.requests', new=backend_mock):
            account = ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD,
                                            TEST_REGION)
            vehicle = account.get_vehicle(G31_VIN)
            with self.assertRaises(IOError):
                vehicle.remote_services._get_remote_service_status(
                    remote_services._Services.REMOTE_LIGHT_FLASH)

            backend_mock.add_response(
                r'https://.+/webapi/v1/user/vehicles/{vin}/serviceExecutionStatus\?.+'
                .format(vin=G31_VIN),
                data_files=['G31_NBTevo/flash_executed.json'])
            status = vehicle.remote_services._get_remote_service_status(
                remote_services._Services.REMOTE_LIGHT_FLASH)
            self.assertEqual(ExecutionState.EXECUTED, status.state)
Пример #13
0
    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_invalid_auth(self):
     """Test if the host is set correctly in the request."""
     backend_mock = BackendMock()
     with mock.patch('bimmer_connected.account.requests', new=backend_mock):
         with mock.patch('bimmer_connected.account.get_gcdm_oauth_endpoint'
                         ) as mocked_endpoint:
             mocked_endpoint(
             ).return_value = 'customer.bmwgroup.com/gcdm/invalid'
             with self.assertRaises(OSError):
                 ConnectedDriveAccount(TEST_USERNAME, TEST_PASSWORD,
                                       Regions.REST_OF_WORLD)
Пример #15
0
    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)
Пример #16
0
class BMWConnectedDriveAccount:
    """Representation of a BMW vehicle."""

    def __init__(
        self, username: str, password: str, region_str: str, name: str, read_only
    ) -> 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.read_only = read_only
        self.account = ConnectedDriveAccount(username, password, region)
        self.name = name
        self._update_listeners = []

    def update(self, *_):
        """Update the state of all vehicles.

        Notify all listeners about the update.
        """
        _LOGGER.debug(
            "Updating vehicle state for account %s, notifying %d listeners",
            self.name,
            len(self._update_listeners),
        )
        try:
            self.account.update_vehicle_states()
            for listener in self._update_listeners:
                listener()
        except IOError as exception:
            _LOGGER.error(
                "Could not connect to the BMW Connected Drive portal. "
                "The vehicle state could not be updated."
            )
            _LOGGER.exception(exception)

    def add_update_listener(self, listener):
        """Add a listener for update notifications."""
        self._update_listeners.append(listener)
Пример #17
0
    def test_available_attributes(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)

        for vin, dirname in TEST_VEHICLE_DATA.items():
            vehicle = account.get_vehicle(vin)
            print(vehicle.name)
            status_data = load_response_json('{}/status.json'.format(dirname))
            existing_attributes = status_data['vehicleStatus'].keys()
            existing_attributes = sorted([
                ATTRIBUTE_MAPPING.get(a, a) for a in existing_attributes
                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)
Пример #18
0
    def test_drive_train_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_REGION)

        for vehicle in account.vehicles:
            print(vehicle.name)
            self.assertEqual(vehicle.vin in [G31_VIN, F48_VIN, F15_VIN, I01_VIN, F45_VIN],
                             vehicle.has_internal_combustion_engine)
            self.assertEqual(vehicle.vin in [I01_VIN, I01_NOREX_VIN, F45_VIN],
                             vehicle.has_hv_battery)
 def test_charging_end_time(self):
     """Test if the parsing of mileage and range is working"""
     account = get_mocked_account()
     status = account.get_vehicle(VIN_G08).status
     self.assertEqual(
         datetime.datetime(2011,
                           11,
                           29,
                           4,
                           1,
                           tzinfo=ConnectedDriveAccount.timezone()),
         status.charging_end_time)
Пример #20
0
    def test_parsing_attributes(self):
        """Test parsing different attributes of the vehicle.

        Just make sure parsing that no exception is raised and we get not-None values.
        """
        backend_mock = BackendMock()
        # list of attributes that are ignored at the moment
        ignored_attributes = [
            ATTRIBUTE_MAPPING.get(a, a) for a in MISSING_ATTRIBUTES
        ]
        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(vehicle.name)
                for attribute in (a for a in vehicle.available_attributes
                                  if a not in ignored_attributes):
                    self.assertIsNotNone(getattr(vehicle.state, attribute),
                                         attribute)
def main():
    """Store all responses from the backend in log files for later analysis."""
    logging.basicConfig(level=logging.WARNING)
    print('Generating vehicle fingerprint...')

    parser = argparse.ArgumentParser()
    parser.add_argument('username')
    parser.add_argument('password')
    parser.add_argument('country')
    args = parser.parse_args()

    if os.path.exists(FINGERPRINT_DIR):
        shutil.rmtree(FINGERPRINT_DIR)

    os.mkdir(FINGERPRINT_DIR)

    account = ConnectedDriveAccount(args.username,
                                    args.password,
                                    args.country,
                                    log_responses=FINGERPRINT_DIR)
    account.update_vehicle_states()

    print('fingerprint of the vehicles written to {}'.format(FINGERPRINT_DIR))
Пример #22
0
    def __init__(
        self,
        username: str,
        password: str,
        region_str: str,
        name: str,
        read_only: bool,
        lat=None,
        lon=None,
    ) -> 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 = []

        # Set observer position once for older cars to be in range for
        # GPS position (pre-7/2014, <2km) and get new data from API
        if lat and lon:
            self.account.set_observer_position(lat, lon)
            self.account.update_vehicle_states()
Пример #23
0
    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_REGION)

        for vehicle in account.vehicles:
            print(vehicle.name)
            self.assertIsNotNone(vehicle.drive_train)
            self.assertIsNotNone(vehicle.name)
            self.assertIsNotNone(vehicle.has_internal_combustion_engine)
            self.assertIsNotNone(vehicle.has_hv_battery)
            self.assertIsNotNone(vehicle.drive_train_attributes)
            self.assertIsNotNone(vehicle.statistics_available)
Пример #24
0
def main():
    """Main function."""
    logging.basicConfig(level=logging.DEBUG)

    parser = argparse.ArgumentParser()
    parser.add_argument('username')
    parser.add_argument('password')
    parser.add_argument('country')
    args = parser.parse_args()

    account = ConnectedDriveAccount(args.username, args.password, args.country)
    account.update_vehicle_states()

    print('Found {} vehicles: {}'.format(
        len(account.vehicles),
        ','.join([v.modelName for v in account.vehicles])))

    for vehicle in account.vehicles:
        print('VIN: {}'.format(vehicle.vin))
        print('mileage: {}'.format(vehicle.state.mileage))
        print('Response from the server:')
        print(json.dumps(vehicle.state.attributes, indent=4))
        print('vehicle specs:')
        print(json.dumps(vehicle.specs.attributes, indent=4))
Пример #25
0
    def test_set_observer_some_none(self):
        """Test set_observer_position with invalid arguments."""
        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(ValueError):
                account.set_observer_position(None, 2.0)

            with self.assertRaises(ValueError):
                account.set_observer_position(1.0, None)
Пример #26
0
class BMWConnectedDriveAccount:
    """Representation of a BMW vehicle."""

    def __init__(
        self,
        username: str,
        password: str,
        region_str: str,
        name: str,
        read_only: bool,
        lat=None,
        lon=None,
    ) -> 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 = []

        # Set observer position once for older cars to be in range for
        # GPS position (pre-7/2014, <2km) and get new data from API
        if lat and lon:
            self.account.set_observer_position(lat, lon)
            self.account.update_vehicle_states()

    def update(self, *_):
        """Update the state of all vehicles.

        Notify all listeners about the update.
        """
        _LOGGER.debug(
            "Updating vehicle state for account %s, notifying %d listeners",
            self.name,
            len(self._update_listeners),
        )
        try:
            self.account.update_vehicle_states()
            for listener in self._update_listeners:
                listener()
        except OSError as exception:
            _LOGGER.error(
                "Could not connect to the BMW Connected Drive portal. "
                "The vehicle state could not be updated"
            )
            _LOGGER.exception(exception)

    def add_update_listener(self, listener):
        """Add a listener for update notifications."""
        self._update_listeners.append(listener)
Пример #27
0
def fingerprint(args) -> None:
    """Save the vehicle fingerprint."""
    time_dir = Path.home() / 'vehicle_fingerprint' / time.strftime("%Y-%m-%d_%H-%M-%S")
    time_dir.mkdir(parents=True)

    account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region),
                                    log_responses=time_dir)
    account.set_observer_position(args.lat, args.lng)
    if args.lat and args.lng:
        for vehicle in account.vehicles:
            vehicle.set_observer_position(args.lat, args.lng)
    account.update_vehicle_states()

    print('fingerprint of the vehicles written to {}'.format(time_dir))
Пример #28
0
def fingerprint(args) -> None:
    """Save the vehicle fingerprint."""
    time_str = time.strftime("%Y-%m-%d_%H-%M-%S")
    time_dir = os.path.join(FINGERPRINT_DIR, time_str)
    os.makedirs(time_dir)

    account = ConnectedDriveAccount(args.username, args.password, get_region_from_name(args.region),
                                    log_responses=time_dir)
    account.set_observer_position(args.lat, args.lng)
    if args.lat and args.lng:
        for vehicle in account.vehicles:
            vehicle.set_observer_position(args.lat, args.lng)
    account.update_vehicle_states()

    print('fingerprint of the vehicles written to {}'.format(time_dir))
Пример #29
0
def fingerprint(args) -> None:
    """Save the vehicle fingerprint."""
    time_dir = Path.home() / 'vehicle_fingerprint' / time.strftime(
        "%Y-%m-%d_%H-%M-%S")
    time_dir.mkdir(parents=True)

    account = ConnectedDriveAccount(args.username,
                                    args.password,
                                    get_region_from_name(args.region),
                                    log_responses=time_dir)

    if args.lat and args.lng:
        for vehicle in account.vehicles:
            vehicle.set_observer_position(args.lat, args.lng)
    # doesn't work anymore
    # account.update_vehicle_states()

    # Patching in new My BMW endpoints for fingerprinting
    server_url = get_server_url(get_region_from_name(args.region))

    for vehicle in account.vehicles:
        if vehicle.drive_train in HV_BATTERY_DRIVE_TRAINS:
            print("Getting 'charging-sessions' for {}".format(vehicle.vin))
            account.send_request(
                "https://{}/eadrax-chs/v1/charging-sessions".format(
                    server_url),
                params={
                    "vin": vehicle.vin,
                    "maxResults": 40,
                    "include_date_picker": "true"
                },
                logfilename="charging-sessions")

            print("Getting 'charging-statistics' for {}".format(vehicle.vin))
            account.send_request(
                "https://{}/eadrax-chs/v1/charging-statistics".format(
                    server_url),
                params={
                    "vin": vehicle.vin,
                    "currentDate": datetime.utcnow().isoformat()
                },
                logfilename="charging-statistics")

    print('fingerprint of the vehicles written to {}'.format(time_dir))
Пример #30
0
 def test_anonymize_data(self):
     """Test anonymization function."""
     test_dict = {
         'vin': 'secret',
         'a sub-dict': {
             'lat': 666,
             'lon': 666,
             'heading': 666,
         },
         'licensePlate': 'secret',
         'public': 'public_data',
         'a_list': [
             {'vin': 'secret'},
             {
                 'lon': 666,
                 'public': 'more_public_data',
             },
         ]
     }
     anon_text = json.dumps(ConnectedDriveAccount._anonymize_data(test_dict))
     self.assertNotIn('secret', anon_text)
     self.assertNotIn('666', anon_text)
     self.assertIn('public_data', anon_text)
     self.assertIn('more_public_data', anon_text)
Пример #31
0
    def __init__(self, config, vehicles, logger):
        self.vehicles = [
            vehicle for vehicle in vehicles if vehicle["manufacturer"] == "bmw"
        ]
        self.config = config
        self.logger = logger
        self.logger.info("BMW Connected Drive Cache started...")

        self.cache = get_cache_dir()

        self.max_staleness = self.config["max_staleness"]  # minutes

        credentials = self.config["credentials"]
        region = get_region_from_name(credentials["region"])
        self.account = ConnectedDriveAccount(credentials["user"],
                                             credentials["pwd"], region)
        self.vehicle_aliases: Dict[str, str] = {
            vehicle["vin"]: vehicle["alias"]
            for vehicle in self.vehicles
        }
        self.vehicle_update_timestamps: Dict[str, float] = {
            vehicle["vin"]: 0.
            for vehicle in self.vehicles
        }
def setupThing(info):
    # Setup for the account
    if info.thing.thingClassId == accountThingClassId:
        logger.log("SetupThing for account:", info.thing.name)

        region = regions[info.thing.paramValue(accountThingRegionParamTypeId)]
        pluginStorage().beginGroup(info.thing.id)
        username = pluginStorage().value("username")
        password = pluginStorage().value("password")
        pluginStorage().endGroup()

        try:
            account = ConnectedDriveAccount(username, password, region)
            account.update_vehicle_states()

            accountsMap[info.thing.id] = account
        except Exception as e:
            # Login error
            logger.warn(f"Error setting up BMW account: {str(e)}")
            info.finish(nymea.ThingErrorAuthenticationFailure, str(e))
            return

        # Mark the account as logged-in and connected
        info.thing.setStateValue(accountLoggedInStateTypeId, True)
        info.thing.setStateValue(accountConnectedStateTypeId, True)

        # Login went well, finish the setup
        info.finish(nymea.ThingErrorNoError)

        logger.log(
            f"Found {len(account.vehicles)} vehicles: {', '.join([v.name for v in account.vehicles])}"
        )

        thingDescriptors = []
        for vehicle in account.vehicles:
            if any(thing.thingClassId == vehicleThingClassId and
                   thing.paramValue(vehicleThingVinParamTypeId) == vehicle.vin
                   for thing in myThings()):
                continue

            if not vehicle.has_hv_battery:
                logger.log(
                    f"Ignoring combustion vehicle {vehicle.name} ({vehicle.vin[-7:]})"
                )
                continue

            logger.log(
                f"Adding new vehicle {vehicle.name} ({vehicle.vin[-7:]}) to the system with parent {info.thing.id}"
            )
            thingDescriptor = nymea.ThingDescriptor(
                vehicleThingClassId,
                "BMW {} ({})".format(vehicle.name, vehicle.vin[-7:]),
                parentId=info.thing.id,
            )
            thingDescriptor.params = [
                nymea.Param(vehicleThingVinParamTypeId, vehicle.vin)
            ]
            thingDescriptors.append(thingDescriptor)

        autoThingsAppeared(thingDescriptors)

        # If no poll timer is set up yet, start it now
        logger.log("Creating polltimer @ setupThing")
        global pollTimer
        if pollTimer is None:
            pollTimer = nymea.PluginTimer(60 * 5, pollService)
            logger.log("timer interval @ setupThing", pollTimer.interval)

    # Setup for the vehicles
    if info.thing.thingClassId == vehicleThingClassId:
        info.finish(nymea.ThingErrorNoError)
Пример #33
0
# Your car's full VIN, if you have more then one. You can leave it empty otherwise.
BMW_CONNECTED_DRIVE_VIN = ""

LOGO = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABX1BMVEV0dHT///9ubm5vb29JbYdwcHALY6QrapgqZpYAZrGmkH6cinssapgkZZk3a5GXl5cfaZjkoGtlb3ZxcXFycnJwcHBvb29wcHBwcHBwcHBwcHBwcHBwcHBxcXFwcHBwcHBvb29wcHBxcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBycnF1cW5rcHRvcHBxcXBwcHBwcHBwcHBzc3NwcHBwcHBxcHAUaKYEZ68IZ61TboJzcW9wcHBwcHBwcHBwcHBwcHBxcHA0a5MBZrEAZrEMZ6p+cWdxcXFxcXFvb28sa5kCZrAAZrEAZrJ+fn5ycnJxcXFwcHBycG8AZrMAZrEAZrF+cWdzcG9wcHBwcHC1h2QVaKcEZ68AZrEAZrEAZrEOZ6lQbYNkbneLg360hmRxcXFzcW8AZ7JxcXFycnJ7e3sAZrEAZ7Jvb3CAgIBzc3MAZ7JwcHBwcHBwcHAAZrH////TSxzlAAAAc3RSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAAlCc3+FhnxzQRllWyJAkZUxIl1pfXkcL3Z+JzpkcAmPkGwxfpxIOSuRiFxGkInU5iAON2IkQdfjGwI3KncLeOUaDzQLdQEHl+fNMiBBCwEBdDV5HTQC0ygkAStJZzdyTLX+NwAAAAFiS0dEAf8CLd4AAAAHdElNRQflAwEXLBPRp6KhAAABB0lEQVQY0xXPh1qCUBgG4B+MzNwFNLEt7WG2i7ZRUB0CAgFnOXIUeO7/eTpewDdeAIpmOX5qemZ2bp6lKQAqJGQWFpeWV1bXsqIQooAW1jc2R7a2d3aZvf0DgYaceJg/Oj45PTu/uLzKiDngpOub2zt8//BYeMoXshzw8ujzC8aKGn59e2dkHpA29oEx1o1I2PwctxDYRcf1FL1UNtRKtVa3wa7XqhXVKJd0xXOdr29AVrRhxiKGTnLNuIZIKdNq/8RUBeNON9HjyWyy/9s2Kx7u/KXSEgesHzD9VqPqNrupicBnyfVBkGSiNSeeSAcDcp3gfEm26kWtJ/kCM+QO+ci2Ec8R/uQ/WgA6IA6KdS0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDMtMDFUMjM6NDE6MDIrMDA6MDCCqrT8AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAyLTI4VDExOjE0OjI3KzAwOjAwtgzNTQAAAABJRU5ErkJggg=="

from bimmer_connected.account import ConnectedDriveAccount
from bimmer_connected.country_selector import get_region_from_name
from bimmer_connected.vehicle import VehicleViewDirection
from bimmer_connected.state import ChargingState

import base64
import datetime

account = ConnectedDriveAccount(
    BMW_CONNECTED_DRIVE_USERNAME,
    BMW_CONNECTED_DRIVE_PASSWORD,
    get_region_from_name(BMW_CONNECTED_DRIVE_REGION),
)
account.update_vehicle_states()

if not BMW_CONNECTED_DRIVE_VIN:
    account_vins = []
    for vehicle in account.vehicles:
        account_vins.append(vehicle.vin)
    # we'll just pick the first one
    BMW_CONNECTED_DRIVE_VIN = account_vins[0]

vehicle = account.get_vehicle(BMW_CONNECTED_DRIVE_VIN)

image_data = vehicle.get_vehicle_image(200, 80, VehicleViewDirection.FRONT)
b64image = base64.b64encode(image_data)