axis 59__tar.gz → 61__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. {axis-59 → axis-61}/PKG-INFO +3 -3
  2. {axis-59 → axis-61}/axis/__main__.py +1 -1
  3. {axis-59 → axis-61}/axis/errors.py +7 -2
  4. {axis-59 → axis-61}/axis/interfaces/api_handler.py +1 -2
  5. {axis-59 → axis-61}/axis/interfaces/applications/motion_guard.py +1 -1
  6. {axis-59 → axis-61}/axis/interfaces/light_control.py +35 -45
  7. {axis-59 → axis-61}/axis/interfaces/mqtt.py +7 -14
  8. {axis-59 → axis-61}/axis/interfaces/port_management.py +1 -1
  9. {axis-59 → axis-61}/axis/interfaces/vapix.py +13 -8
  10. {axis-59 → axis-61}/axis/interfaces/view_areas.py +1 -1
  11. {axis-59 → axis-61}/axis/models/light_control.py +9 -7
  12. {axis-59 → axis-61}/axis/models/mqtt.py +178 -19
  13. {axis-59 → axis-61}/axis/models/parameters/properties.py +5 -4
  14. {axis-59 → axis-61}/axis/rtsp.py +19 -19
  15. {axis-59 → axis-61}/axis/stream_manager.py +2 -1
  16. {axis-59 → axis-61}/axis.egg-info/PKG-INFO +3 -3
  17. {axis-59 → axis-61}/axis.egg-info/requires.txt +3 -3
  18. {axis-59 → axis-61}/pyproject.toml +53 -35
  19. {axis-59 → axis-61}/tests/test_light_control.py +43 -25
  20. {axis-59 → axis-61}/tests/test_mqtt.py +4 -1
  21. {axis-59 → axis-61}/tests/test_user_groups.py +6 -3
  22. {axis-59 → axis-61}/tests/test_vapix.py +4 -2
  23. {axis-59 → axis-61}/LICENSE +0 -0
  24. {axis-59 → axis-61}/README.md +0 -0
  25. {axis-59 → axis-61}/axis/__init__.py +0 -0
  26. {axis-59 → axis-61}/axis/device.py +0 -0
  27. {axis-59 → axis-61}/axis/interfaces/__init__.py +0 -0
  28. {axis-59 → axis-61}/axis/interfaces/api_discovery.py +0 -0
  29. {axis-59 → axis-61}/axis/interfaces/applications/__init__.py +0 -0
  30. {axis-59 → axis-61}/axis/interfaces/applications/application_handler.py +0 -0
  31. {axis-59 → axis-61}/axis/interfaces/applications/applications.py +0 -0
  32. {axis-59 → axis-61}/axis/interfaces/applications/fence_guard.py +0 -0
  33. {axis-59 → axis-61}/axis/interfaces/applications/loitering_guard.py +0 -0
  34. {axis-59 → axis-61}/axis/interfaces/applications/object_analytics.py +0 -0
  35. {axis-59 → axis-61}/axis/interfaces/applications/vmd4.py +0 -0
  36. {axis-59 → axis-61}/axis/interfaces/basic_device_info.py +0 -0
  37. {axis-59 → axis-61}/axis/interfaces/event_instances.py +0 -0
  38. {axis-59 → axis-61}/axis/interfaces/event_manager.py +0 -0
  39. {axis-59 → axis-61}/axis/interfaces/parameters/__init__.py +0 -0
  40. {axis-59 → axis-61}/axis/interfaces/parameters/brand.py +0 -0
  41. {axis-59 → axis-61}/axis/interfaces/parameters/image.py +0 -0
  42. {axis-59 → axis-61}/axis/interfaces/parameters/io_port.py +0 -0
  43. {axis-59 → axis-61}/axis/interfaces/parameters/param_cgi.py +0 -0
  44. {axis-59 → axis-61}/axis/interfaces/parameters/param_handler.py +0 -0
  45. {axis-59 → axis-61}/axis/interfaces/parameters/properties.py +0 -0
  46. {axis-59 → axis-61}/axis/interfaces/parameters/ptz.py +0 -0
  47. {axis-59 → axis-61}/axis/interfaces/parameters/stream_profile.py +0 -0
  48. {axis-59 → axis-61}/axis/interfaces/pir_sensor_configuration.py +0 -0
  49. {axis-59 → axis-61}/axis/interfaces/port_cgi.py +0 -0
  50. {axis-59 → axis-61}/axis/interfaces/ptz.py +0 -0
  51. {axis-59 → axis-61}/axis/interfaces/pwdgrp_cgi.py +0 -0
  52. {axis-59 → axis-61}/axis/interfaces/stream_profiles.py +0 -0
  53. {axis-59 → axis-61}/axis/interfaces/user_groups.py +0 -0
  54. {axis-59 → axis-61}/axis/models/__init__.py +0 -0
  55. {axis-59 → axis-61}/axis/models/api.py +0 -0
  56. {axis-59 → axis-61}/axis/models/api_discovery.py +0 -0
  57. {axis-59 → axis-61}/axis/models/applications/__init__.py +0 -0
  58. {axis-59 → axis-61}/axis/models/applications/application.py +0 -0
  59. {axis-59 → axis-61}/axis/models/applications/fence_guard.py +0 -0
  60. {axis-59 → axis-61}/axis/models/applications/loitering_guard.py +0 -0
  61. {axis-59 → axis-61}/axis/models/applications/motion_guard.py +0 -0
  62. {axis-59 → axis-61}/axis/models/applications/object_analytics.py +0 -0
  63. {axis-59 → axis-61}/axis/models/applications/vmd4.py +0 -0
  64. {axis-59 → axis-61}/axis/models/basic_device_info.py +0 -0
  65. {axis-59 → axis-61}/axis/models/configuration.py +0 -0
  66. {axis-59 → axis-61}/axis/models/event.py +0 -0
  67. {axis-59 → axis-61}/axis/models/event_instance.py +0 -0
  68. {axis-59 → axis-61}/axis/models/parameters/__init__.py +0 -0
  69. {axis-59 → axis-61}/axis/models/parameters/brand.py +0 -0
  70. {axis-59 → axis-61}/axis/models/parameters/image.py +0 -0
  71. {axis-59 → axis-61}/axis/models/parameters/io_port.py +0 -0
  72. {axis-59 → axis-61}/axis/models/parameters/param_cgi.py +0 -0
  73. {axis-59 → axis-61}/axis/models/parameters/ptz.py +0 -0
  74. {axis-59 → axis-61}/axis/models/parameters/stream_profile.py +0 -0
  75. {axis-59 → axis-61}/axis/models/pir_sensor_configuration.py +0 -0
  76. {axis-59 → axis-61}/axis/models/port_cgi.py +0 -0
  77. {axis-59 → axis-61}/axis/models/port_management.py +0 -0
  78. {axis-59 → axis-61}/axis/models/ptz_cgi.py +0 -0
  79. {axis-59 → axis-61}/axis/models/pwdgrp_cgi.py +0 -0
  80. {axis-59 → axis-61}/axis/models/stream_profile.py +0 -0
  81. {axis-59 → axis-61}/axis/models/user_group.py +0 -0
  82. {axis-59 → axis-61}/axis/models/view_area.py +0 -0
  83. {axis-59 → axis-61}/axis/py.typed +0 -0
  84. {axis-59 → axis-61}/axis.egg-info/SOURCES.txt +0 -0
  85. {axis-59 → axis-61}/axis.egg-info/dependency_links.txt +0 -0
  86. {axis-59 → axis-61}/axis.egg-info/entry_points.txt +0 -0
  87. {axis-59 → axis-61}/axis.egg-info/top_level.txt +0 -0
  88. {axis-59 → axis-61}/setup.cfg +0 -0
  89. {axis-59 → axis-61}/tests/test_api_discovery.py +0 -0
  90. {axis-59 → axis-61}/tests/test_api_handler.py +0 -0
  91. {axis-59 → axis-61}/tests/test_basic_device_info.py +0 -0
  92. {axis-59 → axis-61}/tests/test_configuration.py +0 -0
  93. {axis-59 → axis-61}/tests/test_device.py +0 -0
  94. {axis-59 → axis-61}/tests/test_event.py +0 -0
  95. {axis-59 → axis-61}/tests/test_event_instances.py +0 -0
  96. {axis-59 → axis-61}/tests/test_event_stream.py +0 -0
  97. {axis-59 → axis-61}/tests/test_pir_sensor_configuration.py +0 -0
  98. {axis-59 → axis-61}/tests/test_port_cgi.py +0 -0
  99. {axis-59 → axis-61}/tests/test_port_management.py +0 -0
  100. {axis-59 → axis-61}/tests/test_ptz.py +0 -0
  101. {axis-59 → axis-61}/tests/test_pwdgrp_cgi.py +0 -0
  102. {axis-59 → axis-61}/tests/test_rtsp.py +0 -0
  103. {axis-59 → axis-61}/tests/test_stream_manager.py +0 -0
  104. {axis-59 → axis-61}/tests/test_stream_profiles.py +0 -0
  105. {axis-59 → axis-61}/tests/test_view_areas.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: axis
