Skip to content

Commit 99da94e

Browse files
iMicknlCopilotCopilot
authored
Improve enum structure (#1925)
## Breaking - Various `UIClass` and `UIWidget` enums have been renamed to proper UPPER_SNAKE_CASE. ## Enhancements - `UIClass`, `UIWidget` and `UIProfile` are now auto-generated. - Enums that include an `Unknown` value now inherit from a shared base class (`UnknownEnumMixin`) to reduce code repetition - Add SDK functions to retrieve device information, including controllable types, devices, `ui_classes`, `ui_classifiers`, `ui_profile`, and `ui_widgets` --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: iMicknl <1424596+iMicknl@users.noreply.github.com>
1 parent 6d06fff commit 99da94e

File tree

15 files changed

+3771
-372
lines changed

15 files changed

+3771
-372
lines changed

pyoverkiz/client.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@
6161
Option,
6262
OptionParameter,
6363
Place,
64+
ProtocolType,
6465
ServerConfig,
6566
Setup,
6667
State,
68+
UIProfileDefinition,
6769
)
6870
from pyoverkiz.obfuscate import obfuscate_sensitive_data
6971
from pyoverkiz.serializers import prepare_payload
@@ -651,6 +653,76 @@ async def get_setup_option_parameter(
651653

652654
return None
653655

656+
@retry_on_auth_error
657+
async def get_reference_controllable(self, controllable_name: str) -> JSON:
658+
"""Get a controllable definition."""
659+
return await self.__get(
660+
f"reference/controllable/{urllib.parse.quote_plus(controllable_name)}"
661+
)
662+
663+
@retry_on_auth_error
664+
async def get_reference_controllable_types(self) -> JSON:
665+
"""Get details about all supported controllable types."""
666+
return await self.__get("reference/controllableTypes")
667+
668+
@retry_on_auth_error
669+
async def search_reference_devices_model(self, payload: JSON) -> JSON:
670+
"""Search reference device models using a POST payload."""
671+
return await self.__post("reference/devices/search", payload)
672+
673+
@retry_on_auth_error
674+
async def get_reference_protocol_types(self) -> list[ProtocolType]:
675+
"""Get details about supported protocol types on that server instance.
676+
677+
Returns a list of protocol type definitions, each containing:
678+
- id: Numeric protocol identifier
679+
- prefix: URL prefix used in device addresses
680+
- name: Internal protocol name
681+
- label: Human-readable protocol label
682+
"""
683+
response = await self.__get("reference/protocolTypes")
684+
return [ProtocolType(**protocol) for protocol in response]
685+
686+
@retry_on_auth_error
687+
async def get_reference_timezones(self) -> JSON:
688+
"""Get timezones list."""
689+
return await self.__get("reference/timezones")
690+
691+
@retry_on_auth_error
692+
async def get_reference_ui_classes(self) -> list[str]:
693+
"""Get a list of all defined UI classes."""
694+
return await self.__get("reference/ui/classes")
695+
696+
@retry_on_auth_error
697+
async def get_reference_ui_classifiers(self) -> list[str]:
698+
"""Get a list of all defined UI classifiers."""
699+
return await self.__get("reference/ui/classifiers")
700+
701+
@retry_on_auth_error
702+
async def get_reference_ui_profile(self, profile_name: str) -> UIProfileDefinition:
703+
"""Get a description of a given UI profile (or form-factor variant).
704+
705+
Returns a profile definition containing:
706+
- name: Profile name
707+
- commands: Available commands with parameters and descriptions
708+
- states: Available states with value types and descriptions
709+
- form_factor: Whether profile is tied to a specific physical device type
710+
"""
711+
response = await self.__get(
712+
f"reference/ui/profile/{urllib.parse.quote_plus(profile_name)}"
713+
)
714+
return UIProfileDefinition(**humps.decamelize(response))
715+
716+
@retry_on_auth_error
717+
async def get_reference_ui_profile_names(self) -> list[str]:
718+
"""Get a list of all defined UI profiles (and form-factor variants)."""
719+
return await self.__get("reference/ui/profileNames")
720+
721+
@retry_on_auth_error
722+
async def get_reference_ui_widgets(self) -> list[str]:
723+
"""Get a list of all defined UI widgets."""
724+
return await self.__get("reference/ui/widgets")
725+
654726
async def __get(self, path: str) -> Any:
655727
"""Make a GET request to the OverKiz API."""
656728
await self._refresh_token_if_expired()

pyoverkiz/enums/__init__.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,43 @@
11
"""Convenience re-exports for the enums package."""
22

3-
# flake8: noqa: F403
3+
# Explicitly re-export all Enum subclasses to avoid wildcard import issues
4+
from pyoverkiz.enums.command import CommandMode, OverkizCommand, OverkizCommandParam
5+
from pyoverkiz.enums.execution import (
6+
ExecutionState,
7+
ExecutionSubType,
8+
ExecutionType,
9+
)
10+
from pyoverkiz.enums.gateway import GatewaySubType, GatewayType, UpdateBoxStatus
11+
from pyoverkiz.enums.general import DataType, EventName, FailureType, ProductType
12+
from pyoverkiz.enums.measured_value_type import MeasuredValueType
13+
from pyoverkiz.enums.protocol import Protocol
14+
from pyoverkiz.enums.server import APIType, Server
15+
from pyoverkiz.enums.state import OverkizAttribute, OverkizState
16+
from pyoverkiz.enums.ui import UIClass, UIClassifier, UIWidget
17+
from pyoverkiz.enums.ui_profile import UIProfile
418

5-
from .command import *
6-
from .execution import *
7-
from .gateway import *
8-
from .general import *
9-
from .measured_value_type import *
10-
from .protocol import *
11-
from .server import *
12-
from .state import *
13-
from .ui import *
19+
__all__ = [
20+
"APIType",
21+
"CommandMode",
22+
"DataType",
23+
"EventName",
24+
"ExecutionState",
25+
"ExecutionSubType",
26+
"ExecutionType",
27+
"FailureType",
28+
"GatewaySubType",
29+
"GatewayType",
30+
"MeasuredValueType",
31+
"OverkizAttribute",
32+
"OverkizCommand",
33+
"OverkizCommandParam",
34+
"OverkizState",
35+
"ProductType",
36+
"Protocol",
37+
"Server",
38+
"UIClass",
39+
"UIClassifier",
40+
"UIProfile",
41+
"UIWidget",
42+
"UpdateBoxStatus",
43+
]

pyoverkiz/enums/base.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Shared enum helpers for consistent parsing and logging."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
from typing import Self, cast
7+
8+
9+
class UnknownEnumMixin:
10+
"""Mixin for enums that need an `UNKNOWN` fallback.
11+
12+
Define `UNKNOWN` on the enum and optionally override
13+
`__missing_message__` to customize the log message.
14+
"""
15+
16+
__missing_message__ = "Unsupported value %s has been returned for %s"
17+
18+
@classmethod
19+
def _missing_(cls, value: object) -> Self: # type: ignore[override]
20+
"""Return `UNKNOWN` and log unrecognized values.
21+
22+
Intentionally overrides the Enum base `_missing_` to provide an UNKNOWN fallback.
23+
"""
24+
message = cls.__missing_message__
25+
logging.getLogger(cls.__module__).warning(message, value, cls)
26+
# Type checker cannot infer UNKNOWN exists on Self, but all subclasses define it
27+
return cast(Self, cls.UNKNOWN) # type: ignore[attr-defined]

0 commit comments

Comments
 (0)