|
| 1 | +# Migrating from v1 to v2 |
| 2 | + |
| 3 | +This guide covers every breaking change in pyOverkiz 2.0 and shows how to update your code. |
| 4 | + |
| 5 | +## Python version |
| 6 | + |
| 7 | +pyOverkiz 2.0 requires **Python 3.12 or later**. If you are on an older version, upgrade Python first. |
| 8 | + |
| 9 | +## Client constructor |
| 10 | + |
| 11 | +The `OverkizClient` constructor is now keyword-only and uses dedicated credential classes instead of positional `username`/`password` arguments. |
| 12 | + |
| 13 | +=== "v1" |
| 14 | + |
| 15 | + ```python |
| 16 | + from pyoverkiz.client import OverkizClient |
| 17 | + from pyoverkiz.const import SUPPORTED_SERVERS |
| 18 | + from pyoverkiz.enums import Server |
| 19 | + |
| 20 | + client = OverkizClient("user@example.com", "password", SUPPORTED_SERVERS[Server.SOMFY_EUROPE]) |
| 21 | + ``` |
| 22 | + |
| 23 | +=== "v2" |
| 24 | + |
| 25 | + ```python |
| 26 | + from pyoverkiz.auth.credentials import UsernamePasswordCredentials |
| 27 | + from pyoverkiz.client import OverkizClient |
| 28 | + from pyoverkiz.enums import Server |
| 29 | + |
| 30 | + client = OverkizClient( |
| 31 | + server=Server.SOMFY_EUROPE, |
| 32 | + credentials=UsernamePasswordCredentials("user@example.com", "password"), |
| 33 | + ) |
| 34 | + ``` |
| 35 | + |
| 36 | +Key differences: |
| 37 | + |
| 38 | +- `server` accepts a `Server` enum, a server key string, or a `ServerConfig` instance directly — no need to look up `SUPPORTED_SERVERS` yourself. |
| 39 | +- Authentication details are wrapped in a `Credentials` subclass: `UsernamePasswordCredentials`, `TokenCredentials`, `LocalTokenCredentials`, or `RexelOAuthCodeCredentials`. |
| 40 | +- The `token` parameter is removed. Use `TokenCredentials` or `LocalTokenCredentials` instead. |
| 41 | + |
| 42 | +### Local API |
| 43 | + |
| 44 | +=== "v1" |
| 45 | + |
| 46 | + ```python |
| 47 | + from pyoverkiz.client import OverkizClient |
| 48 | + from pyoverkiz.utils import generate_local_server |
| 49 | + |
| 50 | + server = generate_local_server(host="gateway-xxxx-xxxx-xxxx.local:8443") |
| 51 | + client = OverkizClient("", "", server, token="your-token") |
| 52 | + ``` |
| 53 | + |
| 54 | +=== "v2" |
| 55 | + |
| 56 | + ```python |
| 57 | + from pyoverkiz.auth.credentials import LocalTokenCredentials |
| 58 | + from pyoverkiz.client import OverkizClient |
| 59 | + from pyoverkiz.utils import create_local_server_config |
| 60 | + |
| 61 | + client = OverkizClient( |
| 62 | + server=create_local_server_config(host="gateway-xxxx-xxxx-xxxx.local:8443"), |
| 63 | + credentials=LocalTokenCredentials("your-token"), |
| 64 | + ) |
| 65 | + ``` |
| 66 | + |
| 67 | +## Server configuration |
| 68 | + |
| 69 | +| v1 | v2 | |
| 70 | +|----|-----| |
| 71 | +| `OverkizServer` | `ServerConfig` | |
| 72 | +| `generate_local_server()` | `create_local_server_config()` | |
| 73 | +| `client.api_type` | `client.server_config.api_type` | |
| 74 | + |
| 75 | +`ServerConfig` adds a `server` field (the `Server` enum key) and an explicit `api_type` field (`APIType.CLOUD` or `APIType.LOCAL`). |
| 76 | + |
| 77 | +## Executing commands |
| 78 | + |
| 79 | +The command execution API has been consolidated into a single method. |
| 80 | + |
| 81 | +| v1 | v2 | |
| 82 | +|----|-----| |
| 83 | +| `client.execute_command(device_url, command)` | `client.execute_action_group(actions=[...])` | |
| 84 | +| `client.execute_commands(device_url, commands)` | `client.execute_action_group(actions=[...])` | |
| 85 | +| `Command` | `Action` wrapping one or more `Command` objects | |
| 86 | + |
| 87 | +=== "v1" |
| 88 | + |
| 89 | + ```python |
| 90 | + from pyoverkiz.models import Command |
| 91 | + |
| 92 | + await client.execute_command( |
| 93 | + "io://1234-5678-1234/12345678", |
| 94 | + Command("open", []), |
| 95 | + ) |
| 96 | + ``` |
| 97 | + |
| 98 | +=== "v2" |
| 99 | + |
| 100 | + ```python |
| 101 | + from pyoverkiz.models import Action, Command |
| 102 | + |
| 103 | + await client.execute_action_group( |
| 104 | + actions=[ |
| 105 | + Action( |
| 106 | + device_url="io://1234-5678-1234/12345678", |
| 107 | + commands=[Command(name="open")], |
| 108 | + ) |
| 109 | + ], |
| 110 | + ) |
| 111 | + ``` |
| 112 | + |
| 113 | +v2 also supports sending actions to **multiple devices** in a single call and choosing an `ExecutionMode` (`HIGH_PRIORITY`, `GEOLOCATED`, `INTERNAL`). |
| 114 | + |
| 115 | +## Scenarios → Action groups |
| 116 | + |
| 117 | +| v1 | v2 | |
| 118 | +|----|-----| |
| 119 | +| `Scenario` | `ActionGroup` | |
| 120 | +| `client.get_scenarios()` | `client.get_action_groups()` | |
| 121 | +| `client.execute_scenario(oid)` | `client.execute_persisted_action_group(oid)` | |
| 122 | +| `client.execute_scheduled_scenario(oid, ts)` | `client.schedule_persisted_action_group(oid, ts)` | |
| 123 | + |
| 124 | +`ActionGroup.id` and `ActionGroup.oid` are now `str | None` instead of `str`. `ActionGroup.creation_time` and `ActionGroup.metadata` are now optional. |
| 125 | + |
| 126 | +## Execution methods |
| 127 | + |
| 128 | +| v1 | v2 | |
| 129 | +|----|-----| |
| 130 | +| `CommandMode` | `ExecutionMode` | |
| 131 | +| `client.cancel_command(exec_id)` | `client.cancel_execution(exec_id)` | |
| 132 | +| `client.get_current_execution(exec_id)` returns `Execution` | Returns `Execution | None` | |
| 133 | +| `Execution.state` is `str` | `Execution.state` is `ExecutionState` | |
| 134 | + |
| 135 | +## Device model |
| 136 | + |
| 137 | +### Removed fields |
| 138 | + |
| 139 | +- `Device.id` — was a duplicate of `device_url`. |
| 140 | +- `Device.data_properties` — was never populated by the API. |
| 141 | + |
| 142 | +### Moved fields |
| 143 | + |
| 144 | +Device URL components are now grouped under `device.identifier`: |
| 145 | + |
| 146 | +| v1 | v2 | |
| 147 | +|----|-----| |
| 148 | +| `device.protocol` | `device.identifier.protocol` | |
| 149 | +| `device.gateway_id` | `device.identifier.gateway_id` | |
| 150 | +| `device.device_address` | `device.identifier.device_address` | |
| 151 | +| `device.subsystem_id` | `device.identifier.subsystem_id` | |
| 152 | +| `device.is_sub_device` | `device.identifier.is_sub_device` | |
| 153 | + |
| 154 | +The identifier also provides `device.identifier.base_device_url`. |
| 155 | + |
| 156 | +### New helper methods |
| 157 | + |
| 158 | +The `Device` class now provides convenience methods — see the [device control guide](device-control.md) for details: |
| 159 | + |
| 160 | +- `device.get_state_value()`, `device.select_first_state_value()`, `device.has_state_value()` |
| 161 | +- `device.supports_command()`, `device.supports_any_command()`, `device.select_first_command()` |
| 162 | +- `device.get_attribute_value()`, `device.select_first_attribute_value()` |
| 163 | +- `device.get_state_definition()`, `device.select_first_state_definition()` |
| 164 | + |
| 165 | +## Exceptions |
| 166 | + |
| 167 | +All exception classes have been renamed from `*Exception` to `*Error` following [PEP 8](https://peps.python.org/pep-0008/#exception-names): |
| 168 | + |
| 169 | +| v1 | v2 | |
| 170 | +|----|-----| |
| 171 | +| `BaseOverkizException` | `BaseOverkizError` | |
| 172 | +| `OverkizException` | `OverkizError` | |
| 173 | +| `BadCredentialsException` | `BadCredentialsError` | |
| 174 | +| `NotAuthenticatedException` | `NotAuthenticatedError` | |
| 175 | +| `TooManyRequestsException` | `TooManyRequestsError` | |
| 176 | +| `MaintenanceException` | `MaintenanceError` | |
| 177 | +| `NotSuchTokenException` | `NoSuchTokenError` (typo fixed) | |
| 178 | +| ... | _(all other exceptions follow the same pattern)_ | |
| 179 | + |
| 180 | +New exception types in v2: `NoSuchDeviceError`, `NoSuchActionGroupError`, `UnsupportedOperationError`. |
| 181 | + |
| 182 | +A quick way to update all imports: |
| 183 | + |
| 184 | +```bash |
| 185 | +# Find and replace across your codebase |
| 186 | +find . -name "*.py" -exec sed -i '' 's/Exception\b/Error/g' {} + |
| 187 | +``` |
| 188 | + |
| 189 | +!!! warning |
| 190 | + The sed command above is intentionally broad. Review the result to avoid renaming unrelated exception classes in your own code. |
| 191 | + |
| 192 | +## Enum renames |
| 193 | + |
| 194 | +All enums with an `UNKNOWN` fallback now inherit from `UnknownEnumMixin`. Unrecognized values returned by the API produce an `UNKNOWN` member with a logged warning instead of raising a `ValueError`. |
| 195 | + |
| 196 | +Several enum members have been renamed for consistent `UPPER_SNAKE_CASE` or to fix typos. The tables below list every rename. |
| 197 | + |
| 198 | +### UIClass |
| 199 | + |
| 200 | +??? note "Renamed members" |
| 201 | + |
| 202 | + | v1 | v2 | Note | |
| 203 | + |----|-----|------| |
| 204 | + | `VENTILATION_SYTEM` | `VENTILATION_SYSTEM` | Typo fix | |
| 205 | + |
| 206 | +### UIWidget |
| 207 | + |
| 208 | +??? note "Renamed members" |
| 209 | + |
| 210 | + | v1 | v2 | Note | |
| 211 | + |----|-----|------| |
| 212 | + | `CYCLIC_SWINGING_GATE_OPENER` | `CYCLIC_SWINGING_GATE_OPENER` | Trailing space removed from value | |
| 213 | + | `DIMMER_RGBCOLOURED_LIGHT` | `DIMMER_RGB_COLOURED_LIGHT` | | |
| 214 | + | `EWATTCH_TICCOUNTER` | `EWATTCH_TIC_COUNTER` | | |
| 215 | + | `GENERIC_16_CHANNELS_COUNTER` | `GENERIC16_CHANNELS_COUNTER` | | |
| 216 | + | `GENERIC_1_CHANNEL_COUNTER` | `GENERIC1_CHANNEL_COUNTER` | | |
| 217 | + | `IOGENERIC` | `IO_GENERIC` | | |
| 218 | + | `IOSIREN` | `IO_SIREN` | | |
| 219 | + | `IOSTACK` | `IO_STACK` | | |
| 220 | + | `IRBLASTER` | `IR_BLASTER` | | |
| 221 | + | `JSWCAMERA` | `JSW_CAMERA` | | |
| 222 | + | `OPEN_CLOSE_GATE_4T` | `OPEN_CLOSE_GATE4_T` | | |
| 223 | + | `OPEN_CLOSE_SLIDING_GARAGE_DOOR_4T` | `OPEN_CLOSE_SLIDING_GARAGE_DOOR4_T` | | |
| 224 | + | `OPEN_CLOSE_SLIDING_GATE_4T` | `OPEN_CLOSE_SLIDING_GATE4_T` | | |
| 225 | + | `OVPGENERIC` | `OVP_GENERIC` | | |
| 226 | + | `RTS_GENERIC_4T` | `RTS_GENERIC4_T` | | |
| 227 | + | `ROCKER_SWITCHX_1_CONTROLLER` | `ROCKER_SWITCHX1_CONTROLLER` | | |
| 228 | + | `ROCKER_SWITCHX_2_CONTROLLER` | `ROCKER_SWITCHX2_CONTROLLER` | | |
| 229 | + | `ROCKER_SWITCHX_4_CONTROLLER` | `ROCKER_SWITCHX4_CONTROLLER` | | |
| 230 | + | `TSKALARM_CONTROLLER` | `TSK_ALARM_CONTROLLER` | | |
| 231 | + | `UP_DOWN_GARAGE_DOOR_4T` | `UP_DOWN_GARAGE_DOOR4_T` | | |
| 232 | + | `VOCSENSOR` | `VOC_SENSOR` | | |
| 233 | + | `ZWAVE_DANFOSS_RSLINK` | `ZWAVE_DANFOSS_RS_LINK` | | |
| 234 | + | `ZWAVE_SEDEVICE_CONFIGURATION` | `ZWAVE_SE_DEVICE_CONFIGURATION` | | |
| 235 | + |
| 236 | +### Protocol |
| 237 | + |
| 238 | +??? note "New members" |
| 239 | + |
| 240 | + | Member | Value | |
| 241 | + |--------|-------| |
| 242 | + | `SONOS` | `sonos` | |
| 243 | + |
| 244 | +### GatewaySubType |
| 245 | + |
| 246 | +??? note "New members" |
| 247 | + |
| 248 | + | Member | Value | |
| 249 | + |--------|-------| |
| 250 | + | `TAHOMA_BOX_C_IO` | `17` | |
| 251 | + |
| 252 | +### New enums in v2 |
| 253 | + |
| 254 | +- `UIProfile` — auto-generated from server definitions. |
| 255 | +- `UIClassifier` — categorizes device types (e.g. `SENSOR`, `EMITTER`, `GENERATOR`). |
| 256 | +- `ExecutionState` — typed states for `Execution.state`. |
| 257 | +- `UpdateCriticityLevel` — criticity level of gateway updates. |
| 258 | + |
| 259 | +## Other changes |
| 260 | + |
| 261 | +| v1 | v2 | |
| 262 | +|----|-----| |
| 263 | +| `Setup.id` always set | `Setup.id` is `None` for Local API | |
| 264 | +| `get_diagnostic_data()` | `get_diagnostic_data(mask_sensitive_data=True)` — opt out of PII masking | |
| 265 | + |
| 266 | +## New features in v2 |
| 267 | + |
| 268 | +These are not breaking, but worth knowing about when migrating: |
| 269 | + |
| 270 | +- **Action queue** — batch device executions automatically. See the [action queue guide](action-queue.md). |
| 271 | +- **Reference endpoints** — query server metadata: `get_reference_ui_classes()`, `get_reference_ui_widgets()`, `get_reference_ui_profile()`, `get_reference_controllable_types()`, etc. |
| 272 | +- **Firmware management** — `get_devices_not_up_to_date()`, `get_device_firmware_status()`, `update_device_firmware()`. |
| 273 | +- **boto3 lazy import** — `boto3` is only imported when the Nexity auth strategy is actually used. |
0 commit comments