Skip to content

Commit e20787d

Browse files
authored
Move casting to event listener and add OverkizStateType (#305)
* Add casting to event listener * Remove duplicate test assert * Bugfixes * Make event casting simpler
1 parent 96e9ccf commit e20787d

File tree

3 files changed

+91
-4
lines changed

3 files changed

+91
-4
lines changed

pyoverkiz/models.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
UIWidget,
2020
UpdateBoxStatus,
2121
)
22+
from pyoverkiz.types import DATA_TYPE_TO_PYTHON, StateType
2223

2324
# pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals
2425

@@ -269,20 +270,37 @@ def __len__(self) -> int:
269270
class State:
270271
name: str
271272
type: DataType
272-
value: None | int | float | str | bool
273+
value: StateType
273274

274275
def __init__(
275276
self,
276277
name: str,
277278
type: int,
278-
value: None | int | float | str | bool = None,
279+
value: StateType = None,
279280
**_: Any,
280281
):
281282
self.name = name
282283
self.value = value
283284
self.type = DataType(type)
284285

285286

287+
@define(init=False, kw_only=True)
288+
class EventState(State):
289+
def __init__(
290+
self,
291+
name: str,
292+
type: int,
293+
value: str | None = None,
294+
**_: Any,
295+
):
296+
super().__init__(name, type, value, **_)
297+
298+
# Overkiz returns all state values as a string
299+
# We cast them here based on the data type provided by Overkiz
300+
if self.type in DATA_TYPE_TO_PYTHON:
301+
self.value = DATA_TYPE_TO_PYTHON[self.type](self.value)
302+
303+
286304
@define(init=False)
287305
class States:
288306
_states: list[State]
@@ -351,7 +369,7 @@ class Event:
351369
gateway_id: str | None = field(repr=obfuscate_id, default=None)
352370
exec_id: str | None = None
353371
device_url: str | None = field(repr=obfuscate_id, default=None)
354-
device_states: list[State]
372+
device_states: list[EventState]
355373
old_state: ExecutionState | None = None
356374
new_state: ExecutionState | None = None
357375

@@ -387,7 +405,7 @@ def __init__(
387405
self.exec_id = exec_id
388406
self.device_url = device_url
389407
self.device_states = (
390-
[State(**s) for s in device_states] if device_states else []
408+
[EventState(**s) for s in device_states] if device_states else []
391409
)
392410
self.old_state = ExecutionState(old_state) if old_state else None
393411
self.new_state = ExecutionState(new_state) if new_state else None

pyoverkiz/types.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from typing import Any, Callable, Dict, List, Union
5+
6+
from pyoverkiz.enums import DataType
7+
8+
StateType = Union[str, int, float, bool, Dict[Any, Any], List[Any], None]
9+
10+
11+
DATA_TYPE_TO_PYTHON: dict[DataType, Callable[[Any], StateType]] = {
12+
DataType.INTEGER: int,
13+
DataType.DATE: int,
14+
DataType.FLOAT: float,
15+
DataType.BOOLEAN: bool,
16+
DataType.JSON_ARRAY: json.loads,
17+
DataType.JSON_OBJECT: json.loads,
18+
}

tests/test_client.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from pyoverkiz.client import OverkizClient
1010
from pyoverkiz.const import SUPPORTED_SERVERS
11+
from pyoverkiz.enums import DataType
1112

1213
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
1314

@@ -39,6 +40,56 @@ async def test_fetch_events_basic(self, client):
3940
events = await client.fetch_events()
4041
assert len(events) == 16
4142

43+
@pytest.mark.asyncio
44+
async def test_fetch_events_simple_cast(self, client):
45+
with open(
46+
os.path.join(CURRENT_DIR, "events.json"), encoding="utf-8"
47+
) as raw_events:
48+
resp = MockResponse(raw_events.read())
49+
50+
with patch.object(aiohttp.ClientSession, "post", return_value=resp):
51+
events = await client.fetch_events()
52+
53+
# check if str to integer cast was succesfull
54+
int_state_event = events[2].device_states[0]
55+
assert int_state_event.value == 23247220
56+
assert isinstance(int_state_event.value, int)
57+
assert int_state_event.type == DataType.INTEGER
58+
59+
@pytest.mark.asyncio
60+
async def test_fetch_events_casting(self, client):
61+
with open(
62+
os.path.join(CURRENT_DIR, "events.json"), encoding="utf-8"
63+
) as raw_events:
64+
resp = MockResponse(raw_events.read())
65+
66+
with patch.object(aiohttp.ClientSession, "post", return_value=resp):
67+
events = await client.fetch_events()
68+
69+
for event in events:
70+
for state in event.device_states:
71+
72+
if state.type == 0:
73+
assert state.value is None
74+
75+
if state.type == 1:
76+
assert isinstance(state.value, int)
77+
78+
if state.type == 2:
79+
assert isinstance(state.value, float)
80+
81+
if state.type == 3:
82+
assert isinstance(state.value, str)
83+
84+
if state.type == 6:
85+
assert isinstance(state.value, bool)
86+
87+
if state.type == 10:
88+
assert isinstance(state.value, list)
89+
90+
if state.type == 11:
91+
assert isinstance(state.value, dict)
92+
4293
@pytest.mark.parametrize(
4394
"fixture_name, device_count",
4495
[

0 commit comments

Comments
 (0)