Skip to content

Commit 30a2fb2

Browse files
iMicknlCopilotCopilot
committed
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 0b8a6a3 commit 30a2fb2

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
@@ -62,9 +62,11 @@
6262
Option,
6363
OptionParameter,
6464
Place,
65+
ProtocolType,
6566
ServerConfig,
6667
Setup,
6768
State,
69+
UIProfileDefinition,
6870
)
6971
from pyoverkiz.obfuscate import obfuscate_sensitive_data
7072
from pyoverkiz.serializers import prepare_payload
@@ -652,6 +654,76 @@ async def get_setup_option_parameter(
652654

653655
return None
654656

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