Update plugwise 0.16.2 (#65933)

This commit is contained in:
Franck Nijhof 2022-02-08 11:13:05 +01:00 committed by GitHub
parent 0ea82bdbfb
commit d12a392767
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 837 additions and 317 deletions

View File

@ -50,35 +50,33 @@ async def async_setup_entry(
config_entry.entry_id
][COORDINATOR]
entities: list[BinarySensorEntity] = []
is_thermostat = api.single_master_thermostat()
all_devices = api.get_all_devices()
for dev_id, device_properties in all_devices.items():
entities: list[PlugwiseBinarySensorEntity] = []
for device_id, device_properties in coordinator.data.devices.items():
if device_properties["class"] == "heater_central":
data = api.get_device_data(dev_id)
for description in BINARY_SENSORS:
if description.key not in data:
if (
"binary_sensors" not in device_properties
or description.key not in device_properties["binary_sensors"]
):
continue
entities.append(
PwBinarySensor(
PlugwiseBinarySensorEntity(
api,
coordinator,
device_properties["name"],
dev_id,
device_id,
description,
)
)
if device_properties["class"] == "gateway" and is_thermostat is not None:
if device_properties["class"] == "gateway":
entities.append(
PwNotifySensor(
PlugwiseNotifyBinarySensorEntity(
api,
coordinator,
device_properties["name"],
dev_id,
device_id,
BinarySensorEntityDescription(
key="plugwise_notification",
name="Plugwise Notification",
@ -89,7 +87,7 @@ async def async_setup_entry(
async_add_entities(entities, True)
class SmileBinarySensor(PlugwiseEntity, BinarySensorEntity):
class PlugwiseBinarySensorEntity(PlugwiseEntity, BinarySensorEntity):
"""Represent Smile Binary Sensors."""
def __init__(
@ -106,36 +104,23 @@ class SmileBinarySensor(PlugwiseEntity, BinarySensorEntity):
self._attr_is_on = False
self._attr_unique_id = f"{dev_id}-{description.key}"
if dev_id == self._api.heater_id:
if dev_id == coordinator.data.gateway["heater_id"]:
self._entity_name = "Auxiliary"
self._name = f"{self._entity_name} {description.name}"
if dev_id == self._api.gateway_id:
if dev_id == coordinator.data.gateway["gateway_id"]:
self._entity_name = f"Smile {self._entity_name}"
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
raise NotImplementedError
class PwBinarySensor(SmileBinarySensor):
"""Representation of a Plugwise binary_sensor."""
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self._api.get_device_data(self._dev_id)):
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._dev_id)
self.async_write_ha_state()
return
if self.entity_description.key not in data:
self.async_write_ha_state()
return
self._attr_is_on = data[self.entity_description.key]
self._attr_is_on = data["binary_sensors"].get(self.entity_description.key)
if self.entity_description.key == "dhw_state":
self._attr_icon = FLOW_ON_ICON if self._attr_is_on else FLOW_OFF_ICON
@ -145,27 +130,15 @@ class PwBinarySensor(SmileBinarySensor):
self.async_write_ha_state()
class PwNotifySensor(SmileBinarySensor):
class PlugwiseNotifyBinarySensorEntity(PlugwiseBinarySensorEntity):
"""Representation of a Plugwise Notification binary_sensor."""
def __init__(
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
description: BinarySensorEntityDescription,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, dev_id, description)
self._attr_extra_state_attributes = {}
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
notify = self._api.notifications
notify = self.coordinator.data.gateway["notifications"]
self._attr_extra_state_attributes = {}
for severity in SEVERITIES:
self._attr_extra_state_attributes[f"{severity}_msg"] = []

View File

@ -10,8 +10,9 @@ from homeassistant.components.climate.const import (
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
HVAC_MODE_AUTO,
HVAC_MODE_COOL,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF,
SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE,
)
@ -32,8 +33,8 @@ from .const import (
from .coordinator import PlugwiseDataUpdateCoordinator
from .entity import PlugwiseEntity
HVAC_MODES_HEAT_ONLY = [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
HVAC_MODES_HEAT_COOL = [HVAC_MODE_HEAT_COOL, HVAC_MODE_AUTO]
HVAC_MODES_HEAT_ONLY = [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
HVAC_MODES_HEAT_COOL = [HVAC_MODE_HEAT, HVAC_MODE_COOL, HVAC_MODE_AUTO, HVAC_MODE_OFF]
async def async_setup_entry(
@ -45,24 +46,21 @@ async def async_setup_entry(
api = hass.data[DOMAIN][config_entry.entry_id]["api"]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
entities = []
entities: list[PlugwiseClimateEntity] = []
thermostat_classes = [
"thermostat",
"zone_thermostat",
"thermostatic_radiator_valve",
]
all_devices = api.get_all_devices()
for dev_id, device_properties in all_devices.items():
for device_id, device_properties in coordinator.data.devices.items():
if device_properties["class"] not in thermostat_classes:
continue
thermostat = PwThermostat(
thermostat = PlugwiseClimateEntity(
api,
coordinator,
device_properties["name"],
dev_id,
device_id,
device_properties["location"],
device_properties["class"],
)
@ -72,7 +70,7 @@ async def async_setup_entry(
async_add_entities(entities, True)
class PwThermostat(PlugwiseEntity, ClimateEntity):
class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
"""Representation of an Plugwise thermostat."""
_attr_hvac_mode = HVAC_MODE_HEAT
@ -90,21 +88,19 @@ class PwThermostat(PlugwiseEntity, ClimateEntity):
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
device_id: str,
loc_id: str,
model: str,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, dev_id)
super().__init__(api, coordinator, name, device_id)
self._attr_extra_state_attributes = {}
self._attr_unique_id = f"{dev_id}-climate"
self._attr_unique_id = f"{device_id}-climate"
self._api = api
self._loc_id = loc_id
self._model = model
self._presets = None
self._single_thermostat = self._api.single_master_thermostat()
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
@ -124,7 +120,7 @@ class PwThermostat(PlugwiseEntity, ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set the hvac mode."""
state = SCHEDULE_OFF
climate_data = self._api.get_device_data(self._dev_id)
climate_data = self.coordinator.data.devices[self._dev_id]
if hvac_mode == HVAC_MODE_AUTO:
state = SCHEDULE_ON
@ -161,18 +157,20 @@ class PwThermostat(PlugwiseEntity, ClimateEntity):
@callback
def _async_process_data(self) -> None:
"""Update the data for this climate device."""
climate_data = self._api.get_device_data(self._dev_id)
heater_central_data = self._api.get_device_data(self._api.heater_id)
data = self.coordinator.data.devices[self._dev_id]
heater_central_data = self.coordinator.data.devices[
self.coordinator.data.gateway["heater_id"]
]
# Current & set temperatures
if setpoint := climate_data.get("setpoint"):
if setpoint := data["sensors"].get("setpoint"):
self._attr_target_temperature = setpoint
if temperature := climate_data.get("temperature"):
if temperature := data["sensors"].get("temperature"):
self._attr_current_temperature = temperature
# Presets handling
self._attr_preset_mode = climate_data.get("active_preset")
if presets := climate_data.get("presets"):
self._attr_preset_mode = data.get("active_preset")
if presets := data.get("presets"):
self._presets = presets
self._attr_preset_modes = list(presets)
else:
@ -181,31 +179,22 @@ class PwThermostat(PlugwiseEntity, ClimateEntity):
# Determine current hvac action
self._attr_hvac_action = CURRENT_HVAC_IDLE
if self._single_thermostat:
if heater_central_data.get("heating_state"):
self._attr_hvac_action = CURRENT_HVAC_HEAT
elif heater_central_data.get("cooling_state"):
self._attr_hvac_action = CURRENT_HVAC_COOL
elif (
self.target_temperature is not None
and self.current_temperature is not None
and self.target_temperature > self.current_temperature
):
if heater_central_data.get("heating_state"):
self._attr_hvac_action = CURRENT_HVAC_HEAT
elif heater_central_data.get("cooling_state"):
self._attr_hvac_action = CURRENT_HVAC_COOL
# Determine hvac modes and current hvac mode
self._attr_hvac_mode = HVAC_MODE_HEAT
self._attr_hvac_modes = HVAC_MODES_HEAT_ONLY
if heater_central_data.get("compressor_state") is not None:
self._attr_hvac_mode = HVAC_MODE_HEAT_COOL
if self.coordinator.data.gateway.get("cooling_present"):
self._attr_hvac_modes = HVAC_MODES_HEAT_COOL
if climate_data.get("selected_schedule") is not None:
self._attr_hvac_mode = HVAC_MODE_AUTO
if data.get("mode") in self._attr_hvac_modes:
self._attr_hvac_mode = data["mode"]
# Extra attributes
self._attr_extra_state_attributes = {
"available_schemas": climate_data.get("available_schedules"),
"selected_schema": climate_data.get("selected_schedule"),
"available_schemas": data.get("available_schedules"),
"selected_schema": data.get("selected_schedule"),
}
self.async_write_ha_state()

View File

@ -1,17 +1,24 @@
"""DataUpdateCoordinator for Plugwise."""
from datetime import timedelta
from typing import Any, NamedTuple
import async_timeout
from plugwise import Smile
from plugwise.exceptions import XMLDataMissingError
from plugwise.exceptions import PlugwiseException, XMLDataMissingError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DEFAULT_SCAN_INTERVAL, DEFAULT_TIMEOUT, DOMAIN, LOGGER
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER
class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[bool]):
class PlugwiseData(NamedTuple):
"""Plugwise data stored in the DataUpdateCoordinator."""
gateway: dict[str, Any]
devices: dict[str, dict[str, Any]]
class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]):
"""Class to manage fetching Plugwise data from single endpoint."""
def __init__(self, hass: HomeAssistant, api: Smile) -> None:
@ -26,11 +33,14 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[bool]):
)
self.api = api
async def _async_update_data(self) -> bool:
async def _async_update_data(self) -> PlugwiseData:
"""Fetch data from Plugwise."""
try:
async with async_timeout.timeout(DEFAULT_TIMEOUT):
await self.api.full_update_device()
data = await self.api.async_update()
except XMLDataMissingError as err:
raise UpdateFailed("Smile update failed") from err
return True
raise UpdateFailed(
f"No XML data received for: {self.api.smile_name}"
) from err
except PlugwiseException as err:
raise UpdateFailed(f"Updated failed for: {self.api.smile_name}") from err
return PlugwiseData(*data)

View File

@ -14,14 +14,12 @@ from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import PlugwiseDataUpdateCoordinator
from .coordinator import PlugwiseData, PlugwiseDataUpdateCoordinator
class PlugwiseEntity(CoordinatorEntity[bool]):
class PlugwiseEntity(CoordinatorEntity[PlugwiseData]):
"""Represent a PlugWise Entity."""
_model: str | None = None
def __init__(
self,
api: Smile,
@ -45,6 +43,7 @@ class PlugwiseEntity(CoordinatorEntity[bool]):
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
data = self.coordinator.data.devices[self._dev_id]
device_information = DeviceInfo(
identifiers={(DOMAIN, self._dev_id)},
name=self._entity_name,
@ -56,11 +55,14 @@ class PlugwiseEntity(CoordinatorEntity[bool]):
ATTR_CONFIGURATION_URL
] = f"http://{entry.data[CONF_HOST]}"
if self._model is not None:
device_information[ATTR_MODEL] = self._model.replace("_", " ").title()
if model := data.get("model"):
device_information[ATTR_MODEL] = model
if self._dev_id != self._api.gateway_id:
device_information[ATTR_VIA_DEVICE] = (DOMAIN, str(self._api.gateway_id))
if self._dev_id != self.coordinator.data.gateway["gateway_id"]:
device_information[ATTR_VIA_DEVICE] = (
DOMAIN,
str(self.coordinator.data.gateway["gateway_id"]),
)
return device_information

View File

@ -55,15 +55,14 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not connected:
raise ConfigEntryNotReady("Unable to connect to Smile")
coordinator = PlugwiseDataUpdateCoordinator(hass, api)
await coordinator.async_config_entry_first_refresh()
api.get_all_devices()
if entry.unique_id is None and api.smile_version[0] != "1.8.0":
hass.config_entries.async_update_entry(entry, unique_id=api.smile_hostname)
coordinator = PlugwiseDataUpdateCoordinator(hass, api)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
"api": api,
COORDINATOR: coordinator,
@ -80,10 +79,8 @@ async def async_setup_entry_gw(hass: HomeAssistant, entry: ConfigEntry) -> bool:
sw_version=api.smile_version[0],
)
single_master_thermostat = api.single_master_thermostat()
platforms = PLATFORMS_GATEWAY
if single_master_thermostat is None:
if coordinator.data.gateway["single_master_thermostat"] is None:
platforms = SENSOR_PLATFORMS
hass.config_entries.async_setup_platforms(entry, platforms)

View File

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

View File

@ -289,53 +289,37 @@ async def async_setup_entry(
api = hass.data[DOMAIN][config_entry.entry_id]["api"]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
entities: list[SmileSensor] = []
all_devices = api.get_all_devices()
single_thermostat = api.single_master_thermostat()
for dev_id, device_properties in all_devices.items():
data = api.get_device_data(dev_id)
entities: list[PlugwiseSensorEnity] = []
for device_id, device_properties in coordinator.data.devices.items():
for description in SENSORS:
if data.get(description.key) is None:
if (
"sensors" not in device_properties
or device_properties["sensors"].get(description.key) is None
):
continue
if "power" in device_properties["types"]:
model = None
if "plug" in device_properties["types"]:
model = "Metered Switch"
entities.append(
PwPowerSensor(
api,
coordinator,
device_properties["name"],
dev_id,
model,
description,
)
)
else:
entities.append(
PwThermostatSensor(
api,
coordinator,
device_properties["name"],
dev_id,
description,
)
entities.append(
PlugwiseSensorEnity(
api,
coordinator,
device_properties["name"],
device_id,
description,
)
)
if single_thermostat is False:
if coordinator.data.gateway["single_master_thermostat"] is False:
# These sensors should actually be binary sensors.
for description in INDICATE_ACTIVE_LOCAL_DEVICE_SENSORS:
if description.key not in data:
if description.key not in device_properties:
continue
entities.append(
PwAuxDeviceSensor(
PlugwiseAuxSensorEntity(
api,
coordinator,
device_properties["name"],
dev_id,
device_id,
description,
)
)
@ -344,47 +328,43 @@ async def async_setup_entry(
async_add_entities(entities, True)
class SmileSensor(PlugwiseEntity, SensorEntity):
"""Represent Smile Sensors."""
class PlugwiseSensorEnity(PlugwiseEntity, SensorEntity):
"""Represent Plugwise Sensors."""
def __init__(
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
device_id: str,
description: SensorEntityDescription,
) -> None:
"""Initialise the sensor."""
super().__init__(api, coordinator, name, dev_id)
super().__init__(api, coordinator, name, device_id)
self.entity_description = description
self._attr_unique_id = f"{dev_id}-{description.key}"
self._attr_unique_id = f"{device_id}-{description.key}"
if dev_id == self._api.heater_id:
if device_id == coordinator.data.gateway["heater_id"]:
self._entity_name = "Auxiliary"
self._name = f"{self._entity_name} {description.name}"
if dev_id == self._api.gateway_id:
if device_id == coordinator.data.gateway["gateway_id"]:
self._entity_name = f"Smile {self._entity_name}"
class PwThermostatSensor(SmileSensor):
"""Thermostat (or generic) sensor devices."""
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self._api.get_device_data(self._dev_id)):
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._entity_name)
self.async_write_ha_state()
return
self._attr_native_value = data.get(self.entity_description.key)
self._attr_native_value = data["sensors"].get(self.entity_description.key)
self.async_write_ha_state()
class PwAuxDeviceSensor(SmileSensor):
class PlugwiseAuxSensorEntity(PlugwiseSensorEnity):
"""Auxiliary Device Sensors."""
_cooling_state = False
@ -393,7 +373,7 @@ class PwAuxDeviceSensor(SmileSensor):
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self._api.get_device_data(self._dev_id)):
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._entity_name)
self.async_write_ha_state()
return
@ -413,33 +393,3 @@ class PwAuxDeviceSensor(SmileSensor):
self._attr_icon = COOL_ICON
self.async_write_ha_state()
class PwPowerSensor(SmileSensor):
"""Power sensor entities."""
def __init__(
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
model: str | None,
description: SensorEntityDescription,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, dev_id, description)
self._model = model
if dev_id == self._api.gateway_id:
self._model = "P1 DSMR"
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self._api.get_device_data(self._dev_id)):
LOGGER.error("Received no data for device %s", self._entity_name)
self.async_write_ha_state()
return
self._attr_native_value = data.get(self.entity_description.key)
self.async_write_ha_state()

View File

@ -25,34 +25,27 @@ async def async_setup_entry(
api = hass.data[DOMAIN][config_entry.entry_id]["api"]
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
entities = []
switch_classes = ["plug", "switch_group"]
all_devices = api.get_all_devices()
for dev_id, device_properties in all_devices.items():
members = None
model = None
if any(
switch_class in device_properties["types"]
for switch_class in switch_classes
entities: list[PlugwiseSwitchEntity] = []
for device_id, device_properties in coordinator.data.devices.items():
if (
"switches" not in device_properties
or "relay" not in device_properties["switches"]
):
if "plug" in device_properties["types"]:
model = "Metered Switch"
if "switch_group" in device_properties["types"]:
members = device_properties["members"]
model = "Switch Group"
continue
entities.append(
GwSwitch(
api, coordinator, device_properties["name"], dev_id, members, model
)
entities.append(
PlugwiseSwitchEntity(
api,
coordinator,
device_properties["name"],
device_id,
)
)
async_add_entities(entities, True)
class GwSwitch(PlugwiseEntity, SwitchEntity):
class PlugwiseSwitchEntity(PlugwiseEntity, SwitchEntity):
"""Representation of a Plugwise plug."""
_attr_icon = SWITCH_ICON
@ -62,24 +55,19 @@ class GwSwitch(PlugwiseEntity, SwitchEntity):
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
members: list[str] | None,
model: str | None,
device_id: str,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, dev_id)
self._attr_unique_id = f"{dev_id}-plug"
self._members = members
self._model = model
super().__init__(api, coordinator, name, device_id)
self._attr_unique_id = f"{device_id}-plug"
self._members = coordinator.data.devices[device_id].get("members")
self._attr_is_on = False
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
try:
state_on = await self._api.set_relay_state(
self._dev_id, self._members, "on"
state_on = await self._api.set_switch_state(
self._dev_id, self._members, "relay", "on"
)
except PlugwiseException:
LOGGER.error("Error while communicating to device")
@ -91,8 +79,8 @@ class GwSwitch(PlugwiseEntity, SwitchEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
try:
state_off = await self._api.set_relay_state(
self._dev_id, self._members, "off"
state_off = await self._api.set_switch_state(
self._dev_id, self._members, "relay", "off"
)
except PlugwiseException:
LOGGER.error("Error while communicating to device")
@ -104,12 +92,10 @@ class GwSwitch(PlugwiseEntity, SwitchEntity):
@callback
def _async_process_data(self) -> None:
"""Update the data from the Plugs."""
if not (data := self._api.get_device_data(self._dev_id)):
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._name)
self.async_write_ha_state()
return
if "relay" in data:
self._attr_is_on = data["relay"]
self._attr_is_on = data["switches"].get("relay")
self.async_write_ha_state()

View File

@ -1264,7 +1264,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13
# homeassistant.components.plugwise
plugwise==0.8.5
plugwise==0.16.2
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11

View File

@ -10,7 +10,6 @@
codecov==2.1.12
coverage==6.3.1
freezegun==1.1.0
jsonpickle==1.4.1
mock-open==1.4.0
mypy==0.931
pre-commit==2.17.0

View File

@ -786,7 +786,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13
# homeassistant.components.plugwise
plugwise==0.8.5
plugwise==0.16.2
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11

View File

@ -3,7 +3,7 @@ PIP_CACHE=$1
# Number of existing dependency conflicts
# Update if a PR resolve one!
DEPENDENCY_CONFLICTS=11
DEPENDENCY_CONFLICTS=10
PIP_CHECK=$(pip check --cache-dir=$PIP_CACHE)
LINE_COUNT=$(echo "$PIP_CHECK" | wc -l)

View File

@ -4,10 +4,10 @@ from __future__ import annotations
from collections.abc import Generator
from functools import partial
from http import HTTPStatus
import json
import re
from unittest.mock import AsyncMock, MagicMock, Mock, patch
import jsonpickle
from plugwise.exceptions import (
ConnectionFailedError,
InvalidAuthentication,
@ -23,7 +23,7 @@ from tests.test_util.aiohttp import AiohttpClientMocker
def _read_json(environment, call):
"""Undecode the json data."""
fixture = load_fixture(f"plugwise/{environment}/{call}.json")
return jsonpickle.decode(fixture)
return json.loads(fixture)
@pytest.fixture
@ -93,13 +93,11 @@ def mock_smile_adam():
smile_mock.return_value.smile_version = "3.0.15"
smile_mock.return_value.smile_type = "thermostat"
smile_mock.return_value.smile_hostname = "smile98765"
smile_mock.return_value.smile_name = "Adam"
smile_mock.return_value.notifications = _read_json(chosen_env, "notifications")
smile_mock.return_value.connect.side_effect = AsyncMock(return_value=True)
smile_mock.return_value.full_update_device.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.single_master_thermostat.side_effect = Mock(
return_value=False
)
@ -110,17 +108,13 @@ def mock_smile_adam():
smile_mock.return_value.set_temperature.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.set_relay_state.side_effect = AsyncMock(
smile_mock.return_value.async_update.side_effect = AsyncMock(
return_value=_read_json(chosen_env, "all_data")
)
smile_mock.return_value.set_switch_state.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.get_all_devices.return_value = _read_json(
chosen_env, "get_all_devices"
)
smile_mock.return_value.get_device_data.side_effect = partial(
_get_device_data, chosen_env
)
yield smile_mock.return_value
@ -138,13 +132,11 @@ def mock_smile_anna():
smile_mock.return_value.smile_version = "4.0.15"
smile_mock.return_value.smile_type = "thermostat"
smile_mock.return_value.smile_hostname = "smile98765"
smile_mock.return_value.smile_name = "Anna"
smile_mock.return_value.notifications = _read_json(chosen_env, "notifications")
smile_mock.return_value.connect.side_effect = AsyncMock(return_value=True)
smile_mock.return_value.full_update_device.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.single_master_thermostat.side_effect = Mock(
return_value=True
)
@ -155,12 +147,12 @@ def mock_smile_anna():
smile_mock.return_value.set_temperature.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.set_relay_state.side_effect = AsyncMock(
smile_mock.return_value.set_switch_state.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.get_all_devices.return_value = _read_json(
chosen_env, "get_all_devices"
smile_mock.return_value.async_update.side_effect = AsyncMock(
return_value=_read_json(chosen_env, "all_data")
)
smile_mock.return_value.get_device_data.side_effect = partial(
_get_device_data, chosen_env
@ -183,25 +175,24 @@ def mock_smile_p1():
smile_mock.return_value.smile_version = "3.3.9"
smile_mock.return_value.smile_type = "power"
smile_mock.return_value.smile_hostname = "smile98765"
smile_mock.return_value.smile_name = "Smile P1"
smile_mock.return_value.notifications = _read_json(chosen_env, "notifications")
smile_mock.return_value.connect.side_effect = AsyncMock(return_value=True)
smile_mock.return_value.full_update_device.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.single_master_thermostat.side_effect = Mock(
return_value=None
)
smile_mock.return_value.get_all_devices.return_value = _read_json(
chosen_env, "get_all_devices"
)
smile_mock.return_value.get_device_data.side_effect = partial(
_get_device_data, chosen_env
)
smile_mock.return_value.async_update.side_effect = AsyncMock(
return_value=_read_json(chosen_env, "all_data")
)
yield smile_mock.return_value
@ -219,20 +210,18 @@ def mock_stretch():
smile_mock.return_value.smile_version = "3.1.11"
smile_mock.return_value.smile_type = "stretch"
smile_mock.return_value.smile_hostname = "stretch98765"
smile_mock.return_value.smile_name = "Stretch"
smile_mock.return_value.connect.side_effect = AsyncMock(return_value=True)
smile_mock.return_value.full_update_device.side_effect = AsyncMock(
smile_mock.return_value.set_switch_state.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.set_relay_state.side_effect = AsyncMock(
return_value=True
)
smile_mock.return_value.get_all_devices.return_value = _read_json(
chosen_env, "get_all_devices"
)
smile_mock.return_value.get_device_data.side_effect = partial(
_get_device_data, chosen_env
)
smile_mock.return_value.async_update.side_effect = AsyncMock(
return_value=_read_json(chosen_env, "all_data")
)
yield smile_mock.return_value

View File

@ -0,0 +1,358 @@
[
{
"active_device": true,
"cooling_present": null,
"gateway_id": "fe799307f1624099878210aa0b9f1475",
"heater_id": "90986d591dcd426cae3ec3e8111ff730",
"single_master_thermostat": false,
"smile_name": "Adam",
"notifications": {
"af82e4ccf9c548528166d38e560662a4": {
"warning": "Node Plug (with MAC address 000D6F000D13CB01, in room 'n.a.') has been unreachable since 23:03 2020-01-18. Please check the connection and restart the device."
}
}
},
{
"df4a4a8169904cdb9c03d61a21f42140": {
"class": "zone_thermostat",
"fw": "2016-10-27T02:00:00+02:00",
"location": "12493538af164a409c6a1c79e38afe1c",
"model": "Lisa",
"name": "Zone Lisa Bios",
"vendor": "Plugwise",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"active_preset": "away",
"presets": {
"home": [20.0, 22.0],
"asleep": [17.0, 24.0],
"away": [15.0, 25.0],
"vacation": [15.0, 28.0],
"no_frost": [10.0, 30.0]
},
"available_schedules": [
"CV Roan",
"Bios Schema met Film Avond",
"GF7 Woonkamer",
"Badkamer Schema",
"CV Jessie"
],
"selected_schedule": "None",
"schedule_temperature": 15.0,
"last_used": "Badkamer Schema",
"mode": "off",
"sensors": { "temperature": 16.5, "setpoint": 13.0, "battery": 67 }
},
"b310b72a0e354bfab43089919b9a88bf": {
"class": "thermo_sensor",
"fw": "2019-03-27T01:00:00+01:00",
"location": "c50f167537524366a5af7aa3942feb1e",
"model": "Tom/Floor",
"name": "Floor kraan",
"vendor": "Plugwise",
"sensors": {
"temperature": 26.0,
"setpoint": 21.5,
"temperature_difference": 3.5,
"valve_position": 100
}
},
"a2c3583e0a6349358998b760cea82d2a": {
"class": "thermo_sensor",
"fw": "2019-03-27T01:00:00+01:00",
"location": "12493538af164a409c6a1c79e38afe1c",
"model": "Tom/Floor",
"name": "Bios Cv Thermostatic Radiator ",
"vendor": "Plugwise",
"sensors": {
"temperature": 17.2,
"setpoint": 13.0,
"battery": 62,
"temperature_difference": -0.2,
"valve_position": 0.0
}
},
"b59bcebaf94b499ea7d46e4a66fb62d8": {
"class": "zone_thermostat",
"fw": "2016-08-02T02:00:00+02:00",
"location": "c50f167537524366a5af7aa3942feb1e",
"model": "Lisa",
"name": "Zone Lisa WK",
"vendor": "Plugwise",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"active_preset": "home",
"presets": {
"home": [20.0, 22.0],
"asleep": [17.0, 24.0],
"away": [15.0, 25.0],
"vacation": [15.0, 28.0],
"no_frost": [10.0, 30.0]
},
"available_schedules": [
"CV Roan",
"Bios Schema met Film Avond",
"GF7 Woonkamer",
"Badkamer Schema",
"CV Jessie"
],
"selected_schedule": "GF7 Woonkamer",
"schedule_temperature": 20.0,
"last_used": "GF7 Woonkamer",
"mode": "auto",
"sensors": { "temperature": 20.9, "setpoint": 21.5, "battery": 34 }
},
"fe799307f1624099878210aa0b9f1475": {
"class": "gateway",
"fw": "3.0.15",
"location": "1f9dcf83fd4e4b66b72ff787957bfe5d",
"model": "Adam",
"name": "Adam",
"vendor": "Plugwise B.V.",
"binary_sensors": { "plugwise_notification": true },
"sensors": { "outdoor_temperature": 7.81 }
},
"d3da73bde12a47d5a6b8f9dad971f2ec": {
"class": "thermo_sensor",
"fw": "2019-03-27T01:00:00+01:00",
"location": "82fa13f017d240daa0d0ea1775420f24",
"model": "Tom/Floor",
"name": "Thermostatic Radiator Jessie",
"vendor": "Plugwise",
"sensors": {
"temperature": 17.1,
"setpoint": 15.0,
"battery": 62,
"temperature_difference": 0.1,
"valve_position": 0.0
}
},
"21f2b542c49845e6bb416884c55778d6": {
"class": "game_console",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "Playstation Smart Plug",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 82.6,
"electricity_consumed_interval": 8.6,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": false }
},
"78d1126fc4c743db81b61c20e88342a7": {
"class": "central_heating_pump",
"fw": "2019-06-21T02:00:00+02:00",
"location": "c50f167537524366a5af7aa3942feb1e",
"model": "Plug",
"name": "CV Pomp",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 35.6,
"electricity_consumed_interval": 7.37,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true }
},
"90986d591dcd426cae3ec3e8111ff730": {
"class": "heater_central",
"fw": null,
"location": "1f9dcf83fd4e4b66b72ff787957bfe5d",
"model": "Unknown",
"name": "OnOff",
"vendor": null,
"cooling_active": false,
"heating_state": true,
"sensors": {
"water_temperature": 70.0,
"intended_boiler_temperature": 70.0,
"modulation_level": 1,
"device_state": "heating"
}
},
"cd0ddb54ef694e11ac18ed1cbce5dbbd": {
"class": "vcr",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "NAS",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 16.5,
"electricity_consumed_interval": 0.5,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"4a810418d5394b3f82727340b91ba740": {
"class": "router",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "USG Smart Plug",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 8.5,
"electricity_consumed_interval": 0.0,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"02cf28bfec924855854c544690a609ef": {
"class": "vcr",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "NVR",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 34.0,
"electricity_consumed_interval": 9.15,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"a28f588dc4a049a483fd03a30361ad3a": {
"class": "settop",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "Fibaro HC2",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 12.5,
"electricity_consumed_interval": 3.8,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"6a3bf693d05e48e0b460c815a4fdd09d": {
"class": "zone_thermostat",
"fw": "2016-10-27T02:00:00+02:00",
"location": "82fa13f017d240daa0d0ea1775420f24",
"model": "Lisa",
"name": "Zone Thermostat Jessie",
"vendor": "Plugwise",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"active_preset": "asleep",
"presets": {
"home": [20.0, 22.0],
"asleep": [17.0, 24.0],
"away": [15.0, 25.0],
"vacation": [15.0, 28.0],
"no_frost": [10.0, 30.0]
},
"available_schedules": [
"CV Roan",
"Bios Schema met Film Avond",
"GF7 Woonkamer",
"Badkamer Schema",
"CV Jessie"
],
"selected_schedule": "CV Jessie",
"schedule_temperature": 15.0,
"last_used": "CV Jessie",
"mode": "auto",
"sensors": { "temperature": 17.2, "setpoint": 15.0, "battery": 37 }
},
"680423ff840043738f42cc7f1ff97a36": {
"class": "thermo_sensor",
"fw": "2019-03-27T01:00:00+01:00",
"location": "08963fec7c53423ca5680aa4cb502c63",
"model": "Tom/Floor",
"name": "Thermostatic Radiator Badkamer",
"vendor": "Plugwise",
"sensors": {
"temperature": 19.1,
"setpoint": 14.0,
"battery": 51,
"temperature_difference": -0.4,
"valve_position": 0.0
}
},
"f1fee6043d3642a9b0a65297455f008e": {
"class": "zone_thermostat",
"fw": "2016-10-27T02:00:00+02:00",
"location": "08963fec7c53423ca5680aa4cb502c63",
"model": "Lisa",
"name": "Zone Thermostat Badkamer",
"vendor": "Plugwise",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"active_preset": "away",
"presets": {
"home": [20.0, 22.0],
"asleep": [17.0, 24.0],
"away": [15.0, 25.0],
"vacation": [15.0, 28.0],
"no_frost": [10.0, 30.0]
},
"available_schedules": [
"CV Roan",
"Bios Schema met Film Avond",
"GF7 Woonkamer",
"Badkamer Schema",
"CV Jessie"
],
"selected_schedule": "Badkamer Schema",
"schedule_temperature": 15.0,
"last_used": "Badkamer Schema",
"mode": "auto",
"sensors": { "temperature": 18.9, "setpoint": 14.0, "battery": 92 }
},
"675416a629f343c495449970e2ca37b5": {
"class": "router",
"fw": "2019-06-21T02:00:00+02:00",
"location": "cd143c07248f491493cea0533bc3d669",
"model": "Plug",
"name": "Ziggo Modem",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 12.2,
"electricity_consumed_interval": 2.97,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"e7693eb9582644e5b865dba8d4447cf1": {
"class": "thermostatic_radiator_valve",
"fw": "2019-03-27T01:00:00+01:00",
"location": "446ac08dd04d4eff8ac57489757b7314",
"model": "Tom/Floor",
"name": "CV Kraan Garage",
"vendor": "Plugwise",
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
"active_preset": "no_frost",
"presets": {
"home": [20.0, 22.0],
"asleep": [17.0, 24.0],
"away": [15.0, 25.0],
"vacation": [15.0, 28.0],
"no_frost": [10.0, 30.0]
},
"available_schedules": [
"CV Roan",
"Bios Schema met Film Avond",
"GF7 Woonkamer",
"Badkamer Schema",
"CV Jessie"
],
"selected_schedule": "None",
"schedule_temperature": 15.0,
"last_used": "Badkamer Schema",
"mode": "heat",
"sensors": {
"temperature": 15.6,
"setpoint": 5.5,
"battery": 68,
"temperature_difference": 0.0,
"valve_position": 0.0
}
}
}
]

View File

@ -1 +0,0 @@
{"df4a4a8169904cdb9c03d61a21f42140": {"name": "Zone Lisa Bios", "model": "Zone Thermostat", "types": {"py/set": ["thermostat"]}, "class": "zone_thermostat", "location": "12493538af164a409c6a1c79e38afe1c"}, "b310b72a0e354bfab43089919b9a88bf": {"name": "Floor kraan", "model": "Thermostatic Radiator Valve", "types": {"py/set": ["thermostat"]}, "class": "thermo_sensor", "location": "c50f167537524366a5af7aa3942feb1e"}, "a2c3583e0a6349358998b760cea82d2a": {"name": "Bios Cv Thermostatic Radiator ", "model": "Thermostatic Radiator Valve", "types": {"py/set": ["thermostat"]}, "class": "thermo_sensor", "location": "12493538af164a409c6a1c79e38afe1c"}, "b59bcebaf94b499ea7d46e4a66fb62d8": {"name": "Zone Lisa WK", "model": "Zone Thermostat", "types": {"py/set": ["thermostat"]}, "class": "zone_thermostat", "location": "c50f167537524366a5af7aa3942feb1e"}, "fe799307f1624099878210aa0b9f1475": {"name": "Adam", "model": "Smile Adam", "types": {"py/set": ["temperature", "thermostat", "home"]}, "class": "gateway", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d"}, "d3da73bde12a47d5a6b8f9dad971f2ec": {"name": "Thermostatic Radiator Jessie", "model": "Thermostatic Radiator Valve", "types": {"py/set": ["thermostat"]}, "class": "thermo_sensor", "location": "82fa13f017d240daa0d0ea1775420f24"}, "21f2b542c49845e6bb416884c55778d6": {"name": "Playstation Smart Plug", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "game_console", "location": "cd143c07248f491493cea0533bc3d669"}, "78d1126fc4c743db81b61c20e88342a7": {"name": "CV Pomp", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "central_heating_pump", "location": "c50f167537524366a5af7aa3942feb1e"}, "90986d591dcd426cae3ec3e8111ff730": {"name": "Adam", "model": "Heater Central", "types": {"py/set": ["temperature", "thermostat", "home"]}, "class": "heater_central", "location": "1f9dcf83fd4e4b66b72ff787957bfe5d"}, "cd0ddb54ef694e11ac18ed1cbce5dbbd": {"name": "NAS", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "vcr", "location": "cd143c07248f491493cea0533bc3d669"}, "4a810418d5394b3f82727340b91ba740": {"name": "USG Smart Plug", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "router", "location": "cd143c07248f491493cea0533bc3d669"}, "02cf28bfec924855854c544690a609ef": {"name": "NVR", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "vcr", "location": "cd143c07248f491493cea0533bc3d669"}, "a28f588dc4a049a483fd03a30361ad3a": {"name": "Fibaro HC2", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "settop", "location": "cd143c07248f491493cea0533bc3d669"}, "6a3bf693d05e48e0b460c815a4fdd09d": {"name": "Zone Thermostat Jessie", "model": "Zone Thermostat", "types": {"py/set": ["thermostat"]}, "class": "zone_thermostat", "location": "82fa13f017d240daa0d0ea1775420f24"}, "680423ff840043738f42cc7f1ff97a36": {"name": "Thermostatic Radiator Badkamer", "model": "Thermostatic Radiator Valve", "types": {"py/set": ["thermostat"]}, "class": "thermo_sensor", "location": "08963fec7c53423ca5680aa4cb502c63"}, "f1fee6043d3642a9b0a65297455f008e": {"name": "Zone Thermostat Badkamer", "model": "Zone Thermostat", "types": {"py/set": ["thermostat"]}, "class": "zone_thermostat", "location": "08963fec7c53423ca5680aa4cb502c63"}, "675416a629f343c495449970e2ca37b5": {"name": "Ziggo Modem", "model": "Plug", "types": {"py/set": ["plug", "power"]}, "class": "router", "location": "cd143c07248f491493cea0533bc3d669"}, "e7693eb9582644e5b865dba8d4447cf1": {"name": "CV Kraan Garage", "model": "Thermostatic Radiator Valve", "types": {"py/set": ["thermostat"]}, "class": "thermostatic_radiator_valve", "location": "446ac08dd04d4eff8ac57489757b7314"}}

View File

@ -1 +0,0 @@
{"electricity_consumed": 34.0, "electricity_consumed_interval": 9.15, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 82.6, "electricity_consumed_interval": 8.6, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 8.5, "electricity_consumed_interval": 0.0, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 12.2, "electricity_consumed_interval": 2.97, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"temperature": 19.1, "setpoint": 14.0, "battery": 51, "temperature_difference": -0.4, "valve_position": 0.0}

View File

@ -1 +0,0 @@
{"temperature": 17.2, "setpoint": 15.0, "battery": 37, "active_preset": "asleep", "presets": {"home": [20.0, 22.0], "no_frost": [10.0, 30.0], "away": [12.0, 25.0], "vacation": [11.0, 28.0], "asleep": [16.0, 24.0]}, "schedule_temperature": 15.0, "available_schedules": ["CV Jessie"], "selected_schedule": "CV Jessie", "last_used": "CV Jessie"}

View File

@ -1 +0,0 @@
{"electricity_consumed": 35.6, "electricity_consumed_interval": 7.37, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"water_temperature": 70.0, "intended_boiler_temperature": 70.0, "modulation_level": 1, "heating_state": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 12.5, "electricity_consumed_interval": 3.8, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"temperature": 17.2, "setpoint": 13.0, "battery": 62, "temperature_difference": -0.2, "valve_position": 0.0}

View File

@ -1 +0,0 @@
{"temperature": 26.0, "setpoint": 21.5, "temperature_difference": 3.5, "valve_position": 100}

View File

@ -1 +0,0 @@
{"temperature": 20.9, "setpoint": 21.5, "battery": 34, "active_preset": "home", "presets": {"vacation": [15.0, 28.0], "asleep": [18.0, 24.0], "no_frost": [12.0, 30.0], "away": [17.0, 25.0], "home": [21.5, 22.0]}, "schedule_temperature": 21.5, "available_schedules": ["GF7 Woonkamer"], "selected_schedule": "GF7 Woonkamer", "last_used": "GF7 Woonkamer"}

View File

@ -1 +0,0 @@
{"electricity_consumed": 16.5, "electricity_consumed_interval": 0.5, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"temperature": 17.1, "setpoint": 15.0, "battery": 62, "temperature_difference": 0.1, "valve_position": 0.0}

View File

@ -1 +0,0 @@
{"temperature": 16.5, "setpoint": 13.0, "battery": 67, "active_preset": "away", "presets": {"home": [20.0, 22.0], "away": [12.0, 25.0], "vacation": [12.0, 28.0], "no_frost": [8.0, 30.0], "asleep": [15.0, 24.0]}, "schedule_temperature": null, "available_schedules": [], "selected_schedule": null, "last_used": null}

View File

@ -1 +0,0 @@
{"temperature": 15.6, "setpoint": 5.5, "battery": 68, "temperature_difference": 0.0, "valve_position": 0.0, "active_preset": "no_frost", "presets": {"home": [20.0, 22.0], "asleep": [17.0, 24.0], "away": [15.0, 25.0], "vacation": [15.0, 28.0], "no_frost": [10.0, 30.0]}, "schedule_temperature": null, "available_schedules": [], "selected_schedule": null, "last_used": null}

View File

@ -1 +0,0 @@
{"temperature": 18.9, "setpoint": 14.0, "battery": 92, "active_preset": "away", "presets": {"asleep": [17.0, 24.0], "no_frost": [10.0, 30.0], "away": [14.0, 25.0], "home": [21.0, 22.0], "vacation": [12.0, 28.0]}, "schedule_temperature": 14.0, "available_schedules": ["Badkamer Schema"], "selected_schedule": "Badkamer Schema", "last_used": "Badkamer Schema"}

View File

@ -1 +1,5 @@
{"af82e4ccf9c548528166d38e560662a4": {"warning": "Node Plug (with MAC address 000D6F000D13CB01, in room 'n.a.') has been unreachable since 23:03 2020-01-18. Please check the connection and restart the device."}}
{
"af82e4ccf9c548528166d38e560662a4": {
"warning": "Node Plug (with MAC address 000D6F000D13CB01, in room 'n.a.') has been unreachable since 23:03 2020-01-18. Please check the connection and restart the device."
}
}

View File

@ -0,0 +1,79 @@
[
{
"active_device": true,
"cooling_present": true,
"gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"single_master_thermostat": true,
"smile_name": "Anna",
"notifications": {}
},
{
"1cbf783bb11e4a7c8a6843dee3a86927": {
"class": "heater_central",
"fw": null,
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Generic heater",
"name": "OpenTherm",
"vendor": "Techneco",
"heating_state": true,
"compressor_state": true,
"cooling_state": false,
"cooling_active": false,
"binary_sensors": {
"dhw_state": false,
"slave_boiler_state": false,
"flame_state": false
},
"sensors": {
"outdoor_temperature": 3.0,
"water_temperature": 29.1,
"intended_boiler_temperature": 0.0,
"modulation_level": 52,
"return_temperature": 25.1,
"water_pressure": 1.57,
"device_state": "heating"
},
"switches": { "dhw_cm_switch": false }
},
"015ae9ea3f964e668e490fa39da3870b": {
"class": "gateway",
"fw": "4.0.15",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise B.V.",
"binary_sensors": { "plugwise_notification": false },
"sensors": { "outdoor_temperature": 20.2 }
},
"3cb70739631c4d17a86b8b12e8a5161b": {
"class": "thermostat",
"fw": "2018-02-08T11:15:53+01:00",
"location": "c784ee9fdab44e1395b8dee7d7a497d5",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise",
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home",
"presets": {
"no_frost": [10.0, 30.0],
"home": [21.0, 22.0],
"away": [20.0, 25.0],
"asleep": [20.5, 24.0],
"vacation": [17.0, 28.0]
},
"available_schedules": ["None"],
"selected_schedule": "None",
"schedule_temperature": null,
"last_used": null,
"mode": "heat",
"sensors": {
"temperature": 19.3,
"setpoint": 21.0,
"illuminance": 86.0,
"cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4
}
}
}
]

View File

@ -1 +0,0 @@
{"1cbf783bb11e4a7c8a6843dee3a86927": {"name": "Anna", "model": "Heater Central", "types": {"py/set": ["temperature", "thermostat", "home"]}, "class": "heater_central", "location": "a57efe5f145f498c9be62a9b63626fbf"}, "015ae9ea3f964e668e490fa39da3870b": {"name": "Anna", "model": "Smile Anna", "types": {"py/set": ["temperature", "thermostat", "home"]}, "class": "gateway", "location": "a57efe5f145f498c9be62a9b63626fbf"}, "3cb70739631c4d17a86b8b12e8a5161b": {"name": "Anna", "model": "Thermostat", "types": {"py/set": ["thermostat"]}, "class": "thermostat", "location": "c784ee9fdab44e1395b8dee7d7a497d5"}}

View File

@ -1 +0,0 @@
{"water_temperature": 29.1, "dhw_state": false, "intended_boiler_temperature": 0.0, "heating_state": false, "modulation_level": 52, "return_temperature": 25.1, "compressor_state": true, "cooling_state": false, "slave_boiler_state": false, "flame_state": false, "water_pressure": 1.57, "outdoor_temperature": 18.0}

View File

@ -1 +0,0 @@
{"temperature": 23.3, "setpoint": 21.0, "heating_state": false, "active_preset": "home", "presets": {"no_frost": [10.0, 30.0], "home": [21.0, 22.0], "away": [20.0, 25.0], "asleep": [20.5, 24.0], "vacation": [17.0, 28.0]}, "schedule_temperature": null, "available_schedules": ["standaard"], "selected_schedule": "standaard", "last_used": "standaard", "illuminance": 86.0}

View File

@ -0,0 +1,39 @@
[
{
"active_device": false,
"cooling_present": null,
"gateway_id": "e950c7d5e1ee407a858e2a8b5016c8b3",
"heater_id": null,
"single_master_thermostat": null,
"smile_name": "P1",
"notifications": {}
},
{
"e950c7d5e1ee407a858e2a8b5016c8b3": {
"class": "gateway",
"fw": "3.3.9",
"location": "cd3e822288064775a7c4afcdd70bdda2",
"model": "P1",
"name": "P1",
"vendor": "Plugwise B.V.",
"sensors": {
"net_electricity_point": -2816,
"electricity_consumed_peak_point": 0,
"electricity_consumed_off_peak_point": 0,
"net_electricity_cumulative": 442.972,
"electricity_consumed_peak_cumulative": 442.932,
"electricity_consumed_off_peak_cumulative": 551.09,
"electricity_consumed_peak_interval": 0,
"electricity_consumed_off_peak_interval": 0,
"electricity_produced_peak_point": 2816,
"electricity_produced_off_peak_point": 0,
"electricity_produced_peak_cumulative": 396.559,
"electricity_produced_off_peak_cumulative": 154.491,
"electricity_produced_peak_interval": 0,
"electricity_produced_off_peak_interval": 0,
"gas_consumed_cumulative": 584.85,
"gas_consumed_interval": 0.0
}
}
}
]

View File

@ -1 +0,0 @@
{"e950c7d5e1ee407a858e2a8b5016c8b3": {"name": "P1", "model": "Smile P1", "types": {"py/set": ["home", "power"]}, "class": "gateway", "location": "cd3e822288064775a7c4afcdd70bdda2"}}

View File

@ -1 +0,0 @@
{"net_electricity_point": -2761, "electricity_consumed_peak_point": 0, "electricity_consumed_off_peak_point": 0, "net_electricity_cumulative": 442.972, "electricity_consumed_peak_cumulative": 442.932, "electricity_consumed_off_peak_cumulative": 551.09, "net_electricity_interval": 0, "electricity_consumed_peak_interval": 0, "electricity_consumed_off_peak_interval": 0, "electricity_produced_peak_point": 2761, "electricity_produced_off_peak_point": 0, "electricity_produced_peak_cumulative": 396.559, "electricity_produced_off_peak_cumulative": 154.491, "electricity_produced_peak_interval": 0, "electricity_produced_off_peak_interval": 0, "gas_consumed_cumulative": 584.85, "gas_consumed_interval": 0.0}

View File

@ -0,0 +1,178 @@
[
{
"active_device": false,
"cooling_present": null,
"gateway_id": "0000aaaa0000aaaa0000aaaa0000aa00",
"heater_id": null,
"single_master_thermostat": null,
"smile_name": "Stretch",
"notifications": {}
},
{
"0000aaaa0000aaaa0000aaaa0000aa00": {
"class": "gateway",
"fw": "3.1.11",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"vendor": "Plugwise B.V.",
"model": "Stretch",
"name": "Stretch"
},
"5ca521ac179d468e91d772eeeb8a2117": {
"class": "zz_misc",
"fw": null,
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": null,
"name": "Oven (793F84)",
"vendor": null,
"sensors": {
"electricity_consumed": 0.0,
"electricity_consumed_interval": 0.0,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": false }
},
"5871317346d045bc9f6b987ef25ee638": {
"class": "water_heater_vessel",
"fw": "2011-06-27T10:52:18+02:00",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": "Circle type F",
"name": "Boiler (1EB31)",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 1.19,
"electricity_consumed_interval": 0.0,
"electricity_produced": 0.0
},
"switches": { "relay": true, "lock": false }
},
"e1c884e7dede431dadee09506ec4f859": {
"class": "refrigerator",
"fw": "2011-06-27T10:47:37+02:00",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": "Circle+ type F",
"name": "Koelkast (92C4A)",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 50.5,
"electricity_consumed_interval": 0.08,
"electricity_produced": 0.0
},
"switches": { "relay": true, "lock": false }
},
"aac7b735042c4832ac9ff33aae4f453b": {
"class": "dishwasher",
"fw": "2011-06-27T10:52:18+02:00",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": "Circle type F",
"name": "Vaatwasser (2a1ab)",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 0.0,
"electricity_consumed_interval": 0.71,
"electricity_produced": 0.0
},
"switches": { "relay": true, "lock": false }
},
"cfe95cf3de1948c0b8955125bf754614": {
"class": "dryer",
"fw": "2011-06-27T10:52:18+02:00",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": "Circle type F",
"name": "Droger (52559)",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 0.0,
"electricity_consumed_interval": 0.0,
"electricity_produced": 0.0
},
"switches": { "relay": true, "lock": false }
},
"99f89d097be34fca88d8598c6dbc18ea": {
"class": "router",
"fw": null,
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": null,
"name": "Meterkast (787BFB)",
"vendor": null,
"sensors": {
"electricity_consumed": 27.6,
"electricity_consumed_interval": 28.2,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"059e4d03c7a34d278add5c7a4a781d19": {
"class": "washingmachine",
"fw": "2011-06-27T10:52:18+02:00",
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": "Circle type F",
"name": "Wasmachine (52AC1)",
"vendor": "Plugwise",
"sensors": {
"electricity_consumed": 0.0,
"electricity_consumed_interval": 0.0,
"electricity_produced": 0.0
},
"switches": { "relay": true, "lock": false }
},
"e309b52ea5684cf1a22f30cf0cd15051": {
"class": "computer_desktop",
"fw": null,
"location": "0000aaaa0000aaaa0000aaaa0000aa00",
"model": null,
"name": "Computer (788618)",
"vendor": null,
"sensors": {
"electricity_consumed": 156,
"electricity_consumed_interval": 163,
"electricity_produced": 0.0,
"electricity_produced_interval": 0.0
},
"switches": { "relay": true, "lock": true }
},
"71e1944f2a944b26ad73323e399efef0": {
"class": "switching",
"fw": null,
"location": null,
"model": "Switchgroup",
"name": "Test",
"members": ["5ca521ac179d468e91d772eeeb8a2117"],
"types": ["switch_group"],
"vendor": null,
"switches": { "relay": true }
},
"d950b314e9d8499f968e6db8d82ef78c": {
"class": "report",
"fw": null,
"location": null,
"model": "Switchgroup",
"name": "Stroomvreters",
"members": [
"059e4d03c7a34d278add5c7a4a781d19",
"5871317346d045bc9f6b987ef25ee638",
"aac7b735042c4832ac9ff33aae4f453b",
"cfe95cf3de1948c0b8955125bf754614",
"e1c884e7dede431dadee09506ec4f859"
],
"types": ["switch_group"],
"vendor": null,
"switches": { "relay": true }
},
"d03738edfcc947f7b8f4573571d90d2d": {
"class": "switching",
"fw": null,
"location": null,
"model": "Switchgroup",
"name": "Schakel",
"members": [
"059e4d03c7a34d278add5c7a4a781d19",
"cfe95cf3de1948c0b8955125bf754614"
],
"types": ["switch_group"],
"vendor": null,
"switches": { "relay": true }
}
}
]

View File

@ -1 +0,0 @@
{"5ca521ac179d468e91d772eeeb8a2117": {"name": "Oven (793F84)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "zz_misc", "location": 0}, "5871317346d045bc9f6b987ef25ee638": {"name": "Boiler (1EB31)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "water_heater_vessel", "location": 0}, "e1c884e7dede431dadee09506ec4f859": {"name": "Koelkast (92C4A)", "model": "Circle+", "types": {"py/set": ["plug", "power"]}, "class": "refrigerator", "location": 0}, "aac7b735042c4832ac9ff33aae4f453b": {"name": "Vaatwasser (2a1ab)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "dishwasher", "location": 0}, "cfe95cf3de1948c0b8955125bf754614": {"name": "Droger (52559)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "dryer", "location": 0}, "99f89d097be34fca88d8598c6dbc18ea": {"name": "Meterkast (787BFB)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "router", "location": 0}, "059e4d03c7a34d278add5c7a4a781d19": {"name": "Wasmachine (52AC1)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "washingmachine", "location": 0}, "e309b52ea5684cf1a22f30cf0cd15051": {"name": "Computer (788618)", "model": "Circle", "types": {"py/set": ["plug", "power"]}, "class": "computer_desktop", "location": 0}, "71e1944f2a944b26ad73323e399efef0": {"name": "Test", "model": "group_switch", "types": {"py/set": ["switch_group"]}, "class": "switching", "members": ["5ca521ac179d468e91d772eeeb8a2117"], "location": null}, "d950b314e9d8499f968e6db8d82ef78c": {"name": "Stroomvreters", "model": "group_switch", "types": {"py/set": ["switch_group"]}, "class": "report", "members": ["059e4d03c7a34d278add5c7a4a781d19", "5871317346d045bc9f6b987ef25ee638", "aac7b735042c4832ac9ff33aae4f453b", "cfe95cf3de1948c0b8955125bf754614", "e1c884e7dede431dadee09506ec4f859"], "location": null}, "d03738edfcc947f7b8f4573571d90d2d": {"name": "Schakel", "model": "group_switch", "types": {"py/set": ["switch_group"]}, "class": "switching", "members": ["059e4d03c7a34d278add5c7a4a781d19", "cfe95cf3de1948c0b8955125bf754614"], "location": null}}

View File

@ -1 +0,0 @@
{"electricity_consumed": 0.0, "electricity_consumed_interval": 0.0, "electricity_produced": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 1.19, "electricity_consumed_interval": 0.0, "electricity_produced": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 0.0, "electricity_consumed_interval": 0.0, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 27.6, "electricity_consumed_interval": 28.2, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 0.0, "electricity_consumed_interval": 0.71, "electricity_produced": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 0.0, "electricity_consumed_interval": 0.0, "electricity_produced": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 50.5, "electricity_consumed_interval": 0.08, "electricity_produced": 0.0, "relay": true}

View File

@ -1 +0,0 @@
{"electricity_consumed": 156, "electricity_consumed_interval": 163, "electricity_produced": 0.0, "electricity_produced_interval": 0.0, "relay": true}

View File

@ -2,7 +2,11 @@
from plugwise.exceptions import PlugwiseException
from homeassistant.components.climate.const import HVAC_MODE_AUTO, HVAC_MODE_HEAT
from homeassistant.components.climate.const import (
HVAC_MODE_AUTO,
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
)
from homeassistant.config_entries import ConfigEntryState
from tests.components.plugwise.common import async_init_integration
@ -16,7 +20,7 @@ async def test_adam_climate_entity_attributes(hass, mock_smile_adam):
state = hass.states.get("climate.zone_lisa_wk")
attrs = state.attributes
assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
assert "preset_modes" in attrs
assert "no_frost" in attrs["preset_modes"]
@ -32,7 +36,7 @@ async def test_adam_climate_entity_attributes(hass, mock_smile_adam):
state = hass.states.get("climate.zone_thermostat_jessie")
attrs = state.attributes
assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO]
assert attrs["hvac_modes"] == [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
assert "preset_modes" in attrs
assert "no_frost" in attrs["preset_modes"]
@ -144,17 +148,17 @@ async def test_anna_climate_entity_attributes(hass, mock_smile_anna):
attrs = state.attributes
assert "hvac_modes" in attrs
assert "heat_cool" in attrs["hvac_modes"]
assert "heat" in attrs["hvac_modes"]
assert "preset_modes" in attrs
assert "no_frost" in attrs["preset_modes"]
assert "home" in attrs["preset_modes"]
assert attrs["current_temperature"] == 23.3
assert attrs["current_temperature"] == 19.3
assert attrs["temperature"] == 21.0
assert state.state == HVAC_MODE_AUTO
assert attrs["hvac_action"] == "idle"
assert state.state == HVAC_MODE_HEAT
assert attrs["hvac_action"] == "heating"
assert attrs["preset_mode"] == "home"
assert attrs["supported_features"] == 17

View File

@ -39,7 +39,7 @@ async def test_smile_timeout(hass, mock_smile_notconnect):
async def test_smile_adam_xmlerror(hass, mock_smile_adam):
"""Detect malformed XML by Smile in Adam environment."""
mock_smile_adam.full_update_device.side_effect = XMLDataMissingError
mock_smile_adam.async_update.side_effect = XMLDataMissingError
entry = await async_init_integration(hass, mock_smile_adam)
assert entry.state is ConfigEntryState.SETUP_RETRY

View File

@ -37,7 +37,7 @@ async def test_anna_as_smt_climate_sensor_entities(hass, mock_smile_anna):
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("sensor.auxiliary_outdoor_temperature")
assert float(state.state) == 18.0
assert float(state.state) == 3.0
state = hass.states.get("sensor.auxiliary_water_temperature")
assert float(state.state) == 29.1
@ -53,7 +53,7 @@ async def test_anna_climate_sensor_entities(hass, mock_smile_anna):
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("sensor.auxiliary_outdoor_temperature")
assert float(state.state) == 18.0
assert float(state.state) == 3.0
async def test_p1_dsmr_sensor_entities(hass, mock_smile_p1):
@ -62,13 +62,13 @@ async def test_p1_dsmr_sensor_entities(hass, mock_smile_p1):
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("sensor.p1_net_electricity_point")
assert float(state.state) == -2761.0
assert float(state.state) == -2816.0
state = hass.states.get("sensor.p1_electricity_consumed_off_peak_cumulative")
assert float(state.state) == 551.09
state = hass.states.get("sensor.p1_electricity_produced_peak_point")
assert float(state.state) == 2761.0
assert float(state.state) == 2816.0
state = hass.states.get("sensor.p1_electricity_consumed_peak_cumulative")
assert float(state.state) == 442.932

View File

@ -21,7 +21,7 @@ async def test_adam_climate_switch_entities(hass, mock_smile_adam):
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
mock_smile_adam.set_switch_state.side_effect = PlugwiseException
entry = await async_init_integration(hass, mock_smile_adam)
assert entry.state is ConfigEntryState.LOADED