diff --git a/custom_components/fordpass/__init__.py b/custom_components/fordpass/__init__.py index 1603da3..6bed789 100644 --- a/custom_components/fordpass/__init__.py +++ b/custom_components/fordpass/__init__.py @@ -160,6 +160,7 @@ def refresh_status(hass, service, coordinator): _LOGGER.debug("Invalid VIN") elif status == 200: _LOGGER.debug("Refresh Sent") + def clear_tokens(hass, service, coordinator): diff --git a/custom_components/fordpass/fordpass_new.py b/custom_components/fordpass/fordpass_new.py index 3dad02e..2c1dfc2 100644 --- a/custom_components/fordpass/fordpass_new.py +++ b/custom_components/fordpass/fordpass_new.py @@ -32,8 +32,8 @@ } baseUrl = "https://usapi.cv.ford.com/api" - guardUrl = "https://api.mps.ford.com/api" +ssoUrl = "https://sso.ci.ford.com" session = requests.Session() @@ -48,6 +48,7 @@ def __init__( self.password = password self.saveToken = saveToken self.region = region_lookup[region] + self.region2 = region self.vin = vin self.token = None self.expires = None @@ -82,14 +83,14 @@ def auth(self): } code1 = ''.join(random.choice(string.ascii_lowercase) for i in range(43)) code_verifier = self.generate_hash(code1) - url1 = "https://sso.ci.ford.com/v1.0/endpoint/default/authorize?redirect_uri=fordapp://userauthorized&response_type=code&scope=openid&max_age=3600&client_id=9fb503e0-715b-47e8-adfd-ad4b7770f73b&code_challenge=" + code_verifier + "&code_challenge_method=S256" + url1 = f"{ssoUrl}/v1.0/endpoint/default/authorize?redirect_uri=fordapp://userauthorized&response_type=code&scope=openid&max_age=3600&client_id=9fb503e0-715b-47e8-adfd-ad4b7770f73b&code_challenge={code_verifier}&code_challenge_method=S256" r = session.get( url1, headers=headers, ) test = re.findall('data-ibm-login-url="(.*)"\s', r.text)[0] - nextUrl = "https://sso.ci.ford.com" + test + nextUrl = ssoUrl + test # Auth Step2 @@ -157,7 +158,7 @@ def auth(self): } r = session.post( - "https://sso.ci.ford.com/oidc/endpoint/default/token", + f"{ssoUrl}/oidc/endpoint/default/token", headers = headers, data = data @@ -176,7 +177,7 @@ def auth(self): data = {"ciToken": access_token} headers = {**apiHeaders, "Application-Id": self.region} r = session.post( - "https://api.mps.ford.com/api/token/v2/cat-with-ci-access-token", + f"{guardUrl}/token/v2/cat-with-ci-access-token", data=json.dumps(data), headers=headers, ) @@ -201,7 +202,7 @@ def refreshToken(self, token): headers = {**apiHeaders, "Application-Id": self.region} r = session.post( - "https://api.mps.ford.com/api/token/v2/cat-with-refresh-token", + f"{guardUrl}/token/v2/cat-with-refresh-token", data=json.dumps(data), headers=headers, ) @@ -339,16 +340,32 @@ def messages(self): def vehicles(self): self.__acquireToken() + if (self.region2 == "Australia"): + countryheader = "AUS" + elif (self.region2 == "North America & Canada"): + countryheader = "USA" + elif (self.region2 == "UK&Europe"): + countryheader = "GBR" + else: + countryheader = "USA" headers = { **apiHeaders, "Auth-Token": self.token, "Application-Id": self.region, + "Countrycode": countryheader, + "Locale": "EN-US" } - r = session.get( - "https://services.cx.ford.com/api/dashboard/v1/users/vehicles", + + + data = { + "dashboardRefreshRequest":"All" + } + r = session.post( + guardUrl + "/expdashboard/v1/details/", headers=headers, + data=json.dumps(data) ) - if r.status_code == 200: + if r.status_code == 207: result = r.json() _LOGGER.debug(result) @@ -481,6 +498,9 @@ def __requestAndPoll(self, method, url): if command.status_code == 200: result = command.json() - return self.__pollStatus(url, result["commandId"]) + if "commandId" in result: + return self.__pollStatus(url, result["commandId"]) + else: + return False else: - command.raise_for_status() + return False diff --git a/custom_components/fordpass/lock.py b/custom_components/fordpass/lock.py index dde641e..45a0b81 100644 --- a/custom_components/fordpass/lock.py +++ b/custom_components/fordpass/lock.py @@ -13,8 +13,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Add the lock from the config.""" entry = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR] - locks = [Lock(entry)] - async_add_entities(locks, False) + lock = Lock(entry) + if lock.coordinator.data.get("lockStatus", {}) and lock.coordinator.data["lockStatus"]["value"] != "ERROR": + async_add_entities([lock], False) + else: + _LOGGER.debug("Ford model doesn't support remote locking") class Lock(FordPassEntity, LockEntity): diff --git a/custom_components/fordpass/manifest.json b/custom_components/fordpass/manifest.json index 7c78745..a005fd5 100644 --- a/custom_components/fordpass/manifest.json +++ b/custom_components/fordpass/manifest.json @@ -12,6 +12,6 @@ "loggers": ["custom_components.fordpass"], "requirements": [], "ssdp": [], - "version": "0.1.47", + "version": "0.1.48", "zeroconf": [] } \ No newline at end of file diff --git a/custom_components/fordpass/sensor.py b/custom_components/fordpass/sensor.py index d070d37..ff36f3d 100644 --- a/custom_components/fordpass/sensor.py +++ b/custom_components/fordpass/sensor.py @@ -61,7 +61,7 @@ def get_value(self, ftype): if self.sensor == "odometer": if self.fordoptions[CONF_DISTANCE_UNIT] != None: if self.fordoptions[CONF_DISTANCE_UNIT] == "mi": - if self.fordoptions[DISTANCE_CONVERSION_DISABLED] == True: + if DISTANCE_CONVERSION_DISABLED in self.fordoptions and self.fordoptions[DISTANCE_CONVERSION_DISABLED] == True: return self.coordinator.data[self.sensor]["value"] else: return round( @@ -200,6 +200,11 @@ def get_value(self, ftype): return None elif self.sensor == "messages": return "Messages" + elif self.sensor == "elVeh": + if self.fordoptions[CONF_DISTANCE_UNIT] == "mi": + return "mi" + else: + return "km" elif self.sensor == "exhaustFluidLevel": return "%" elif ftype == "attribute": diff --git a/custom_components/fordpass/strings.json b/custom_components/fordpass/strings.json index c2a11e7..8e053e7 100644 --- a/custom_components/fordpass/strings.json +++ b/custom_components/fordpass/strings.json @@ -18,7 +18,8 @@ "unknown": "[%key:common::config_flow::error::unknown%]" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_account%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_account%]", + "no_vehicles": "[%key:common::config_flow::abort::no_vehicles%]" } }, "options": { diff --git a/custom_components/fordpass/switch.py b/custom_components/fordpass/switch.py index aa5e762..78e6a21 100644 --- a/custom_components/fordpass/switch.py +++ b/custom_components/fordpass/switch.py @@ -49,6 +49,7 @@ async def async_turn_on(self, **kwargs): self.coordinator.vehicle.enableGuard ) await self.coordinator.async_request_refresh() + self.async_write_ha_state() async def async_turn_off(self, **kwargs): if self.switch == "ignition": @@ -61,6 +62,7 @@ async def async_turn_off(self, **kwargs): self.coordinator.vehicle.disableGuard ) await self.coordinator.async_request_refresh() + self.async_write_ha_state() @property def name(self): diff --git a/custom_components/fordpass/translations/en.json b/custom_components/fordpass/translations/en.json index 72b540c..c72a057 100644 --- a/custom_components/fordpass/translations/en.json +++ b/custom_components/fordpass/translations/en.json @@ -1,11 +1,12 @@ { "config": { "abort": { - "already_configured": "Account is already configured" + "already_configured": "Account is already configured", + "no_vehicles": "No vehicles on account or all are configured already" }, "error": { "cannot_connect": "Failed to connect", - "invalid_auth": "Invalid authentication", + "invalid_auth": "Invalid Credentials", "invalid_vin": "Vin not found for given account", "unknown": "Unexpected error" }, @@ -33,5 +34,29 @@ } } }, + "services": { + "refresh_status": { + "name": "Refresh Vehicle Status", + "description": "Poll car for latest status (Takes up to 5mins to update once this function has been run!)", + "fields": { + "vin": { + "name": "VIN", + "description": "Parse a vin number to only refresh the specified vehicle (Default refreshes all added vehicles)" + } + } + }, + "clear_tokens": { + "name": "Clear Tokens", + "description": "Clear the token cache" + }, + "reload": { + "name": "Reload", + "description": "Reload the Fordpass Integration" + }, + "poll_api": { + "name": "Poll API", + "description": "Manually poll API for data update (Warning: doing this too often could result in a ban)" + } + }, "title": "Fordpass" } \ No newline at end of file diff --git a/custom_components/fordpass/translations/fr.json b/custom_components/fordpass/translations/fr.json index 0499962..b6f79a7 100644 --- a/custom_components/fordpass/translations/fr.json +++ b/custom_components/fordpass/translations/fr.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Le compte est déjà configuré" + "already_configured": "Le compte est déjà configuré", + "no_vehicles": "Aucun véhicule en compte ou tous sont déjà configurés" }, "error": { "cannot_connect": "Impossible de se connecter", @@ -33,5 +34,29 @@ } } }, + "services": { + "refresh_status": { + "name": "Actualiser l'état du véhicule", + "description": "Sonder la voiture pour le dernier statut (Prend jusqu'à 5 minutes pour mettre à jour une fois que cette fonction a été exécutée !)", + "fields": { + "vin": { + "name": "VIN", + "description": "Analyser un numéro vin pour actualiser uniquement le véhicule spécifié (la valeur par défaut actualise tous les véhicules ajoutés)" + } + } + }, + "clear_tokens": { + "name": "Effacer les jetons", + "description": "Vider le cache des jetons" + }, + "reload": { + "name": "Recharger", + "description": "Recharger l'intégration Fordpass" + }, + "poll_api": { + "name": "API de sondage", + "description": "Interroger manuellement l'API pour la mise à jour des données (Attention : le faire trop souvent pourrait entraîner une interdiction)" + } + }, "title": "Fordpass" } diff --git a/custom_components/fordpass/translations/nl.json b/custom_components/fordpass/translations/nl.json index ae17728..7490075 100644 --- a/custom_components/fordpass/translations/nl.json +++ b/custom_components/fordpass/translations/nl.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "already_configured": "Account is al geconfigureerd" + "already_configured": "Account is al geconfigureerd", + "no_vehicles": "Geen voertuigen op account of ze zijn allemaal al geconfigureerd" }, "error": { "cannot_connect": "Kan geen verbinding maken", @@ -33,5 +34,29 @@ } } }, + "services": { + "refresh_status": { + "name": "Voertuigstatus vernieuwen", + "description": "Poll auto voor de laatste status (duurt tot 5 minuten om te updaten zodra deze functie is uitgevoerd!)", + "fields": { + "vin": { + "name": "VIN", + "description": "Parseer een chassisnummer om alleen het opgegeven voertuig te vernieuwen (standaard vernieuwt alle toegevoegde voertuigen)" + } + } + }, + "clear_tokens": { + "name": "Tokens wissen", + "description": "Wis de tokencache" + }, + "reload": { + "name": "herladen", + "description": "Laad de Fordpass-integratie opnieuw" + }, + "poll_api": { + "name": "Poll API", + "description": "API handmatig peilen voor gegevensupdate (Waarschuwing: als u dit te vaak doet, kan dit resulteren in een ban)" + } + }, "title": "FordPass" } diff --git a/info.md b/info.md index 081138f..af6e1d1 100644 --- a/info.md +++ b/info.md @@ -1,4 +1,9 @@ ## **Changelog** +### Version 1.48 +- Add translations for service strings +- Fix error on odometer missing config +- Handle unsupported car locks +- Add Units for elVeh DTE ### Version 1.47 - Add poll_api service to allow for manual refreshing of data outside of poll interval (e.g. poll more when driving) - Add option to disable distance conversion when units displaying wrong in certain countries