Update Netatmo climate platform (#59974)
This commit is contained in:
parent
3307e54363
commit
653fb5b637
12 changed files with 626 additions and 754 deletions
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
import pyatmo
|
||||
import voluptuous as vol
|
||||
|
@ -22,7 +22,6 @@ from homeassistant.components.climate.const import (
|
|||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL,
|
||||
ATTR_SUGGESTED_AREA,
|
||||
ATTR_TEMPERATURE,
|
||||
PRECISION_HALVES,
|
||||
|
@ -32,7 +31,6 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.device_registry import async_get_registry
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
@ -41,7 +39,6 @@ from .const import (
|
|||
ATTR_HEATING_POWER_REQUEST,
|
||||
ATTR_SCHEDULE_NAME,
|
||||
ATTR_SELECTED_SCHEDULE,
|
||||
DATA_DEVICE_IDS,
|
||||
DATA_HANDLER,
|
||||
DATA_HOMES,
|
||||
DATA_SCHEDULES,
|
||||
|
@ -50,17 +47,15 @@ from .const import (
|
|||
EVENT_TYPE_SCHEDULE,
|
||||
EVENT_TYPE_SET_POINT,
|
||||
EVENT_TYPE_THERM_MODE,
|
||||
MANUFACTURER,
|
||||
SERVICE_SET_SCHEDULE,
|
||||
SIGNAL_NAME,
|
||||
TYPE_ENERGY,
|
||||
)
|
||||
from .data_handler import (
|
||||
HOMEDATA_DATA_CLASS_NAME,
|
||||
HOMESTATUS_DATA_CLASS_NAME,
|
||||
CLIMATE_STATE_CLASS_NAME,
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
NetatmoDataHandler,
|
||||
)
|
||||
from .helper import get_all_home_ids, update_climate_schedules
|
||||
from .netatmo_entity_base import NetatmoBase
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -125,44 +120,42 @@ async def async_setup_entry(
|
|||
data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER]
|
||||
|
||||
await data_handler.register_data_class(
|
||||
HOMEDATA_DATA_CLASS_NAME, HOMEDATA_DATA_CLASS_NAME, None
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME, CLIMATE_TOPOLOGY_CLASS_NAME, None
|
||||
)
|
||||
home_data = data_handler.data.get(HOMEDATA_DATA_CLASS_NAME)
|
||||
climate_topology = data_handler.data.get(CLIMATE_TOPOLOGY_CLASS_NAME)
|
||||
|
||||
if not home_data or home_data.raw_data == {}:
|
||||
if not climate_topology or climate_topology.raw_data == {}:
|
||||
raise PlatformNotReady
|
||||
|
||||
entities = []
|
||||
for home_id in get_all_home_ids(home_data):
|
||||
for room_id in home_data.rooms[home_id]:
|
||||
signal_name = f"{HOMESTATUS_DATA_CLASS_NAME}-{home_id}"
|
||||
await data_handler.register_data_class(
|
||||
HOMESTATUS_DATA_CLASS_NAME, signal_name, None, home_id=home_id
|
||||
)
|
||||
home_status = data_handler.data.get(signal_name)
|
||||
if home_status and room_id in home_status.rooms:
|
||||
entities.append(NetatmoThermostat(data_handler, home_id, room_id))
|
||||
|
||||
hass.data[DOMAIN][DATA_SCHEDULES].update(
|
||||
update_climate_schedules(
|
||||
home_ids=get_all_home_ids(home_data),
|
||||
schedules=data_handler.data[HOMEDATA_DATA_CLASS_NAME].schedules,
|
||||
for home_id in climate_topology.home_ids:
|
||||
signal_name = f"{CLIMATE_STATE_CLASS_NAME}-{home_id}"
|
||||
await data_handler.register_data_class(
|
||||
CLIMATE_STATE_CLASS_NAME, signal_name, None, home_id=home_id
|
||||
)
|
||||
)
|
||||
climate_state = data_handler.data[signal_name]
|
||||
climate_topology.register_handler(home_id, climate_state.process_topology)
|
||||
|
||||
hass.data[DOMAIN][DATA_HOMES] = {
|
||||
home_id: home_data.get("name")
|
||||
for home_id, home_data in (
|
||||
data_handler.data[HOMEDATA_DATA_CLASS_NAME].homes.items()
|
||||
)
|
||||
}
|
||||
for room in climate_state.homes[home_id].rooms.values():
|
||||
if room.device_type is None or room.device_type.value not in [
|
||||
NA_THERM,
|
||||
NA_VALVE,
|
||||
]:
|
||||
continue
|
||||
entities.append(NetatmoThermostat(data_handler, room))
|
||||
|
||||
hass.data[DOMAIN][DATA_SCHEDULES][home_id] = climate_state.homes[
|
||||
home_id
|
||||
].schedules
|
||||
|
||||
hass.data[DOMAIN][DATA_HOMES][home_id] = climate_state.homes[home_id].name
|
||||
|
||||
_LOGGER.debug("Adding climate devices %s", entities)
|
||||
async_add_entities(entities, True)
|
||||
|
||||
platform = entity_platform.async_get_current_platform()
|
||||
|
||||
if home_data is not None:
|
||||
if climate_topology is not None:
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_SET_SCHEDULE,
|
||||
{vol.Required(ATTR_SCHEDULE_NAME): cv.string},
|
||||
|
@ -174,67 +167,61 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
"""Representation a Netatmo thermostat."""
|
||||
|
||||
_attr_hvac_mode = HVAC_MODE_AUTO
|
||||
_attr_hvac_modes = [HVAC_MODE_AUTO, HVAC_MODE_HEAT]
|
||||
_attr_max_temp = DEFAULT_MAX_TEMP
|
||||
_attr_preset_modes = SUPPORT_PRESET
|
||||
_attr_supported_features = SUPPORT_FLAGS
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = TEMP_CELSIUS
|
||||
|
||||
def __init__(
|
||||
self, data_handler: NetatmoDataHandler, home_id: str, room_id: str
|
||||
self, data_handler: NetatmoDataHandler, room: pyatmo.climate.NetatmoRoom
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
ClimateEntity.__init__(self)
|
||||
super().__init__(data_handler)
|
||||
|
||||
self._id = room_id
|
||||
self._home_id = home_id
|
||||
self._room = room
|
||||
self._id = self._room.entity_id
|
||||
|
||||
self._home_status_class = f"{HOMESTATUS_DATA_CLASS_NAME}-{self._home_id}"
|
||||
self._climate_state_class = (
|
||||
f"{CLIMATE_STATE_CLASS_NAME}-{self._room.home.entity_id}"
|
||||
)
|
||||
self._climate_state: pyatmo.AsyncClimate = data_handler.data[
|
||||
self._climate_state_class
|
||||
]
|
||||
|
||||
self._data_classes.extend(
|
||||
[
|
||||
{
|
||||
"name": HOMEDATA_DATA_CLASS_NAME,
|
||||
SIGNAL_NAME: HOMEDATA_DATA_CLASS_NAME,
|
||||
"name": CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
SIGNAL_NAME: CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
},
|
||||
{
|
||||
"name": HOMESTATUS_DATA_CLASS_NAME,
|
||||
"home_id": self._home_id,
|
||||
SIGNAL_NAME: self._home_status_class,
|
||||
"name": CLIMATE_STATE_CLASS_NAME,
|
||||
"home_id": self._room.home.entity_id,
|
||||
SIGNAL_NAME: self._climate_state_class,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self._home_status = self.data_handler.data[self._home_status_class]
|
||||
self._room_status = self._home_status.rooms[room_id]
|
||||
self._room_data: dict = self._data.rooms[home_id][room_id]
|
||||
|
||||
self._model: str = NA_VALVE
|
||||
for module in self._room_data.get("module_ids", []):
|
||||
if self._home_status.thermostats.get(module):
|
||||
self._model = NA_THERM
|
||||
break
|
||||
self._model: str = getattr(room.device_type, "value")
|
||||
|
||||
self._netatmo_type = TYPE_ENERGY
|
||||
|
||||
self._device_name = self._data.rooms[home_id][room_id]["name"]
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name}"
|
||||
self._attr_name = self._room.name
|
||||
self._away: bool | None = None
|
||||
self._support_flags = SUPPORT_FLAGS
|
||||
self._battery_level = None
|
||||
self._connected: bool | None = None
|
||||
|
||||
self._away_temperature: float | None = None
|
||||
self._hg_temperature: float | None = None
|
||||
self._boilerstatus: bool | None = None
|
||||
self._setpoint_duration = None
|
||||
self._selected_schedule = None
|
||||
|
||||
self._attr_hvac_modes = [HVAC_MODE_AUTO, HVAC_MODE_HEAT]
|
||||
if self._model == NA_THERM:
|
||||
self._attr_hvac_modes.append(HVAC_MODE_OFF)
|
||||
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
self._attr_unique_id = f"{self._room.entity_id}-{self._model}"
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Entity created."""
|
||||
|
@ -254,33 +241,32 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
)
|
||||
)
|
||||
|
||||
registry = await async_get_registry(self.hass)
|
||||
device = registry.async_get_device({(DOMAIN, self._id)}, set())
|
||||
assert device
|
||||
self.hass.data[DOMAIN][DATA_DEVICE_IDS][self._home_id] = device.id
|
||||
|
||||
@callback
|
||||
def handle_event(self, event: dict) -> None:
|
||||
"""Handle webhook events."""
|
||||
data = event["data"]
|
||||
|
||||
if self._home_id != data["home_id"]:
|
||||
if self._room.home.entity_id != data["home_id"]:
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE and "schedule_id" in data:
|
||||
self._selected_schedule = self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._home_id
|
||||
].get(data["schedule_id"])
|
||||
self._attr_extra_state_attributes.update(
|
||||
{"selected_schedule": self._selected_schedule}
|
||||
self._selected_schedule = getattr(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._room.home.entity_id].get(
|
||||
data["schedule_id"]
|
||||
),
|
||||
"name",
|
||||
None,
|
||||
)
|
||||
self._attr_extra_state_attributes[
|
||||
ATTR_SELECTED_SCHEDULE
|
||||
] = self._selected_schedule
|
||||
self.async_write_ha_state()
|
||||
self.data_handler.async_force_update(self._home_status_class)
|
||||
self.data_handler.async_force_update(self._climate_state_class)
|
||||
return
|
||||
|
||||
home = data["home"]
|
||||
|
||||
if self._home_id != home["id"]:
|
||||
if self._room.home.entity_id != home["id"]:
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_THERM_MODE:
|
||||
|
@ -292,12 +278,15 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
self._attr_target_temperature = self._away_temperature
|
||||
elif self._attr_preset_mode == PRESET_SCHEDULE:
|
||||
self.async_update_callback()
|
||||
self.data_handler.async_force_update(self._home_status_class)
|
||||
self.data_handler.async_force_update(self._climate_state_class)
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
for room in home.get("rooms", []):
|
||||
if data["event_type"] == EVENT_TYPE_SET_POINT and self._id == room["id"]:
|
||||
if (
|
||||
data["event_type"] == EVENT_TYPE_SET_POINT
|
||||
and self._room.entity_id == room["id"]
|
||||
):
|
||||
if room["therm_setpoint_mode"] == STATE_NETATMO_OFF:
|
||||
self._attr_hvac_mode = HVAC_MODE_OFF
|
||||
self._attr_preset_mode = STATE_NETATMO_OFF
|
||||
|
@ -318,31 +307,21 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
|
||||
if (
|
||||
data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT
|
||||
and self._id == room["id"]
|
||||
and self._room.entity_id == room["id"]
|
||||
):
|
||||
self.async_update_callback()
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
@property
|
||||
def _data(self) -> pyatmo.AsyncHomeData:
|
||||
"""Return data for this entity."""
|
||||
return cast(
|
||||
pyatmo.AsyncHomeData, self.data_handler.data[self._data_classes[0]["name"]]
|
||||
)
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return the list of supported features."""
|
||||
return self._support_flags
|
||||
|
||||
@property
|
||||
def hvac_action(self) -> str | None:
|
||||
"""Return the current running hvac operation if supported."""
|
||||
if self._model == NA_THERM and self._boilerstatus is not None:
|
||||
return CURRENT_HVAC_MAP_NETATMO[self._boilerstatus]
|
||||
# Maybe it is a valve
|
||||
if self._room_status and self._room_status.get("heating_power_request", 0) > 0:
|
||||
if (
|
||||
heating_req := getattr(self._room, "heating_power_request", 0)
|
||||
) is not None and heating_req > 0:
|
||||
return CURRENT_HVAC_HEAT
|
||||
return CURRENT_HVAC_IDLE
|
||||
|
||||
|
@ -363,8 +342,8 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
await self.async_turn_on()
|
||||
|
||||
if self.target_temperature == 0:
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id,
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id,
|
||||
STATE_NETATMO_HOME,
|
||||
)
|
||||
|
||||
|
@ -373,15 +352,15 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
and self._model == NA_VALVE
|
||||
and self.hvac_mode == HVAC_MODE_HEAT
|
||||
):
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id,
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id,
|
||||
STATE_NETATMO_HOME,
|
||||
)
|
||||
elif (
|
||||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX) and self._model == NA_VALVE
|
||||
):
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id,
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id,
|
||||
STATE_NETATMO_MANUAL,
|
||||
DEFAULT_MAX_TEMP,
|
||||
)
|
||||
|
@ -389,15 +368,17 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX)
|
||||
and self.hvac_mode == HVAC_MODE_HEAT
|
||||
):
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id, STATE_NETATMO_HOME
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id, STATE_NETATMO_HOME
|
||||
)
|
||||
elif preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX):
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id, PRESET_MAP_NETATMO[preset_mode]
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id, PRESET_MAP_NETATMO[preset_mode]
|
||||
)
|
||||
elif preset_mode in (PRESET_SCHEDULE, PRESET_FROST_GUARD, PRESET_AWAY):
|
||||
await self._home_status.async_set_thermmode(PRESET_MAP_NETATMO[preset_mode])
|
||||
await self._climate_state.async_set_thermmode(
|
||||
PRESET_MAP_NETATMO[preset_mode]
|
||||
)
|
||||
else:
|
||||
_LOGGER.error("Preset mode '%s' not available", preset_mode)
|
||||
|
||||
|
@ -407,8 +388,8 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
"""Set new target temperature for 2 hours."""
|
||||
if (temp := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||
return
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id, STATE_NETATMO_MANUAL, min(temp, DEFAULT_MAX_TEMP)
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id, STATE_NETATMO_MANUAL, min(temp, DEFAULT_MAX_TEMP)
|
||||
)
|
||||
|
||||
self.async_write_ha_state()
|
||||
|
@ -416,20 +397,22 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
async def async_turn_off(self) -> None:
|
||||
"""Turn the entity off."""
|
||||
if self._model == NA_VALVE:
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id,
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id,
|
||||
STATE_NETATMO_MANUAL,
|
||||
DEFAULT_MIN_TEMP,
|
||||
)
|
||||
elif self.hvac_mode != HVAC_MODE_OFF:
|
||||
await self._home_status.async_set_room_thermpoint(
|
||||
self._id, STATE_NETATMO_OFF
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id, STATE_NETATMO_OFF
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the entity on."""
|
||||
await self._home_status.async_set_room_thermpoint(self._id, STATE_NETATMO_HOME)
|
||||
await self._climate_state.async_set_room_thermpoint(
|
||||
self._room.entity_id, STATE_NETATMO_HOME
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
|
@ -440,135 +423,57 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._home_status = self.data_handler.data[self._home_status_class]
|
||||
if self._home_status is None:
|
||||
if not self._room.reachable:
|
||||
if self.available:
|
||||
self._connected = False
|
||||
return
|
||||
|
||||
self._room_status = self._home_status.rooms.get(self._id)
|
||||
self._room_data = self._data.rooms.get(self._home_id, {}).get(self._id, {})
|
||||
|
||||
if not self._room_status or not self._room_data:
|
||||
if self._connected:
|
||||
_LOGGER.info(
|
||||
"The thermostat in room %s seems to be out of reach",
|
||||
self._device_name,
|
||||
)
|
||||
|
||||
self._connected = False
|
||||
return
|
||||
|
||||
roomstatus = {"roomID": self._room_status.get("id", {})}
|
||||
if self._room_status.get("reachable"):
|
||||
roomstatus.update(self._build_room_status())
|
||||
|
||||
self._away_temperature = self._data.get_away_temp(self._home_id)
|
||||
self._hg_temperature = self._data.get_hg_temp(self._home_id)
|
||||
self._setpoint_duration = self._data.setpoint_duration[self._home_id]
|
||||
self._selected_schedule = roomstatus.get("selected_schedule")
|
||||
|
||||
if "current_temperature" not in roomstatus:
|
||||
return
|
||||
|
||||
self._attr_current_temperature = roomstatus["current_temperature"]
|
||||
self._attr_target_temperature = roomstatus["target_temperature"]
|
||||
self._attr_preset_mode = NETATMO_MAP_PRESET[roomstatus["setpoint_mode"]]
|
||||
self._attr_hvac_mode = HVAC_MAP_NETATMO[self._attr_preset_mode]
|
||||
self._battery_level = roomstatus.get("battery_state")
|
||||
self._connected = True
|
||||
|
||||
self._away_temperature = self._room.home.get_away_temp()
|
||||
self._hg_temperature = self._room.home.get_hg_temp()
|
||||
self._attr_current_temperature = self._room.therm_measured_temperature
|
||||
self._attr_target_temperature = self._room.therm_setpoint_temperature
|
||||
self._attr_preset_mode = NETATMO_MAP_PRESET[
|
||||
getattr(self._room, "therm_setpoint_mode", STATE_NETATMO_SCHEDULE)
|
||||
]
|
||||
self._attr_hvac_mode = HVAC_MAP_NETATMO[self._attr_preset_mode]
|
||||
self._away = self._attr_hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
||||
|
||||
if self._battery_level is not None:
|
||||
self._attr_extra_state_attributes[ATTR_BATTERY_LEVEL] = self._battery_level
|
||||
self._selected_schedule = getattr(
|
||||
self._room.home.get_selected_schedule(), "name", None
|
||||
)
|
||||
self._attr_extra_state_attributes[
|
||||
ATTR_SELECTED_SCHEDULE
|
||||
] = self._selected_schedule
|
||||
|
||||
if self._model == NA_VALVE:
|
||||
self._attr_extra_state_attributes[
|
||||
ATTR_HEATING_POWER_REQUEST
|
||||
] = self._room_status.get("heating_power_request", 0)
|
||||
|
||||
if self._selected_schedule is not None:
|
||||
self._attr_extra_state_attributes[
|
||||
ATTR_SELECTED_SCHEDULE
|
||||
] = self._selected_schedule
|
||||
|
||||
def _build_room_status(self) -> dict:
|
||||
"""Construct room status."""
|
||||
try:
|
||||
roomstatus = {
|
||||
"roomname": self._room_data["name"],
|
||||
"target_temperature": self._room_status["therm_setpoint_temperature"],
|
||||
"setpoint_mode": self._room_status["therm_setpoint_mode"],
|
||||
"current_temperature": self._room_status["therm_measured_temperature"],
|
||||
"module_type": self._data.get_thermostat_type(
|
||||
home_id=self._home_id, room_id=self._id
|
||||
),
|
||||
"module_id": None,
|
||||
"heating_status": None,
|
||||
"heating_power_request": None,
|
||||
"selected_schedule": self._data._get_selected_schedule( # pylint: disable=protected-access
|
||||
home_id=self._home_id
|
||||
).get(
|
||||
"name"
|
||||
),
|
||||
}
|
||||
|
||||
batterylevel = None
|
||||
for module_id in self._room_data["module_ids"]:
|
||||
if (
|
||||
self._data.modules[self._home_id][module_id]["type"] == NA_THERM
|
||||
or roomstatus["module_id"] is None
|
||||
):
|
||||
roomstatus["module_id"] = module_id
|
||||
if roomstatus["module_type"] == NA_THERM:
|
||||
self._boilerstatus = self._home_status.boiler_status(
|
||||
roomstatus["module_id"]
|
||||
)
|
||||
roomstatus["heating_status"] = self._boilerstatus
|
||||
batterylevel = self._home_status.thermostats[
|
||||
roomstatus["module_id"]
|
||||
].get("battery_state")
|
||||
elif roomstatus["module_type"] == NA_VALVE:
|
||||
roomstatus["heating_power_request"] = self._room_status[
|
||||
"heating_power_request"
|
||||
]
|
||||
roomstatus["heating_status"] = roomstatus["heating_power_request"] > 0
|
||||
if self._boilerstatus is not None:
|
||||
roomstatus["heating_status"] = (
|
||||
self._boilerstatus and roomstatus["heating_status"]
|
||||
)
|
||||
batterylevel = self._home_status.valves[roomstatus["module_id"]].get(
|
||||
"battery_state"
|
||||
)
|
||||
|
||||
if batterylevel:
|
||||
roomstatus["battery_state"] = batterylevel
|
||||
|
||||
return roomstatus
|
||||
|
||||
except KeyError as err:
|
||||
_LOGGER.error("Update of room %s failed. Error: %s", self._id, err)
|
||||
|
||||
return {}
|
||||
] = self._room.heating_power_request
|
||||
else:
|
||||
for module in self._room.modules.values():
|
||||
self._boilerstatus = module.boiler_status
|
||||
break
|
||||
|
||||
async def _async_service_set_schedule(self, **kwargs: Any) -> None:
|
||||
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
|
||||
schedule_id = None
|
||||
for sid, name in self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].items():
|
||||
if name == schedule_name:
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._room.home.entity_id
|
||||
].items():
|
||||
if schedule.name == schedule_name:
|
||||
schedule_id = sid
|
||||
break
|
||||
|
||||
if not schedule_id:
|
||||
_LOGGER.error("%s is not a valid schedule", kwargs.get(ATTR_SCHEDULE_NAME))
|
||||
return
|
||||
|
||||
await self._data.async_switch_home_schedule(
|
||||
home_id=self._home_id, schedule_id=schedule_id
|
||||
)
|
||||
await self._climate_state.async_switch_home_schedule(schedule_id=schedule_id)
|
||||
_LOGGER.debug(
|
||||
"Setting %s schedule to %s (%s)",
|
||||
self._home_id,
|
||||
self._room.home.entity_id,
|
||||
kwargs.get(ATTR_SCHEDULE_NAME),
|
||||
schedule_id,
|
||||
)
|
||||
|
@ -577,5 +482,5 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info for the thermostat."""
|
||||
device_info: DeviceInfo = super().device_info
|
||||
device_info[ATTR_SUGGESTED_AREA] = self._room_data["name"]
|
||||
device_info[ATTR_SUGGESTED_AREA] = self._room.name
|
||||
return device_info
|
||||
|
|
|
@ -32,23 +32,23 @@ _LOGGER = logging.getLogger(__name__)
|
|||
CAMERA_DATA_CLASS_NAME = "AsyncCameraData"
|
||||
WEATHERSTATION_DATA_CLASS_NAME = "AsyncWeatherStationData"
|
||||
HOMECOACH_DATA_CLASS_NAME = "AsyncHomeCoachData"
|
||||
HOMEDATA_DATA_CLASS_NAME = "AsyncHomeData"
|
||||
HOMESTATUS_DATA_CLASS_NAME = "AsyncHomeStatus"
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME = "AsyncClimateTopology"
|
||||
CLIMATE_STATE_CLASS_NAME = "AsyncClimate"
|
||||
PUBLICDATA_DATA_CLASS_NAME = "AsyncPublicData"
|
||||
|
||||
DATA_CLASSES = {
|
||||
WEATHERSTATION_DATA_CLASS_NAME: pyatmo.AsyncWeatherStationData,
|
||||
HOMECOACH_DATA_CLASS_NAME: pyatmo.AsyncHomeCoachData,
|
||||
CAMERA_DATA_CLASS_NAME: pyatmo.AsyncCameraData,
|
||||
HOMEDATA_DATA_CLASS_NAME: pyatmo.AsyncHomeData,
|
||||
HOMESTATUS_DATA_CLASS_NAME: pyatmo.AsyncHomeStatus,
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME: pyatmo.AsyncClimateTopology,
|
||||
CLIMATE_STATE_CLASS_NAME: pyatmo.AsyncClimate,
|
||||
PUBLICDATA_DATA_CLASS_NAME: pyatmo.AsyncPublicData,
|
||||
}
|
||||
|
||||
BATCH_SIZE = 3
|
||||
DEFAULT_INTERVALS = {
|
||||
HOMEDATA_DATA_CLASS_NAME: 900,
|
||||
HOMESTATUS_DATA_CLASS_NAME: 300,
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME: 3600,
|
||||
CLIMATE_STATE_CLASS_NAME: 300,
|
||||
CAMERA_DATA_CLASS_NAME: 900,
|
||||
WEATHERSTATION_DATA_CLASS_NAME: 600,
|
||||
HOMECOACH_DATA_CLASS_NAME: 300,
|
||||
|
|
|
@ -4,8 +4,6 @@ from __future__ import annotations
|
|||
from dataclasses import dataclass
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pyatmo
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetatmoArea:
|
||||
|
@ -19,25 +17,3 @@ class NetatmoArea:
|
|||
mode: str
|
||||
show_on_map: bool
|
||||
uuid: UUID = uuid4()
|
||||
|
||||
|
||||
def get_all_home_ids(home_data: pyatmo.HomeData | None) -> list[str]:
|
||||
"""Get all the home ids returned by NetAtmo API."""
|
||||
if home_data is None:
|
||||
return []
|
||||
return [
|
||||
home_data.homes[home_id]["id"]
|
||||
for home_id in home_data.homes
|
||||
if "modules" in home_data.homes[home_id]
|
||||
]
|
||||
|
||||
|
||||
def update_climate_schedules(home_ids: list[str], schedules: dict) -> dict:
|
||||
"""Get updated list of all climate schedules."""
|
||||
return {
|
||||
home_id: {
|
||||
schedule_id: schedule_data.get("name")
|
||||
for schedule_id, schedule_data in schedules[home_id].items()
|
||||
}
|
||||
for home_id in home_ids
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class NetatmoBase(Entity):
|
|||
await self.data_handler.unregister_data_class(signal_name, None)
|
||||
|
||||
registry = await self.hass.helpers.device_registry.async_get_registry()
|
||||
device = registry.async_get_device({(DOMAIN, self._id)}, set())
|
||||
device = registry.async_get_device({(DOMAIN, self._id)})
|
||||
self.hass.data[DOMAIN][DATA_DEVICE_IDS][self._id] = device.id
|
||||
|
||||
self.async_update_callback()
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import cast
|
||||
|
||||
import pyatmo
|
||||
|
||||
|
@ -22,8 +21,11 @@ from .const import (
|
|||
SIGNAL_NAME,
|
||||
TYPE_ENERGY,
|
||||
)
|
||||
from .data_handler import HOMEDATA_DATA_CLASS_NAME, NetatmoDataHandler
|
||||
from .helper import get_all_home_ids, update_climate_schedules
|
||||
from .data_handler import (
|
||||
CLIMATE_STATE_CLASS_NAME,
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
NetatmoDataHandler,
|
||||
)
|
||||
from .netatmo_entity_base import NetatmoBase
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -36,25 +38,34 @@ async def async_setup_entry(
|
|||
data_handler = hass.data[DOMAIN][entry.entry_id][DATA_HANDLER]
|
||||
|
||||
await data_handler.register_data_class(
|
||||
HOMEDATA_DATA_CLASS_NAME, HOMEDATA_DATA_CLASS_NAME, None
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME, CLIMATE_TOPOLOGY_CLASS_NAME, None
|
||||
)
|
||||
home_data = data_handler.data.get(HOMEDATA_DATA_CLASS_NAME)
|
||||
climate_topology = data_handler.data.get(CLIMATE_TOPOLOGY_CLASS_NAME)
|
||||
|
||||
if not home_data or home_data.raw_data == {}:
|
||||
if not climate_topology or climate_topology.raw_data == {}:
|
||||
raise PlatformNotReady
|
||||
|
||||
hass.data[DOMAIN][DATA_SCHEDULES].update(
|
||||
update_climate_schedules(
|
||||
home_ids=get_all_home_ids(home_data),
|
||||
schedules=data_handler.data[HOMEDATA_DATA_CLASS_NAME].schedules,
|
||||
entities = []
|
||||
for home_id in climate_topology.home_ids:
|
||||
signal_name = f"{CLIMATE_STATE_CLASS_NAME}-{home_id}"
|
||||
await data_handler.register_data_class(
|
||||
CLIMATE_STATE_CLASS_NAME, signal_name, None, home_id=home_id
|
||||
)
|
||||
)
|
||||
climate_state = data_handler.data.get(signal_name)
|
||||
climate_topology.register_handler(home_id, climate_state.process_topology)
|
||||
|
||||
hass.data[DOMAIN][DATA_SCHEDULES][home_id] = climate_state.homes[
|
||||
home_id
|
||||
].schedules
|
||||
|
||||
entities = [
|
||||
NetatmoScheduleSelect(
|
||||
data_handler,
|
||||
home_id,
|
||||
list(hass.data[DOMAIN][DATA_SCHEDULES][home_id].values()),
|
||||
[
|
||||
schedule.name
|
||||
for schedule in hass.data[DOMAIN][DATA_SCHEDULES][home_id].values()
|
||||
],
|
||||
)
|
||||
for home_id in hass.data[DOMAIN][DATA_SCHEDULES]
|
||||
]
|
||||
|
@ -75,16 +86,28 @@ class NetatmoScheduleSelect(NetatmoBase, SelectEntity):
|
|||
|
||||
self._home_id = home_id
|
||||
|
||||
self._climate_state_class = f"{CLIMATE_STATE_CLASS_NAME}-{self._home_id}"
|
||||
self._climate_state: pyatmo.AsyncClimate = data_handler.data[
|
||||
self._climate_state_class
|
||||
]
|
||||
|
||||
self._home = self._climate_state.homes[self._home_id]
|
||||
|
||||
self._data_classes.extend(
|
||||
[
|
||||
{
|
||||
"name": HOMEDATA_DATA_CLASS_NAME,
|
||||
SIGNAL_NAME: HOMEDATA_DATA_CLASS_NAME,
|
||||
"name": CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
SIGNAL_NAME: CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
},
|
||||
{
|
||||
"name": CLIMATE_STATE_CLASS_NAME,
|
||||
"home_id": self._home_id,
|
||||
SIGNAL_NAME: self._climate_state_class,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self._device_name = self._data.homes[home_id]["name"]
|
||||
self._device_name = self._home.name
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name}"
|
||||
|
||||
self._model: str = "NATherm1"
|
||||
|
@ -92,9 +115,7 @@ class NetatmoScheduleSelect(NetatmoBase, SelectEntity):
|
|||
|
||||
self._attr_unique_id = f"{self._home_id}-schedule-select"
|
||||
|
||||
self._attr_current_option = self._data._get_selected_schedule(
|
||||
home_id=self._home_id
|
||||
).get("name")
|
||||
self._attr_current_option = getattr(self._home.get_selected_schedule(), "name")
|
||||
self._attr_options = options
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
|
@ -119,23 +140,20 @@ class NetatmoScheduleSelect(NetatmoBase, SelectEntity):
|
|||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE and "schedule_id" in data:
|
||||
self._attr_current_option = self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._home_id
|
||||
].get(data["schedule_id"])
|
||||
self._attr_current_option = getattr(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].get(
|
||||
data["schedule_id"]
|
||||
),
|
||||
"name",
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def _data(self) -> pyatmo.AsyncHomeData:
|
||||
"""Return data for this entity."""
|
||||
return cast(
|
||||
pyatmo.AsyncHomeData,
|
||||
self.data_handler.data[self._data_classes[0]["name"]],
|
||||
)
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
for sid, name in self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].items():
|
||||
if name != option:
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._home_id
|
||||
].items():
|
||||
if schedule.name != option:
|
||||
continue
|
||||
_LOGGER.debug(
|
||||
"Setting %s schedule to %s (%s)",
|
||||
|
@ -143,25 +161,17 @@ class NetatmoScheduleSelect(NetatmoBase, SelectEntity):
|
|||
option,
|
||||
sid,
|
||||
)
|
||||
await self._data.async_switch_home_schedule(
|
||||
home_id=self._home_id, schedule_id=sid
|
||||
)
|
||||
await self._climate_state.async_switch_home_schedule(schedule_id=sid)
|
||||
break
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_current_option = (
|
||||
self._data._get_selected_schedule( # pylint: disable=protected-access
|
||||
home_id=self._home_id
|
||||
).get("name")
|
||||
)
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id] = {
|
||||
schedule_id: schedule_data.get("name")
|
||||
for schedule_id, schedule_data in (
|
||||
self._data.schedules[self._home_id].items()
|
||||
)
|
||||
}
|
||||
self._attr_options = list(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].values()
|
||||
)
|
||||
self._attr_current_option = getattr(self._home.get_selected_schedule(), "name")
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id] = self._home.schedules
|
||||
self._attr_options = [
|
||||
schedule.name
|
||||
for schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._home_id
|
||||
].values()
|
||||
]
|
||||
|
|
|
@ -51,7 +51,7 @@ async def fake_post_request(*args, **kwargs):
|
|||
if endpoint in "snapshot_720.jpg":
|
||||
return b"test stream image bytes"
|
||||
|
||||
elif endpoint in [
|
||||
if endpoint in [
|
||||
"setpersonsaway",
|
||||
"setpersonshome",
|
||||
"setstate",
|
||||
|
@ -61,6 +61,10 @@ async def fake_post_request(*args, **kwargs):
|
|||
]:
|
||||
payload = f'{{"{endpoint}": true}}'
|
||||
|
||||
elif endpoint == "homestatus":
|
||||
home_id = kwargs.get("params", {}).get("home_id")
|
||||
payload = json.loads(load_fixture(f"netatmo/{endpoint}_{home_id}.json"))
|
||||
|
||||
else:
|
||||
payload = json.loads(load_fixture(f"netatmo/{endpoint}.json"))
|
||||
|
||||
|
|
|
@ -1,430 +1,407 @@
|
|||
{
|
||||
"body": {
|
||||
"homes": [
|
||||
{
|
||||
"id": "91763b24c43d3e344f424e8b",
|
||||
"name": "MYHOME",
|
||||
"altitude": 112,
|
||||
"coordinates": [
|
||||
52.516263,
|
||||
13.377726
|
||||
],
|
||||
"country": "DE",
|
||||
"timezone": "Europe/Berlin",
|
||||
"rooms": [
|
||||
{
|
||||
"id": "2746182631",
|
||||
"name": "Livingroom",
|
||||
"type": "livingroom",
|
||||
"module_ids": [
|
||||
"12:34:56:00:01:ae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "3688132631",
|
||||
"name": "Hall",
|
||||
"type": "custom",
|
||||
"module_ids": [
|
||||
"12:34:56:00:f1:62"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2833524037",
|
||||
"name": "Entrada",
|
||||
"type": "lobby",
|
||||
"module_ids": [
|
||||
"12:34:56:03:a5:54"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2940411577",
|
||||
"name": "Cocina",
|
||||
"type": "kitchen",
|
||||
"module_ids": [
|
||||
"12:34:56:03:a0:ac"
|
||||
]
|
||||
}
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"id": "12:34:56:00:fa:d0",
|
||||
"type": "NAPlug",
|
||||
"name": "Thermostat",
|
||||
"setup_date": 1494963356,
|
||||
"modules_bridged": [
|
||||
"12:34:56:00:01:ae",
|
||||
"12:34:56:03:a0:ac",
|
||||
"12:34:56:03:a5:54"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:00:01:ae",
|
||||
"type": "NATherm1",
|
||||
"name": "Livingroom",
|
||||
"setup_date": 1494963356,
|
||||
"room_id": "2746182631",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:03:a5:54",
|
||||
"type": "NRV",
|
||||
"name": "Valve1",
|
||||
"setup_date": 1554549767,
|
||||
"room_id": "2833524037",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:03:a0:ac",
|
||||
"type": "NRV",
|
||||
"name": "Valve2",
|
||||
"setup_date": 1554554444,
|
||||
"room_id": "2940411577",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:00:f1:62",
|
||||
"type": "NACamera",
|
||||
"name": "Hall",
|
||||
"setup_date": 1544828430,
|
||||
"room_id": "3688132631"
|
||||
}
|
||||
],
|
||||
"schedules": [
|
||||
{
|
||||
"zones": [
|
||||
{
|
||||
"type": 0,
|
||||
"name": "Comfort",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 21,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 0
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"name": "Night",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"type": 5,
|
||||
"name": "Eco",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 4
|
||||
}
|
||||
],
|
||||
"timetable": [
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 0
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 360
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 420
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 960
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 1410
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 1800
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 1860
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 2400
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 2850
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3240
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 3300
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3840
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 4290
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 4680
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 4740
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 5280
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 5730
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6120
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 6180
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6720
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 7170
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 7620
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 8610
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 9060
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 10050
|
||||
}
|
||||
],
|
||||
"hg_temp": 7,
|
||||
"away_temp": 14,
|
||||
"name": "Default",
|
||||
"selected": true,
|
||||
"id": "591b54a2764ff4d50d8b5795",
|
||||
"type": "therm"
|
||||
},
|
||||
{
|
||||
"zones": [
|
||||
{
|
||||
"type": 0,
|
||||
"name": "Comfort",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 21,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 0
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"name": "Night",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"type": 5,
|
||||
"name": "Eco",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 4
|
||||
}
|
||||
],
|
||||
"timetable": [
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 0
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 360
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 420
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 960
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 1410
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 1800
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 1860
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 2400
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 2850
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3240
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 3300
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3840
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 4290
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 4680
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 4740
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 5280
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 5730
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6120
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 6180
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6720
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 7170
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 7620
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 8610
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 9060
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 10050
|
||||
}
|
||||
],
|
||||
"hg_temp": 7,
|
||||
"away_temp": 14,
|
||||
"name": "Winter",
|
||||
"id": "b1b54a2f45795764f59d50d8",
|
||||
"type": "therm"
|
||||
}
|
||||
],
|
||||
"therm_setpoint_default_duration": 120,
|
||||
"persons": [
|
||||
{
|
||||
"id": "91827374-7e04-5298-83ad-a0cb8372dff1",
|
||||
"pseudo": "John Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c24b808a89f8d1730fb69ecdf2bb8b72039d2c69928b029d67fc40cb2d7"
|
||||
},
|
||||
{
|
||||
"id": "91827375-7e04-5298-83ae-a0cb8372dff2",
|
||||
"pseudo": "Jane Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c24b808a89f8d1730039d2c69928b029d67fc40cb2d7fb69ecdf2bb8b72"
|
||||
},
|
||||
{
|
||||
"id": "91827376-7e04-5298-83af-a0cb8372dff3",
|
||||
"pseudo": "Richard Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c2d1730fb69ecdf2bb8b72039d2c69928b029d67fc40cb2d74b808a89f8"
|
||||
}
|
||||
],
|
||||
"therm_mode": "schedule"
|
||||
},
|
||||
{
|
||||
"id": "91763b24c43d3e344f424e8c",
|
||||
"altitude": 112,
|
||||
"coordinates": [
|
||||
52.516263,
|
||||
13.377726
|
||||
],
|
||||
"country": "DE",
|
||||
"timezone": "Europe/Berlin",
|
||||
"therm_setpoint_default_duration": 180,
|
||||
"therm_mode": "schedule"
|
||||
}
|
||||
"body": {
|
||||
"homes": [
|
||||
{
|
||||
"id": "91763b24c43d3e344f424e8b",
|
||||
"name": "MYHOME",
|
||||
"altitude": 112,
|
||||
"coordinates": [52.516263, 13.377726],
|
||||
"country": "DE",
|
||||
"timezone": "Europe/Berlin",
|
||||
"rooms": [
|
||||
{
|
||||
"id": "2746182631",
|
||||
"name": "Livingroom",
|
||||
"type": "livingroom",
|
||||
"module_ids": ["12:34:56:00:01:ae"]
|
||||
},
|
||||
{
|
||||
"id": "3688132631",
|
||||
"name": "Hall",
|
||||
"type": "custom",
|
||||
"module_ids": ["12:34:56:00:f1:62"]
|
||||
},
|
||||
{
|
||||
"id": "2833524037",
|
||||
"name": "Entrada",
|
||||
"type": "lobby",
|
||||
"module_ids": ["12:34:56:03:a5:54"]
|
||||
},
|
||||
{
|
||||
"id": "2940411577",
|
||||
"name": "Cocina",
|
||||
"type": "kitchen",
|
||||
"module_ids": ["12:34:56:03:a0:ac"]
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"email": "john@doe.com",
|
||||
"language": "de-DE",
|
||||
"locale": "de-DE",
|
||||
"feel_like_algorithm": 0,
|
||||
"unit_pressure": 0,
|
||||
"unit_system": 0,
|
||||
"unit_wind": 0,
|
||||
"id": "91763b24c43d3e344f424e8b"
|
||||
}
|
||||
},
|
||||
"status": "ok",
|
||||
"time_exec": 0.056135892868042,
|
||||
"time_server": 1559171003
|
||||
"modules": [
|
||||
{
|
||||
"id": "12:34:56:00:fa:d0",
|
||||
"type": "NAPlug",
|
||||
"name": "Thermostat",
|
||||
"setup_date": 1494963356,
|
||||
"modules_bridged": [
|
||||
"12:34:56:00:01:ae",
|
||||
"12:34:56:03:a0:ac",
|
||||
"12:34:56:03:a5:54"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:00:01:ae",
|
||||
"type": "NATherm1",
|
||||
"name": "Livingroom",
|
||||
"setup_date": 1494963356,
|
||||
"room_id": "2746182631",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:03:a5:54",
|
||||
"type": "NRV",
|
||||
"name": "Valve1",
|
||||
"setup_date": 1554549767,
|
||||
"room_id": "2833524037",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:03:a0:ac",
|
||||
"type": "NRV",
|
||||
"name": "Valve2",
|
||||
"setup_date": 1554554444,
|
||||
"room_id": "2940411577",
|
||||
"bridge": "12:34:56:00:fa:d0"
|
||||
},
|
||||
{
|
||||
"id": "12:34:56:00:f1:62",
|
||||
"type": "NACamera",
|
||||
"name": "Hall",
|
||||
"setup_date": 1544828430,
|
||||
"room_id": "3688132631"
|
||||
}
|
||||
],
|
||||
"schedules": [
|
||||
{
|
||||
"zones": [
|
||||
{
|
||||
"type": 0,
|
||||
"name": "Comfort",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 21,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 0
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"name": "Night",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"type": 5,
|
||||
"name": "Eco",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 4
|
||||
}
|
||||
],
|
||||
"timetable": [
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 0
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 360
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 420
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 960
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 1410
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 1800
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 1860
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 2400
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 2850
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3240
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 3300
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3840
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 4290
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 4680
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 4740
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 5280
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 5730
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6120
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 6180
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6720
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 7170
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 7620
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 8610
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 9060
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 10050
|
||||
}
|
||||
],
|
||||
"hg_temp": 7,
|
||||
"away_temp": 14,
|
||||
"name": "Default",
|
||||
"selected": true,
|
||||
"id": "591b54a2764ff4d50d8b5795",
|
||||
"type": "therm"
|
||||
},
|
||||
{
|
||||
"zones": [
|
||||
{
|
||||
"type": 0,
|
||||
"name": "Comfort",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 21,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 0
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"name": "Night",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"type": 5,
|
||||
"name": "Eco",
|
||||
"rooms_temp": [
|
||||
{
|
||||
"temp": 17,
|
||||
"room_id": "2746182631"
|
||||
}
|
||||
],
|
||||
"id": 4
|
||||
}
|
||||
],
|
||||
"timetable": [
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 0
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 360
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 420
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 960
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 1410
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 1800
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 1860
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 2400
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 2850
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3240
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 3300
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 3840
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 4290
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 4680
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 4740
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 5280
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 5730
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6120
|
||||
},
|
||||
{
|
||||
"zone_id": 4,
|
||||
"m_offset": 6180
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 6720
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 7170
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 7620
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 8610
|
||||
},
|
||||
{
|
||||
"zone_id": 0,
|
||||
"m_offset": 9060
|
||||
},
|
||||
{
|
||||
"zone_id": 1,
|
||||
"m_offset": 10050
|
||||
}
|
||||
],
|
||||
"hg_temp": 7,
|
||||
"away_temp": 14,
|
||||
"name": "Winter",
|
||||
"id": "b1b54a2f45795764f59d50d8",
|
||||
"type": "therm"
|
||||
}
|
||||
],
|
||||
"therm_setpoint_default_duration": 120,
|
||||
"persons": [
|
||||
{
|
||||
"id": "91827374-7e04-5298-83ad-a0cb8372dff1",
|
||||
"pseudo": "John Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c24b808a89f8d1730fb69ecdf2bb8b72039d2c69928b029d67fc40cb2d7"
|
||||
},
|
||||
{
|
||||
"id": "91827375-7e04-5298-83ae-a0cb8372dff2",
|
||||
"pseudo": "Jane Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c24b808a89f8d1730039d2c69928b029d67fc40cb2d7fb69ecdf2bb8b72"
|
||||
},
|
||||
{
|
||||
"id": "91827376-7e04-5298-83af-a0cb8372dff3",
|
||||
"pseudo": "Richard Doe",
|
||||
"url": "https://netatmocameraimage.blob.core.windows.net/production/d74fad765b9100ef480720a9a4a95c2d1730fb69ecdf2bb8b72039d2c69928b029d67fc40cb2d74b808a89f8"
|
||||
}
|
||||
],
|
||||
"therm_mode": "schedule"
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"email": "john@doe.com",
|
||||
"language": "de-DE",
|
||||
"locale": "de-DE",
|
||||
"feel_like_algorithm": 0,
|
||||
"unit_pressure": 0,
|
||||
"unit_system": 0,
|
||||
"unit_wind": 0,
|
||||
"id": "91763b24c43d3e344f424e8b"
|
||||
}
|
||||
},
|
||||
"status": "ok",
|
||||
"time_exec": 0.056135892868042,
|
||||
"time_server": 1559171003
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"status": "ok",
|
||||
"time_server": 1559292041,
|
||||
"body": {
|
||||
"home": {
|
||||
"modules": [],
|
||||
"rooms": [],
|
||||
"id": "91763b24c43d3e344f424e8c",
|
||||
"persons": []
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
"""The tests for the Netatmo climate platform."""
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
DOMAIN as CLIMATE_DOMAIN,
|
||||
|
@ -18,7 +18,6 @@ from homeassistant.components.climate.const import (
|
|||
PRESET_AWAY,
|
||||
PRESET_BOOST,
|
||||
)
|
||||
from homeassistant.components.netatmo import climate
|
||||
from homeassistant.components.netatmo.climate import PRESET_FROST_GUARD, PRESET_SCHEDULE
|
||||
from homeassistant.components.netatmo.const import (
|
||||
ATTR_SCHEDULE_NAME,
|
||||
|
@ -37,7 +36,7 @@ async def test_webhook_event_handling_thermostats(hass, config_entry, netatmo_au
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||
climate_entity_livingroom = "climate.livingroom"
|
||||
|
||||
assert hass.states.get(climate_entity_livingroom).state == "auto"
|
||||
assert (
|
||||
|
@ -214,7 +213,7 @@ async def test_service_preset_mode_frost_guard_thermostat(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||
climate_entity_livingroom = "climate.livingroom"
|
||||
|
||||
assert hass.states.get(climate_entity_livingroom).state == "auto"
|
||||
assert (
|
||||
|
@ -287,7 +286,7 @@ async def test_service_preset_modes_thermostat(hass, config_entry, netatmo_auth)
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||
climate_entity_livingroom = "climate.livingroom"
|
||||
|
||||
assert hass.states.get(climate_entity_livingroom).state == "auto"
|
||||
assert (
|
||||
|
@ -415,11 +414,11 @@ async def test_service_schedule_thermostats(hass, config_entry, caplog, netatmo_
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||
climate_entity_livingroom = "climate.livingroom"
|
||||
|
||||
# Test setting a valid schedule
|
||||
with patch(
|
||||
"pyatmo.thermostat.AsyncHomeData.async_switch_home_schedule"
|
||||
"pyatmo.climate.AsyncClimate.async_switch_home_schedule"
|
||||
) as mock_switch_home_schedule:
|
||||
await hass.services.async_call(
|
||||
"netatmo",
|
||||
|
@ -429,7 +428,7 @@ async def test_service_schedule_thermostats(hass, config_entry, caplog, netatmo_
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_switch_home_schedule.assert_called_once_with(
|
||||
home_id="91763b24c43d3e344f424e8b", schedule_id="b1b54a2f45795764f59d50d8"
|
||||
schedule_id="b1b54a2f45795764f59d50d8"
|
||||
)
|
||||
|
||||
# Fake backend response for valve being turned on
|
||||
|
@ -448,7 +447,7 @@ async def test_service_schedule_thermostats(hass, config_entry, caplog, netatmo_
|
|||
|
||||
# Test setting an invalid schedule
|
||||
with patch(
|
||||
"pyatmo.thermostat.AsyncHomeData.async_switch_home_schedule"
|
||||
"pyatmo.climate.AsyncClimate.async_switch_home_schedule"
|
||||
) as mock_switch_home_schedule:
|
||||
await hass.services.async_call(
|
||||
"netatmo",
|
||||
|
@ -472,7 +471,7 @@ async def test_service_preset_mode_already_boost_valves(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||
assert (
|
||||
|
@ -550,7 +549,7 @@ async def test_service_preset_mode_boost_valves(hass, config_entry, netatmo_auth
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
# Test service setting the preset mode to "boost"
|
||||
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||
|
@ -602,7 +601,7 @@ async def test_service_preset_mode_invalid(hass, config_entry, caplog, netatmo_a
|
|||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_PRESET_MODE,
|
||||
{ATTR_ENTITY_ID: "climate.netatmo_cocina", ATTR_PRESET_MODE: "invalid"},
|
||||
{ATTR_ENTITY_ID: "climate.cocina", ATTR_PRESET_MODE: "invalid"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -618,7 +617,12 @@ async def test_valves_service_turn_off(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
assert hass.states.get(climate_entity_entrada).attributes["hvac_modes"] == [
|
||||
"auto",
|
||||
"heat",
|
||||
]
|
||||
|
||||
# Test turning valve off
|
||||
await hass.services.async_call(
|
||||
|
@ -663,7 +667,7 @@ async def test_valves_service_turn_on(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
# Test turning valve on
|
||||
await hass.services.async_call(
|
||||
|
@ -700,21 +704,6 @@ async def test_valves_service_turn_on(hass, config_entry, netatmo_auth):
|
|||
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||
|
||||
|
||||
async def test_get_all_home_ids():
|
||||
"""Test extracting all home ids returned by NetAtmo API."""
|
||||
# Test with backend returning no data
|
||||
assert climate.get_all_home_ids(None) == []
|
||||
|
||||
# Test with fake data
|
||||
home_data = Mock()
|
||||
home_data.homes = {
|
||||
"123": {"id": "123", "name": "Home 1", "modules": [], "therm_schedules": []},
|
||||
"987": {"id": "987", "name": "Home 2", "modules": [], "therm_schedules": []},
|
||||
}
|
||||
expected = ["123", "987"]
|
||||
assert climate.get_all_home_ids(home_data) == expected
|
||||
|
||||
|
||||
async def test_webhook_home_id_mismatch(hass, config_entry, netatmo_auth):
|
||||
"""Test service turn on for valves."""
|
||||
with selected_platforms(["climate"]):
|
||||
|
@ -723,7 +712,7 @@ async def test_webhook_home_id_mismatch(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||
|
||||
|
@ -761,7 +750,7 @@ async def test_webhook_set_point(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
climate_entity_entrada = "climate.netatmo_entrada"
|
||||
climate_entity_entrada = "climate.entrada"
|
||||
|
||||
# Fake backend response for valve being turned on
|
||||
response = {
|
||||
|
|
|
@ -136,7 +136,7 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth):
|
|||
await simulate_webhook(hass, webhook_id, FAKE_WEBHOOK_ACTIVATION)
|
||||
|
||||
# Assert webhook is established successfully
|
||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||
climate_entity_livingroom = "climate.livingroom"
|
||||
assert hass.states.get(climate_entity_livingroom).state == "auto"
|
||||
await simulate_webhook(hass, webhook_id, FAKE_WEBHOOK)
|
||||
assert hass.states.get(climate_entity_livingroom).state == "heat"
|
||||
|
@ -440,7 +440,6 @@ async def test_setup_component_invalid_token(hass, config_entry):
|
|||
"""Test handling of invalid token."""
|
||||
|
||||
async def fake_ensure_valid_token(*args, **kwargs):
|
||||
print("fake_ensure_valid_token")
|
||||
raise aiohttp.ClientResponseError(
|
||||
request_info=aiohttp.client.RequestInfo(
|
||||
url="http://example.com",
|
||||
|
|
|
@ -38,7 +38,7 @@ async def test_select_schedule_thermostats(hass, config_entry, caplog, netatmo_a
|
|||
|
||||
# Test setting a different schedule
|
||||
with patch(
|
||||
"pyatmo.thermostat.AsyncHomeData.async_switch_home_schedule"
|
||||
"pyatmo.climate.AsyncClimate.async_switch_home_schedule"
|
||||
) as mock_switch_home_schedule:
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
|
@ -51,7 +51,7 @@ async def test_select_schedule_thermostats(hass, config_entry, caplog, netatmo_a
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_switch_home_schedule.assert_called_once_with(
|
||||
home_id="91763b24c43d3e344f424e8b", schedule_id="591b54a2764ff4d50d8b5795"
|
||||
schedule_id="591b54a2764ff4d50d8b5795"
|
||||
)
|
||||
|
||||
# Fake backend response changing schedule
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue