Skip to content

Commit

Permalink
Support markers for sysvar/program selection (#1928)
Browse files Browse the repository at this point in the history
  • Loading branch information
SukramJ authored Dec 18, 2024
1 parent 25b293d commit 7be34ab
Show file tree
Hide file tree
Showing 22 changed files with 187 additions and 108 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ name: "CodeQL"

# yamllint disable-line rule:truthy
on:
push:
branches: ["devel"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["devel"]
pull_request: ~
schedule:
- cron: "44 3 * * 2"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lock.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
lock:
if: github.repository_owner == 'danielperna84'
if: github.repository_owner == 'SukramJ'
runs-on: ubuntu-latest
steps:
- uses: dessant/[email protected]
Expand Down
5 changes: 0 additions & 5 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ name: Pylint

# yamllint disable-line rule:truthy
on:
push:
branches:
- "dev**"
- devel
- master
pull_request: ~
workflow_dispatch:
jobs:
Expand Down
56 changes: 39 additions & 17 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This workflow will upload a Python Package using Twine when a release is created
# This workflow will upload a Python Package to PyPI when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries

# This workflow uses actions that are not certified by GitHub.
Expand All @@ -17,26 +17,48 @@ permissions:
contents: read

jobs:
deploy:
release-build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
python-version: "3.12"

- name: Build release distributions
run: |
python -m pip install --upgrade pip
# NOTE: put your own distribution build steps here.
python -m pip install build
pip install -r requirements_test.txt
pip install build setuptools wheel
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70
python -m build
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/

pypi-publish:
runs-on: ubuntu-latest
needs:
- release-build
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write

environment:
name: pypi

steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/

- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
packages-dir: dist/
5 changes: 0 additions & 5 deletions .github/workflows/test-run.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ name: "Test-Run"

# yamllint disable-line rule:truthy
on:
push:
branches:
- "dev**"
- devel
- master
pull_request: ~
workflow_dispatch:

Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Version 2024.12.6 (2024-12-18)

- Support markers for sysvar/program selection
- Remove danielperna84 from links after repository transfer to sukramj

#Version 2024.12.5 (2024-12-15)
Expand Down
2 changes: 1 addition & 1 deletion example_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ async def example_run(self):
"VCU0000045": "HM-LC-Bl1-FM.json",
"VCU0000276": "ST6-SH.json",
"VCU2128127": "HmIP-BSM.json",
"VCU3716619": "HmIP-BSL.json",
"VCU6985973": "HmIP-BSL.json",
"VCU6153495": "HmIP-FCI1.json",
"VCU0000265": "HM-Sen-LI-O.json",
"VCU0000146": "HM-Sec-Key.json",
Expand Down
6 changes: 6 additions & 0 deletions hahomematic/central/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
DEFAULT_INCLUDE_INTERNAL_SYSVARS,
DEFAULT_MAX_READ_WORKERS,
DEFAULT_PERIODIC_REFRESH_INTERVAL,
DEFAULT_PROGRAM_MARKERS,
DEFAULT_PROGRAM_SCAN_ENABLED,
DEFAULT_SYS_SCAN_INTERVAL,
DEFAULT_SYSVAR_MARKERS,
DEFAULT_SYSVAR_SCAN_ENABLED,
DEFAULT_TLS,
DEFAULT_UN_IGNORES,
Expand Down Expand Up @@ -1675,9 +1677,11 @@ def __init__(
listen_port: int | None = None,
max_read_workers: int = DEFAULT_MAX_READ_WORKERS,
periodic_refresh_interval: int = DEFAULT_PERIODIC_REFRESH_INTERVAL,
program_markers: tuple[str, ...] = DEFAULT_PROGRAM_MARKERS,
program_scan_enabled: bool = DEFAULT_PROGRAM_SCAN_ENABLED,
start_direct: bool = False,
sys_scan_interval: int = DEFAULT_SYS_SCAN_INTERVAL,
sysvar_markers: tuple[str, ...] = DEFAULT_SYSVAR_MARKERS,
sysvar_scan_enabled: bool = DEFAULT_SYSVAR_SCAN_ENABLED,
tls: bool = DEFAULT_TLS,
un_ignore_list: tuple[str, ...] = DEFAULT_UN_IGNORES,
Expand Down Expand Up @@ -1707,10 +1711,12 @@ def __init__(
self.name: Final = name
self.password: Final = password
self.periodic_refresh_interval = periodic_refresh_interval
self.program_markers: Final = program_markers
self.program_scan_enabled: Final = program_scan_enabled
self.start_direct: Final = start_direct
self.storage_folder: Final = storage_folder
self.sys_scan_interval: Final = sys_scan_interval
self.sysvar_markers: Final = sysvar_markers
self.sysvar_scan_enabled: Final = sysvar_scan_enabled
self.tls: Final = tls
self.un_ignore_list: Final = un_ignore_list
Expand Down
28 changes: 18 additions & 10 deletions hahomematic/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,12 +381,14 @@ async def get_system_variable(self, name: str) -> Any:

@abstractmethod
async def get_all_system_variables(
self, include_internal: bool
self, sysvar_markers: tuple[str, ...], include_internal: bool
) -> tuple[SystemVariableData, ...]:
"""Get all system variables from CCU / Homegear."""

@abstractmethod
async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, ...]:
async def get_all_programs(
self, program_markers: tuple[str, ...], include_internal: bool
) -> tuple[ProgramData, ...]:
"""Get all programs, if available."""

@abstractmethod
Expand Down Expand Up @@ -1164,17 +1166,21 @@ async def get_system_variable(self, name: str) -> Any:

@service(re_raise=False, no_raise_return=())
async def get_all_system_variables(
self, include_internal: bool
self, sysvar_markers: tuple[str, ...], include_internal: bool
) -> tuple[SystemVariableData, ...]:
"""Get all system variables from CCU / Homegear."""
"""Get all system variables from CCU."""
return await self._json_rpc_client.get_all_system_variables(
include_internal=include_internal
sysvar_markers=sysvar_markers, include_internal=include_internal
)

@service(re_raise=False, no_raise_return=())
async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, ...]:
async def get_all_programs(
self, program_markers: tuple[str, ...], include_internal: bool
) -> tuple[ProgramData, ...]:
"""Get all programs, if available."""
return await self._json_rpc_client.get_all_programs(include_internal=include_internal)
return await self._json_rpc_client.get_all_programs(
program_markers=program_markers, include_internal=include_internal
)

@service(re_raise=False, no_raise_return={})
async def get_all_rooms(self) -> dict[str, set[str]]:
Expand Down Expand Up @@ -1496,17 +1502,19 @@ async def get_system_variable(self, name: str) -> Any:

@service(re_raise=False, no_raise_return=())
async def get_all_system_variables(
self, include_internal: bool
self, sysvar_markers: tuple[str, ...], include_internal: bool
) -> tuple[SystemVariableData, ...]:
"""Get all system variables from CCU / Homegear."""
"""Get all system variables from Homegear."""
variables: list[SystemVariableData] = []
if hg_variables := await self._proxy.getAllSystemVariables():
for name, value in hg_variables.items():
variables.append(SystemVariableData(vid=name, name=name, value=value))
return tuple(variables)

@service(re_raise=False, no_raise_return=())
async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, ...]:
async def get_all_programs(
self, program_markers: tuple[str, ...], include_internal: bool
) -> tuple[ProgramData, ...]:
"""Get all programs, if available."""
return ()

Expand Down
37 changes: 31 additions & 6 deletions hahomematic/client/json_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from hahomematic.model.support import convert_value
from hahomematic.support import (
cleanup_text_from_html_tags,
element_matches_key,
get_tls_context,
parse_sys_var,
reduce_args,
Expand Down Expand Up @@ -538,7 +539,7 @@ async def get_system_variable(self, name: str) -> Any:
return response[_JsonKey.RESULT]

async def get_all_system_variables(
self, include_internal: bool
self, sysvar_markers: tuple[str, ...], include_internal: bool
) -> tuple[SystemVariableData, ...]:
"""Get all system variables from CCU / Homegear."""
variables: list[SystemVariableData] = []
Expand All @@ -551,22 +552,32 @@ async def get_all_system_variables(
if json_result := response[_JsonKey.RESULT]:
descriptions = await self._get_system_variable_descriptions()
for var in json_result:
has_markers = False
extended_sysvar = False
is_internal = var[_JsonKey.IS_INTERNAL]
if include_internal is False and is_internal is True:
continue
extended_sysvar = False
var_id = var[_JsonKey.ID]
description = descriptions.get(var_id)
if not is_internal and sysvar_markers:
if not element_matches_key(
search_elements=sysvar_markers,
compare_with=description,
do_wildcard_search=True,
):
continue
has_markers = True

name = var[_JsonKey.NAME]
org_data_type = var[_JsonKey.TYPE]
raw_value = var[_JsonKey.VALUE]
if org_data_type == SysvarType.NUMBER:
data_type = SysvarType.FLOAT if "." in raw_value else SysvarType.INTEGER
else:
data_type = org_data_type
if (description := descriptions.get(var_id)) and (
extended_sysvar := EXTENDED_SYSVAR_MARKER in description
):
if description and (extended_sysvar := EXTENDED_SYSVAR_MARKER in description):
description = description.replace(EXTENDED_SYSVAR_MARKER, "").strip()
has_markers = True
unit = var[_JsonKey.UNIT]
values: tuple[str, ...] | None = None
if val_list := var.get(_JsonKey.VALUE_LIST):
Expand All @@ -591,6 +602,7 @@ async def get_all_system_variables(
max_value=max_value,
min_value=min_value,
extended_sysvar=extended_sysvar,
has_markers=has_markers,
)
)
except (ValueError, TypeError) as vterr:
Expand Down Expand Up @@ -927,7 +939,9 @@ async def get_all_device_data(self, interface: Interface) -> dict[str, Any]:

return all_device_data

async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, ...]:
async def get_all_programs(
self, program_markers: tuple[str, ...], include_internal: bool
) -> tuple[ProgramData, ...]:
"""Get the all programs of the backend."""
all_programs: list[ProgramData] = []

Expand All @@ -939,11 +953,21 @@ async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, .
if json_result := response[_JsonKey.RESULT]:
descriptions = await self._get_program_descriptions()
for prog in json_result:
has_markers = False
is_internal = prog[_JsonKey.IS_INTERNAL]
if include_internal is False and is_internal is True:
continue
pid = prog[_JsonKey.ID]
description = descriptions.get(pid)
if not is_internal and program_markers:
if not element_matches_key(
search_elements=program_markers,
compare_with=description,
do_wildcard_search=True,
):
continue
has_markers = True

name = prog[_JsonKey.NAME]
is_active = prog[_JsonKey.IS_ACTIVE]
last_execute_time = prog[_JsonKey.LAST_EXECUTE_TIME]
Expand All @@ -956,6 +980,7 @@ async def get_all_programs(self, include_internal: bool) -> tuple[ProgramData, .
is_active=is_active,
is_internal=is_internal,
last_execute_time=last_execute_time,
has_markers=has_markers,
)
)

