Skip to content

Commit

Permalink
Merge pull request #322 from itchannel/1.53
Browse files Browse the repository at this point in the history
1.53
  • Loading branch information
itchannel committed Oct 12, 2023
2 parents 6da909a + 40b3aca commit 5e30114
Show file tree
Hide file tree
Showing 10 changed files with 554 additions and 222 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
- https://github.com/JacobWasFramed - Updated unit conversions
- https://github.com/heehoo59 - French Translation

## As of 10/10/2023 Ford has switched to a new API!
This has caused the integration to stop working, I have started work on integrating the new API and there is a Beta but there is a lot of changes so it will take time to get it back and 100% operational again.

## Account Warning (Sep 2023)
A number of users have encountered their accounts being banned for containing "+" symbols in their email. It appears Ford thinks this is a disposable email. So if you have a + in your email I recommend changing it.

Expand Down
142 changes: 142 additions & 0 deletions custom_components/fordpass/autonomicData.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import json
import requests
import sys
import os
import re
from datetime import datetime


# Place this script in the /config/custom_components/fordpass folder on your HomeAssistant
# Add the details below
# run from a terminal in the /config/custom_components/fordpass folder: python3 autonomicData.py
# It will create username_status_timestamp.json in the same folder
# Script will automatically redact your VIN, VehicleID, and Geolocation details (lat, long)

#GitHub username to append to the filename
gitHub_username = ""
#FordPass VIN for vehicle to get data from
fp_vin = ""
#Name of the file for the user_fordpass_token.txt from the fordpass-ha integration
fp_token = "_fordpass_token.txt"

def get_autonomic_token(ford_access_token):
url = "https://accounts.autonomic.ai/v1/auth/oidc/token"
headers = {
"accept": "*/*",
"content-type": "application/x-www-form-urlencoded"
}
data = {
"subject_token": ford_access_token,
"subject_issuer": "fordpass",
"client_id": "fordpass-prod",
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt"
}

try:
response = requests.post(url, headers=headers, data=data)
response.raise_for_status()
autonomic_token_data = response.json()
return autonomic_token_data

except requests.exceptions.HTTPError as errh:
print(f"HTTP Error: {errh}")
print(f"Trying refresh token")
get_autonomic_token(ford_refresh_token)
except requests.exceptions.ConnectionError as errc:
print(f"Error Connecting: {errc}")
sys.exit()
except requests.exceptions.Timeout as errt:
print(f"Timeout Error: {errt}")
sys.exit()
except requests.exceptions.RequestException as err:
print(f"Something went wrong: {err}")
sys.exit()


def get_vehicle_status(vin, access_token):
BASE_URL = "https://api.autonomic.ai/"
endpoint = f"v1beta/telemetry/sources/fordpass/vehicles/{vin}:query"
url = f"{BASE_URL}{endpoint}"
headers = {
"Authorization": f"Bearer {access_token}", # Replace 'your_autonom_token' with the actual Autonomic API token
"Content-Type": "application/json",
"accept": "*/*"
}
redactionItems = ["lat", "lon", "vehicleId", "vin", "latitude", "longitude"]

try:
response = requests.post(url, headers=headers, json={})
response.raise_for_status() # Raise HTTPError for bad requests (4xx and 5xx status codes)

# Parse the JSON response
vehicle_status_data = response.json()

# Redact sensitive information
redact_json(vehicle_status_data, redactionItems)
return vehicle_status_data

except requests.exceptions.HTTPError as errh:
print(f"HTTP Error: {errh}")
except requests.exceptions.ConnectionError as errc:
print(f"Error Connecting: {errc}")
except requests.exceptions.Timeout as errt:
print(f"Timeout Error: {errt}")
except requests.exceptions.RequestException as err:
print(f"Something went wrong: {err}")

def redact_json(data, redaction):
# Regular expression to match GPS coordinates
gps_pattern = r'"gpsDegree":\s*-?\d+\.\d+,\s*"gpsFraction":\s*-?\d+\.\d+,\s*"gpsSign":\s*-?\d+\.\d+'

if isinstance(data, dict):
for key in list(data.keys()):
if key in redaction:
data[key] = 'REDACTED'
else:
if isinstance(data[key], str):
# Redact GPS coordinates in string values
data[key] = re.sub(gps_pattern, '"gpsDegree": "REDACTED", "gpsFraction": "REDACTED", "gpsSign": "REDACTED"', data[key])
else:
redact_json(data[key], redaction)
# Special handling for 'stringArrayValue'
if key == 'stringArrayValue':
for i in range(len(data[key])):
data[key][i] = re.sub(gps_pattern, '"gpsDegree": "REDACTED", "gpsFraction": "REDACTED", "gpsSign": "REDACTED"', data[key][i])
elif isinstance(data, list):
for item in data:
redact_json(item, redaction)


