Bump plugwise to v0.25.12 (#82146)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
fixes undefined
This commit is contained in:
Bouwe Westerdijk 2022-11-25 10:55:51 +01:00 committed by GitHub
parent 9feb64cebd
commit ea1868b7b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 123 additions and 64 deletions

View File

@ -36,6 +36,12 @@ BINARY_SENSORS: tuple[PlugwiseBinarySensorEntityDescription, ...] = (
icon_off="mdi:hvac-off", icon_off="mdi:hvac-off",
entity_category=EntityCategory.DIAGNOSTIC, entity_category=EntityCategory.DIAGNOSTIC,
), ),
PlugwiseBinarySensorEntityDescription(
key="cooling_enabled",
name="Cooling enabled",
icon="mdi:snowflake-thermometer",
entity_category=EntityCategory.DIAGNOSTIC,
),
PlugwiseBinarySensorEntityDescription( PlugwiseBinarySensorEntityDescription(
key="dhw_state", key="dhw_state",
name="DHW state", name="DHW state",

View File

@ -4,9 +4,12 @@ from __future__ import annotations
from typing import Any from typing import Any
from plugwise.exceptions import ( from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication, InvalidAuthentication,
InvalidSetupError, InvalidSetupError,
PlugwiseException, InvalidXMLError,
ResponseError,
UnsupportedDeviceError,
) )
from plugwise.smile import Smile from plugwise.smile import Smile
import voluptuous as vol import voluptuous as vol
@ -32,7 +35,6 @@ from .const import (
DOMAIN, DOMAIN,
FLOW_SMILE, FLOW_SMILE,
FLOW_STRETCH, FLOW_STRETCH,
LOGGER,
PW_TYPE, PW_TYPE,
SMILE, SMILE,
STRETCH, STRETCH,
@ -175,14 +177,17 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN):
try: try:
api = await validate_gw_input(self.hass, user_input) api = await validate_gw_input(self.hass, user_input)
except InvalidSetupError: except ConnectionFailedError:
errors[CONF_BASE] = "invalid_setup" errors[CONF_BASE] = "cannot_connect"
except InvalidAuthentication: except InvalidAuthentication:
errors[CONF_BASE] = "invalid_auth" errors[CONF_BASE] = "invalid_auth"
except PlugwiseException: except InvalidSetupError:
errors[CONF_BASE] = "cannot_connect" errors[CONF_BASE] = "invalid_setup"
except (InvalidXMLError, ResponseError):
errors[CONF_BASE] = "response_error"
except UnsupportedDeviceError:
errors[CONF_BASE] = "unsupported"
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
LOGGER.exception("Unexpected exception")
errors[CONF_BASE] = "unknown" errors[CONF_BASE] = "unknown"
else: else:
await self.async_set_unique_id( await self.async_set_unique_id(

View File

@ -4,7 +4,13 @@ from typing import NamedTuple, cast
from plugwise import Smile from plugwise import Smile
from plugwise.constants import DeviceData, GatewayData from plugwise.constants import DeviceData, GatewayData
from plugwise.exceptions import PlugwiseException, XMLDataMissingError from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication,
InvalidXMLError,
ResponseError,
UnsupportedDeviceError,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.debounce import Debouncer from homeassistant.helpers.debounce import Debouncer
@ -47,12 +53,16 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]):
"""Fetch data from Plugwise.""" """Fetch data from Plugwise."""
try: try:
data = await self.api.async_update() data = await self.api.async_update()
except XMLDataMissingError as err: except InvalidAuthentication as err:
raise UpdateFailed("Authentication failed") from err
except (InvalidXMLError, ResponseError) as err:
raise UpdateFailed( raise UpdateFailed(
f"No XML data received for: {self.api.smile_name}" "Invalid XML data, or error indication received for the Plugwise Adam/Smile/Stretch"
) from err ) from err
except PlugwiseException as err: except UnsupportedDeviceError as err:
raise UpdateFailed(f"Updated failed for: {self.api.smile_name}") from err raise UpdateFailed("Device with unsupported firmware") from err
except ConnectionFailedError as err:
raise UpdateFailed("Failed to connect") from err
return PlugwiseData( return PlugwiseData(
gateway=cast(GatewayData, data[0]), gateway=cast(GatewayData, data[0]),
devices=cast(dict[str, DeviceData], data[1]), devices=cast(dict[str, DeviceData], data[1]),

View File

@ -1,17 +1,21 @@
"""Plugwise platform for Home Assistant Core.""" """Plugwise platform for Home Assistant Core."""
from __future__ import annotations from __future__ import annotations
import asyncio
from typing import Any from typing import Any
from aiohttp import ClientConnectionError from plugwise.exceptions import (
from plugwise.exceptions import InvalidAuthentication, PlugwiseException ConnectionFailedError,
InvalidAuthentication,
InvalidXMLError,
ResponseError,
UnsupportedDeviceError,
)
from plugwise.smile import Smile from plugwise.smile import Smile
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -42,17 +46,16 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
try: try:
connected = await api.connect() connected = await api.connect()
except InvalidAuthentication: except ConnectionFailedError as err:
LOGGER.error("Invalid username or Smile ID") raise ConfigEntryNotReady("Failed to connect to the Plugwise Smile") from err
return False except InvalidAuthentication as err:
except (ClientConnectionError, PlugwiseException) as err: raise HomeAssistantError("Invalid username or Smile ID") from err
except (InvalidXMLError, ResponseError) as err:
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
f"Error while communicating to device {api.smile_name}" "Error while communicating to the Plugwise Smile"
) from err
except asyncio.TimeoutError as err:
raise ConfigEntryNotReady(
f"Timeout while connecting to Smile {api.smile_name}"
) from err ) from err
except UnsupportedDeviceError as err:
raise HomeAssistantError("Device with unsupported firmware") from err
if not connected: if not connected:
raise ConfigEntryNotReady("Unable to connect to Smile") raise ConfigEntryNotReady("Unable to connect to Smile")

View File

@ -2,7 +2,7 @@
"domain": "plugwise", "domain": "plugwise",
"name": "Plugwise", "name": "Plugwise",
"documentation": "https://www.home-assistant.io/integrations/plugwise", "documentation": "https://www.home-assistant.io/integrations/plugwise",
"requirements": ["plugwise==0.25.7"], "requirements": ["plugwise==0.25.12"],
"codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"],
"zeroconf": ["_plugwise._tcp.local."], "zeroconf": ["_plugwise._tcp.local."],
"config_flow": true, "config_flow": true,

