Skip to content

Commit

Permalink
Multiple vehicle support and config flow updates (#70)
Browse files Browse the repository at this point in the history
* Replace region box with dropdown and tidy config flow

* Supporting documentation and translations

* Update unique id handling for multiple vehicles/accounts

* Fix password change bug

* Fix uppercase region translation keys

* Updated tested vehicles list
  • Loading branch information
dan-r authored Nov 6, 2024
1 parent 2ed6a70 commit 2dfcf60
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 39 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ If you find any bugs or would like to request a feature, please open an issue.

## Tested Vehicles
This integration has been tested with the following vehicles:
* Nissan Leaf (2022) - UK [@dan-r]
* Nissan Qashqai (2021) - EU
* Nissan Ariya - EU
* Nissan Leaf (2022) [@dan-r]
* Nissan Qashqai (2021)
* Nissan Ariya
* Nissan X-Trail (2024)
* Nissan Juke (2021)

## Supported Regions
* Europe

Currently only Nissan vehicles within Europe are supported.

### North America
The API used in North America is completely separate to Europe and it appears that Nissan USA are [a lot more hostile](https://tobis.dk/blog/the-farce-of-nissanconnect-north-america/) towards third-party access. Any future US support would rely on library support (such as [dartnissanconnectna](https://gitlab.com/tobiaswkjeldsen/dartnissanconnectna)) or someone in North America maintaining that side of things. If you're interested, get in touch!
Expand Down
3 changes: 2 additions & 1 deletion custom_components/nissan_connect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ async def async_setup_entry(hass, entry):
config = dict(entry.data)

kamereon_session = NCISession(
region=config["region"]
region=config["region"],
unique_id=entry.unique_id
)

data = hass.data[DOMAIN] = {
Expand Down
4 changes: 4 additions & 0 deletions custom_components/nissan_connect/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def _vehicle_name(self):
@property
def unique_id(self):
"""Return unique ID of the entity."""
# New unique ID format for multiple cars and multiple accounts
if self.vehicle.session.unique_id:
return f"{self.vehicle.session.unique_id}_{self.vehicle.vin}_{self._attr_translation_key}"

return f"{self._vehicle_name}_{self._attr_translation_key}"

@property
Expand Down
54 changes: 32 additions & 22 deletions custom_components/nissan_connect/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,31 @@
from .const import DOMAIN, CONFIG_VERSION, DEFAULT_INTERVAL_POLL, DEFAULT_INTERVAL_CHARGING, DEFAULT_INTERVAL_STATISTICS, DEFAULT_INTERVAL_FETCH, DEFAULT_REGION, REGIONS
from .kamereon import NCISession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import selector

USER_SCHEMA = vol.Schema({
vol.Required("email"): cv.string,
vol.Required("password"): cv.string,
# vol.Required(
# "interval", default=DEFAULT_INTERVAL_POLL
# ): int,
# vol.Required(
# "interval_charging", default=DEFAULT_INTERVAL_CHARGING
# ): int,
# vol.Required(
# "interval_fetch", default=DEFAULT_INTERVAL_FETCH
# ): int,
# vol.Required(
# "interval_statistics", default=DEFAULT_INTERVAL_STATISTICS
# ): int,
vol.Required(
"interval", default=DEFAULT_INTERVAL_POLL
): int,
vol.Required(
"interval_charging", default=DEFAULT_INTERVAL_CHARGING
): int,
vol.Required(
"interval_fetch", default=DEFAULT_INTERVAL_FETCH
): int,
vol.Required(
"interval_statistics", default=DEFAULT_INTERVAL_STATISTICS
): int,
vol.Required(
"region", default=DEFAULT_REGION): cv.string,
"region", default=DEFAULT_REGION.lower()): selector.SelectSelector(
selector.SelectSelectorConfig(
options=[el.lower() for el in REGIONS], # Translation keys must be lowercase
mode=selector.SelectSelectorMode.DROPDOWN,
translation_key="region"
),
),
vol.Required(
"imperial_distance", default=False): bool
})
Expand All @@ -32,9 +39,12 @@ class NissanConfigFlow(ConfigFlow, domain=DOMAIN):

async def async_step_user(self, info):
errors = {}
if info is not None and info["region"] not in REGIONS:
errors["base"] = "region_error"
elif info is not None:
if info is not None:
info["region"] = info["region"].upper()

await self.async_set_unique_id(info["email"])
self._abort_if_unique_id_configured()

# Validate credentials
kamereon_session = NCISession(
region=info["region"]
Expand All @@ -50,7 +60,7 @@ async def async_step_user(self, info):

if len(errors) == 0:
return self.async_create_entry(
title="NissanConnect Account",
title=info["email"],
data=info
)

Expand Down Expand Up @@ -80,9 +90,9 @@ async def async_step_init(self, options):
if "password" in options:
try:
await self.hass.async_add_executor_job(kamereon_session.login,
options["email"],
options["password"]
)
self._config_entry.data.get("email"),
options["password"]
)
except:
errors["base"] = "auth_error"

Expand All @@ -92,7 +102,7 @@ async def async_step_init(self, options):
if not "password" in options:
options.pop('email', None)
options.pop('password', None)

# Update data
data.update(options)
self.hass.config_entries.async_update_entry(
Expand All @@ -107,7 +117,7 @@ async def async_step_init(self, options):

return self.async_show_form(
step_id="init", data_schema=vol.Schema({
vol.Required("email", default=self._config_entry.data.get("email", "")): cv.string,
# vol.Required("email", default=self._config_entry.data.get("email", "")): cv.string,
vol.Optional("password"): cv.string,
vol.Required(
"interval", default=self._config_entry.data.get("interval", DEFAULT_INTERVAL_POLL)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/nissan_connect/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
DEFAULT_INTERVAL_FETCH = 10

DEFAULT_REGION = "EU"
REGIONS = ["EU", "US"]
REGIONS = ["EU"]
4 changes: 3 additions & 1 deletion custom_components/nissan_connect/kamereon.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,13 +605,15 @@ class KamereonSession:

tenant = None
copy_realm = None
unique_id = None

def __init__(self, region):
def __init__(self, region, unique_id=None):
self.settings = settings_map[self.tenant][region]
session = requests.session()
self.session = session
self._oauth = None
self._user_id = None
self.unique_id = unique_id
# ugly hack
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

Expand Down
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Polling-Intervall während des Ladevorgangs (Minuten)",
"interval_statistics": "Update-Intervall für Statistiken (Minuten)",
"interval_fetch": "Update-Intervall (Minuten)",
"region": "Region (EU, US)",
"region": "Region",
"imperial_distance": "Imperiale (amerikanische) Einheiten verwenden"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Anzahl Fahrten (akt. Monat)"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/dk.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Opdateringsfrekvens under ladning(minutter)",
"interval_statistics": "Opdateringsfrekvens for daglige/månedlige statistikker (minutter)",
"interval_fetch": "Opdateringsfrekvens for hentning (minutter)",
"region": "Region (EU, US)",
"region": "Region",
"imperial_distance": "Brug Engelske afstands enheder"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Månedlig ture"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Polling interval while charging (minutes)",
"interval_statistics": "Update interval for daily/monthly statistics (minutes)",
"interval_fetch": "Update interval (minutes)",
"region": "Region (EU, US)",
"region": "Region",
"imperial_distance": "Use imperial distance units (miles)"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Monthly Trips"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europe"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Intervalo de sondeo durante la carga (minutos)",
"interval_statistics": "Intervalo de actualización de estadísticas diarias/mensuales (minutos)",
"interval_fetch": "Intervalo de actualización (minutos)",
"region": "Región (UE, EE. UU.)",
"region": "Región",
"imperial_distance": "Usar unidades de distancia imperiales"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Viajes Mensuales"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Intervalle de sondage pendant la charge (minutes)",
"interval_statistics": "Intervalle de mise à jour des statistiques quotidiennes/mensuelles (minutes)",
"interval_fetch": "Intervalle de mise à jour (minutes)",
"region": "Région (EU, US)",
"region": "Région",
"imperial_distance": "Utiliser les unités de distance impériales"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Trajets mensuels"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europe"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Intervallo di polling durante la ricarica (minuti)",
"interval_statistics": "Intervallo di aggiornamento per le statistiche giornaliere/mensili (minuti)",
"interval_fetch": "Intervallo di aggiornamento (minuti)",
"region": "Regione (EU, US)",
"region": "Regione",
"imperial_distance": "Utilizza unità di misura imperiali per la distanza"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Viaggi mensili"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Polling interval tijdens laden (Minuten)",
"interval_statistics": "Update-interval voor dagelijkse/maandelijkse statistieken (Minuten)",
"interval_fetch": "Update interval (Minuten)",
"region": "Regio (EU, US)",
"region": "Regio",
"imperial_distance": "Gebruik imperiale afstandseenheden"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Maandelijkse ritafstand"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Oppdateringsfrekvens under lading for henting av data (i minutter)",
"interval_statistics": "Oppdateringsfrekvens for daglig / månedlig statistikk (i minutter)",
"interval_fetch": "Oppdateringsfrekvens for henting av data (i minutter)",
"region": "Region (EU, US)",
"region": "Region",
"imperial_distance": "Bruk Britiske/Amerikanske (Imperial) måleenheter"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Månedlige turer"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Interwał odpytywania podczas ładowania (minuty)",
"interval_statistics": "Interwał aktualizacji dziennych/miesięcznych statystyk (minuty)",
"interval_fetch": "Interwał aktualizacji (minuty)",
"region": "Region (EU, US)",
"region": "Region",
"imperial_distance": "Używaj jednostek imperialnych"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Miesięczne Trasy"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
9 changes: 8 additions & 1 deletion custom_components/nissan_connect/translations/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"interval_charging": "Intervalo de consulta durante o carregamento (minutos)",
"interval_statistics": "Intervalo de atualização para estatísticas diárias/mensais (minutos)",
"interval_fetch": "Intervalo de atualização (minutos)",
"region": "Região (EU, US)",
"region": "Região",
"imperial_distance": "Usar unidades de distância imperiais"
},
"data_description": {
Expand Down Expand Up @@ -135,5 +135,12 @@
"name": "Viagens mensais"
}
}
},
"selector": {
"region": {
"options": {
"eu": "Europa"
}
}
}
}
Loading

0 comments on commit 2dfcf60

Please sign in to comment.