Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty port for some interfaces #1850

Merged
merged 1 commit into from
Nov 17, 2024
Merged
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
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Version 2024.11.2 (2024-11-15)
# Version 2024.11.2 (2024-11-17)

- Add get_data_point_path to central
- Allow empty port for some interfaces
- Do reconnect/reload only for affected interfaces
- Ignore unknown interfaces
- Remove clients for not available interfaces
Expand Down
2 changes: 1 addition & 1 deletion example.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
class Example:
"""Example for hahomematic."""

# Create a server that listens on 127.0.0.1:* and identifies itself as myserver.
# Create a server that listens on LOCAL_HOST:* and identifies itself as myserver.
got_devices = False

def __init__(self):
Expand Down
4 changes: 2 additions & 2 deletions example_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
logging.basicConfig(level=logging.INFO)
_LOGGER = logging.getLogger(__name__)

CCU_HOST = "127.0.0.1"
CCU_HOST = const.LOCAL_HOST
CCU_USERNAME = "xxx"
CCU_PASSWORD = "xxx"
CENTRAL_NAME = "ccu-dev"
Expand All @@ -26,7 +26,7 @@
class Example:
"""Example for hahomematic."""

# Create a server that listens on 127.0.0.1:* and identifies itself as myserver.
# Create a server that listens on LOCAL_HOST:* and identifies itself as myserver.
got_devices = False

def __init__(self):
Expand Down
8 changes: 5 additions & 3 deletions hahomematic/central/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
DP_KEY,
IGNORE_FOR_UN_IGNORE_PARAMETERS,
IP_ANY_V4,
LOCAL_HOST,
PORT_ANY,
PRIMARY_CLIENT_CANDIDATE_INTERFACES,
UN_IGNORE_WILDCARD,
Expand Down Expand Up @@ -633,8 +634,9 @@ def fire_interface_event(
event_data=cast(dict[EventKey, Any], INTERFACE_EVENT_SCHEMA(event_data)),
)

async def _identify_ip_addr(self, port: int) -> str:
"""Identify IP used for callbacks, xmlrpc_server."""
async def _identify_ip_addr(self, port: int | None) -> str:
if port is None:
return LOCAL_HOST

