mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Improve error handling for Powerwall (#34580)
* Improve error handling modified: homeassistant/components/powerwall/__init__.py modified: homeassistant/components/powerwall/config_flow.py modified: homeassistant/components/powerwall/const.py modified: homeassistant/components/powerwall/strings.json modified: homeassistant/components/powerwall/translations/en.json * Change exception name modified: homeassistant/components/powerwall/__init__.py modified: homeassistant/components/powerwall/config_flow.py * Add test * Rename PowerwallError to POWERWALL_ERROR * Modify handling of APIChangedErrors modified: homeassistant/components/powerwall/__init__.py modified: homeassistant/components/powerwall/const.py
This commit is contained in:
parent
aa2bfbb541
commit
4b998ea6af
@ -4,7 +4,7 @@ from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import requests
|
||||
from tesla_powerwall import APIError, Powerwall, PowerwallUnreachableError
|
||||
from tesla_powerwall import APIChangedError, Powerwall, PowerwallUnreachableError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
@ -13,10 +13,11 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import entity_registry
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
POWERWALL_API_CHANGED,
|
||||
POWERWALL_API_CHARGE,
|
||||
POWERWALL_API_DEVICE_TYPE,
|
||||
POWERWALL_API_GRID_STATUS,
|
||||
@ -64,7 +65,7 @@ async def _migrate_old_unique_ids(hass, entry_id, powerwall_data):
|
||||
@callback
|
||||
def _async_migrator(entity_entry: entity_registry.RegistryEntry):
|
||||
parts = entity_entry.unique_id.split("_")
|
||||
# Check if the unique_id starts with the serial_numbers of the powerwakks
|
||||
# Check if the unique_id starts with the serial_numbers of the powerwalls
|
||||
if parts[0 : len(serial_numbers)] != serial_numbers:
|
||||
# The old unique_id ended with the nomianal_system_engery_kWh so we can use that
|
||||
# to find the old base unique_id and extract the device_suffix.
|
||||
@ -87,6 +88,17 @@ async def _migrate_old_unique_ids(hass, entry_id, powerwall_data):
|
||||
await entity_registry.async_migrate_entries(hass, entry_id, _async_migrator)
|
||||
|
||||
|
||||
async def _async_handle_api_changed_error(hass: HomeAssistant, error: APIChangedError):
|
||||
# The error might include some important information about what exactly changed.
|
||||
_LOGGER.error(str(error))
|
||||
hass.components.persistent_notification.async_create(
|
||||
"It seems like your powerwall uses an unsupported version. "
|
||||
"Please update the software of your powerwall or if it is"
|
||||
"already the newest consider reporting this issue.\nSee logs for more information",
|
||||
title="Unknown powerwall software version",
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Set up Tesla Powerwall from a config entry."""
|
||||
|
||||
@ -97,16 +109,37 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
power_wall = Powerwall(entry.data[CONF_IP_ADDRESS], http_session=http_session)
|
||||
try:
|
||||
await hass.async_add_executor_job(power_wall.detect_and_pin_version)
|
||||
await hass.async_add_executor_job(_fetch_powerwall_data, power_wall)
|
||||
powerwall_data = await hass.async_add_executor_job(call_base_info, power_wall)
|
||||
except (PowerwallUnreachableError, APIError, ConnectionError):
|
||||
except PowerwallUnreachableError:
|
||||
http_session.close()
|
||||
raise ConfigEntryNotReady
|
||||
except APIChangedError as err:
|
||||
http_session.close()
|
||||
await _async_handle_api_changed_error(hass, err)
|
||||
return False
|
||||
|
||||
await _migrate_old_unique_ids(hass, entry_id, powerwall_data)
|
||||
|
||||
async def async_update_data():
|
||||
"""Fetch data from API endpoint."""
|
||||
return await hass.async_add_executor_job(_fetch_powerwall_data, power_wall)
|
||||
# Check if we had an error before
|
||||
_LOGGER.info("Checking if update failed")
|
||||
if not hass.data[DOMAIN][entry.entry_id][POWERWALL_API_CHANGED]:
|
||||
_LOGGER.info("Updating data")
|
||||
try:
|
||||
return await hass.async_add_executor_job(
|
||||
_fetch_powerwall_data, power_wall
|
||||
)
|
||||
except PowerwallUnreachableError:
|
||||
raise UpdateFailed("Unable to fetch data from powerwall")
|
||||
except APIChangedError as err:
|
||||
await _async_handle_api_changed_error(hass, err)
|
||||
hass.data[DOMAIN][entry.entry_id][POWERWALL_API_CHANGED] = True
|
||||
# Returns the cached data. This data can also be None
|
||||
return hass.data[DOMAIN][entry.entry_id][POWERWALL_COORDINATOR].data
|
||||
else:
|
||||
return hass.data[DOMAIN][entry.entry_id][POWERWALL_COORDINATOR].data
|
||||
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
@ -122,6 +155,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
POWERWALL_OBJECT: power_wall,
|
||||
POWERWALL_COORDINATOR: coordinator,
|
||||
POWERWALL_HTTP_SESSION: http_session,
|
||||
POWERWALL_API_CHANGED: False,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Config flow for Tesla Powerwall integration."""
|
||||
import logging
|
||||
|
||||
from tesla_powerwall import APIError, Powerwall, PowerwallUnreachableError
|
||||
from tesla_powerwall import APIChangedError, Powerwall, PowerwallUnreachableError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, core, exceptions
|
||||
@ -25,8 +25,12 @@ async def validate_input(hass: core.HomeAssistant, data):
|
||||
try:
|
||||
await hass.async_add_executor_job(power_wall.detect_and_pin_version)
|
||||
site_info = await hass.async_add_executor_job(power_wall.get_site_info)
|
||||
except (PowerwallUnreachableError, APIError, ConnectionError):
|
||||
except PowerwallUnreachableError:
|
||||
raise CannotConnect
|
||||
except APIChangedError as err:
|
||||
# Only log the exception without the traceback
|
||||
_LOGGER.error(str(err))
|
||||
raise WrongVersion
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {"title": site_info.site_name}
|
||||
@ -46,6 +50,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
info = await validate_input(self.hass, user_input)
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except WrongVersion:
|
||||
errors["base"] = "wrong_version"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
@ -69,3 +75,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
class CannotConnect(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
||||
|
||||
class WrongVersion(exceptions.HomeAssistantError):
|
||||
"""Error to indicate the powerwall uses a software version we cannot interact with."""
|
||||
|
@ -4,6 +4,7 @@ DOMAIN = "powerwall"
|
||||
|
||||
POWERWALL_OBJECT = "powerwall"
|
||||
POWERWALL_COORDINATOR = "coordinator"
|
||||
POWERWALL_API_CHANGED = "api_changed"
|
||||
|
||||
UPDATE_INTERVAL = 30
|
||||
|
||||
@ -16,14 +17,6 @@ ATTR_INSTANT_AVERAGE_VOLTAGE = "instant_average_voltage"
|
||||
ATTR_NOMINAL_SYSTEM_POWER = "nominal_system_power_kW"
|
||||
ATTR_IS_ACTIVE = "is_active"
|
||||
|
||||
SITE_INFO_UTILITY = "utility"
|
||||
SITE_INFO_GRID_CODE = "grid_code"
|
||||
SITE_INFO_NOMINAL_SYSTEM_POWER_KW = "nominal_system_power_kW"
|
||||
SITE_INFO_NOMINAL_SYSTEM_ENERGY_KWH = "nominal_system_energy_kWh"
|
||||
SITE_INFO_REGION = "region"
|
||||
|
||||
DEVICE_TYPE_DEVICE_TYPE = "device_type"
|
||||
|
||||
STATUS_VERSION = "version"
|
||||
|
||||
POWERWALL_SITE_NAME = "site_name"
|
||||
@ -39,10 +32,6 @@ POWERWALL_API_SERIAL_NUMBERS = "serial_numbers"
|
||||
|
||||
POWERWALL_HTTP_SESSION = "http_session"
|
||||
|
||||
POWERWALL_GRID_ONLINE = "SystemGridConnected"
|
||||
POWERWALL_CONNECTED_KEY = "connected_to_tesla"
|
||||
POWERWALL_RUNNING_KEY = "running"
|
||||
|
||||
POWERWALL_BATTERY_METER = "battery"
|
||||
|
||||
MODEL = "PowerWall 2"
|
||||
|
@ -8,6 +8,7 @@
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect, please try again",
|
||||
"wrong_version": "Your powerwall uses a software version that is not supported. Please consider upgrading or reporting this issue so it can be resolved.",
|
||||
"unknown": "Unexpected error"
|
||||
},
|
||||
"abort": { "already_configured": "The powerwall is already configured" }
|
||||
|
@ -5,7 +5,8 @@
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect, please try again",
|
||||
"unknown": "Unexpected error"
|
||||
"unknown": "Unexpected error",
|
||||
"wrong_version": "Your powerwall uses a software version that is not supported. Please consider upgrading or reporting this issue so it can be resolved."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Test the Powerwall config flow."""
|
||||
|
||||
from asynctest import patch
|
||||
from tesla_powerwall import PowerwallUnreachableError
|
||||
from tesla_powerwall import APIChangedError, PowerwallUnreachableError
|
||||
|
||||
from homeassistant import config_entries, setup
|
||||
from homeassistant.components.powerwall.const import DOMAIN
|
||||
@ -86,3 +86,23 @@ async def test_form_cannot_connect(hass):
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_form_wrong_version(hass):
|
||||
"""Test we can handle wrong version error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
mock_powerwall = _mock_powerwall_side_effect(site_info=APIChangedError(object, {}))
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.powerwall.config_flow.Powerwall",
|
||||
return_value=mock_powerwall,
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_IP_ADDRESS: "1.2.3.4"},
|
||||
)
|
||||
|
||||
assert result3["type"] == "form"
|
||||
assert result3["errors"] == {"base": "wrong_version"}
|
||||
|
Loading…
x
Reference in New Issue
Block a user