Пример #1
0
    def __decrypt_payload_using_key(self, payload):
        if self.enc_key is None or self.enc_key == "":
            raise MazdaException("Missing encryption key")

        buf = base64.b64decode(payload)
        decrypted = decrypt_aes128cbc_buffer_to_str(buf, self.enc_key, IV)
        return json.loads(decrypted)
Пример #2
0
    async def set_hvac_setting(self, internal_vin, temperature,
                               temperature_unit, front_defroster,
                               rear_defroster):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin,
            "hvacsettings": {
                "FrontDefroster": 1 if front_defroster else 0,
                "RearDefogger": 1 if rear_defroster else 0,
                "Temperature": temperature,
                "TemperatureType": 1 if temperature_unit.lower() == "c" else 2
            }
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/updateHVACSetting/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to set HVAC setting")

        return response
Пример #3
0
    async def send_poi(self, internal_vin, latitude, longitude, name):
        # Calculate a POI ID that is unique to the name and location
        poi_id = hashlib.sha256((str(name) + str(latitude) +
                                 str(longitude)).encode()).hexdigest()[0:10]

        post_body = {
            "internaluserid":
            "__INTERNAL_ID__",
            "internalvin":
            internal_vin,
            "placemarkinfos": [{
                "Altitude": 0,
                "Latitude": abs(latitude),
                "LatitudeFlag": 0 if (latitude >= 0) else 1,
                "Longitude": abs(longitude),
                "LongitudeFlag": 0 if (longitude < 0) else 1,
                "Name": name,
                "OtherInformation": "{}",
                "PoiId": poi_id,
                "source": "google"
            }]
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/sendPOI/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to send POI")
Пример #4
0
    async def get_nickname(self, vin):
        if len(vin) != 17:
            raise MazdaException("Invalid VIN")

        post_body = {"internaluserid": "__INTERNAL_ID__", "vin": vin}

        response = await self.connection.api_request(
            "POST",
            "remoteServices/getNickName/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to get vehicle nickname")

        return response["carlineDesc"]
Пример #5
0
    def __encrypt_payload_using_key(self, payload):
        if self.enc_key is None or self.enc_key == "":
            raise MazdaException("Missing encryption key")
        if payload is None or payload == "":
            return ""

        return encrypt_aes128cbc_buffer_to_base64_str(payload.encode("utf-8"),
                                                      self.enc_key, IV)
Пример #6
0
    def __get_sign_from_payload_and_timestamp(self, payload, timestamp):
        if timestamp is None or timestamp == "":
            return ""
        if self.sign_key is None or self.sign_key == "":
            raise MazdaException("Missing sign key")

        return self.__get_payload_sign(
            self.__encrypt_payload_using_key(payload) + timestamp +
            timestamp[6:] + timestamp[3:], self.sign_key)
Пример #7
0
    async def __api_request_retry(self,
                                  method,
                                  uri,
                                  query_dict={},
                                  body_dict={},
                                  needs_keys=True,
                                  needs_auth=False,
                                  num_retries=0):
        if num_retries > MAX_RETRIES:
            raise MazdaException("Request exceeded max number of retries")

        if needs_keys:
            await self.__ensure_keys_present()
        if needs_auth:
            await self.__ensure_token_is_valid()

        retry_message = (" - attempt #" +
                         str(num_retries + 1)) if (num_retries > 0) else ""
        self.logger.debug(f"Sending {method} request to {uri}{retry_message}")

        try:
            return await self.__send_api_request(method, uri, query_dict,
                                                 body_dict, needs_keys,
                                                 needs_auth)
        except (MazdaAPIEncryptionException):
            self.logger.debug(
                "Server reports request was not encrypted properly. Retrieving new encryption keys."
            )
            await self.__retrieve_keys()
            return await self.__api_request_retry(method, uri, query_dict,
                                                  body_dict, needs_keys,
                                                  needs_auth, num_retries + 1)
        except (MazdaTokenExpiredException):
            self.logger.debug(
                "Server reports access token was expired. Retrieving new access token."
            )
            await self.login()
            return await self.__api_request_retry(method, uri, query_dict,
                                                  body_dict, needs_keys,
                                                  needs_auth, num_retries + 1)
        except (MazdaLoginFailedException):
            self.logger.debug(
                "Login failed for an unknown reason. Trying again.")
            await self.login()
            return await self.__api_request_retry(method, uri, query_dict,
                                                  body_dict, needs_keys,
                                                  needs_auth, num_retries + 1)
        except (MazdaRequestInProgressException):
            self.logger.debug(
                "Request failed because another request was already in progress. Waiting 30 seconds and trying again."
            )
            await asyncio.sleep(30)
            return await self.__api_request_retry(method, uri, query_dict,
                                                  body_dict, needs_keys,
                                                  needs_auth, num_retries + 1)
Пример #8
0
    async def update_nickname(self, vin, new_nickname):
        if len(vin) != 17:
            raise MazdaException("Invalid VIN")
        if len(new_nickname) > 20:
            raise MazdaException("Nickname is too long")

        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "vin": vin,
            "vtitle": new_nickname
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/updateNickName/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to update vehicle nickname")
Пример #9
0
    async def door_unlock(self, internal_vin):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/doorUnlock/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to unlock door")

        return response
Пример #10
0
    async def refresh_vehicle_status(self, internal_vin):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/activeRealTimeVehicleStatus/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to refresh vehicle status")

        return response
Пример #11
0
    async def get_hvac_setting(self, internal_vin):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/getHVACSetting/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to get HVAC setting")

        return response
Пример #12
0
    async def get_health_report(self, internal_vin):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin,
            "limit": 1,
            "offset": 0
        }

        response = await self.connection.api_request(
            "POST",
            "remoteServices/getHealthReport/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to get health report")

        return response
