Skip to content

Commit

Permalink
fix: enhance schedule handling and cooling
Browse files Browse the repository at this point in the history
  • Loading branch information
cgtobi committed Sep 17, 2024
1 parent 593ac31 commit d1f1ad0
Show file tree
Hide file tree
Showing 15 changed files with 159 additions and 48 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exclude: ^(fixtures/)

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.4
rev: v0.6.5
hooks:
- id: ruff
args:
Expand Down
2 changes: 1 addition & 1 deletion fixtures/homesdata.json
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@
"name": "Default",
"selected": true,
"id": "591b54a2764ff4d50d8b5795",
"type": "therm"
"type": "cooling"
},
{
"zones": [
Expand Down
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ ignore = [
"UP007", # keep type annotation style as is
# Ignored due to performance: https://github.com/charliermarsh/ruff/issues/2923
"UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)`
# need cleanup
"FBT001",
"FBT002",
"FBT003",
"DTZ006",
"DTZ005",
"PGH003",
"ANN401",
]

[tool.ruff.lint.flake8-pytest-style]
Expand Down
4 changes: 2 additions & 2 deletions src/pyatmo/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class AsyncAccount:
def __init__(
self,
auth: AbstractAsyncAuth,
favorite_stations: bool = True, # noqa: FBT001, FBT002
favorite_stations: bool = True,
) -> None:
"""Initialize the Netatmo account."""

Expand Down Expand Up @@ -148,7 +148,7 @@ def register_public_weather_area(
lat_sw: str,
lon_sw: str,
required_data_type: str | None = None,
filtering: bool = False, # noqa: FBT001, FBT002
filtering: bool = False,
*,
area_id: str = str(uuid4()),
) -> str:
Expand Down
4 changes: 2 additions & 2 deletions src/pyatmo/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ async def async_get_image(
url = (base_url or self.base_url) + endpoint
async with self.websession.get(
url,
**req_args, # type: ignore # noqa: PGH003
**req_args, # type: ignore
headers=headers,
timeout=ClientTimeout(total=timeout),
) as resp:
Expand Down Expand Up @@ -115,7 +115,7 @@ async def async_post_request(

async with self.websession.post(
url,
**req_args, # type: ignore # noqa: PGH003
**req_args, # type: ignore
headers=headers,
timeout=ClientTimeout(total=timeout),
) as resp:
Expand Down
15 changes: 10 additions & 5 deletions src/pyatmo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,20 @@
"write_thermostat", # Netatmo climate products
]

EVENTS = "events"
SCHEDULES = "schedules"

MANUAL = "manual"
MAX = "max"
HOME = "home"
FROSTGUARD = "hg"
SCHEDULES = "schedules"
EVENTS = "events"
SCHEDULE = "schedule"
OFF = "off"
AWAY = "away"

HEATING = "heating"
COOLING = "cooling"
IDLE = "idle"

STATION_TEMPERATURE_TYPE = "temperature"
STATION_PRESSURE_TYPE = "pressure"
Expand All @@ -106,6 +114,3 @@
MAX_HISTORY_TIME_FRAME = 24 * 2 * 3600

UNKNOWN = "unknown"

ON = True
OFF = False
2 changes: 1 addition & 1 deletion src/pyatmo/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def fix_id(raw_data: RawData) -> dict[str, Any]:
return raw_data


def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]: # noqa: ANN401
def extract_raw_data(resp: Any, tag: str) -> dict[str, Any]:
"""Extract raw data from server response."""
raw_data = {}

Expand Down
29 changes: 26 additions & 3 deletions src/pyatmo/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from pyatmo.modules.netatmo import NACamera
from pyatmo.person import Person
from pyatmo.room import Room
from pyatmo.schedule import Schedule
from pyatmo.schedule import Schedule, ScheduleType

if TYPE_CHECKING:
from aiohttp import ClientResponse
Expand All @@ -38,6 +38,12 @@
LOG = logging.getLogger(__name__)


SCHEDULE_TYPE_MAPPING = {
"heating": ScheduleType.THERM,
"cooling": ScheduleType.COOLING,
}


class Home:
"""Class to represent a Netatmo home."""

Expand Down Expand Up @@ -151,7 +157,7 @@ def update_topology(self, raw_data: RawData) -> None:
async def update(
self,
raw_data: RawData,
do_raise_for_reachability_error: bool = False, # noqa: FBT002, FBT001
do_raise_for_reachability_error: bool = False,
) -> None:
"""Update home with the latest data."""
has_error = False
Expand Down Expand Up @@ -210,10 +216,27 @@ def get_selected_schedule(self) -> Schedule | None:
"""Return selected schedule for given home."""

return next(
(schedule for schedule in self.schedules.values() if schedule.selected),
(
schedule
for schedule in self.schedules.values()
if schedule.selected
and self.temperature_control_mode
and schedule.type
== SCHEDULE_TYPE_MAPPING[self.temperature_control_mode]
),
None,
)

def get_available_schedules(self) -> list[Schedule]:
"""Return available schedules for given home."""

return [
schedule
for schedule in self.schedules.values()
if self.temperature_control_mode
and schedule.type == SCHEDULE_TYPE_MAPPING[self.temperature_control_mode]
]

def is_valid_schedule(self, schedule_id: str) -> bool:
"""Check if valid schedule."""

Expand Down
4 changes: 2 additions & 2 deletions src/pyatmo/modules/base_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
}


def default(key: str, val: Any) -> Any: # noqa: ANN401
def default(key: str, val: Any) -> Any:
"""Return default value."""

return lambda x, _: x.get(key, val)
Expand Down Expand Up @@ -103,7 +103,7 @@ def _update_attributes(self, raw_data: RawData) -> None:
def add_history_data(
self,
feature: str,
value: Any, # noqa: ANN401
value: Any,
time: int,
) -> None:
"""Add historical data at the given time."""
Expand Down
4 changes: 2 additions & 2 deletions src/pyatmo/modules/device_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from enum import Enum
import logging
from typing import Literal
from typing import Any, Literal

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -117,7 +117,7 @@ class DeviceType(str, Enum):
# pylint: enable=C0103

@classmethod
def _missing_(cls, key) -> Literal[DeviceType.NLunknown]: # noqa: ANN001
def _missing_(cls, key: Any) -> Literal[DeviceType.NLunknown]:
"""Handle unknown device types."""

msg = f"{key} device is unknown"
Expand Down
55 changes: 30 additions & 25 deletions src/pyatmo/modules/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from aiohttp import ClientConnectorError

from pyatmo.const import GETMEASURE_ENDPOINT, OFF, ON, RawData
from pyatmo.const import GETMEASURE_ENDPOINT, RawData
from pyatmo.exceptions import ApiError
from pyatmo.modules.base_class import EntityBase, NetatmoBase, Place, update_name
from pyatmo.modules.device_types import DEVICE_CATEGORY_MAP, DeviceCategory, DeviceType
Expand All @@ -23,8 +23,8 @@

LOG = logging.getLogger(__name__)

ModuleT = dict[str, Any]

ModuleT = dict[str, Any]
# Hide from features list
ATTRIBUTE_FILTER = {
"battery_state",
Expand Down Expand Up @@ -226,6 +226,7 @@ def __init__(self, home: Home, module: ModuleT) -> None:

super().__init__(home, module) # type: ignore # mypy issue 4335
self.boiler_status: bool | None = None
self.boiler_valve_comfort_boost: bool | None = None


class CoolerMixin(EntityBase):
Expand Down Expand Up @@ -354,7 +355,7 @@ def __init__(self, home: Home, module: ModuleT) -> None:
super().__init__(home, module) # type: ignore # mypy issue 4335
self.on: bool | None = None

async def async_set_switch(self, target_position: bool) -> bool: # noqa: FBT001
async def async_set_switch(self, target_position: bool) -> bool:
"""Set switch to target position."""

json_switch = {
Expand All @@ -371,12 +372,12 @@ async def async_set_switch(self, target_position: bool) -> bool: # noqa: FBT001
async def async_on(self) -> bool:
"""Switch on."""

return await self.async_set_switch(ON)
return await self.async_set_switch(True)

async def async_off(self) -> bool:
"""Switch off."""

return await self.async_set_switch(OFF)
return await self.async_set_switch(False)


class FanSpeedMixin(EntityBase):
Expand Down Expand Up @@ -657,7 +658,7 @@ class MeasureType(Enum):

def compute_riemann_sum(
power_data: list[tuple[int, float]],
conservative: bool = False, # noqa: FBT001, FBT002
conservative: bool = False,
) -> float:
"""Compute energy from power with a rieman sum."""

Expand Down Expand Up @@ -690,7 +691,7 @@ class EnergyHistoryMixin(EntityBase):
def __init__(self, home: Home, module: ModuleT) -> None:
"""Initialize history mixin."""

super().__init__(home, module) # type: ignore # mypy issue 4335
super().__init__(home, module) # type: ignore
self.historical_data: list[dict[str, Any]] | None = None
self.start_time: float | None = None
self.end_time: float | None = None
Expand All @@ -704,7 +705,7 @@ def __init__(self, home: Home, module: ModuleT) -> None:
def reset_measures(
self,
start_power_time: datetime,
in_reset: bool = True, # noqa: FBT001, FBT002
in_reset: bool = True,
) -> None:
"""Reset energy measures."""
self.in_reset = in_reset
Expand All @@ -720,7 +721,7 @@ def reset_measures(
def get_sum_energy_elec_power_adapted(
self,
to_ts: float | None = None,
conservative: bool = False, # noqa: FBT001, FBT002
conservative: bool = False,
) -> tuple[None, float] | tuple[float, float]:
"""Compute proper energy value with adaptation from power."""
v = self.sum_energy_elec
Expand Down Expand Up @@ -766,8 +767,8 @@ def _log_energy_error(
"ENERGY collection error %s %s %s %s %s %s %s",
msg,
self.name,
datetime.fromtimestamp(start_time), # noqa: DTZ006
datetime.fromtimestamp(end_time), # noqa: DTZ006
datetime.fromtimestamp(start_time),
datetime.fromtimestamp(end_time),
start_time,
end_time,
body or "NO BODY",
Expand All @@ -783,10 +784,10 @@ async def async_update_measures(
"""Update historical data."""

if end_time is None:
end_time = int(datetime.now().timestamp()) # noqa: DTZ005
end_time = int(datetime.now().timestamp())

if start_time is None:
end = datetime.fromtimestamp(end_time) # noqa: DTZ006
end = datetime.fromtimestamp(end_time)
start_time = int((end - timedelta(days=days)).timestamp())

prev_start_time = self.start_time
Expand Down Expand Up @@ -831,8 +832,8 @@ async def async_update_measures(
LOG.debug(
"NO VALUES energy update %s from: %s to %s, prev_sum=%s",
self.name,
datetime.fromtimestamp(start_time), # noqa: DTZ006
datetime.fromtimestamp(end_time), # noqa: DTZ006
datetime.fromtimestamp(start_time),
datetime.fromtimestamp(end_time),
prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING",
)
else:
Expand Down Expand Up @@ -912,14 +913,14 @@ async def _prepare_exported_historical_data(
LOG.debug(
msg,
self.name,
datetime.fromtimestamp(start_time), # noqa: DTZ006
datetime.fromtimestamp(end_time), # noqa: DTZ006
datetime.fromtimestamp(computed_start), # noqa: DTZ006
datetime.fromtimestamp(computed_end), # noqa: DTZ006
datetime.fromtimestamp(start_time),
datetime.fromtimestamp(end_time),
datetime.fromtimestamp(computed_start),
datetime.fromtimestamp(computed_end),
self.sum_energy_elec,
prev_sum_energy_elec,
datetime.fromtimestamp(prev_start_time), # noqa: DTZ006
datetime.fromtimestamp(prev_end_time), # noqa: DTZ006
datetime.fromtimestamp(prev_start_time),
datetime.fromtimestamp(prev_end_time),
)
else:
msg = (
Expand All @@ -929,10 +930,10 @@ async def _prepare_exported_historical_data(
LOG.debug(
msg,
self.name,
datetime.fromtimestamp(start_time), # noqa: DTZ006
datetime.fromtimestamp(end_time), # noqa: DTZ006
datetime.fromtimestamp(computed_start), # noqa: DTZ006
datetime.fromtimestamp(computed_end), # noqa: DTZ006
datetime.fromtimestamp(start_time),
datetime.fromtimestamp(end_time),
datetime.fromtimestamp(computed_start),
datetime.fromtimestamp(computed_end),
self.sum_energy_elec,
prev_sum_energy_elec if prev_sum_energy_elec is not None else "NOTHING",
)
Expand Down Expand Up @@ -1161,4 +1162,8 @@ class Energy(EnergyHistoryMixin, Module):
"""Class to represent a Netatmo energy module."""


class Boiler(BoilerMixin, Module):
"""Class to represent a Netatmo boiler."""


# pylint: enable=too-many-ancestors
2 changes: 1 addition & 1 deletion src/pyatmo/modules/netatmo.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def __init__(
lat_sw: str,
lon_sw: str,
required_data_type: str | None = None,
filtering: bool = False, # noqa: FBT001, FBT002
filtering: bool = False,
) -> None:
"""Initialize self."""

Expand Down
Loading

0 comments on commit d1f1ad0

Please sign in to comment.