Skip to content

Commit

Permalink
Merge pull request #41 from itchannel/Region-Beta
Browse files Browse the repository at this point in the history
Region beta
  • Loading branch information
itchannel committed Nov 28, 2020
2 parents 1c711fd + 9bc0d92 commit e18d365
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 21 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Fordpass Home Assistant Integration

## Credit
https://github.com/clarkd - Initial Home Assistant automation idea and Python code (Lock/Unlock)
- https://github.com/clarkd - Initial Home Assistant automation idea and Python code (Lock/Unlock)
- https://github.com/pinballnewf - Figuring out the application ID issue

## Install
Use HACS and add as a custom repo. Once the integration is installed go to your integrations and follow the configuration options to specify the below:
- Username (Fordpass App)
- Password (Fordpass App)
- VIN Number
- Region (Where you are based, required for tokens to work correctly)

## Usage
Your car must have the lastest onboard modem functionality and have registered/authorised the fordpass application
Expand Down Expand Up @@ -37,6 +39,7 @@ Click on options and choose imperial or metric to display in km/miles. Takes eff
- Window Status (Only if your car supports it!)
- Last Car Refresh status
- Car Tracker
- Supports Multiple Regions


## Coming Soon
Expand Down
17 changes: 12 additions & 5 deletions custom_components/fordpass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
UpdateFailed,
)

from .const import CONF_UNIT, DEFAULT_UNIT, DOMAIN, MANUFACTURER, VEHICLE, VIN
from .const import CONF_UNIT, DEFAULT_UNIT, DOMAIN, MANUFACTURER, REGION, VEHICLE, VIN
from .fordpass_new import Vehicle

CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
Expand All @@ -39,8 +39,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
user = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD]
vin = entry.data[VIN]

coordinator = FordPassDataUpdateCoordinator(hass, user, password, vin, 1)
for ar in entry.data:
_LOGGER.debug(ar)
if REGION in entry.data.keys():
_LOGGER.debug(entry.data[REGION])
region = entry.data[REGION]
else:
_LOGGER.debug("CANT GET REGION")
region = "North America & Canada"
coordinator = FordPassDataUpdateCoordinator(hass, user, password, vin, region, 1)

await coordinator.async_refresh() # Get initial data

Expand Down Expand Up @@ -100,11 +107,11 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
class FordPassDataUpdateCoordinator(DataUpdateCoordinator):
"""DataUpdateCoordinator to handle fetching new data about the vehicle."""

def __init__(self, hass, user, password, vin, saveToken=False):
def __init__(self, hass, user, password, vin, region, saveToken=False):
"""Initialize the coordinator and set up the Vehicle object."""
self._hass = hass
self.vin = vin
self.vehicle = Vehicle(user, password, vin, saveToken)
self.vehicle = Vehicle(user, password, vin, region, saveToken)
self._available = True

super().__init__(
Expand Down
6 changes: 5 additions & 1 deletion custom_components/fordpass/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
CONF_UNITS,
DEFAULT_UNIT,
DOMAIN,
REGION,
REGION_OPTIONS,
VIN,
)
from .fordpass_new import Vehicle
Expand All @@ -22,6 +24,7 @@
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
vol.Required(VIN): vol.All(str, vol.Length(min=17, max=17)),
vol.Required(REGION): vol.In(REGION_OPTIONS),
}
)

Expand All @@ -31,7 +34,8 @@ async def validate_input(hass: core.HomeAssistant, data):
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
vehicle = Vehicle(data[CONF_USERNAME], data[CONF_PASSWORD], data[VIN])
_LOGGER.debug(data[REGION])
vehicle = Vehicle(data[CONF_USERNAME], data[CONF_PASSWORD], data[VIN], data[REGION])

try:
result = await hass.async_add_executor_job(vehicle.auth)
Expand Down
5 changes: 5 additions & 0 deletions custom_components/fordpass/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@
CONF_UNIT = "units"

CONF_UNITS = ["imperial", "metric"]


REGION = "region"

REGION_OPTIONS = ["UK&Europe", "Australia", "North America & Canada"]
32 changes: 23 additions & 9 deletions custom_components/fordpass/fordpass_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,26 @@

apiHeaders = {
**defaultHeaders,
"Application-Id": "5C80A6BB-CF0D-4A30-BDBF-FC804B5C1A98",
"Content-Type": "application/json",
}