if __name__ == "__main__":
workingDir = "/config/custom_components/fordpass"
if gitHub_username == "":
gitHub_username = 'my'
if fp_vin == "":
print("Please enter your VIN into the python script")
sys.exit()
if fp_token == "":
print("Please enter your FordPass token text file name into the python script")
sys.exit()
elif os.path.isfile(os.path.join(workingDir, fp_token)) == False:
print(f"Error finding FordPass token text file: {os.path.join(workingDir, fp_token)}")
sys.exit()

fp_token = os.path.join(workingDir, fp_token)
# Get FordPass token
with open(fp_token, 'r') as file:
fp_token_data = json.load(file)

ford_access_token = fp_token_data['access_token']
ford_refresh_token = fp_token_data['refresh_token']
# Exchange Fordpass token for Autonomic Token
autonomic_token = get_autonomic_token(ford_access_token)
vehicle_status = get_vehicle_status(fp_vin, autonomic_token["access_token"])

current_datetime = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
fileName = os.path.join(workingDir, f"{gitHub_username}_status_{current_datetime}.json")

# Write the updated JSON data to the file
with open(fileName, 'w') as file:
json.dump(vehicle_status, file, indent=4)
print("done")
29 changes: 17 additions & 12 deletions custom_components/fordpass/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,21 @@
"windowPosition": {"icon": "mdi:car-door"},
"lastRefresh": {"icon": "mdi:clock", "device_class": "timestamp"},
"elVeh": {"icon": "mdi:ev-station"},
"deepSleepInProgress": {
"icon": "mdi:power-sleep",
"name": "Deep Sleep Mode Active",
},
"firmwareUpgInProgress": {
"icon": "mdi:one-up",
"name": "Firmware Update In Progress",
},
"elVehCharging": {"icon": "mdi:ev-station"},
"speed": {"icon": "mdi:speedometer"},
# "deepSleepInProgress": {
# "icon": "mdi:power-sleep",
# "name": "Deep Sleep Mode Active",
# },
# "firmwareUpgInProgress": {
# "icon": "mdi:one-up",
# "name": "Firmware Update In Progress",
# },
"remoteStartStatus": {"icon": "mdi:remote"},
"zoneLighting": {"icon": "mdi:spotlight-beam"},
# "zoneLighting": {"icon": "mdi:spotlight-beam"},
"messages": {"icon": "mdi:message-text"},
"dieselSystemStatus": {"icon": "mdi:smoking-pipe"},
"exhaustFluidLevel": {"icon": "mdi:barrel"}
# "dieselSystemStatus": {"icon": "mdi:smoking-pipe"},
# "exhaustFluidLevel": {"icon": "mdi:barrel"}
}

SWITCHES = {"ignition": {"icon": "hass:power"}, "guardmode": {"icon": "mdi:shield-key"}}
Expand All @@ -72,4 +74,7 @@
},
}

SWITCHES = {"ignition": {"icon": "hass:power"}, "guardmode": {"icon": "mdi:shield-key"}}
SWITCHES = {
"ignition": {"icon": "hass:power"},
# "guardmode": {"icon": "mdi:shield-key"}
}
17 changes: 6 additions & 11 deletions custom_components/fordpass/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entry = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]

# Added a check to see if the car supports GPS
if entry.data["gps"] is not None:
if entry.data["metrics"]["position"] is not None:
async_add_entities([CarTracker(entry, "gps")], True)
else:
_LOGGER.debug("Vehicle does not support GPS")
Expand All @@ -24,28 +24,23 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class CarTracker(FordPassEntity, TrackerEntity):
def __init__(self, coordinator, sensor):

super().__init__(
device_id="fordpass_" + sensor,
name="fordpass_" + sensor,
coordinator=coordinator
)

self._attr = {}
self.sensor = sensor
self.coordinator = coordinator
self.data = coordinator.data["metrics"]
self._device_id = "fordpass_tracker"
# Required for HA 2022.7
self.coordinator_context = object()

@property
def latitude(self):
"""Return latitude from Vehicle GPS"""
return float(self.coordinator.data[self.sensor]["latitude"])
"""Return latitude"""
return float(self.coordinator.data["metrics"]["position"]["value"]["location"]["lat"])

@property
def longitude(self):
"""Return longitude from Vehicle GPS"""
return float(self.coordinator.data[self.sensor]["longitude"])
"""Return longtitude"""
return float(self.coordinator.data["metrics"]["position"]["value"]["location"]["lon"])

@property
def source_type(self):
Expand Down
Loading

0 comments on commit 5e30114

Please sign in to comment.