View File

@ -15,8 +15,10 @@
"error": { "error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", "invalid_setup": "Add your Adam instead of your Anna, see the documentation",
"unknown": "[%key:common::config_flow::error::unknown%]" "response_error": "Invalid XML data, or error indication received",
"unknown": "[%key:common::config_flow::error::unknown%]",
"unsupported": "Device with unsupported firmware"
}, },
"abort": { "abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]", "already_configured": "[%key:common::config_flow::abort::already_configured_service%]",

View File

@ -7,8 +7,10 @@
"error": { "error": {
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"invalid_auth": "Invalid authentication", "invalid_auth": "Invalid authentication",
"invalid_setup": "Add your Adam instead of your Anna, see the Home Assistant Plugwise integration documentation for more information", "invalid_setup": "Add your Adam instead of your Anna, see the documentation",
"unknown": "Unexpected error" "response_error": "Invalid XML data, or error indication received",
"unknown": "Unexpected error",
"unsupported": "Device with unsupported firmware"
}, },
"step": { "step": {
"user": { "user": {

View File

@ -1329,7 +1329,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13 plexwebsocket==0.0.13
# homeassistant.components.plugwise # homeassistant.components.plugwise
plugwise==0.25.7 plugwise==0.25.12
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -956,7 +956,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13 plexwebsocket==0.0.13
# homeassistant.components.plugwise # homeassistant.components.plugwise
plugwise==0.25.7 plugwise==0.25.12
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -3,7 +3,7 @@
"smile_name": "Smile Anna", "smile_name": "Smile Anna",
"gateway_id": "015ae9ea3f964e668e490fa39da3870b", "gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927", "heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"cooling_present": true, "cooling_present": false,
"notifications": {} "notifications": {}
}, },
{ {
@ -21,10 +21,10 @@
}, },
"available": true, "available": true,
"binary_sensors": { "binary_sensors": {
"cooling_enabled": false,
"dhw_state": false, "dhw_state": false,
"heating_state": true, "heating_state": true,
"compressor_state": true, "compressor_state": true,
"cooling_state": false,
"slave_boiler_state": false, "slave_boiler_state": false,
"flame_state": false "flame_state": false
}, },
@ -66,13 +66,11 @@
"name": "Anna", "name": "Anna",
"vendor": "Plugwise", "vendor": "Plugwise",
"thermostat": { "thermostat": {
"setpoint_low": 20.5, "setpoint": 20.5,
"setpoint_high": 24.0,
"lower_bound": 4.0, "lower_bound": 4.0,
"upper_bound": 30.0, "upper_bound": 30.0,
"resolution": 0.1 "resolution": 0.1
}, },
"available": true,
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home", "active_preset": "home",
"available_schedules": ["standaard"], "available_schedules": ["standaard"],
@ -81,11 +79,10 @@
"mode": "auto", "mode": "auto",
"sensors": { "sensors": {
"temperature": 19.3, "temperature": 19.3,
"setpoint": 20.5,
"illuminance": 86.0, "illuminance": 86.0,
"cooling_activation_outdoor_temperature": 21.0, "cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4.0, "cooling_deactivation_threshold": 4.0
"setpoint_low": 20.5,
"setpoint_high": 24.0
} }
} }
} }

