diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 749ec3a..1e47a69 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,8 +7,8 @@ jobs: name: mypy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 with: python-version: "3.13" - name: Install dependencies diff --git a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py index 92f1de3..67bd336 100644 --- a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py +++ b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py @@ -1,7 +1,9 @@ """The switch device class.""" + # -*- coding: utf-8 -*- import logging +from typing import Optional from .fritzhomedevicebase import FritzhomeDeviceBase from .fritzhomedevicefeatures import FritzhomeDeviceFeatures @@ -14,9 +16,10 @@ class FritzhomeDeviceSwitch(FritzhomeDeviceBase): switch_state = None switch_mode = None - lock = None + lock: Optional[bool] = None + device_lock: Optional[bool] = None - def _update_from_node(self, node): + def _update_from_node(self, node) -> None: super()._update_from_node(node) if self.present is False: return @@ -26,7 +29,7 @@ def _update_from_node(self, node): # Switch @property - def has_switch(self): + def has_switch(self) -> bool: """Check if the device has switch function.""" if self._has_feature(FritzhomeDeviceFeatures.SWITCH): # for AVM plugs like FRITZ!DECT 200 and FRITZ!DECT 210 @@ -38,7 +41,7 @@ def has_switch(self): return True return False - def _update_switch_from_node(self, node): + def _update_switch_from_node(self, node) -> None: _LOGGER.debug("update switch device") if self._has_feature(FritzhomeDeviceFeatures.SWITCH): val = node.find("switch") diff --git a/pyfritzhome/devicetypes/fritzhomedevicethermostat.py b/pyfritzhome/devicetypes/fritzhomedevicethermostat.py index 29c8ebd..47bbc13 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicethermostat.py +++ b/pyfritzhome/devicetypes/fritzhomedevicethermostat.py @@ -1,8 +1,10 @@ """The thermostat device class.""" + # -*- coding: utf-8 -*- import logging import time +from typing import Optional from .fritzhomedevicebase import FritzhomeDeviceBase from .fritzhomedevicefeatures import FritzhomeDeviceFeatures @@ -17,8 +19,8 @@ class FritzhomeDeviceThermostat(FritzhomeDeviceBase): target_temperature = None eco_temperature = None comfort_temperature = None - device_lock = None - lock = None + device_lock: Optional[bool] = None + lock: Optional[bool] = None error_code = None window_open = None window_open_endtime = None diff --git a/pyfritzhome/devicetypes/fritzhomeentitybase.py b/pyfritzhome/devicetypes/fritzhomeentitybase.py index b15278f..b96cf86 100644 --- a/pyfritzhome/devicetypes/fritzhomeentitybase.py +++ b/pyfritzhome/devicetypes/fritzhomeentitybase.py @@ -2,12 +2,18 @@ # -*- coding: utf-8 -*- -from __future__ import print_function +from __future__ import annotations + +import logging from abc import ABC +from typing import TYPE_CHECKING +# Avoid circular import +if TYPE_CHECKING: + from pyfritzhome.fritzhome import Fritzhome -import logging from xml.etree import ElementTree + from .fritzhomedevicefeatures import FritzhomeDeviceFeatures _LOGGER = logging.getLogger(__name__) @@ -16,29 +22,26 @@ class FritzhomeEntityBase(ABC): """The Fritzhome Entity class.""" - _fritz = None + _fritz: Fritzhome ain: str _functionsbitmask: int = 0 - supported_features = None + supported_features: list = [] - def __init__(self, fritz=None, node=None): + def __init__(self, fritz=None, node=None) -> None: """Create an entity base object.""" if fritz is not None: self._fritz = fritz if node is not None: self._update_from_node(node) - def __repr__(self): + def __repr__(self) -> str: """Return a string.""" - return "{ain} {name}".format( - ain=self.ain, - name=self.name, - ) + return f"{self.ain} {self.name}" def _has_feature(self, feature: FritzhomeDeviceFeatures) -> bool: return feature in FritzhomeDeviceFeatures(self._functionsbitmask) - def _update_from_node(self, node): + def _update_from_node(self, node) -> None: _LOGGER.debug(ElementTree.tostring(node)) self.ain = node.attrib["identifier"] self._functionsbitmask = int(node.attrib["functionbitmask"]) @@ -51,7 +54,7 @@ def _update_from_node(self, node): self.supported_features.append(feature) @property - def device_and_unit_id(self): + def device_and_unit_id(self) -> tuple: """Get the device and possible unit id.""" if self.ain.startswith("tmp") or self.ain.startswith("grp"): return (self.ain, None) @@ -75,6 +78,6 @@ def get_node_value_as_int_as_bool(self, elem, node) -> bool: """Get the node value as boolean.""" return bool(self.get_node_value_as_int(elem, node)) - def get_temp_from_node(self, elem, node): + def get_temp_from_node(self, elem, node) -> float: """Get the node temp value as float.""" return float(self.get_node_value(elem, node)) / 2 diff --git a/pyfritzhome/devicetypes/fritzhometemplate.py b/pyfritzhome/devicetypes/fritzhometemplate.py index 06de21d..5d0ddb8 100644 --- a/pyfritzhome/devicetypes/fritzhometemplate.py +++ b/pyfritzhome/devicetypes/fritzhometemplate.py @@ -1,10 +1,11 @@ """The template class.""" + # -*- coding: utf-8 -*- import logging -from .fritzhomeentitybase import FritzhomeEntityBase from .fritzhomedevicefeatures import FritzhomeDeviceFeatures +from .fritzhomeentitybase import FritzhomeEntityBase _LOGGER = logging.getLogger(__name__) @@ -12,7 +13,7 @@ class FritzhomeTemplate(FritzhomeEntityBase): """The Fritzhome Template class.""" - devices = None + devices: list = [] features = None apply_hkr_summer = None apply_hkr_temperature = None @@ -24,7 +25,7 @@ class FritzhomeTemplate(FritzhomeEntityBase): apply_color = None apply_dialhelper = None - def _update_from_node(self, node): + def _update_from_node(self, node) -> None: _LOGGER.debug("update template") super()._update_from_node(node) diff --git a/pyfritzhome/fritzhome.py b/pyfritzhome/fritzhome.py index e6ed45b..8c7dd3a 100644 --- a/pyfritzhome/fritzhome.py +++ b/pyfritzhome/fritzhome.py @@ -1,33 +1,30 @@ """The main fritzhome handling class.""" -# -*- coding: utf-8 -*- -from __future__ import print_function +# -*- coding: utf-8 -*- import hashlib import logging import time +from typing import Optional from xml.etree import ElementTree from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - -from requests import exceptions, Session +from requests import Session, exceptions from .errors import InvalidError, LoginError, NotLoggedInError -from .fritzhomedevice import FritzhomeDevice -from .fritzhomedevice import FritzhomeTemplate -from typing import Dict, Optional +from .fritzhomedevice import FritzhomeDevice, FritzhomeTemplate _LOGGER = logging.getLogger(__name__) -class Fritzhome(object): +class Fritzhome: """Fritzhome object to communicate with the device.""" _sid = None - _session = None - _devices: Optional[Dict[str, FritzhomeDevice]] = None - _templates: Optional[Dict[str, FritzhomeTemplate]] = None + _session: Session + _devices: Optional[dict[str, FritzhomeDevice]] = None + _templates: Optional[dict[str, FritzhomeTemplate]] = None def __init__(self, host, user, password, ssl_verify=True): """Create a fritzhome object.""" @@ -59,7 +56,7 @@ def _login_request(self, username=None, secret=None): plain = self._request(url, params) dom = ElementTree.fromstring(plain) sid = dom.findtext("SID") - blocktime = int(dom.findtext("BlockTime")) + blocktime = int(dom.findtext("BlockTime") or "0") challenge = dom.findtext("Challenge") return (sid, challenge, blocktime) diff --git a/setup.cfg b/setup.cfg index f5fbbbc..71d5123 100644 --- a/setup.cfg +++ b/setup.cfg @@ -66,3 +66,10 @@ match-dir = pyfritzhome/* [pydocstyle] ignore = D203,D213 match-dir = pyfritzhome/* + +[mypy] +warn_unused_configs = True +warn_redundant_casts = True +warn_unused_ignores = True +strict_equality = True +check_untyped_defs = True \ No newline at end of file