ip_addr: str | None = None
while ip_addr is None:
Expand All @@ -643,7 +645,7 @@ async def _identify_ip_addr(self, port: int) -> str:
get_ip_addr, self._config.host, port, name="get_ip_addr"
)
except HaHomematicException:
ip_addr = "127.0.0.1"
ip_addr = LOCAL_HOST
if ip_addr is None:
_LOGGER.warning(
"GET_IP_ADDR: Waiting for %i s,", config.CONNECTION_CHECKER_INTERVAL
Expand Down
10 changes: 4 additions & 6 deletions hahomematic/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1637,7 +1637,7 @@ def __init__(
self,
central_name: str,
interface: Interface,
port: int,
port: int | None = None,
remote_path: str | None = None,
) -> None:
"""Init the interface config."""
Expand All @@ -1650,11 +1650,9 @@ def __init__(

def _init_validate(self) -> None:
"""Validate the client_config."""
if self.interface not in list(Interface):
_LOGGER.warning(
"VALIDATE interface config failed: "
"Interface names must be within [%s] for production use",
", ".join(list(Interface)),
if not self.port and self.interface in INTERFACES_SUPPORTING_XML_RPC:
raise ClientException(
f"VALIDATE interface config failed: Port must defined for interface{self.interface}"
)

@property
Expand Down
3 changes: 2 additions & 1 deletion hahomematic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,16 @@
)
SCHEDULER_TIME_PATTERN = re.compile(r"^(([0-1]{0,1}[0-9])|(2[0-4])):[0-5][0-9]")

HUB_PATH: Final = "hub"
BLOCK_LOG_TIMEOUT = 60
CACHE_PATH: Final = "cache"
DATETIME_FORMAT: Final = "%d.%m.%Y %H:%M:%S"
DATETIME_FORMAT_MILLIS: Final = "%d.%m.%Y %H:%M:%S.%f'"
HUB_PATH: Final = "hub"
IDENTIFIER_SEPARATOR: Final = "@"
INIT_DATETIME: Final = datetime.strptime("01.01.1970 00:00:00", DATETIME_FORMAT)
IP_ANY_V4: Final = "0.0.0.0"
KWARGS_ARG_DATA_POINT = "data_point"
LOCAL_HOST: Final = "127.0.0.1"
PATH_JSON_RPC: Final = "/api/homematic.cgi"
PORT_ANY: Final = 0

Expand Down
5 changes: 3 additions & 2 deletions hahomematic/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,20 @@ def reduce_args(args: tuple[Any, ...]) -> tuple[Any, ...] | Any:

def build_xml_rpc_uri(
host: str,
port: int,
port: int | None,
path: str | None,
tls: bool = False,
) -> str:
"""Build XML-RPC API URL from components."""
scheme = "http"
s_port = f":{port}" if port else ""
if not path:
path = ""
if path and not path.startswith("/"):
path = f"/{path}"
if tls:
scheme += "s"
return f"{scheme}://{host}:{port}{path}"
return f"{scheme}://{host}{s_port}{path}"


def build_headers(
Expand Down
4 changes: 2 additions & 2 deletions tests/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

from __future__ import annotations

from hahomematic.const import Interface, ProgramData, SystemVariableData, SysvarType
from hahomematic.const import LOCAL_HOST, Interface, ProgramData, SystemVariableData, SysvarType

CENTRAL_NAME = "CentralTest"
CCU_HOST = "127.0.0.1"
CCU_HOST = LOCAL_HOST
CCU_USERNAME = "user"
CCU_PASSWORD = "pass"
CCU_PORT = 2002
Expand Down
6 changes: 2 additions & 4 deletions tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from hahomematic import const as hahomematic_const
from hahomematic.central import CentralConfig, CentralUnit
from hahomematic.client import Client, InterfaceConfig, _ClientConfig
from hahomematic.const import BackendSystemEvent, Interface
from hahomematic.const import LOCAL_HOST, BackendSystemEvent, Interface
from hahomematic.model.custom import CustomDataPoint
from hahomematic.model.decorators import _get_public_attributes_by_class_decorator
from hahomematic_support.client_local import ClientLocal, LocalRessources
Expand Down Expand Up @@ -130,9 +130,7 @@ async def get_default_central(
"hahomematic_support.client_local.ClientLocal.get_all_programs",
return_value=const.PROGRAM_DATA if add_programs else [],
).start()
patch(
"hahomematic.central.CentralUnit._identify_ip_addr", return_value="127.0.0.1"
).start()
patch("hahomematic.central.CentralUnit._identify_ip_addr", return_value=LOCAL_HOST).start()

await central.start()
if new_device_addresses := central._check_for_new_device_addresses():
Expand Down
7 changes: 4 additions & 3 deletions tests/test_central.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
DATETIME_FORMAT_MILLIS,
DEFAULT_INCLUDE_INTERNAL_PROGRAMS,
DEFAULT_INCLUDE_INTERNAL_SYSVARS,
LOCAL_HOST,
DataPointCategory,
DataPointUsage,
EventKey,
Expand Down Expand Up @@ -55,7 +56,7 @@ async def test_central_basics(
) -> None:
"""Test central basics."""
central, client, _ = central_client_factory
assert central.central_url == "http://127.0.0.1"
assert central.central_url == f"http://{LOCAL_HOST}"
assert central.is_alive is True
assert central.system_information.serial == "0815_4711"
assert central.version == "0"
Expand Down Expand Up @@ -135,9 +136,9 @@ async def test_identify_ip_addr(
) -> None:
"""Test identify_ip_addr."""
central, _, _ = central_client_factory
assert await central._identify_ip_addr(port=54321) == "127.0.0.1"
assert await central._identify_ip_addr(port=54321) == LOCAL_HOST
central.config.host = "no_host"
assert await central._identify_ip_addr(port=54321) == "127.0.0.1"
assert await central._identify_ip_addr(port=54321) == LOCAL_HOST


@pytest.mark.parametrize(
Expand Down