Пример #13
0
    async def get_ev_vehicle_status(self, internal_vin):
        post_body = {
            "internaluserid": "__INTERNAL_ID__",
            "internalvin": internal_vin,
            "limit": 1,
            "offset": 0,
            "vecinfotype": "0"
        }
        response = await self.connection.api_request(
            "POST",
            "remoteServices/getEVVehicleStatus/v4",
            body_dict=post_body,
            needs_keys=True,
            needs_auth=True)

        if response["resultCode"] != "200S00":
            raise MazdaException("Failed to get EV vehicle status")

        return response
Пример #14
0
    async def __send_api_request(self,
                                 method,
                                 uri,
                                 query_dict={},
                                 body_dict={},
                                 needs_keys=True,
                                 needs_auth=False):
        timestamp = self.__get_timestamp_str_ms()

        original_query_str = ""
        encrypted_query_dict = {}

        if query_dict:
            original_query_str = urlencode(query_dict)
            encrypted_query_dict["params"] = self.__encrypt_payload_using_key(
                original_query_str)

        original_body_str = ""
        encrypted_body_Str = ""
        if body_dict:
            original_body_str = json.dumps(body_dict)
            encrypted_body_Str = self.__encrypt_payload_using_key(
                original_body_str)

        headers = {
            "device-id": self.base_api_device_id,
            "app-code": self.app_code,
            "app-os": APP_OS,
            "user-agent": USER_AGENT_BASE_API,
            "app-version": APP_VERSION,
            "app-unique-id": APP_PACKAGE_ID,
            "access-token": (self.access_token if needs_auth else ""),
            "X-acf-sensor-data": "",
            "req-id": "req_" + timestamp,
            "timestamp": timestamp
        }

        if "checkVersion" in uri:
            headers["sign"] = self.__get_sign_from_timestamp(timestamp)
        elif method == "GET":
            headers["sign"] = self.__get_sign_from_payload_and_timestamp(
                original_query_str, timestamp)
        elif method == "POST":
            headers["sign"] = self.__get_sign_from_payload_and_timestamp(
                original_body_str, timestamp)

        response = await self._session.request(method,
                                               self.base_url + uri,
                                               headers=headers,
                                               data=encrypted_body_Str)

        response_json = await response.json()

        if response_json["state"] == "S":
            if "checkVersion" in uri:
                return self.__decrypt_payload_using_app_code(
                    response_json["payload"])
            else:
                return self.__decrypt_payload_using_key(
                    response_json["payload"])
        elif response_json["errorCode"] == 600001:
            raise MazdaAPIEncryptionException(
                "Server rejected encrypted request")
        elif response_json["errorCode"] == 600002:
            raise MazdaTokenExpiredException("Token expired")
        else:
            raise MazdaException("Request failed for an unknown reason")
Пример #15
0
    async def __send_api_request(self,
                                 method,
                                 uri,
                                 query_dict={},
                                 body_dict={},
                                 needs_keys=True,
                                 needs_auth=False):
        timestamp = self.__get_timestamp_str_ms()

        original_query_str = ""
        encrypted_query_dict = {}

        if query_dict:
            original_query_str = urlencode(query_dict)
            encrypted_query_dict["params"] = self.__encrypt_payload_using_key(
                original_query_str)

        original_body_str = ""
        encrypted_body_Str = ""
        if body_dict:
            original_body_str = json.dumps(body_dict)
            encrypted_body_Str = self.__encrypt_payload_using_key(
                original_body_str)

        headers = {
            "device-id": self.base_api_device_id,
            "app-code": self.app_code,
            "app-os": APP_OS,
            "user-agent": USER_AGENT_BASE_API,
            "app-version": APP_VERSION,
            "app-unique-id": APP_PACKAGE_ID,
            "access-token": (self.access_token if needs_auth else ""),
            "X-acf-sensor-data":
            self.sensor_data_builder.generate_sensor_data(),
            "req-id": "req_" + timestamp,
            "timestamp": timestamp
        }

        if "checkVersion" in uri:
            headers["sign"] = self.__get_sign_from_timestamp(timestamp)
        elif method == "GET":
            headers["sign"] = self.__get_sign_from_payload_and_timestamp(
                original_query_str, timestamp)
        elif method == "POST":
            headers["sign"] = self.__get_sign_from_payload_and_timestamp(
                original_body_str, timestamp)

        response = await self._session.request(method,
                                               self.base_url + uri,
                                               headers=headers,
                                               data=encrypted_body_Str)

        response_json = await response.json()

        if response_json.get("state") == "S":
            if "checkVersion" in uri:
                return self.__decrypt_payload_using_app_code(
                    response_json["payload"])
            else:
                decrypted_payload = self.__decrypt_payload_using_key(
                    response_json["payload"])
                self.logger.debug("Response payload: %s", decrypted_payload)
                return decrypted_payload
        elif response_json.get("errorCode") == 600001:
            raise MazdaAPIEncryptionException(
                "Server rejected encrypted request")
        elif response_json.get("errorCode") == 600002:
            raise MazdaTokenExpiredException("Token expired")
        elif response_json.get("errorCode") == 920000 and response_json.get(
                "extraCode") == "400S01":
            raise MazdaRequestInProgressException(
                "Request already in progress, please wait and try again")
        elif response_json.get("errorCode") == 920000 and response_json.get(
                "extraCode") == "400S11":
            raise MazdaException(
                "The engine can only be remotely started 2 consecutive times. Please drive the vehicle to reset the counter."
            )
        elif "error" in response_json:
            raise MazdaException("Request failed: " + response_json["error"])
        else:
            raise MazdaException("Request failed for an unknown reason")