region_lookup = {
"UK&Europe": "1E8C7794-FF5F-49BC-9596-A1E0C86C5B19",
"Australia": "5C80A6BB-CF0D-4A30-BDBF-FC804B5C1A98",
"North America & Canada": "71A3AD0A-CF46-4CCF-B473-FC7FE5BC4592",
}

baseUrl = "https://usapi.cv.ford.com/api"


class Vehicle(object):
# Represents a Ford vehicle, with methods for status and issuing commands

def __init__(self, username, password, vin, saveToken=False):
def __init__(self, username, password, vin, region, saveToken=False):
self.username = username
self.password = password
self.saveToken = saveToken
self.region = region_lookup[region]
self.vin = vin
self.token = None
self.expires = None
Expand Down Expand Up @@ -59,7 +65,7 @@ def auth(self):
logging.info("Succesfully fetched token Stage1")
result = r.json()
data = {"code": result["access_token"]}
headers = {**apiHeaders}
headers = {**apiHeaders, "Application-Id": self.region}
# Fetch OAUTH token stage 2 and refresh token
r = requests.put(
"https://api.mps.ford.com/api/oauth2/v1/token",
Expand All @@ -81,7 +87,7 @@ def auth(self):
def refreshToken(self, token):
# Token is invalid so let's try refreshing it
data = {"refresh_token": token["refresh_token"]}
headers = {**apiHeaders}
headers = {**apiHeaders, "Application-Id": self.region}

r = requests.put(
"https://api.mps.ford.com/api/oauth2/v1/refresh",
Expand All @@ -101,7 +107,7 @@ def __acquireToken(self):
# Fetch and refresh token as needed
# If file exists read in token file and check it's valid
if self.saveToken:
if os.path.isfile("/tmp/token.txt"):
if os.path.isfile("/tmp/fordpass_token.txt"):
data = self.readToken()
else:
data = dict()
Expand Down Expand Up @@ -129,13 +135,13 @@ def __acquireToken(self):

def writeToken(self, token):
# Save token to file to be reused
with open("/tmp/token.txt", "w") as outfile:
with open("/tmp/fordpass_token.txt", "w") as outfile:
token["expiry_date"] = time.time() + token["expires_in"]
json.dump(token, outfile)

def readToken(self):
# Get saved token from file
with open("/tmp/token.txt") as token_file:
with open("/tmp/fordpass_token.txt") as token_file:
return json.load(token_file)

def status(self):
Expand All @@ -145,7 +151,11 @@ def status(self):

params = {"lrdt": "01-01-1970 00:00:00"}

headers = {**apiHeaders, "auth-token": self.token}
headers = {
**apiHeaders,
"auth-token": self.token,
"Application-Id": self.region,
}

r = requests.get(
f"{baseUrl}/vehicles/v4/{self.vin}/status", params=params, headers=headers
Expand Down Expand Up @@ -203,7 +213,11 @@ def __makeRequest(self, method, url, data, params):
Make a request to the given URL, passing data/params as needed
"""

headers = {**apiHeaders, "auth-token": self.token}
headers = {
**apiHeaders,
"auth-token": self.token,
"Application-Id": self.region
}

return getattr(requests, method.lower())(
url, headers=headers, data=data, params=params
Expand Down
3 changes: 2 additions & 1 deletion custom_components/fordpass/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"data": {
"vin": "VIN",
"username": "FordPass Username (Email)",
"password": "FordPass Password"
"password": "FordPass Password",
"region": "FordPass Region"
}
}
},
Expand Down
13 changes: 12 additions & 1 deletion custom_components/fordpass/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@
"data": {
"password": "FordPass Password",
"username": "FordPass Username (Email)",
"vin": "VIN"
"vin": "VIN",
"region" : "FordPass Region"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"units": "Unit of Measurement"
},
"description": "Configure fordpass options"
}
}
},
"title": "FordPass"
}
11 changes: 8 additions & 3 deletions info.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@

# **Changelog**
### Version 1.07
- Support for multiple regions (Fixes unavaliable bug)
- Token renamed to fordpass_token

**In order to support regions you will need to reinstall the integration to change region** (Existing installs will default to North America)

### Version 1.06
- Minor bug fix
### Version 1.05
- Added device_tracker type (fordpass_tracker)
- Added imperial or metric selection
Expand All @@ -19,9 +27,6 @@
- Added car poll refresh





Fordpass can be configured via Integrations UI

## Integration page
Expand Down

0 comments on commit e18d365

Please sign in to comment.