Files
core/homeassistant/components/wallbox/__init__.py
T
hesselonlineandjan iversen c497c0eadd Add wallbox integration (#48082)
* Wallbox component added

* resolved mergeconflicts from upstream

* fixed an incorrect removal in CODEOWNERS file

* fixes for pullrequest automatic test

* clean up code after PR tests

* fixed strings.json

* fix config_flow error > wallbox

* fixed some formatting issues

* fix pylint warnings

* fixed error in number.py > set value

* pylint warnings fixed

* some more pylint fixes

* isort fixes

* fix unused_import pylint

* remove tests

* remove test requirements

* config flow test

* test errors resolved

* test file formatting

* isort on test file

* sensor test

* isort on test

* isort test const

* remove not working sensor test

* remove test const

* add switch, number and lock test

* docstrings for test classes

* sort test_number, create test_sensor

* additional tests

* fix test error

* reduced PR to 1 component

* newline in const

* ignore test coverage -> dependency on external device (wallbox)

* do not ignore config_flow

* add test for validate_input

* remove obsolete import

* additional test config flow

* change test sensor

* docstring

* add additional test for exceptions

* fix test_config

* more tests

* fix test_config_flow

* fixed http error test

* catch connectionerror and introduce testing for this error

* remove .coveragefile

* change comment

* Update homeassistant/components/wallbox/__init__.py

review suggestion by janiversen

Co-authored-by: jan iversen <jancasacondor@gmail.com>

* Update homeassistant/components/wallbox/__init__.py

review suggestion by janiversen (format only)

Co-authored-by: jan iversen <jancasacondor@gmail.com>

* Processed review comments, include more testing for sensor component

* Isolated the async_add_executor_job to make the solution more async

* add a config flow test

* Revert "add a config flow test"

This reverts commit 9c1af82fffeb0b46f11ada1000e19b66fd5fd0f1.

* Revert "Isolated the async_add_executor_job to make the solution more async"

This reverts commit 0bf034c3318f27e649389830d4ad7a7e10eb2d6f.

* Make component more async and add config flow tests

* Changes based on review comments

* made _ methods in WallboxHub for the 'non-async' call to the API and try-catch. Stored the wallbox in the class.

* moved the coordinator to __init__ and pass it as part of the WallboxHub class

* removed obsolete function in __init__

* removed CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

* fixed spelling and imports on test files

* did isort on component files

Co-authored-by: jan iversen <jancasacondor@gmail.com>
2021-05-24 13:08:24 +02:00

148 lines
4.6 KiB
Python

"""The Wallbox integration."""
import asyncio
from datetime import timedelta
import logging
import requests
from wallbox import Wallbox
from homeassistant import exceptions
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_CONNECTIONS, CONF_ROUND, CONF_SENSOR_TYPES, CONF_STATION, DOMAIN
_LOGGER = logging.getLogger(__name__)
PLATFORMS = ["sensor"]
UPDATE_INTERVAL = 30
class WallboxHub:
"""Wallbox Hub class."""
def __init__(self, station, username, password, hass):
"""Initialize."""
self._station = station
self._username = username
self._password = password
self._wallbox = Wallbox(self._username, self._password)
self._hass = hass
self._coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
# Name of the data. For logging purposes.
name="wallbox",
update_method=self.async_get_data,
# Polling interval. Will only be polled if there are subscribers.
update_interval=timedelta(seconds=UPDATE_INTERVAL),
)
def _authenticate(self):
"""Authenticate using Wallbox API."""
try:
self._wallbox.authenticate()
return True
except requests.exceptions.HTTPError as wallbox_connection_error:
if wallbox_connection_error.response.status_code == 403:
raise InvalidAuth from wallbox_connection_error
raise ConnectionError from wallbox_connection_error
def _get_data(self):
"""Get new sensor data for Wallbox component."""
try:
self._authenticate()
data = self._wallbox.getChargerStatus(self._station)
filtered_data = {k: data[k] for k in CONF_SENSOR_TYPES if k in data}
for key, value in filtered_data.items():
sensor_round = CONF_SENSOR_TYPES[key][CONF_ROUND]
if sensor_round:
try:
filtered_data[key] = round(value, sensor_round)
except TypeError:
_LOGGER.debug("Cannot format %s", key)
return filtered_data
except requests.exceptions.HTTPError as wallbox_connection_error:
raise ConnectionError from wallbox_connection_error
async def async_coordinator_first_refresh(self):
"""Refresh coordinator for the first time."""
await self._coordinator.async_config_entry_first_refresh()
async def async_authenticate(self) -> bool:
"""Authenticate using Wallbox API."""
return await self._hass.async_add_executor_job(self._authenticate)
async def async_get_data(self) -> bool:
"""Get new sensor data for Wallbox component."""
data = await self._hass.async_add_executor_job(self._get_data)
return data
@property
def coordinator(self):
"""Return the coordinator."""
return self._coordinator
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up Wallbox from a config entry."""
wallbox = WallboxHub(
entry.data[CONF_STATION],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
hass,
)
await wallbox.async_authenticate()
await wallbox.async_coordinator_first_refresh()
hass.data.setdefault(DOMAIN, {CONF_CONNECTIONS: {}})
hass.data[DOMAIN][CONF_CONNECTIONS][entry.entry_id] = wallbox
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry."""
unload_ok = all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
for platform in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN]["connections"].pop(entry.entry_id)
return unload_ok
class CannotConnect(exceptions.HomeAssistantError):
"""Error to indicate we cannot connect."""
def __init__(self, msg=""):
"""Create a log record."""
super().__init__()
_LOGGER.error("Cannot connect to Wallbox API. %s", msg)
class InvalidAuth(exceptions.HomeAssistantError):
"""Error to indicate there is invalid auth."""
def __init__(self, msg=""):
"""Create a log record."""
super().__init__()
_LOGGER.error("Cannot authenticate with Wallbox API. %s", msg)