mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add Netatmo schedule event handling (#46573)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
02b0a4ca1f
commit
3bc6497cbd
@ -42,6 +42,7 @@ from .const import (
|
|||||||
DATA_SCHEDULES,
|
DATA_SCHEDULES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
EVENT_TYPE_CANCEL_SET_POINT,
|
EVENT_TYPE_CANCEL_SET_POINT,
|
||||||
|
EVENT_TYPE_SCHEDULE,
|
||||||
EVENT_TYPE_SET_POINT,
|
EVENT_TYPE_SET_POINT,
|
||||||
EVENT_TYPE_THERM_MODE,
|
EVENT_TYPE_THERM_MODE,
|
||||||
MANUFACTURER,
|
MANUFACTURER,
|
||||||
@ -236,6 +237,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
EVENT_TYPE_SET_POINT,
|
EVENT_TYPE_SET_POINT,
|
||||||
EVENT_TYPE_THERM_MODE,
|
EVENT_TYPE_THERM_MODE,
|
||||||
EVENT_TYPE_CANCEL_SET_POINT,
|
EVENT_TYPE_CANCEL_SET_POINT,
|
||||||
|
EVENT_TYPE_SCHEDULE,
|
||||||
):
|
):
|
||||||
self._listeners.append(
|
self._listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
@ -253,7 +255,15 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
"""Handle webhook events."""
|
"""Handle webhook events."""
|
||||||
data = event["data"]
|
data = event["data"]
|
||||||
|
|
||||||
if data.get("home") is None:
|
if self._home_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.async_write_ha_state()
|
||||||
|
self.data_handler.async_force_update(self._home_status_class)
|
||||||
return
|
return
|
||||||
|
|
||||||
home = data["home"]
|
home = data["home"]
|
||||||
@ -270,35 +280,37 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||||||
self._target_temperature = self._away_temperature
|
self._target_temperature = self._away_temperature
|
||||||
elif self._preset == PRESET_SCHEDULE:
|
elif self._preset == PRESET_SCHEDULE:
|
||||||
self.async_update_callback()
|
self.async_update_callback()
|
||||||
|
self.data_handler.async_force_update(self._home_status_class)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
if not home.get("rooms"):
|
for room in home.get("rooms", []):
|
||||||
return
|
if data["event_type"] == EVENT_TYPE_SET_POINT and self._id == room["id"]:
|
||||||
|
if room["therm_setpoint_mode"] == STATE_NETATMO_OFF:
|
||||||
for room in home["rooms"]:
|
self._hvac_mode = HVAC_MODE_OFF
|
||||||
if data["event_type"] == EVENT_TYPE_SET_POINT:
|
self._preset = STATE_NETATMO_OFF
|
||||||
if self._id == room["id"]:
|
self._target_temperature = 0
|
||||||
if room["therm_setpoint_mode"] == STATE_NETATMO_OFF:
|
elif room["therm_setpoint_mode"] == STATE_NETATMO_MAX:
|
||||||
self._hvac_mode = HVAC_MODE_OFF
|
self._hvac_mode = HVAC_MODE_HEAT
|
||||||
elif room["therm_setpoint_mode"] == STATE_NETATMO_MAX:
|
self._preset = PRESET_MAP_NETATMO[PRESET_BOOST]
|
||||||
|
self._target_temperature = DEFAULT_MAX_TEMP
|
||||||
|
elif room["therm_setpoint_mode"] == STATE_NETATMO_MANUAL:
|
||||||
|
self._hvac_mode = HVAC_MODE_HEAT
|
||||||
|
self._target_temperature = room["therm_setpoint_temperature"]
|
||||||
|
else:
|
||||||
|
self._target_temperature = room["therm_setpoint_temperature"]
|
||||||
|
if self._target_temperature == DEFAULT_MAX_TEMP:
|
||||||
self._hvac_mode = HVAC_MODE_HEAT
|
self._hvac_mode = HVAC_MODE_HEAT
|
||||||
self._target_temperature = DEFAULT_MAX_TEMP
|
self.async_write_ha_state()
|
||||||
elif room["therm_setpoint_mode"] == STATE_NETATMO_MANUAL:
|
return
|
||||||
self._hvac_mode = HVAC_MODE_HEAT
|
|
||||||
self._target_temperature = room["therm_setpoint_temperature"]
|
|
||||||
else:
|
|
||||||
self._target_temperature = room["therm_setpoint_temperature"]
|
|
||||||
if self._target_temperature == DEFAULT_MAX_TEMP:
|
|
||||||
self._hvac_mode = HVAC_MODE_HEAT
|
|
||||||
self.async_write_ha_state()
|
|
||||||
break
|
|
||||||
|
|
||||||
elif data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT:
|
if (
|
||||||
if self._id == room["id"]:
|
data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT
|
||||||
self.async_update_callback()
|
and self._id == room["id"]
|
||||||
self.async_write_ha_state()
|
):
|
||||||
break
|
self.async_update_callback()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
|
@ -94,6 +94,7 @@ SERVICE_SET_PERSON_AWAY = "set_person_away"
|
|||||||
EVENT_TYPE_SET_POINT = "set_point"
|
EVENT_TYPE_SET_POINT = "set_point"
|
||||||
EVENT_TYPE_CANCEL_SET_POINT = "cancel_set_point"
|
EVENT_TYPE_CANCEL_SET_POINT = "cancel_set_point"
|
||||||
EVENT_TYPE_THERM_MODE = "therm_mode"
|
EVENT_TYPE_THERM_MODE = "therm_mode"
|
||||||
|
EVENT_TYPE_SCHEDULE = "schedule"
|
||||||
# Camera events
|
# Camera events
|
||||||
EVENT_TYPE_LIGHT_MODE = "light_mode"
|
EVENT_TYPE_LIGHT_MODE = "light_mode"
|
||||||
EVENT_TYPE_CAMERA_OUTDOOR = "outdoor"
|
EVENT_TYPE_CAMERA_OUTDOOR = "outdoor"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""The tests for the Netatmo climate platform."""
|
"""The tests for the Netatmo climate platform."""
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -394,30 +394,50 @@ async def test_webhook_event_handling_no_data(hass, climate_entry):
|
|||||||
|
|
||||||
async def test_service_schedule_thermostats(hass, climate_entry, caplog):
|
async def test_service_schedule_thermostats(hass, climate_entry, caplog):
|
||||||
"""Test service for selecting Netatmo schedule with thermostats."""
|
"""Test service for selecting Netatmo schedule with thermostats."""
|
||||||
|
webhook_id = climate_entry.data[CONF_WEBHOOK_ID]
|
||||||
climate_entity_livingroom = "climate.netatmo_livingroom"
|
climate_entity_livingroom = "climate.netatmo_livingroom"
|
||||||
|
|
||||||
# Test setting a valid schedule
|
# Test setting a valid schedule
|
||||||
await hass.services.async_call(
|
with patch(
|
||||||
"netatmo",
|
"pyatmo.thermostat.HomeData.switch_home_schedule"
|
||||||
SERVICE_SET_SCHEDULE,
|
) as mock_switch_home_schedule:
|
||||||
{ATTR_ENTITY_ID: climate_entity_livingroom, ATTR_SCHEDULE_NAME: "Winter"},
|
await hass.services.async_call(
|
||||||
blocking=True,
|
"netatmo",
|
||||||
)
|
SERVICE_SET_SCHEDULE,
|
||||||
await hass.async_block_till_done()
|
{ATTR_ENTITY_ID: climate_entity_livingroom, ATTR_SCHEDULE_NAME: "Winter"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_switch_home_schedule.assert_called_once_with(
|
||||||
|
home_id="91763b24c43d3e344f424e8b", schedule_id="b1b54a2f45795764f59d50d8"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fake backend response for valve being turned on
|
||||||
|
response = {
|
||||||
|
"event_type": "schedule",
|
||||||
|
"schedule_id": "b1b54a2f45795764f59d50d8",
|
||||||
|
"previous_schedule_id": "59d32176d183948b05ab4dce",
|
||||||
|
"push_type": "home_event_changed",
|
||||||
|
}
|
||||||
|
await simulate_webhook(hass, webhook_id, response)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
"Setting 91763b24c43d3e344f424e8b schedule to Winter (b1b54a2f45795764f59d50d8)"
|
hass.states.get(climate_entity_livingroom).attributes["selected_schedule"]
|
||||||
in caplog.text
|
== "Winter"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Test setting an invalid schedule
|
# Test setting an invalid schedule
|
||||||
await hass.services.async_call(
|
with patch(
|
||||||
"netatmo",
|
"pyatmo.thermostat.HomeData.switch_home_schedule"
|
||||||
SERVICE_SET_SCHEDULE,
|
) as mock_switch_home_schedule:
|
||||||
{ATTR_ENTITY_ID: climate_entity_livingroom, ATTR_SCHEDULE_NAME: "summer"},
|
await hass.services.async_call(
|
||||||
blocking=True,
|
"netatmo",
|
||||||
)
|
SERVICE_SET_SCHEDULE,
|
||||||
await hass.async_block_till_done()
|
{ATTR_ENTITY_ID: climate_entity_livingroom, ATTR_SCHEDULE_NAME: "summer"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
mock_switch_home_schedule.assert_not_called()
|
||||||
|
|
||||||
assert "summer is not a invalid schedule" in caplog.text
|
assert "summer is not a invalid schedule" in caplog.text
|
||||||
|
|
||||||
@ -668,3 +688,69 @@ async def test_get_all_home_ids():
|
|||||||
}
|
}
|
||||||
expected = ["123", "987"]
|
expected = ["123", "987"]
|
||||||
assert climate.get_all_home_ids(home_data) == expected
|
assert climate.get_all_home_ids(home_data) == expected
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_home_id_mismatch(hass, climate_entry):
|
||||||
|
"""Test service turn on for valves."""
|
||||||
|
webhook_id = climate_entry.data[CONF_WEBHOOK_ID]
|
||||||
|
climate_entity_entrada = "climate.netatmo_entrada"
|
||||||
|
|
||||||
|
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||||
|
|
||||||
|
# Fake backend response for valve being turned on
|
||||||
|
response = {
|
||||||
|
"room_id": "2833524037",
|
||||||
|
"home": {
|
||||||
|
"id": "123",
|
||||||
|
"name": "MYHOME",
|
||||||
|
"country": "DE",
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"id": "2833524037",
|
||||||
|
"name": "Entrada",
|
||||||
|
"type": "lobby",
|
||||||
|
"therm_setpoint_mode": "home",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modules": [{"id": "12:34:56:00:01:ae", "name": "Entrada", "type": "NRV"}],
|
||||||
|
},
|
||||||
|
"mode": "home",
|
||||||
|
"event_type": "cancel_set_point",
|
||||||
|
"push_type": "display_change",
|
||||||
|
}
|
||||||
|
await simulate_webhook(hass, webhook_id, response)
|
||||||
|
|
||||||
|
assert hass.states.get(climate_entity_entrada).state == "auto"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_webhook_set_point(hass, climate_entry):
|
||||||
|
"""Test service turn on for valves."""
|
||||||
|
webhook_id = climate_entry.data[CONF_WEBHOOK_ID]
|
||||||
|
climate_entity_entrada = "climate.netatmo_entrada"
|
||||||
|
|
||||||
|
# Fake backend response for valve being turned on
|
||||||
|
response = {
|
||||||
|
"room_id": "2746182631",
|
||||||
|
"home": {
|
||||||
|
"id": "91763b24c43d3e344f424e8b",
|
||||||
|
"name": "MYHOME",
|
||||||
|
"country": "DE",
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"id": "2833524037",
|
||||||
|
"name": "Entrada",
|
||||||
|
"type": "lobby",
|
||||||
|
"therm_setpoint_mode": "home",
|
||||||
|
"therm_setpoint_temperature": 30,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modules": [{"id": "12:34:56:00:01:ae", "name": "Entrada", "type": "NRV"}],
|
||||||
|
},
|
||||||
|
"mode": "home",
|
||||||
|
"event_type": "set_point",
|
||||||
|
"temperature": 21,
|
||||||
|
"push_type": "display_change",
|
||||||
|
}
|
||||||
|
await simulate_webhook(hass, webhook_id, response)
|
||||||
|
|
||||||
|
assert hass.states.get(climate_entity_entrada).state == "heat"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user