Change Plugwise integration to plugwise module (#43036)

* Switch to plugwise module and forthcoming changes

* Adjusted according to review

* Fix leaving out domain for tests

* Add tests for exceptions

* Add more tests for exceptions

* Version bump

* Wording on test

* Catch-up with dev
This commit is contained in:
Tom 2020-11-21 03:43:20 +01:00 committed by GitHub
parent e32669a2d9
commit db60a71603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 163 additions and 86 deletions

View File

@ -336,7 +336,7 @@ homeassistant/components/pi_hole/* @fabaff @johnluetke @shenxn
homeassistant/components/pilight/* @trekky12 homeassistant/components/pilight/* @trekky12
homeassistant/components/plaato/* @JohNan homeassistant/components/plaato/* @JohNan
homeassistant/components/plex/* @jjlawren homeassistant/components/plex/* @jjlawren
homeassistant/components/plugwise/* @CoMPaTech @bouwew homeassistant/components/plugwise/* @CoMPaTech @bouwew @brefra
homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa homeassistant/components/plum_lightpad/* @ColinHarrington @prystupa
homeassistant/components/point/* @fredrike homeassistant/components/point/* @fredrike
homeassistant/components/poolsense/* @haemishkyd homeassistant/components/poolsense/* @haemishkyd

View File

@ -1,16 +1,10 @@
"""Plugwise platform for Home Assistant Core.""" """Plugwise platform for Home Assistant Core."""
import asyncio
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .const import ALL_PLATFORMS, DOMAIN, UNDO_UPDATE_LISTENER from .gateway import async_setup_entry_gw, async_unload_entry_gw
from .gateway import async_setup_entry_gw
CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass: HomeAssistant, config: dict): async def async_setup(hass: HomeAssistant, config: dict):
@ -27,19 +21,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry.""" """Unload the Plugwise components."""
unload_ok = all( if entry.data.get(CONF_HOST):
await asyncio.gather( return await async_unload_entry_gw(hass, entry)
*[ # PLACEHOLDER USB entry setup
hass.config_entries.async_forward_entry_unload(entry, component) return False
for component in ALL_PLATFORMS
]
)
)
hass.data[DOMAIN][entry.entry_id][UNDO_UPDATE_LISTENER]()
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -2,7 +2,7 @@
import logging import logging
from Plugwise_Smile.Smile import Smile from plugwise.exceptions import PlugwiseException
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
@ -192,7 +192,7 @@ class PwThermostat(SmileGateway, ClimateEntity):
await self._api.set_temperature(self._loc_id, temperature) await self._api.set_temperature(self._loc_id, temperature)
self._setpoint = temperature self._setpoint = temperature
self.async_write_ha_state() self.async_write_ha_state()
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
else: else:
_LOGGER.error("Invalid temperature requested") _LOGGER.error("Invalid temperature requested")
@ -205,7 +205,7 @@ class PwThermostat(SmileGateway, ClimateEntity):
try: try:
await self._api.set_temperature(self._loc_id, self._schedule_temp) await self._api.set_temperature(self._loc_id, self._schedule_temp)
self._setpoint = self._schedule_temp self._setpoint = self._schedule_temp
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
try: try:
await self._api.set_schedule_state( await self._api.set_schedule_state(
@ -213,7 +213,7 @@ class PwThermostat(SmileGateway, ClimateEntity):
) )
self._hvac_mode = hvac_mode self._hvac_mode = hvac_mode
self.async_write_ha_state() self.async_write_ha_state()
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
async def async_set_preset_mode(self, preset_mode): async def async_set_preset_mode(self, preset_mode):
@ -223,7 +223,7 @@ class PwThermostat(SmileGateway, ClimateEntity):
self._preset_mode = preset_mode self._preset_mode = preset_mode
self._setpoint = self._presets.get(self._preset_mode, "none")[0] self._setpoint = self._presets.get(self._preset_mode, "none")[0]
self.async_write_ha_state() self.async_write_ha_state()
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
@callback @callback

View File

@ -1,7 +1,8 @@
"""Config flow for Plugwise integration.""" """Config flow for Plugwise integration."""
import logging import logging
from Plugwise_Smile.Smile import Smile from plugwise.exceptions import InvalidAuthentication, PlugwiseException
from plugwise.smile import Smile
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries, core, exceptions from homeassistant import config_entries, core, exceptions
@ -67,9 +68,9 @@ async def validate_gw_input(hass: core.HomeAssistant, data):
try: try:
await api.connect() await api.connect()
except Smile.InvalidAuthentication as err: except InvalidAuthentication as err:
raise InvalidAuth from err raise InvalidAuth from err
except Smile.PlugwiseError as err: except PlugwiseException as err:
raise CannotConnect from err raise CannotConnect from err
return api return api

View File

@ -2,7 +2,9 @@
DOMAIN = "plugwise" DOMAIN = "plugwise"
SENSOR_PLATFORMS = ["sensor", "switch"] SENSOR_PLATFORMS = ["sensor", "switch"]
ALL_PLATFORMS = ["binary_sensor", "climate", "sensor", "switch"] PLATFORMS_GATEWAY = ["binary_sensor", "climate", "sensor", "switch"]
PW_TYPE = "plugwise_type"
GATEWAY = "gateway"
# Sensor mapping # Sensor mapping
SENSOR_MAP_DEVICE_CLASS = 2 SENSOR_MAP_DEVICE_CLASS = 2

View File

@ -5,8 +5,13 @@ from datetime import timedelta
import logging import logging
from typing import Dict from typing import Dict
from Plugwise_Smile.Smile import Smile
import async_timeout import async_timeout
from plugwise.exceptions import (
InvalidAuthentication,
PlugwiseException,
XMLDataMissingError,
)
from plugwise.smile import Smile
import voluptuous as vol import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -28,13 +33,15 @@ from homeassistant.helpers.update_coordinator import (
) )
from .const import ( from .const import (
ALL_PLATFORMS,
COORDINATOR, COORDINATOR,
DEFAULT_PORT, DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
DEFAULT_USERNAME, DEFAULT_USERNAME,
DOMAIN, DOMAIN,
GATEWAY,
PLATFORMS_GATEWAY,
PW_TYPE,
SENSOR_PLATFORMS, SENSOR_PLATFORMS,
UNDO_UPDATE_LISTENER, UNDO_UPDATE_LISTENER,
) )
@ -64,11 +71,11 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.error("Unable to connect to Smile") _LOGGER.error("Unable to connect to Smile")
raise ConfigEntryNotReady raise ConfigEntryNotReady
except Smile.InvalidAuthentication: except InvalidAuthentication:
_LOGGER.error("Invalid username or Smile ID") _LOGGER.error("Invalid username or Smile ID")
return False return False
except Smile.PlugwiseError as err: except PlugwiseException as err:
_LOGGER.error("Error while communicating to device %s", api.smile_name) _LOGGER.error("Error while communicating to device %s", api.smile_name)
raise ConfigEntryNotReady from err raise ConfigEntryNotReady from err
@ -88,7 +95,7 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async with async_timeout.timeout(DEFAULT_TIMEOUT): async with async_timeout.timeout(DEFAULT_TIMEOUT):
await api.full_update_device() await api.full_update_device()
return True return True
except Smile.XMLDataMissingError as err: except XMLDataMissingError as err:
raise UpdateFailed("Smile update failed") from err raise UpdateFailed("Smile update failed") from err
coordinator = DataUpdateCoordinator( coordinator = DataUpdateCoordinator(
@ -115,6 +122,7 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
"api": api, "api": api,
COORDINATOR: coordinator, COORDINATOR: coordinator,
PW_TYPE: GATEWAY,
UNDO_UPDATE_LISTENER: undo_listener, UNDO_UPDATE_LISTENER: undo_listener,
} }
@ -130,7 +138,7 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
single_master_thermostat = api.single_master_thermostat() single_master_thermostat = api.single_master_thermostat()
platforms = ALL_PLATFORMS platforms = PLATFORMS_GATEWAY
if single_master_thermostat is None: if single_master_thermostat is None:
platforms = SENSOR_PLATFORMS platforms = SENSOR_PLATFORMS
@ -150,13 +158,13 @@ async def _update_listener(hass: HomeAssistant, entry: ConfigEntry):
) )
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_unload_entry_gw(hass: HomeAssistant, entry: ConfigEntry):
"""Unload a config entry.""" """Unload a config entry."""
unload_ok = all( unload_ok = all(
await asyncio.gather( await asyncio.gather(
*[ *[
hass.config_entries.async_forward_entry_unload(entry, component) hass.config_entries.async_forward_entry_unload(entry, component)
for component in ALL_PLATFORMS for component in PLATFORMS_GATEWAY
] ]
) )
) )

View File

@ -2,8 +2,8 @@
"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_Smile==1.6.0"], "requirements": ["plugwise==0.8.3"],
"codeowners": ["@CoMPaTech", "@bouwew"], "codeowners": ["@CoMPaTech", "@bouwew", "@brefra"],
"zeroconf": ["_plugwise._tcp.local."], "zeroconf": ["_plugwise._tcp.local."],
"config_flow": true "config_flow": true
} }

View File

@ -2,7 +2,7 @@
import logging import logging
from Plugwise_Smile.Smile import Smile from plugwise.exceptions import PlugwiseException
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.core import callback from homeassistant.core import callback
@ -14,6 +14,12 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Smile switches from a config entry."""
# PLACEHOLDER USB entry setup
return await async_setup_entry_gateway(hass, config_entry, async_add_entities)
async def async_setup_entry_gateway(hass, config_entry, async_add_entities):
"""Set up the Smile switches from a config entry.""" """Set up the Smile switches from a config entry."""
api = hass.data[DOMAIN][config_entry.entry_id]["api"] api = hass.data[DOMAIN][config_entry.entry_id]["api"]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR] coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
@ -37,7 +43,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
model = "Switch Group" model = "Switch Group"
entities.append( entities.append(
PwSwitch( GwSwitch(
api, coordinator, device_properties["name"], dev_id, members, model api, coordinator, device_properties["name"], dev_id, members, model
) )
) )
@ -45,7 +51,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_entities(entities, True) async_add_entities(entities, True)
class PwSwitch(SmileGateway, SwitchEntity): class GwSwitch(SmileGateway, SwitchEntity):
"""Representation of a Plugwise plug.""" """Representation of a Plugwise plug."""
def __init__(self, api, coordinator, name, dev_id, members, model): def __init__(self, api, coordinator, name, dev_id, members, model):
@ -79,7 +85,7 @@ class PwSwitch(SmileGateway, SwitchEntity):
if state_on: if state_on:
self._is_on = True self._is_on = True
self.async_write_ha_state() self.async_write_ha_state()
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
@ -91,7 +97,7 @@ class PwSwitch(SmileGateway, SwitchEntity):
if state_off: if state_off:
self._is_on = False self._is_on = False
self.async_write_ha_state() self.async_write_ha_state()
except Smile.PlugwiseError: except PlugwiseException:
_LOGGER.error("Error while communicating to device") _LOGGER.error("Error while communicating to device")
@callback @callback

View File

@ -25,9 +25,6 @@ Mastodon.py==1.5.1
# homeassistant.components.orangepi_gpio # homeassistant.components.orangepi_gpio
OPi.GPIO==0.4.0 OPi.GPIO==0.4.0
# homeassistant.components.plugwise
Plugwise_Smile==1.6.0
# homeassistant.components.essent # homeassistant.components.essent
PyEssent==0.14 PyEssent==0.14
@ -1139,6 +1136,9 @@ plexauth==0.0.6
# homeassistant.components.plex # homeassistant.components.plex
plexwebsocket==0.0.12 plexwebsocket==0.0.12
# homeassistant.components.plugwise
plugwise==0.8.3
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -6,9 +6,6 @@
# homeassistant.components.homekit # homeassistant.components.homekit
HAP-python==3.0.0 HAP-python==3.0.0
# homeassistant.components.plugwise
Plugwise_Smile==1.6.0
# homeassistant.components.flick_electric # homeassistant.components.flick_electric
PyFlick==0.0.2 PyFlick==0.0.2
@ -562,6 +559,9 @@ plexauth==0.0.6
# homeassistant.components.plex # homeassistant.components.plex
plexwebsocket==0.0.12 plexwebsocket==0.0.12
# homeassistant.components.plugwise
plugwise==0.8.3
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -1,6 +1,6 @@
"""Common initialisation for the Plugwise integration.""" """Common initialisation for the Plugwise integration."""
from homeassistant.components.plugwise import DOMAIN from homeassistant.components.plugwise.const import DOMAIN
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry from tests.common import MockConfigEntry

View File

@ -3,8 +3,13 @@
from functools import partial from functools import partial
import re import re
from Plugwise_Smile.Smile import Smile
import jsonpickle import jsonpickle
from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication,
PlugwiseException,
XMLDataMissingError,
)
import pytest import pytest
from tests.async_mock import AsyncMock, Mock, patch from tests.async_mock import AsyncMock, Mock, patch
@ -24,8 +29,8 @@ 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.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
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
@ -48,9 +53,9 @@ def mock_smile_error(aioclient_mock: AiohttpClientMocker) -> None:
def mock_smile_notconnect(): def mock_smile_notconnect():
"""Mock the Plugwise Smile general connection failure for Home Assistant.""" """Mock the Plugwise Smile general connection failure for Home Assistant."""
with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock: with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock:
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.PlugwiseError = Smile.PlugwiseError smile_mock.PlugwiseException = PlugwiseException
smile_mock.return_value.connect.side_effect = AsyncMock(return_value=False) smile_mock.return_value.connect.side_effect = AsyncMock(return_value=False)
yield smile_mock.return_value yield smile_mock.return_value
@ -65,9 +70,9 @@ def mock_smile_adam():
"""Create a Mock Adam environment for testing exceptions.""" """Create a Mock Adam environment for testing exceptions."""
chosen_env = "adam_multiple_devices_per_zone" chosen_env = "adam_multiple_devices_per_zone"
with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock: with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock:
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.XMLDataMissingError = Smile.XMLDataMissingError smile_mock.XMLDataMissingError = XMLDataMissingError
smile_mock.return_value.gateway_id = "fe799307f1624099878210aa0b9f1475" smile_mock.return_value.gateway_id = "fe799307f1624099878210aa0b9f1475"
smile_mock.return_value.heater_id = "90986d591dcd426cae3ec3e8111ff730" smile_mock.return_value.heater_id = "90986d591dcd426cae3ec3e8111ff730"
@ -110,9 +115,9 @@ def mock_smile_anna():
"""Create a Mock Anna environment for testing exceptions.""" """Create a Mock Anna environment for testing exceptions."""
chosen_env = "anna_heatpump" chosen_env = "anna_heatpump"
with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock: with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock:
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.XMLDataMissingError = Smile.XMLDataMissingError smile_mock.XMLDataMissingError = XMLDataMissingError
smile_mock.return_value.gateway_id = "015ae9ea3f964e668e490fa39da3870b" smile_mock.return_value.gateway_id = "015ae9ea3f964e668e490fa39da3870b"
smile_mock.return_value.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927" smile_mock.return_value.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927"
@ -155,9 +160,9 @@ def mock_smile_p1():
"""Create a Mock P1 DSMR environment for testing exceptions.""" """Create a Mock P1 DSMR environment for testing exceptions."""
chosen_env = "p1v3_full_option" chosen_env = "p1v3_full_option"
with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock: with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock:
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.XMLDataMissingError = Smile.XMLDataMissingError smile_mock.XMLDataMissingError = XMLDataMissingError
smile_mock.return_value.gateway_id = "e950c7d5e1ee407a858e2a8b5016c8b3" smile_mock.return_value.gateway_id = "e950c7d5e1ee407a858e2a8b5016c8b3"
smile_mock.return_value.heater_id = None smile_mock.return_value.heater_id = None
@ -191,9 +196,9 @@ def mock_stretch():
"""Create a Mock Stretch environment for testing exceptions.""" """Create a Mock Stretch environment for testing exceptions."""
chosen_env = "stretch_v31" chosen_env = "stretch_v31"
with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock: with patch("homeassistant.components.plugwise.gateway.Smile") as smile_mock:
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
smile_mock.XMLDataMissingError = Smile.XMLDataMissingError smile_mock.XMLDataMissingError = XMLDataMissingError
smile_mock.return_value.gateway_id = "259882df3c05415b99c2d962534ce820" smile_mock.return_value.gateway_id = "259882df3c05415b99c2d962534ce820"
smile_mock.return_value.heater_id = None smile_mock.return_value.heater_id = None

View File

@ -1,5 +1,8 @@
"""Tests for the Plugwise Climate integration.""" """Tests for the Plugwise Climate integration."""
from plugwise.exceptions import PlugwiseException
from homeassistant.components.climate.const import HVAC_MODE_AUTO, HVAC_MODE_HEAT
from homeassistant.config_entries import ENTRY_STATE_LOADED from homeassistant.config_entries import ENTRY_STATE_LOADED
from tests.components.plugwise.common import async_init_integration from tests.components.plugwise.common import async_init_integration
@ -13,7 +16,7 @@ async def test_adam_climate_entity_attributes(hass, mock_smile_adam):
state = hass.states.get("climate.zone_lisa_wk") state = hass.states.get("climate.zone_lisa_wk")
attrs = state.attributes attrs = state.attributes
assert attrs["hvac_modes"] == ["heat", "auto"] assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
assert "preset_modes" in attrs assert "preset_modes" in attrs
assert "no_frost" in attrs["preset_modes"] assert "no_frost" in attrs["preset_modes"]
@ -29,7 +32,7 @@ async def test_adam_climate_entity_attributes(hass, mock_smile_adam):
state = hass.states.get("climate.zone_thermostat_jessie") state = hass.states.get("climate.zone_thermostat_jessie")
attrs = state.attributes attrs = state.attributes
assert attrs["hvac_modes"] == ["heat", "auto"] assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
assert "preset_modes" in attrs assert "preset_modes" in attrs
assert "no_frost" in attrs["preset_modes"] assert "no_frost" in attrs["preset_modes"]
@ -41,6 +44,44 @@ async def test_adam_climate_entity_attributes(hass, mock_smile_adam):
assert attrs["preset_mode"] == "asleep" assert attrs["preset_mode"] == "asleep"
async def test_adam_climate_adjust_negative_testing(hass, mock_smile_adam):
"""Test exceptions of climate entities."""
mock_smile_adam.set_preset.side_effect = PlugwiseException
mock_smile_adam.set_schedule_state.side_effect = PlugwiseException
mock_smile_adam.set_temperature.side_effect = PlugwiseException
entry = await async_init_integration(hass, mock_smile_adam)
assert entry.state == ENTRY_STATE_LOADED
await hass.services.async_call(
"climate",
"set_temperature",
{"entity_id": "climate.zone_lisa_wk", "temperature": 25},
blocking=True,
)
state = hass.states.get("climate.zone_lisa_wk")
attrs = state.attributes
assert attrs["temperature"] == 21.5
await hass.services.async_call(
"climate",
"set_preset_mode",
{"entity_id": "climate.zone_thermostat_jessie", "preset_mode": "home"},
blocking=True,
)
state = hass.states.get("climate.zone_thermostat_jessie")
attrs = state.attributes
assert attrs["preset_mode"] == "asleep"
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.zone_thermostat_jessie", "hvac_mode": HVAC_MODE_AUTO},
blocking=True,
)
state = hass.states.get("climate.zone_thermostat_jessie")
attrs = state.attributes
async def test_adam_climate_entity_climate_changes(hass, mock_smile_adam): async def test_adam_climate_entity_climate_changes(hass, mock_smile_adam):
"""Test handling of user requests in adam climate device environment.""" """Test handling of user requests in adam climate device environment."""
entry = await async_init_integration(hass, mock_smile_adam) entry = await async_init_integration(hass, mock_smile_adam)
@ -112,7 +153,7 @@ async def test_anna_climate_entity_attributes(hass, mock_smile_anna):
assert attrs["current_temperature"] == 23.3 assert attrs["current_temperature"] == 23.3
assert attrs["temperature"] == 21.0 assert attrs["temperature"] == 21.0
assert state.state == "auto" assert state.state == HVAC_MODE_AUTO
assert attrs["hvac_action"] == "idle" assert attrs["hvac_action"] == "idle"
assert attrs["preset_mode"] == "home" assert attrs["preset_mode"] == "home"

View File

@ -1,5 +1,9 @@
"""Test the Plugwise config flow.""" """Test the Plugwise config flow."""
from Plugwise_Smile.Smile import Smile from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication,
PlugwiseException,
)
import pytest import pytest
from homeassistant import config_entries, data_entry_flow, setup from homeassistant import config_entries, data_entry_flow, setup
@ -47,9 +51,9 @@ 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.PlugwiseError = Smile.PlugwiseError smile_mock.PlugwiseError = PlugwiseException
smile_mock.InvalidAuthentication = Smile.InvalidAuthentication smile_mock.InvalidAuthentication = InvalidAuthentication
smile_mock.ConnectionFailedError = Smile.ConnectionFailedError smile_mock.ConnectionFailedError = ConnectionFailedError
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
@ -207,7 +211,7 @@ async def test_form_invalid_auth(hass, mock_smile):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mock_smile.connect.side_effect = Smile.InvalidAuthentication mock_smile.connect.side_effect = InvalidAuthentication
mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a" mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a"
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
@ -225,7 +229,7 @@ async def test_form_cannot_connect(hass, mock_smile):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mock_smile.connect.side_effect = Smile.ConnectionFailedError mock_smile.connect.side_effect = ConnectionFailedError
mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a" mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a"
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(
@ -243,7 +247,7 @@ async def test_form_cannot_connect_port(hass, mock_smile):
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mock_smile.connect.side_effect = Smile.ConnectionFailedError mock_smile.connect.side_effect = ConnectionFailedError
mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a" mock_smile.gateway_id = "0a636a4fc1704ab4a24e4f7e37fb187a"
result2 = await hass.config_entries.flow.async_configure( result2 = await hass.config_entries.flow.async_configure(

View File

@ -2,9 +2,9 @@
import asyncio import asyncio
from Plugwise_Smile.Smile import Smile from plugwise.exceptions import XMLDataMissingError
from homeassistant.components.plugwise import DOMAIN from homeassistant.components.plugwise.const import DOMAIN
from homeassistant.config_entries import ( from homeassistant.config_entries import (
ENTRY_STATE_NOT_LOADED, ENTRY_STATE_NOT_LOADED,
ENTRY_STATE_SETUP_ERROR, ENTRY_STATE_SETUP_ERROR,
@ -43,7 +43,7 @@ async def test_smile_timeout(hass, mock_smile_notconnect):
async def test_smile_adam_xmlerror(hass, mock_smile_adam): async def test_smile_adam_xmlerror(hass, mock_smile_adam):
"""Detect malformed XML by Smile in Adam environment.""" """Detect malformed XML by Smile in Adam environment."""
mock_smile_adam.full_update_device.side_effect = Smile.XMLDataMissingError mock_smile_adam.full_update_device.side_effect = XMLDataMissingError
entry = await async_init_integration(hass, mock_smile_adam) entry = await async_init_integration(hass, mock_smile_adam)
assert entry.state == ENTRY_STATE_SETUP_RETRY assert entry.state == ENTRY_STATE_SETUP_RETRY

View File

@ -1,5 +1,7 @@
"""Tests for the Plugwise switch integration.""" """Tests for the Plugwise switch integration."""
from plugwise.exceptions import PlugwiseException
from homeassistant.config_entries import ENTRY_STATE_LOADED from homeassistant.config_entries import ENTRY_STATE_LOADED
from tests.components.plugwise.common import async_init_integration from tests.components.plugwise.common import async_init_integration
@ -17,6 +19,31 @@ async def test_adam_climate_switch_entities(hass, mock_smile_adam):
assert str(state.state) == "on" assert str(state.state) == "on"
async def test_adam_climate_switch_negative_testing(hass, mock_smile_adam):
"""Test exceptions of climate related switch entities."""
mock_smile_adam.set_relay_state.side_effect = PlugwiseException
entry = await async_init_integration(hass, mock_smile_adam)
assert entry.state == ENTRY_STATE_LOADED
await hass.services.async_call(
"switch",
"turn_off",
{"entity_id": "switch.cv_pomp"},
blocking=True,
)
state = hass.states.get("switch.cv_pomp")
assert str(state.state) == "on"
await hass.services.async_call(
"switch",
"turn_on",
{"entity_id": "switch.fibaro_hc2"},
blocking=True,
)
state = hass.states.get("switch.fibaro_hc2")
assert str(state.state) == "on"
async def test_adam_climate_switch_changes(hass, mock_smile_adam): async def test_adam_climate_switch_changes(hass, mock_smile_adam):
"""Test changing of climate related switch entities.""" """Test changing of climate related switch entities."""
entry = await async_init_integration(hass, mock_smile_adam) entry = await async_init_integration(hass, mock_smile_adam)