3
- Version: 59
3
+ Version: 61
4
4
  Summary: A Python library for communicating with devices from Axis Communications
5
5
  Author-email: Robert Svensson <Kane610@users.noreply.github.com>
6
6
  License: MIT
@@ -12,9 +12,9 @@ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Topic :: Home Automation
17
- Requires-Python: >=3.11.0
17
+ Requires-Python: >=3.12.0
18
18
  Description-Content-Type: text/markdown
19
19
  Provides-Extra: requirements
20
20
  Provides-Extra: requirements_test
@@ -20,7 +20,7 @@ async def axis_device(
20
20
  host: str, port: int, username: str, password: str
21
21
  ) -> axis.device.AxisDevice:
22
22
  """Create a Axis device."""
23
- session = AsyncClient(verify=False)
23
+ session = AsyncClient(verify=False) # noqa: S501
24
24
  device = axis.device.AxisDevice(
25
25
  axis.models.configuration.Configuration(
26
26
  session, host, port=port, username=username, password=password
@@ -36,10 +36,15 @@ class PathNotFound(AxisException):
36
36
  """Path not found."""
37
37
 
38
38
 
39
- ERRORS = {401: Unauthorized, 403: Forbidden, 404: PathNotFound, 405: MethodNotAllowed}
39
+ ERRORS = {
40
+ 401: Unauthorized,
41
+ 403: Forbidden,
42
+ 404: PathNotFound,
43
+ 405: MethodNotAllowed,
44
+ }
40
45
 
41
46
 
42
47
  def raise_error(error: int) -> None:
43
48
  """Raise error."""
44
49
  cls = ERRORS.get(error, AxisException)
45
- raise cls(f"{error}")
50
+ raise cls(error)
@@ -1,6 +1,5 @@
1
1
  """API handler class and base class for an API endpoint."""
2
2
 
3
- from abc import ABC
4
3
  from collections.abc import (
5
4
  Callable,
6
5
  ItemsView,
@@ -26,7 +25,7 @@ UnsubscribeType = Callable[[], None]
26
25
  ID_FILTER_ALL = "*"
27
26
 
28
27
 
29
- class SubscriptionHandler(ABC):
28
+ class SubscriptionHandler:
30
29
  """Manage subscription and notification to subscribers."""
31
30
 
32
31
  def __init__(self) -> None:
@@ -2,7 +2,7 @@
2
2
 
3
3
  AXIS Motion Guard is a video motion detection application that detects
4
4
  and triggers an alarm whenever an object, such as a person or vehicle,
5
- moves within predefined areas in a cameras field of view.
5
+ moves within predefined areas in a camera's field of view.
6
6
  """
7
7
 
8
8
  from ...models.applications.application import ApplicationName
@@ -67,62 +67,60 @@ class LightHandler(ApiHandler[LightInformation]):
67
67
 
68
68
  async def get_light_information(self) -> dict[str, LightInformation]:
69
69
  """List the light control information."""
70
- assert isinstance(self.api_version, str)
71
70
  bytes_data = await self.vapix.api_request(
72
- GetLightInformationRequest(api_version=self.api_version)
71
+ GetLightInformationRequest(api_version=self.default_api_version)
73
72
  )
74
73
  return GetLightInformationResponse.decode(bytes_data).data
75
74
 
76
75
  async def get_service_capabilities(self) -> ServiceCapabilities:
77
76
  """List the light control information."""
78
- assert isinstance(self.api_version, str)
79
77
  bytes_data = await self.vapix.api_request(
80
- GetServiceCapabilitiesRequest(api_version=self.api_version)
78
+ GetServiceCapabilitiesRequest(api_version=self.default_api_version)
81
79
  )
82
80
  return GetServiceCapabilitiesResponse.decode(bytes_data).data
83
81
 
84
82
  async def activate_light(self, light_id: str) -> None:
85
83
  """Activate the light."""
86
- assert isinstance(self.api_version, str)
87
84
  await self.vapix.api_request(
88
- ActivateLightRequest(api_version=self.api_version, light_id=light_id)
85
+ ActivateLightRequest(
86
+ api_version=self.default_api_version, light_id=light_id
87
+ )
89
88
  )
90
89
 
91
90
  async def deactivate_light(self, light_id: str) -> None:
92
91
  """Deactivate the light."""
93
- assert isinstance(self.api_version, str)
94
92
  await self.vapix.api_request(
95
- DeactivateLightRequest(api_version=self.api_version, light_id=light_id)
93
+ DeactivateLightRequest(
94
+ api_version=self.default_api_version, light_id=light_id
95
+ )
96
96
  )
97
97
 
98
98
  async def enable_light(self, light_id: str) -> None:
99
99
  """Activate the light."""
100
- assert isinstance(self.api_version, str)
101
100
  await self.vapix.api_request(
102
- EnableLightRequest(api_version=self.api_version, light_id=light_id)
101
+ EnableLightRequest(api_version=self.default_api_version, light_id=light_id)
103
102
  )
104
103
 
105
104
  async def disable_light(self, light_id: str) -> None:
106
105
  """Deactivate the light."""
107
- assert isinstance(self.api_version, str)
108
106
  await self.vapix.api_request(
109
- DisableLightRequest(api_version=self.api_version, light_id=light_id)
107
+ DisableLightRequest(api_version=self.default_api_version, light_id=light_id)
110
108
  )
111
109
 
112
110
  async def get_light_status(self, light_id: str) -> bool:
113
111
  """Get light status if its on or off."""
114
- assert isinstance(self.api_version, str)
115
112
  bytes_data = await self.vapix.api_request(
116
- GetLightStatusRequest(api_version=self.api_version, light_id=light_id)
113
+ GetLightStatusRequest(
114
+ api_version=self.default_api_version, light_id=light_id
115
+ )
117
116
  )
118
117
  return GetLightStatusResponse.decode(bytes_data).data
119
118
 
120
119
  async def set_automatic_intensity_mode(self, light_id: str, enabled: bool) -> None:
121
120
  """Enable the automatic light intensity control."""
122
- assert isinstance(self.api_version, str)
123
121
  await self.vapix.api_request(
124
122
  SetAutomaticIntensityModeRequest(
125
- api_version=self.api_version,
123
+ api_version=self.default_api_version,
126
124
  light_id=light_id,
127
125
  enabled=enabled,
128
126
  )
@@ -130,18 +128,18 @@ class LightHandler(ApiHandler[LightInformation]):
130
128
 
131
129
  async def get_valid_intensity(self, light_id: str) -> Range:
132
130
  """Get valid intensity range for light."""
133
- assert isinstance(self.api_version, str)
134
131
  bytes_data = await self.vapix.api_request(
135
- GetValidIntensityRequest(api_version=self.api_version, light_id=light_id)
132
+ GetValidIntensityRequest(
133
+ api_version=self.default_api_version, light_id=light_id
134
+ )
136
135
  )
137
136
  return GetValidIntensityResponse.decode(bytes_data).data
138
137
 
139
138
  async def set_manual_intensity(self, light_id: str, intensity: int) -> None:
140
139
  """Manually sets the intensity."""
141
- assert isinstance(self.api_version, str)
142
140
  await self.vapix.api_request(
143
141
  SetManualIntensityRequest(
144
- api_version=self.api_version,
142
+ api_version=self.default_api_version,
145
143
  light_id=light_id,
146
144
  intensity=intensity,
147
145
  )
@@ -149,9 +147,10 @@ class LightHandler(ApiHandler[LightInformation]):
149
147
 
150
148
  async def get_manual_intensity(self, light_id: str) -> int:
151
149
  """Enable the automatic light intensity control."""
152
- assert isinstance(self.api_version, str)
153
150
  bytes_data = await self.vapix.api_request(
154
- GetManualIntensityRequest(api_version=self.api_version, light_id=light_id)
151
+ GetManualIntensityRequest(
152
+ api_version=self.default_api_version, light_id=light_id
153
+ )
155
154
  )
156
155
  return GetManualIntensityResponse.decode(bytes_data).data
157
156
 
@@ -159,10 +158,9 @@ class LightHandler(ApiHandler[LightInformation]):
159
158
  self, light_id: str, led_id: int, intensity: int
160
159
  ) -> None:
161
160
  """Manually sets the intensity for an individual LED."""
162
- assert isinstance(self.api_version, str)
163
161
  await self.vapix.api_request(
164
162
  SetIndividualIntensityRequest(
165
- api_version=self.api_version,
163
+ api_version=self.default_api_version,
166
164
  light_id=light_id,
167
165
  led_id=led_id,
168
166
  intensity=intensity,
@@ -171,10 +169,9 @@ class LightHandler(ApiHandler[LightInformation]):
171
169
 
172
170
  async def get_individual_intensity(self, light_id: str, led_id: int) -> int:
173
171
  """Receives the intensity from the setIndividualIntensity request."""
174
- assert isinstance(self.api_version, str)
175
172
  bytes_data = await self.vapix.api_request(
176
173
  GetIndividualIntensityRequest(
177
- api_version=self.api_version,
174
+ api_version=self.default_api_version,
178
175
  light_id=light_id,
179
176
  led_id=led_id,
180
177
  )
@@ -183,9 +180,10 @@ class LightHandler(ApiHandler[LightInformation]):
183
180
 
184
181
  async def get_current_intensity(self, light_id: str) -> int:
185
182
  """Receives the intensity from the setIndividualIntensity request."""
186
- assert isinstance(self.api_version, str)
187
183
  bytes_data = await self.vapix.api_request(
188
- GetCurrentIntensityRequest(api_version=self.api_version, light_id=light_id)
184
+ GetCurrentIntensityRequest(
185
+ api_version=self.default_api_version, light_id=light_id
186
+ )
189
187
  )
190
188
  return GetCurrentIntensityResponse.decode(bytes_data).data
191
189
 
@@ -195,21 +193,19 @@ class LightHandler(ApiHandler[LightInformation]):
195
193
  """Automatically control the angle of illumination.
196
194
 
197
195
  Using this mode means that the angle of illumination
198
- is the same as the cameras angle of view.
196
+ is the same as the camera's angle of view.
199
197
  """
200
- assert isinstance(self.api_version, str)
201
198
  await self.vapix.api_request(
202
199
  SetAutomaticAngleOfIlluminationModeRequest(
203
- api_version=self.api_version, light_id=light_id, enabled=enabled
200
+ api_version=self.default_api_version, light_id=light_id, enabled=enabled
204
201
  )
205
202
  )
206
203
 
207
204
  async def get_valid_angle_of_illumination(self, light_id: str) -> list[Range]:
208
205
  """List the valid angle of illumination values."""
209
- assert isinstance(self.api_version, str)
210
206
  bytes_data = await self.vapix.api_request(
211
207
  GetValidAngleOfIlluminationRequest(
212
- api_version=self.api_version, light_id=light_id
208
+ api_version=self.default_api_version, light_id=light_id
213
209
  )
214
210
  )
215
211
  return GetValidAngleOfIlluminationResponse.decode(bytes_data).data
@@ -220,12 +216,11 @@ class LightHandler(ApiHandler[LightInformation]):
220
216
  """Set the manual angle of illumination.
221
217
 
222
218
  This is useful when the angle of illumination needs
223
- to be different from the cameras view angle.
219
+ to be different from the camera's view angle.
224
220
  """
225
- assert isinstance(self.api_version, str)
226
221
  await self.vapix.api_request(
227
222
  SetManualAngleOfIlluminationModeRequest(
228
- api_version=self.api_version,
223
+ api_version=self.default_api_version,
229
224
  light_id=light_id,
230
225
  angle_of_illumination=angle_of_illumination,
231
226
  )
@@ -233,20 +228,18 @@ class LightHandler(ApiHandler[LightInformation]):
233
228
 
234
229
  async def get_manual_angle_of_illumination(self, light_id: str) -> int:
235
230
  """Get the angle of illumination."""
236
- assert isinstance(self.api_version, str)
237
231
  bytes_data = await self.vapix.api_request(
238
232
  GetManualAngleOfIlluminationRequest(
239
- api_version=self.api_version, light_id=light_id
233
+ api_version=self.default_api_version, light_id=light_id
240
234
  )
241
235
  )
242
236
  return GetManualAngleOfIlluminationResponse.decode(bytes_data).data
243
237
 
244
238
  async def get_current_angle_of_illumination(self, light_id: str) -> int:
245
239
  """Receive the current angle of illumination."""
246
- assert isinstance(self.api_version, str)
247
240
  bytes_data = await self.vapix.api_request(
248
241
  GetCurrentAngleOfIlluminationRequest(
249
- api_version=self.api_version, light_id=light_id
242
+ api_version=self.default_api_version, light_id=light_id
250
243
  )
251
244
  )
252
245
  return GetCurrentAngleOfIlluminationResponse.decode(bytes_data).data
@@ -255,25 +248,22 @@ class LightHandler(ApiHandler[LightInformation]):
255
248
  self, light_id: str, enabled: bool
256
249
  ) -> None:
257
250
  """Enable automatic synchronization with the day/night mode."""
258
- assert isinstance(self.api_version, str)
259
251
  await self.vapix.api_request(
260
252
  SetLightSynchronizeDayNightModeRequest(
261
- api_version=self.api_version, light_id=light_id, enabled=enabled
253
+ api_version=self.default_api_version, light_id=light_id, enabled=enabled
262
254
  )
263
255
  )
264
256
 
265
257
  async def get_light_synchronization_day_night_mode(self, light_id: str) -> bool:
266
258
  """Check if the automatic synchronization is enabled with the day/night mode."""
267
- assert isinstance(self.api_version, str)
268
259
  bytes_data = await self.vapix.api_request(
269
260
  GetLightSynchronizeDayNightModeRequest(
270
- api_version=self.api_version, light_id=light_id
261
+ api_version=self.default_api_version, light_id=light_id
271
262
  )
272
263
  )
273
264
  return GetLightSynchronizeDayNightModeResponse.decode(bytes_data).data
274
265
 
275
266
  async def get_supported_versions(self) -> list[str]:
276
267
  """List supported API versions."""
277
- assert isinstance(self.api_version, str)
278
268
  bytes_data = await self.vapix.api_request(GetSupportedVersionsRequest())
279
269
  return GetSupportedVersionsResponse.decode(bytes_data).data
@@ -6,6 +6,7 @@ import orjson
6
6
 
7
7
  from ..models.api_discovery import ApiId
8
8
  from ..models.mqtt import (
9
+ API_VERSION,
9
10
  ActivateClientRequest,
10
11
  ClientConfig,
11
12
  ClientConfigStatus,
@@ -21,8 +22,6 @@ from ..models.mqtt import (
21
22
  )
22
23
  from .api_handler import ApiHandler
23
24
 
24
- API_VERSION = "1.0"
25
-
26
25
  DEFAULT_TOPICS = ["//."]
27
26
 
28
27
 
@@ -56,41 +55,36 @@ class MqttClientHandler(ApiHandler[Any]):
56
55
 
57
56
  async def configure_client(self, client_config: ClientConfig) -> None:
58
57
  """Configure MQTT Client."""
59
- discovery_item = self.vapix.api_discovery[self.api_id]
60
58
  await self.vapix.api_request(
61
59
  ConfigureClientRequest(
62
- api_version=discovery_item.version, client_config=client_config
60
+ api_version=self.default_api_version, client_config=client_config
63
61
  )
64
62
  )
65
63
 
66
64
  async def activate(self) -> None:
67
65
  """Activate MQTT Client."""
68
- discovery_item = self.vapix.api_discovery[self.api_id]
69
66
  await self.vapix.api_request(
70
- ActivateClientRequest(api_version=discovery_item.version)
67
+ ActivateClientRequest(api_version=self.default_api_version)
71
68
  )
72
69
 
73
70
  async def deactivate(self) -> None:
74
71
  """Deactivate MQTT Client."""
75
- discovery_item = self.vapix.api_discovery[self.api_id]
76
72
  await self.vapix.api_request(
77
- DeactivateClientRequest(api_version=discovery_item.version)
73
+ DeactivateClientRequest(api_version=self.default_api_version)
78
74
  )
79
75
 
80
76
  async def get_client_status(self) -> ClientConfigStatus:
81
77
  """Get MQTT Client status."""
82
- discovery_item = self.vapix.api_discovery[self.api_id]
83
78
  bytes_data = await self.vapix.api_request(
84
- GetClientStatusRequest(api_version=discovery_item.version)
79
+ GetClientStatusRequest(api_version=self.default_api_version)
85
80
  )
86
81
  response = GetClientStatusResponse.decode(bytes_data)
87
82
  return response.data
88
83
 
89
84
  async def get_event_publication_config(self) -> EventPublicationConfig:
90
85
  """Get MQTT Client event publication config."""
91
- discovery_item = self.vapix.api_discovery[self.api_id]
92
86
  bytes_data = await self.vapix.api_request(
93
- GetEventPublicationConfigRequest(api_version=discovery_item.version)
87
+ GetEventPublicationConfigRequest(api_version=self.default_api_version)
94
88
  )
95
89
  response = GetEventPublicationConfigResponse.decode(bytes_data)
96
90
  return response.data
@@ -99,13 +93,12 @@ class MqttClientHandler(ApiHandler[Any]):
99
93
  self, topics: list[str] = DEFAULT_TOPICS
100
94
  ) -> None:
101
95
  """Configure MQTT Client event publication."""
102
- discovery_item = self.vapix.api_discovery[self.api_id]
103
96
  event_filters = EventFilter.from_list(
104
97
  [{"topicFilter": topic} for topic in topics]
105
98
  )
106
99
  config = EventPublicationConfig(event_filter_list=event_filters)
107
100
  await self.vapix.api_request(
108
101
  ConfigureEventPublicationRequest(
109
- api_version=discovery_item.version, config=config
102
+ api_version=self.default_api_version, config=config
110
103
  )
111
104
  )
@@ -45,7 +45,7 @@ class IoPortManagement(ApiHandler[Port]):
45
45
  * Configuring the states and what constitutes a normal
46
46
  and triggered state respectively.
47
47
  This will make triggers activate in either open or closed circuits.
48
- The reason the change is treated as a nice name is because it doesnt
48
+ The reason the change is treated as a nice name is because it doesn't
49
49
  affect the underlying behavior of the port.
50
50
  Devices with configurable ports can change the direction
51
51
  to either input or output.
@@ -1,5 +1,7 @@
1
1
  """Python library to enable Axis devices to integrate with Home Assistant."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import asyncio
4
6
  import logging
5
7
  from typing import TYPE_CHECKING, Any
@@ -7,11 +9,8 @@ from typing import TYPE_CHECKING, Any
7
9
  import httpx
8
10
 
9
11
  from ..errors import RequestError, raise_error
10
- from ..models.api import ApiRequest
11
12
  from ..models.pwdgrp_cgi import SecondaryGroup
12
- from ..models.stream_profile import StreamProfile
13
13
  from .api_discovery import ApiDiscoveryHandler
14
- from .api_handler import ApiHandler
15
14
  from .applications import (
16
15
  ApplicationsHandler,
17
16
  )
@@ -40,6 +39,9 @@ from .view_areas import ViewAreaHandler
40
39
 
41
40
  if TYPE_CHECKING:
42
41
  from ..device import AxisDevice
42
+ from ..models.api import ApiRequest
43
+ from ..models.stream_profile import StreamProfile
44
+ from .api_handler import ApiHandler
43
45
 
44
46
  LOGGER = logging.getLogger(__name__)
45
47
 
@@ -49,7 +51,7 @@ TIME_OUT = 15
49
51
  class Vapix:
50
52
  """Vapix parameter request."""
51
53
 
52
- def __init__(self, device: "AxisDevice") -> None:
54
+ def __init__(self, device: AxisDevice) -> None:
53
55
  """Store local reference to device config."""
54
56
  self.device = device
55
57
  self.auth = httpx.DigestAuth(device.config.username, device.config.password)
@@ -274,16 +276,19 @@ class Vapix:
274
276
  timeout=TIME_OUT,
275
277
  )
276
278
 
277
- except httpx.TimeoutException:
278
- raise RequestError("Timeout")
279
+ except httpx.TimeoutException as errt:
280
+ message = "Timeout"
281
+ raise RequestError(message) from errt
279
282
 
280
283
  except httpx.TransportError as errc:
281
284
  LOGGER.debug("%s", errc)
282
- raise RequestError(f"Connection error: {errc}")
285
+ message = f"Connection error: {errc}"
286
+ raise RequestError(message) from errc
283
287
 
284
288
  except httpx.RequestError as err:
285
289
  LOGGER.debug("%s", err)
286
- raise RequestError(f"Unknown error: {err}")
290
+ message = f"Unknown error: {err}"
291
+ raise RequestError(message) from err
287
292
 
288
293
  try:
289
294
  response.raise_for_status()
@@ -1,6 +1,6 @@
1
1
  """View area API.
2
2
 
3
- The View Area API makes it possible to define the subsections of a cameras full view
3
+ The View Area API makes it possible to define the subsections of a camera's full view
4
4
  as individual, virtual channels. This means that a wide angle and/or high resolution
5
5
  camera can provide multiple video streams at a lower resolution where each stream
6
6
  covers a specific region of interest. The API is also able to simplify the installation
@@ -1,5 +1,7 @@
1
1
  """Light Control API data model."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from dataclasses import dataclass
4
6
  from typing import NotRequired, Self
5
7
 
@@ -225,14 +227,14 @@ class Range:
225
227
  high: int
226
228
 
227
229
  @classmethod
228
- def from_dict(cls, data: RangeT) -> "Range":
230
+ def from_dict(cls, data: RangeT) -> Self:
229
231
  """Create range object from dict."""
230
- return Range(low=data["low"], high=data["high"])
232
+ return cls(low=data["low"], high=data["high"])
231
233
 
232
234
  @classmethod
233
- def from_list(cls, data: list[RangeT]) -> list["Range"]:
235
+ def from_list(cls, data: list[RangeT]) -> list[Self]:
234
236
  """Create range object from dict."""
235
- return [Range.from_dict(range) for range in data]
237
+ return [cls.from_dict(range) for range in data]
236
238
 
237
239
 
238
240
  @dataclass
@@ -277,7 +279,7 @@ class GetLightInformationResponse(ApiResponse[dict[str, LightInformation]]):
277
279
  api_version=data["apiVersion"],
278
280
  context=data["context"],
279
281
  method=data["method"],
280
- data=LightInformation.decode_to_dict(data["data"]["items"]),
282
+ data=LightInformation.decode_to_dict(data.get("data", {}).get("items", [])),
281
283
  )
282
284
 
283
285
 
@@ -294,9 +296,9 @@ class ServiceCapabilities:
294
296
  day_night_synchronize_support: bool
295
297
 
296
298
  @classmethod
297
- def from_dict(cls, data: ServiceCapabilitiesT) -> "ServiceCapabilities":
299
+ def from_dict(cls, data: ServiceCapabilitiesT) -> Self:
298
300
  """Create service capabilities object from dict."""
299
- return ServiceCapabilities(
301
+ return cls(
300
302
  automatic_intensity_support=data["automaticIntensitySupport"],
301
303
  manual_intensity_support=data["manualIntensitySupport"],
302
304
  get_current_intensity_support=data["getCurrentIntensitySupport"],