Expand Down
5 changes: 4 additions & 1 deletion hahomematic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import re
from typing import Any, Final, Required, TypedDict

VERSION: Final = "2024.12.5"
VERSION: Final = "2024.12.6"

DEFAULT_CONNECTION_CHECKER_INTERVAL: Final = 15 # check if connection is available via rpc ping
DEFAULT_CUSTOM_ID: Final = "custom_id"
Expand All @@ -22,8 +22,10 @@
DEFAULT_PERIODIC_REFRESH_INTERVAL: Final = 15
DEFAULT_PING_PONG_MISMATCH_COUNT: Final = 15
DEFAULT_PING_PONG_MISMATCH_COUNT_TTL: Final = 300
DEFAULT_PROGRAM_MARKERS: Final[tuple[str, ...]] = ()
DEFAULT_PROGRAM_SCAN_ENABLED: Final = True
DEFAULT_RECONNECT_WAIT: Final = 120 # wait with reconnect after a first ping was successful
DEFAULT_SYSVAR_MARKERS: Final[tuple[str, ...]] = ()
DEFAULT_SYSVAR_SCAN_ENABLED: Final = True
DEFAULT_SYS_SCAN_INTERVAL: Final = 30
DEFAULT_TIMEOUT: Final = 60 # default timeout for a connection
Expand Down Expand Up @@ -605,6 +607,7 @@ class HubData:
"""Dataclass for hub data points."""

name: str
has_markers: bool = False


@dataclass(frozen=True, kw_only=True, slots=True)
Expand Down
6 changes: 4 additions & 2 deletions hahomematic/model/hub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ async def _update_program_data_points(self) -> None:
programs: tuple[ProgramData, ...] = ()
if client := self._central.primary_client:
programs = await client.get_all_programs(
include_internal=self._config.include_internal_programs
program_markers=self._config.program_markers,
include_internal=self._config.include_internal_programs,
)
if not programs:
_LOGGER.debug(
Expand Down Expand Up @@ -125,7 +126,8 @@ async def _update_sysvar_data_points(self) -> None:
variables: tuple[SystemVariableData, ...] = ()
if client := self._central.primary_client:
variables = await client.get_all_system_variables(
include_internal=self._config.include_internal_sysvars
sysvar_markers=self._config.sysvar_markers,
include_internal=self._config.include_internal_sysvars,
)
if not variables:
_LOGGER.debug(
Expand Down
Loading

0 comments on commit 7be34ab

Please sign in to comment.