From c5989a393f3f7d960b3e96738fba37a6c5ed084d Mon Sep 17 00:00:00 2001 From: rikroe Date: Sun, 10 May 2020 11:39:10 +0200 Subject: [PATCH] Enable services for multiple accounts --- .gitignore | 133 ++++++++++++++++++ .../bmw_connected_drive/__init__.py | 41 +++--- .../bmw_connected_drive/binary_sensor.py | 4 +- .../bmw_connected_drive/config_flow.py | 18 +-- custom_components/bmw_connected_drive/lock.py | 2 +- 5 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2473aeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +config/* +config2/* + +tests/testing_config/deps +tests/testing_config/home-assistant.log + +# hass-release +data/ +.token + +# Hide sublime text stuff +*.sublime-project +*.sublime-workspace + +# Hide some OS X stuff +.DS_Store +.AppleDouble +.LSOverride +Icon + +# Thumbnails +._* + +# IntelliJ IDEA +.idea +*.iml + +# pytest +.pytest_cache +.cache + +# GITHUB Proposed Python stuff: +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +.eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 +pip-wheel-metadata + +# Logs +*.log +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +coverage.xml +nosetests.xml +htmlcov/ +test-reports/ +test-results.xml +test-output.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +.python-version + +# emacs auto backups +*~ +*# +*.orig + +# venv stuff +pyvenv.cfg +pip-selfcheck.json +venv +.venv +Pipfile* +share/* +Scripts/ + +# vimmy stuff +*.swp +*.swo +tags +ctags.tmp + +# vagrant stuff +virtualization/vagrant/setup_done +virtualization/vagrant/.vagrant +virtualization/vagrant/config + +# Visual Studio Code +.vscode/* +!.vscode/cSpell.json +!.vscode/extensions.json +!.vscode/tasks.json + +# Built docs +docs/build + +# Windows Explorer +desktop.ini +/home-assistant.pyproj +/home-assistant.sln +/.vs/* + +# mypy +/.mypy_cache/* +/.dmypy.json + +# Secrets +.lokalise_token + +# monkeytype +monkeytype.sqlite3 + +# This is left behind by Azure Restore Cache +tmp_cache + +# python-language-server / Rope +.ropeproject diff --git a/custom_components/bmw_connected_drive/__init__.py b/custom_components/bmw_connected_drive/__init__.py index 486c052..92d9708 100644 --- a/custom_components/bmw_connected_drive/__init__.py +++ b/custom_components/bmw_connected_drive/__init__.py @@ -2,7 +2,6 @@ import asyncio from datetime import timedelta import logging -import json import async_timeout from bimmer_connected.account import ConnectedDriveAccount @@ -19,6 +18,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed import homeassistant.util.dt as dt_util +from .const import CONF_ALLOWED_REGIONS + _LOGGER = logging.getLogger(__name__) DOMAIN = "bmw_connected_drive" @@ -28,10 +29,10 @@ ACCOUNT_SCHEMA = vol.Schema( { - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Required(CONF_REGION): vol.Any("north_america", "china", "rest_of_world"), - vol.Optional(CONF_READ_ONLY, default=False): cv.boolean, + vol.Required(CONF_USERNAME): str, + vol.Required(CONF_PASSWORD): str, + vol.Required(CONF_REGION): vol.In(CONF_ALLOWED_REGIONS), + vol.Optional(CONF_READ_ONLY, default=False): bool, } ) @@ -122,15 +123,19 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ) ) - services = list(_SERVICE_MAP) + [SERVICE_UPDATE_STATE] - unload_services = all( - await asyncio.gather( - *[ - hass.async_add_executor_job(hass.services.remove, DOMAIN, service) - for service in services - ] + # Only remove services if it is the last account + if len(hass.data[DOMAIN]) == 1: + services = list(_SERVICE_MAP) + [SERVICE_UPDATE_STATE] + unload_services = all( + await asyncio.gather( + *[ + hass.async_add_executor_job(hass.services.remove, DOMAIN, service) + for service in services + ] + ) ) - ) + else: + unload_services = True if all([unload_ok, unload_services]): hass.data[DOMAIN].pop(entry.entry_id) @@ -148,13 +153,11 @@ def setup_account(account_config: dict, hass, name: str) -> "BMWConnectedDriveAc cd_account = BMWConnectedDriveAccount(username, password, region, name, read_only) def execute_service(call): - """Execute a service for a vehicle. - - This must be a member function as we need access to the cd_account - object here. - """ + """Execute a service for a vehicle.""" vin = call.data[ATTR_VIN] - vehicle = cd_account.account.get_vehicle(vin) + vehicle = None + for account in hass.data[DOMAIN]: + vehicle = account.get_vehicle(vin) if not vehicle: _LOGGER.error("Could not find a vehicle for VIN %s", vin) return diff --git a/custom_components/bmw_connected_drive/binary_sensor.py b/custom_components/bmw_connected_drive/binary_sensor.py index b00b645..440cbee 100644 --- a/custom_components/bmw_connected_drive/binary_sensor.py +++ b/custom_components/bmw_connected_drive/binary_sensor.py @@ -3,7 +3,9 @@ from bimmer_connected.state import ChargingState, LockState -from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.components.binary_sensor import ( + BinarySensorDevice as BinarySensorEntity, +) from homeassistant.const import ATTR_ATTRIBUTION, LENGTH_KILOMETERS from . import DOMAIN as BMW_DOMAIN diff --git a/custom_components/bmw_connected_drive/config_flow.py b/custom_components/bmw_connected_drive/config_flow.py index 47a8ad4..fe1eca4 100644 --- a/custom_components/bmw_connected_drive/config_flow.py +++ b/custom_components/bmw_connected_drive/config_flow.py @@ -1,25 +1,14 @@ """Config flow for BMW ConnectedDrive integration.""" import logging -import voluptuous as vol - from homeassistant import config_entries, core, exceptions -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.const import CONF_USERNAME -from . import DOMAIN, setup_account -from .const import CONF_ALLOWED_REGIONS, CONF_READ_ONLY, CONF_REGION +from . import ACCOUNT_SCHEMA as DATA_SCHEMA, DOMAIN, setup_account +from .const import CONF_REGION _LOGGER = logging.getLogger(__name__) -DATA_SCHEMA = vol.Schema( - { - vol.Required(CONF_USERNAME): str, - vol.Required(CONF_PASSWORD): str, - vol.Required(CONF_REGION): vol.In(CONF_ALLOWED_REGIONS), - vol.Optional(CONF_READ_ONLY, default=False): bool, - } -) - async def validate_input(hass: core.HomeAssistant, data): """Validate the user input allows us to connect. @@ -57,7 +46,6 @@ class BMWConnectedDriveConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_user(self, user_input=None): """Handle the initial step.""" - _LOGGER.info(user_input) errors = {} if user_input is not None: unique_id = f"{user_input[CONF_REGION]}-{user_input[CONF_USERNAME]}" diff --git a/custom_components/bmw_connected_drive/lock.py b/custom_components/bmw_connected_drive/lock.py index cdfac06..26d87b8 100644 --- a/custom_components/bmw_connected_drive/lock.py +++ b/custom_components/bmw_connected_drive/lock.py @@ -3,7 +3,7 @@ from bimmer_connected.state import LockState -from homeassistant.components.lock import LockEntity +from homeassistant.components.lock import LockDevice as LockEntity from homeassistant.const import ATTR_ATTRIBUTION, STATE_LOCKED, STATE_UNLOCKED from . import DOMAIN as BMW_DOMAIN