Skip to content

Commit 825b6b7

Browse files
iMicknlCopilot
andauthored
Rename CommandMode, cancel_command, and scenario methods (#1997)
## Summary - Rename `CommandMode` → `ExecutionMode` and `cancel_command` → `cancel_execution` - Rename `execute_scenario` → `execute_persisted_action_group` and `execute_scheduled_scenario` → `schedule_persisted_action_group` - Improve documentation for command execution concepts (core-concepts, device-control) - Update method docstrings across client, action queue, and models ## Breaking - `CommandMode` renamed to `ExecutionMode`, `cancel_command()` renamed to `cancel_execution()`, `execute_scenario()` renamed to `execute_persisted_action_group()`, `execute_scheduled_scenario()` renamed to `schedule_persisted_action_group()` --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ed1eb8f commit 825b6b7

File tree

9 files changed

+207
-95
lines changed

9 files changed

+207
-95
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async def main() -> None:
6868
)
6969
],
7070
label="Execution via Python",
71-
# mode=CommandMode.HIGH_PRIORITY
71+
# mode=ExecutionMode.HIGH_PRIORITY
7272
)
7373

7474
while True:

docs/core-concepts.md

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,73 @@ A server describes where the API calls go. A gateway is the physical hub in your
88

99
The setup describes the current gateway configuration and device inventory. Devices expose metadata like `uiClass` and `widget`, plus a list of current `states`.
1010

11-
## Actions, action groups, and commands
11+
## Commands, actions, and action groups
1212

13-
Commands are sent as `Action` objects, grouped into an action group. Each action targets a device URL and a set of commands with parameters.
13+
The Overkiz API uses a three-level hierarchy to control devices:
14+
15+
- **Command** — A single instruction for a device, like `open`, `close`, or `setClosure(50)`. A command has a name and optional parameters.
16+
- **Action** — One or more commands targeting a **single device** (identified by its device URL). The gateway allows at most one action per device in each action group.
17+
- **Action group** — A batch of actions submitted to the gateway as a single execution. An action group can target multiple devices at once.
18+
19+
```
20+
ActionGroup
21+
├── Action (device A)
22+
│ ├── Command("open")
23+
│ └── Command("setClosure", [50])
24+
└── Action (device B)
25+
└── Command("close")
26+
```
27+
28+
Action groups come in two flavors:
29+
30+
- **Ad-hoc** — Built on the fly from `Action` and `Command` objects and executed via `execute_action_group()`.
31+
- **Persisted** — Stored on the server (like saved scenes). Retrieved with `get_action_groups()` and executed by OID via `execute_persisted_action_group()`, or scheduled for a future timestamp via `schedule_persisted_action_group()`.
32+
33+
## Executions
34+
35+
When an action group is submitted, the server returns an `exec_id` identifying the **execution**. An execution tracks the lifecycle of a submitted action group — from queued, to running, to completed or failed.
36+
37+
- **Track** running executions with `get_current_executions()` or `get_current_execution(exec_id)`.
38+
- **Cancel** a running execution with `cancel_execution(exec_id)`.
39+
- **Review** past results with `get_execution_history()`.
40+
41+
Executions run asynchronously on the gateway. State changes are delivered through events, so you typically combine execution with an event listener to know when commands finish.
42+
43+
## Execution modes
44+
45+
An optional `ExecutionMode` can be passed when executing an action group:
46+
47+
- `HIGH_PRIORITY` — Bypasses the normal execution queue.
48+
- `GEOLOCATED` — Triggered by geolocation rules.
49+
- `INTERNAL` — Used for internal/system executions.
1450

1551
## States
1652

1753
States are name/value pairs that represent the current device status, such as closure position or temperature.
1854

1955
## Events and listeners
2056

21-
The API uses an event listener that you register once per session. Fetching events drains the server-side buffer.
22-
23-
## Execution model
24-
25-
Commands are executed asynchronously by the platform. You can poll execution state via events or refresh device states after a delay.
57+
The API uses an event listener that you register once per session. Fetching events drains the server-side buffer. Events include execution state changes, device state updates, and other notifications.
2658

2759
## Relationship diagram
2860

