Skip to content

Commit

Permalink
Fix usage of async_add_executor_job / Improve local_ip identification (
Browse files Browse the repository at this point in the history
…#304)

* Fix usage of async_add_executor_job / Improve local_ip identification

* update changelog
  • Loading branch information
SukramJ authored Feb 19, 2022
1 parent 75f12c7 commit bd0499b
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 44 deletions.
4 changes: 4 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
Version 0.35.0 (2022-02-19)
- Fix usage of async_add_executor_job
- Improve local_ip identification

Version 0.34.2 (2022-02-16)
- Add is_locking/is_unlocking to lock

Expand Down
60 changes: 51 additions & 9 deletions hahomematic/central_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
import json
import logging
import os
import socket
import threading
from typing import Any
from typing import Any, TypeVar

from aiohttp import ClientSession

Expand Down Expand Up @@ -57,6 +58,7 @@
import hahomematic.xml_rpc_server as xml_rpc

_LOGGER = logging.getLogger(__name__)
T = TypeVar("T")


class CentralUnit:
Expand Down Expand Up @@ -245,10 +247,18 @@ async def _create_clients(self) -> bool:
self.instance_name,
)
return False
if len(self._interface_configs) == 0:
_LOGGER.info(
"create_clients: No Interfaces for %s defined.",
self.instance_name,
)
return False

try:
local_ip = await self._identify_callback_ip(list(self._interface_configs)[0].port)
for interface_config in self._interface_configs:
if client := await hm_client.create_client(
central=self, interface_config=interface_config
central=self, interface_config=interface_config, local_ip=local_ip
):
_LOGGER.debug(
"create_clients: Adding client %s to %s.",
Expand Down Expand Up @@ -282,6 +292,38 @@ async def _de_init_client(self) -> None:
if await client.proxy_de_init():
_LOGGER.info("stop: Proxy de-initialized: %s", name)

async def _identify_callback_ip(self, port: int) -> str:
"""Identify local IP used for callbacks."""

# Do not add: pylint disable=no-member
# This is only an issue on MacOS
def get_local_ip(host: str) -> str | None:
"""Get local_ip from socket."""
try:
socket.gethostbyname(host)
except Exception:
_LOGGER.warning("Can't resolve host for %s", host)
return None
tmp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tmp_socket.settimeout(config.TIMEOUT)
tmp_socket.connect((host, port))
local_ip = str(tmp_socket.getsockname()[0])
tmp_socket.close()
_LOGGER.debug("Got local ip: %s", local_ip)
return local_ip

callback_ip: str | None = None
while callback_ip is None:
if (
callback_ip := await self.async_add_executor_job(
get_local_ip, self.central_config.host
)
) is None:
_LOGGER.warning("Waiting for %i s,", config.CONNECTION_CHECKER_INTERVAL)
await asyncio.sleep(config.CONNECTION_CHECKER_INTERVAL)

return callback_ip

def _create_hub(self) -> HmHub | HmDummyHub:
"""Create the hub."""
hub: HmHub | HmDummyHub
Expand Down Expand Up @@ -553,8 +595,8 @@ def run_coroutine(self, coro: Coroutine) -> Any:
return None

async def async_add_executor_job(
self, executor_func: Callable, *args: Any
) -> Awaitable:
self, executor_func: Callable[..., T], *args: Any
) -> T:
"""Add an executor job from within the event loop."""
try:
return await self.loop.run_in_executor(None, executor_func, *args)
Expand Down Expand Up @@ -941,7 +983,7 @@ def __init__(
self._filename = f"{self._central.instance_name}_{filename}"
self._cache_dict = cache_dict

async def save(self) -> Awaitable[int]:
async def save(self) -> int:
"""
Save current name data in NAMES to disk.
"""
Expand All @@ -959,7 +1001,7 @@ def _save() -> int:

return await self._central.async_add_executor_job(_save)

async def load(self) -> Awaitable[int]:
async def load(self) -> int:
"""
Load file from disk into dict.
"""
Expand Down Expand Up @@ -1136,7 +1178,7 @@ def _handle_device_description(
device_address = get_device_address(address)
self._addresses[interface_id][device_address].append(address)

async def load(self) -> Awaitable[int]:
async def load(self) -> int:
"""
Load device data from disk into devices_raw.
"""
Expand Down Expand Up @@ -1272,15 +1314,15 @@ def _init_address_parameter_list(self) -> None:
(device_address, parameter)
].append(get_device_channel(channel_address))

async def load(self) -> Awaitable[int]:
async def load(self) -> int:
"""
Load paramset descriptions from disk into paramset cache.
"""
result = await super().load()
self._init_address_parameter_list()
return result

async def save(self) -> Awaitable[int]:
async def save(self) -> int:
"""
Save current paramset descriptions to disk.
"""
Expand Down
22 changes: 10 additions & 12 deletions hahomematic/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,7 @@
)
from hahomematic.device import HmDevice
from hahomematic.exceptions import BaseHomematicException, HaHomematicException
from hahomematic.helpers import (
build_api_url,
get_channel_no,
get_local_ip,
parse_ccu_sys_var,
)
from hahomematic.helpers import build_api_url, get_channel_no, parse_ccu_sys_var
from hahomematic.json_rpc_client import JsonRpcAioHttpClient
from hahomematic.xml_rpc_proxy import XmlRpcProxy

