Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions homeassistant/components/fritzbox/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
ATTR_STATE_HOLIDAY_MODE,
ATTR_STATE_SUMMER_MODE,
ATTR_STATE_WINDOW_OPEN,
ATTR_STATE_WINDOW_OPEN_ENDTIME,
CONF_COORDINATOR,
DOMAIN as FRITZBOX_DOMAIN,
)
Expand All @@ -37,7 +38,7 @@
MIN_TEMPERATURE = 8
MAX_TEMPERATURE = 28

PRESET_MANUAL = "manual"
PRESET_WINDOW_OPEN = "Window open"

# special temperatures for on/off in Fritz!Box API (modified by pyfritzhome)
ON_API_TEMPERATURE = 127.0
Expand Down Expand Up @@ -100,6 +101,16 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
)
await self.coordinator.async_refresh()

async def async_set_window_open(self, window_open: bool) -> None:
"""Set the thermostate to off (window open) for a pre defined time."""
if window_open:
timeout = 60 * 15
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are the 15minutes coming?
Why do we not make it configurable?

else:
timeout = 0

await self.hass.async_add_executor_job(self.data.set_window_open, timeout)
await self.coordinator.async_refresh()

@property
def hvac_mode(self) -> HVACMode:
"""Return the current operation mode."""
Expand All @@ -126,6 +137,8 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
@property
def preset_mode(self) -> str | None:
"""Return current preset mode."""
if self.data.window_open:
return PRESET_WINDOW_OPEN
if self.data.target_temperature == self.data.comfort_temperature:
return PRESET_COMFORT
if self.data.target_temperature == self.data.eco_temperature:
Expand All @@ -135,7 +148,7 @@ def preset_mode(self) -> str | None:
@property
def preset_modes(self) -> list[str]:
"""Return supported preset modes."""
return [PRESET_ECO, PRESET_COMFORT]
return [PRESET_ECO, PRESET_COMFORT, PRESET_WINDOW_OPEN]

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set preset mode."""
Expand All @@ -144,6 +157,11 @@ async def async_set_preset_mode(self, preset_mode: str) -> None:
elif preset_mode == PRESET_ECO:
await self.async_set_temperature(temperature=self.data.eco_temperature)

if preset_mode == PRESET_WINDOW_OPEN:
await self.async_set_window_open(True)
else:
await self.async_set_window_open(False)

@property
def min_temp(self) -> int:
"""Return the minimum temperature."""
Expand All @@ -170,5 +188,7 @@ def extra_state_attributes(self) -> ClimateExtraAttributes:
attrs[ATTR_STATE_SUMMER_MODE] = self.data.summer_active
if self.data.window_open is not None:
attrs[ATTR_STATE_WINDOW_OPEN] = self.data.window_open
if self.data.window_open_endtime is not None:
attrs[ATTR_STATE_WINDOW_OPEN_ENDTIME] = self.data.window_open_endtime

return attrs
1 change: 1 addition & 0 deletions homeassistant/components/fritzbox/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ATTR_STATE_HOLIDAY_MODE: Final = "holiday_mode"
ATTR_STATE_SUMMER_MODE: Final = "summer_mode"
ATTR_STATE_WINDOW_OPEN: Final = "window_open"
ATTR_STATE_WINDOW_OPEN_ENDTIME: Final = "window_open_endtime"

COLOR_MODE: Final = "1"
COLOR_TEMP_MODE: Final = "4"
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/fritzbox/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class ClimateExtraAttributes(TypedDict, total=False):
holiday_mode: bool
summer_mode: bool
window_open: bool
window_open_endtime: float


@dataclass
Expand Down
2 changes: 1 addition & 1 deletion tests/components/fritzbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class FritzDeviceClimateMock(FritzEntityBaseMock):
present = True
summer_active = "fake_summer"
target_temperature = 19.5
window_open = "fake_window"
window_open = False
nextchange_temperature = 22.0
nextchange_endperiod = 0
nextchange_preset = PRESET_COMFORT
Expand Down
25 changes: 23 additions & 2 deletions tests/components/fritzbox/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
SERVICE_SET_TEMPERATURE,
HVACMode,
)
from homeassistant.components.fritzbox.climate import PRESET_WINDOW_OPEN
from homeassistant.components.fritzbox.const import (
ATTR_STATE_BATTERY_LOW,
ATTR_STATE_HOLIDAY_MODE,
Expand Down Expand Up @@ -65,11 +66,15 @@ async def test_setup(hass: HomeAssistant, fritz: Mock) -> None:
assert state.attributes[ATTR_MAX_TEMP] == 28
assert state.attributes[ATTR_MIN_TEMP] == 8
assert state.attributes[ATTR_PRESET_MODE] is None
assert state.attributes[ATTR_PRESET_MODES] == [PRESET_ECO, PRESET_COMFORT]
assert state.attributes[ATTR_PRESET_MODES] == [
PRESET_ECO,
PRESET_COMFORT,
PRESET_WINDOW_OPEN,
]
assert state.attributes[ATTR_STATE_BATTERY_LOW] is True
assert state.attributes[ATTR_STATE_HOLIDAY_MODE] == "fake_holiday"
assert state.attributes[ATTR_STATE_SUMMER_MODE] == "fake_summer"
assert state.attributes[ATTR_STATE_WINDOW_OPEN] == "fake_window"
assert state.attributes[ATTR_STATE_WINDOW_OPEN] is False
assert state.attributes[ATTR_TEMPERATURE] == 19.5
assert ATTR_STATE_CLASS not in state.attributes
assert state.state == HVACMode.HEAT
Expand Down Expand Up @@ -368,6 +373,22 @@ async def test_set_preset_mode_eco(hass: HomeAssistant, fritz: Mock) -> None:
assert device.set_target_temperature.call_args_list == [call(16)]


async def test_set_preset_mode_window_open(hass: HomeAssistant, fritz: Mock) -> None:
"""Test setting preset mode."""
device = FritzDeviceClimateMock()
assert await setup_config_entry(
hass, MOCK_CONFIG[FB_DOMAIN][CONF_DEVICES][0], ENTITY_ID, device, fritz
)

assert await hass.services.async_call(
DOMAIN,
SERVICE_SET_PRESET_MODE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PRESET_MODE: PRESET_WINDOW_OPEN},
True,
)
assert device.set_window_open.call_args_list == [call(900)]


async def test_preset_mode_update(hass: HomeAssistant, fritz: Mock) -> None:
"""Test preset mode."""
device = FritzDeviceClimateMock()
Expand Down