2961
```
3062
Client
31-
|
32-
|-- Server (cloud or local)
33-
|
34-
|-- Gateway
35-
|
36-
|-- Setup
37-
| |
38-
| |-- Devices
39-
| |
40-
| |-- States
41-
| |-- Actions -> Commands -> Parameters
42-
|
43-
|-- Event Listener -> Events
63+
64+
├── Server (cloud or local)
65+
66+
└── Gateway
67+
68+
├── Setup
69+
│ │
70+
│ └── Devices
71+
│ │
72+
│ ├── States (name/value pairs)
73+
│ └── Actions ──► Commands ──► Parameters
74+
75+
├── Action Groups (persisted)
76+
77+
├── Executions (running action groups)
78+
79+
└── Event Listener ──► Events
4480
```

docs/device-control.md

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -142,58 +142,139 @@ if device.identifier.is_sub_device:
142142
print(f"Sub-device ID: {device.identifier.subsystem_id}")
143143
```
144144

145-
## Send a command
145+
## Send a single command to a device
146+
147+
Create an `Action` with the target device URL and one or more `Command` objects, then wrap it in an action group. The method returns an `exec_id` you can use to track or cancel the execution.
146148

147149
```python
148150
from pyoverkiz.enums import OverkizCommand
149151
from pyoverkiz.models import Action, Command
150152

151-
await client.execute_action_group(
153+
exec_id = await client.execute_action_group(
152154
actions=[
153155
Action(
154156
device_url="io://1234-5678-1234/12345678",
155157
commands=[
156-
Command(
157-
name=OverkizCommand.SET_CLOSURE,
158-
parameters=[50],
159-
)
158+
Command(name=OverkizCommand.SET_CLOSURE, parameters=[50])
160159
],
161160
)
162-
],
163-
label="Execution: set closure",
161+
],
162+
label="Set closure to 50%",
164163
)
165164
```
166165

167-
## Action groups and common patterns
166+
## Send multiple commands to one device
168167

169-
- Use a single action group to batch multiple device commands.
168+
A single action can hold multiple commands. They are executed in order on the device.
170169

171170
```python
172171
from pyoverkiz.enums import OverkizCommand, OverkizCommandParam
173172
from pyoverkiz.models import Action, Command
174173