View File

@ -21,6 +21,7 @@
}, },
"available": true, "available": true,
"binary_sensors": { "binary_sensors": {
"cooling_enabled": true,
"dhw_state": false, "dhw_state": false,
"heating_state": false, "heating_state": false,
"compressor_state": true, "compressor_state": true,
@ -72,7 +73,6 @@
"upper_bound": 30.0, "upper_bound": 30.0,
"resolution": 0.1 "resolution": 0.1
}, },
"available": true,
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home", "active_preset": "home",
"available_schedules": ["standaard"], "available_schedules": ["standaard"],

View File

@ -21,6 +21,7 @@
}, },
"available": true, "available": true,
"binary_sensors": { "binary_sensors": {
"cooling_enabled": true,
"dhw_state": false, "dhw_state": false,
"heating_state": false, "heating_state": false,
"compressor_state": false, "compressor_state": false,
@ -72,7 +73,6 @@
"upper_bound": 30.0, "upper_bound": 30.0,
"resolution": 0.1 "resolution": 0.1
}, },
"available": true,
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"], "preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home", "active_preset": "home",
"available_schedules": ["standaard"], "available_schedules": ["standaard"],

View File

@ -26,7 +26,7 @@ async def test_anna_climate_binary_sensor_entities(
assert state assert state
assert state.state == STATE_ON assert state.state == STATE_ON
state = hass.states.get("binary_sensor.opentherm_cooling") state = hass.states.get("binary_sensor.opentherm_cooling_enabled")
assert state assert state
assert state.state == STATE_OFF assert state.state == STATE_OFF

View File

@ -202,7 +202,7 @@ async def test_anna_climate_entity_attributes(
assert state.state == HVACMode.AUTO assert state.state == HVACMode.AUTO
assert state.attributes["hvac_action"] == "heating" assert state.attributes["hvac_action"] == "heating"
assert state.attributes["hvac_modes"] == [ assert state.attributes["hvac_modes"] == [
HVACMode.HEAT_COOL, HVACMode.HEAT,
HVACMode.AUTO, HVACMode.AUTO,
] ]
@ -211,9 +211,8 @@ async def test_anna_climate_entity_attributes(
assert state.attributes["current_temperature"] == 19.3 assert state.attributes["current_temperature"] == 19.3
assert state.attributes["preset_mode"] == "home" assert state.attributes["preset_mode"] == "home"
assert state.attributes["supported_features"] == 18 assert state.attributes["supported_features"] == 17
assert state.attributes["target_temp_high"] == 24.0 assert state.attributes["temperature"] == 20.5
assert state.attributes["target_temp_low"] == 20.5
assert state.attributes["min_temp"] == 4.0 assert state.attributes["min_temp"] == 4.0
assert state.attributes["max_temp"] == 30.0 assert state.attributes["max_temp"] == 30.0
assert state.attributes["target_temp_step"] == 0.1 assert state.attributes["target_temp_step"] == 0.1
@ -286,7 +285,7 @@ async def test_anna_climate_entity_climate_changes(
await hass.services.async_call( await hass.services.async_call(
"climate", "climate",
"set_hvac_mode", "set_hvac_mode",
{"entity_id": "climate.anna", "hvac_mode": "heat_cool"}, {"entity_id": "climate.anna", "hvac_mode": "heat"},
blocking=True, blocking=True,
) )

View File

@ -5,7 +5,9 @@ from plugwise.exceptions import (
ConnectionFailedError, ConnectionFailedError,
InvalidAuthentication, InvalidAuthentication,
InvalidSetupError, InvalidSetupError,
PlugwiseException, InvalidXMLError,
ResponseError,
UnsupportedDeviceError,
) )
import pytest import pytest
@ -97,9 +99,12 @@ def mock_smile():
with patch( with patch(
"homeassistant.components.plugwise.config_flow.Smile", "homeassistant.components.plugwise.config_flow.Smile",
) as smile_mock: ) as smile_mock:
smile_mock.PlugwiseException = PlugwiseException
smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.InvalidSetupError = InvalidSetupError
smile_mock.InvalidXMLError = InvalidXMLError
smile_mock.ResponseError = ResponseError
smile_mock.UnsupportedDeviceError = UnsupportedDeviceError
smile_mock.return_value.connect.return_value = True smile_mock.return_value.connect.return_value = True
yield smile_mock.return_value yield smile_mock.return_value
@ -266,13 +271,15 @@ async def test_zercoconf_discovery_update_configuration(
@pytest.mark.parametrize( @pytest.mark.parametrize(
"side_effect,reason", "side_effect, reason",
[ [
(ConnectionFailedError, "cannot_connect"),
(InvalidAuthentication, "invalid_auth"), (InvalidAuthentication, "invalid_auth"),
(InvalidSetupError, "invalid_setup"), (InvalidSetupError, "invalid_setup"),
(ConnectionFailedError, "cannot_connect"), (InvalidXMLError, "response_error"),
(PlugwiseException, "cannot_connect"), (ResponseError, "response_error"),
(RuntimeError, "unknown"), (RuntimeError, "unknown"),
(UnsupportedDeviceError, "unsupported"),
], ],
) )
async def test_flow_errors( async def test_flow_errors(

View File

@ -1,12 +1,12 @@
"""Tests for the Plugwise Climate integration.""" """Tests for the Plugwise Climate integration."""
import asyncio
from unittest.mock import MagicMock from unittest.mock import MagicMock
import aiohttp
from plugwise.exceptions import ( from plugwise.exceptions import (
ConnectionFailedError, ConnectionFailedError,
PlugwiseException, InvalidAuthentication,
XMLDataMissingError, InvalidXMLError,
ResponseError,
UnsupportedDeviceError,
) )
import pytest import pytest
@ -43,24 +43,52 @@ async def test_load_unload_config_entry(
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
@pytest.mark.parametrize(
"side_effect, entry_state",
[
(ConnectionFailedError, ConfigEntryState.SETUP_RETRY),
(InvalidAuthentication, ConfigEntryState.SETUP_ERROR),
(InvalidXMLError, ConfigEntryState.SETUP_RETRY),
(ResponseError, ConfigEntryState.SETUP_RETRY),
(UnsupportedDeviceError, ConfigEntryState.SETUP_ERROR),
],
)
async def test_gateway_config_entry_not_ready(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_smile_anna: MagicMock,
side_effect: Exception,
entry_state: ConfigEntryState,
) -> None:
"""Test the Plugwise configuration entry not ready."""
mock_smile_anna.connect.side_effect = side_effect
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert len(mock_smile_anna.connect.mock_calls) == 1
assert mock_config_entry.state is entry_state
@pytest.mark.parametrize( @pytest.mark.parametrize(
"side_effect", "side_effect",
[ [
(ConnectionFailedError), (ConnectionFailedError),
(PlugwiseException), (InvalidAuthentication),
(XMLDataMissingError), (InvalidXMLError),
(asyncio.TimeoutError), (ResponseError),
(aiohttp.ClientConnectionError), (UnsupportedDeviceError),
], ],
) )
async def test_config_entry_not_ready( async def test_coord_config_entry_not_ready(
hass: HomeAssistant, hass: HomeAssistant,
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
mock_smile_anna: MagicMock, mock_smile_anna: MagicMock,
side_effect: Exception, side_effect: Exception,
) -> None: ) -> None:
"""Test the Plugwise configuration entry not ready.""" """Test the Plugwise configuration entry not ready."""
mock_smile_anna.connect.side_effect = side_effect mock_smile_anna.async_update.side_effect = side_effect
mock_config_entry.add_to_hass(hass) mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id) await hass.config_entries.async_setup(mock_config_entry.entry_id)