Expand Down Expand Up @@ -908,17 +903,18 @@ class _ClientConfig:
"""Config for a Client."""

def __init__(
self, central: hm_central.CentralUnit, interface_config: InterfaceConfig
self,
central: hm_central.CentralUnit,
interface_config: InterfaceConfig,
local_ip: str,
):
self.central = central
self.name: str = interface_config.name
self._central_config = self.central.central_config
self._callback_host: str = (
self._central_config.callback_host
if self._central_config.callback_host
else get_local_ip(
host=self._central_config.host, port=interface_config.port
)
else local_ip
)
self._callback_port: int = (
self._central_config.callback_port
Expand Down Expand Up @@ -983,9 +979,11 @@ def __init__(


async def create_client(
central: hm_central.CentralUnit, interface_config: InterfaceConfig
central: hm_central.CentralUnit,
interface_config: InterfaceConfig,
local_ip: str,
) -> Client:
"""Return a new client for with a given interface_config."""
return await _ClientConfig(
central=central, interface_config=interface_config
central=central, interface_config=interface_config, local_ip=local_ip
).get_client()
20 changes: 0 additions & 20 deletions hahomematic/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
from datetime import datetime
import logging
import os
import socket
import ssl
from typing import Any

from hahomematic import config
import hahomematic.central_unit as hm_central
from hahomematic.const import (
ATTR_HM_ALARM,
Expand Down Expand Up @@ -320,24 +318,6 @@ def get_channel_no(address: str) -> int | None:
return int(address.split(":")[1])


# Do not add: pylint disable=no-member
# This is only an issue on MacOS
def get_local_ip(host: str, port: int) -> str:
"""Get local_ip from socket."""
try:
socket.gethostbyname(host)
except Exception as ex:
_LOGGER.warning("Can't resolve host for %s", host)
raise ClientException(ex) from ex
tmp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
tmp_socket.settimeout(config.TIMEOUT)
tmp_socket.connect((host, port))
local_ip = str(tmp_socket.getsockname()[0])
tmp_socket.close()
_LOGGER.debug("Got local ip: %s", local_ip)
return local_ip


def updated_within_seconds(last_update: datetime, age_seconds: int = 120) -> bool:
"""Entity has been updated within X minutes."""
if last_update == INIT_DATETIME:
Expand Down
3 changes: 1 addition & 2 deletions hahomematic/support.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Module to support hahomematic eco system."""
from __future__ import annotations

from collections.abc import Awaitable
from copy import copy
import json
import logging
Expand Down Expand Up @@ -100,7 +99,7 @@ def _anonymize_address(self, address: str) -> str:
address_parts[0] = self._random_id
return ":".join(address_parts)

async def _save(self, file_dir: str, filename: str, data: Any) -> Awaitable[int]:
async def _save(self, file_dir: str, filename: str, data: Any) -> int:
"""Save file to disk."""

def _save() -> int:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def readme():
},
PACKAGE_NAME = "hahomematic"
HERE = os.path.abspath(os.path.dirname(__file__))
VERSION = "0.34.2"
VERSION = "0.35.0"

PACKAGES = find_packages(exclude=["tests", "tests.*", "dist", "build"])

Expand Down

0 comments on commit bd0499b

Please sign in to comment.