175-
await client.execute_action_group(
174+
exec_id = await client.execute_action_group(
176175
actions=[
177176
Action(
178177
device_url="io://1234-5678-1234/12345678",
179178
commands=[
180179
Command(
181180
name=OverkizCommand.SET_DEROGATION,
182181
parameters=[21.5, OverkizCommandParam.FURTHER_NOTICE],
183-
)
184-
],
185-
),
186-
Action(
187-
device_url="io://1234-5678-1234/12345678",
188-
commands=[
182+
),
189183
Command(
190184
name=OverkizCommand.SET_MODE_TEMPERATURE,
191185
parameters=[OverkizCommandParam.MANUAL_MODE, 21.5],
192-
)
186+
),
193187
],
194188
)
195189
],
196-
label="Execution: multiple commands",
190+
label="Set temperature derogation",
191+
)
192+
```
193+
194+
## Control multiple devices at once
195+
196+
An action group can contain one action per device. All actions in the group are submitted as a single execution.
197+
198+
```python
199+
from pyoverkiz.enums import OverkizCommand
200+
from pyoverkiz.models import Action, Command
201+
202+
exec_id = await client.execute_action_group(
203+
actions=[
204+
Action(
205+
device_url="io://1234-5678-1234/11111111",
206+
commands=[Command(name=OverkizCommand.CLOSE)],
207+
),
208+
Action(
209+
device_url="io://1234-5678-1234/22222222",
210+
commands=[Command(name=OverkizCommand.OPEN)],
211+
),
212+
],
213+
label="Close blinds, open garage",
214+
)
215+
```
216+
217+
!!! note
218+
The gateway allows at most **one action per device** in each action group.
219+
If you need to send commands to the same device in separate executions, use
220+
separate `execute_action_group()` calls.
221+
222+
## Execution modes
223+
224+
Pass an `ExecutionMode` to change how the gateway processes the action group:
225+
226+
```python
227+
from pyoverkiz.enums import ExecutionMode, OverkizCommand
228+
from pyoverkiz.models import Action, Command
229+
230+
exec_id = await client.execute_action_group(
231+
actions=[
232+
Action(
233+
device_url="io://1234-5678-1234/12345678",
234+
commands=[Command(name=OverkizCommand.OPEN)],
235+
)
236+
],
237+
mode=ExecutionMode.HIGH_PRIORITY,
238+
)
239+
```
240+
241+
Available modes: `HIGH_PRIORITY`, `GEOLOCATED`, `INTERNAL`. When omitted, the default execution mode is used.
242+
243+
## Track and cancel executions
244+
245+
```python
246+
# List all running executions
247+
executions = await client.get_current_executions()
248+
249+
# Get a specific execution
250+
execution = await client.get_current_execution(exec_id)
251+
252+
# Cancel a running execution
253+
await client.cancel_execution(exec_id)
254+
255+
# Review past executions
256+
history = await client.get_execution_history()
257+
```
258+
259+
## Persisted action groups
260+
261+
Action groups can be stored on the server (like saved scenes). Use these methods to list and execute them:
262+
263+
```python
264+
import time
265+
266+
# List all persisted action groups
267+
action_groups = await client.get_action_groups()
268+
269+
for ag in action_groups:
270+
print(f"{ag.label} (OID: {ag.oid})")
271+
272+
# Execute a persisted action group by OID
273+
exec_id = await client.execute_persisted_action_group(ag.oid)
274+
275+
# Schedule for future execution (e.g. 1 hour from now)
276+
trigger_id = await client.schedule_persisted_action_group(
277+
ag.oid, timestamp=int(time.time()) + 3600
197278
)
198279
```
199280

pyoverkiz/action_queue.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pyoverkiz.models import Action
1212

1313
if TYPE_CHECKING:
14-
from pyoverkiz.enums import CommandMode
14+
from pyoverkiz.enums import ExecutionMode
1515

1616

1717
@dataclass(frozen=True, slots=True)
@@ -78,15 +78,15 @@ class ActionQueue:
7878
The batch is flushed when:
7979
- The delay timer expires
8080
- The max actions limit is reached
81-
- The command mode changes
81+
- The execution mode changes
8282
- The label changes
8383
- Manual flush is requested
8484
"""
8585

8686
def __init__(
8787
self,
8888
executor: Callable[
89-
[list[Action], CommandMode | None, str | None], Coroutine[None, None, str]
89+
[list[Action], ExecutionMode | None, str | None], Coroutine[None, None, str]
9090
],
9191
delay: float = 0.5,
9292
max_actions: int = 20,
@@ -102,7 +102,7 @@ def __init__(
102102
self._max_actions = max_actions
103103

104104
self._pending_actions: list[Action] = []
105-
self._pending_mode: CommandMode | None = None
105+
self._pending_mode: ExecutionMode | None = None
106106
self._pending_label: str | None = None
107107
self._pending_waiters: list[QueuedExecution] = []
108108

@@ -121,7 +121,7 @@ def _copy_action(action: Action) -> Action:
121121
async def add(
122122
self,
123123
actions: list[Action],
124-
mode: CommandMode | None = None,
124+
mode: ExecutionMode | None = None,
125125
label: str | None = None,
126126
) -> QueuedExecution:
127127
"""Add actions to the queue.
@@ -132,7 +132,7 @@ async def add(
132132
133133
Args:
134134
actions: Actions to queue.
135-
mode: Command mode, which triggers a flush if it differs from the
135+
mode: Execution mode, which triggers a flush if it differs from the
136136
pending mode.
137137
label: Label for the action group.
138138
@@ -141,7 +141,7 @@ async def add(
141141
executes.
142142
"""
143143
batches_to_execute: list[
144-
tuple[list[Action], CommandMode | None, str | None, list[QueuedExecution]]
144+
tuple[list[Action], ExecutionMode | None, str | None, list[QueuedExecution]]
145145
] = []
146146

147147
if not actions:
@@ -235,7 +235,7 @@ async def _delayed_flush(self) -> None:
235235

236236
def _prepare_flush(
237237
self,
238-
) -> tuple[list[Action], CommandMode | None, str | None, list[QueuedExecution]]:
238+
) -> tuple[list[Action], ExecutionMode | None, str | None, list[QueuedExecution]]:
239239
"""Prepare a flush by taking snapshot and clearing state (must be called with lock held).
240240
241241
Returns a tuple of (actions, mode, label, waiters) that should be executed
@@ -266,7 +266,7 @@ def _prepare_flush(
266266
async def _execute_batch(
267267
self,
268268
actions: list[Action],
269-
mode: CommandMode | None,
269+
mode: ExecutionMode | None,
270270
label: str | None,
271271
waiters: list[QueuedExecution],
272272
) -> None:

0 commit comments

Comments
 (0)