axis 60__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-60 → axis-61}/PKG-INFO +3 -3
  2. {axis-60 → axis-61}/axis/__main__.py +1 -1
  3. {axis-60 → axis-61}/axis/errors.py +7 -2
  4. {axis-60 → axis-61}/axis/interfaces/api_handler.py +1 -2
  5. {axis-60 → axis-61}/axis/interfaces/applications/motion_guard.py +1 -1
  6. {axis-60 → axis-61}/axis/interfaces/light_control.py +35 -45
  7. {axis-60 → axis-61}/axis/interfaces/port_management.py +1 -1
  8. {axis-60 → axis-61}/axis/interfaces/vapix.py +10 -7
  9. {axis-60 → axis-61}/axis/interfaces/view_areas.py +1 -1
  10. {axis-60 → axis-61}/axis/models/mqtt.py +2 -2
  11. {axis-60 → axis-61}/axis/models/parameters/properties.py +2 -2
  12. {axis-60 → axis-61}/axis/rtsp.py +19 -19
  13. {axis-60 → axis-61}/axis/stream_manager.py +2 -1
  14. {axis-60 → axis-61}/axis.egg-info/PKG-INFO +3 -3
  15. {axis-60 → axis-61}/axis.egg-info/requires.txt +1 -1
  16. {axis-60 → axis-61}/pyproject.toml +51 -33
  17. {axis-60 → axis-61}/tests/test_light_control.py +25 -25
  18. {axis-60 → axis-61}/tests/test_user_groups.py +6 -3
  19. {axis-60 → axis-61}/tests/test_vapix.py +4 -2
  20. {axis-60 → axis-61}/LICENSE +0 -0
  21. {axis-60 → axis-61}/README.md +0 -0
  22. {axis-60 → axis-61}/axis/__init__.py +0 -0
  23. {axis-60 → axis-61}/axis/device.py +0 -0
  24. {axis-60 → axis-61}/axis/interfaces/__init__.py +0 -0
  25. {axis-60 → axis-61}/axis/interfaces/api_discovery.py +0 -0
  26. {axis-60 → axis-61}/axis/interfaces/applications/__init__.py +0 -0
  27. {axis-60 → axis-61}/axis/interfaces/applications/application_handler.py +0 -0
  28. {axis-60 → axis-61}/axis/interfaces/applications/applications.py +0 -0
  29. {axis-60 → axis-61}/axis/interfaces/applications/fence_guard.py +0 -0
  30. {axis-60 → axis-61}/axis/interfaces/applications/loitering_guard.py +0 -0
  31. {axis-60 → axis-61}/axis/interfaces/applications/object_analytics.py +0 -0
  32. {axis-60 → axis-61}/axis/interfaces/applications/vmd4.py +0 -0
  33. {axis-60 → axis-61}/axis/interfaces/basic_device_info.py +0 -0
  34. {axis-60 → axis-61}/axis/interfaces/event_instances.py +0 -0
  35. {axis-60 → axis-61}/axis/interfaces/event_manager.py +0 -0
  36. {axis-60 → axis-61}/axis/interfaces/mqtt.py +0 -0
  37. {axis-60 → axis-61}/axis/interfaces/parameters/__init__.py +0 -0
  38. {axis-60 → axis-61}/axis/interfaces/parameters/brand.py +0 -0
  39. {axis-60 → axis-61}/axis/interfaces/parameters/image.py +0 -0
  40. {axis-60 → axis-61}/axis/interfaces/parameters/io_port.py +0 -0
  41. {axis-60 → axis-61}/axis/interfaces/parameters/param_cgi.py +0 -0
  42. {axis-60 → axis-61}/axis/interfaces/parameters/param_handler.py +0 -0
  43. {axis-60 → axis-61}/axis/interfaces/parameters/properties.py +0 -0
  44. {axis-60 → axis-61}/axis/interfaces/parameters/ptz.py +0 -0
  45. {axis-60 → axis-61}/axis/interfaces/parameters/stream_profile.py +0 -0
  46. {axis-60 → axis-61}/axis/interfaces/pir_sensor_configuration.py +0 -0
  47. {axis-60 → axis-61}/axis/interfaces/port_cgi.py +0 -0
  48. {axis-60 → axis-61}/axis/interfaces/ptz.py +0 -0
  49. {axis-60 → axis-61}/axis/interfaces/pwdgrp_cgi.py +0 -0
  50. {axis-60 → axis-61}/axis/interfaces/stream_profiles.py +0 -0
  51. {axis-60 → axis-61}/axis/interfaces/user_groups.py +0 -0
  52. {axis-60 → axis-61}/axis/models/__init__.py +0 -0
  53. {axis-60 → axis-61}/axis/models/api.py +0 -0
  54. {axis-60 → axis-61}/axis/models/api_discovery.py +0 -0
  55. {axis-60 → axis-61}/axis/models/applications/__init__.py +0 -0
  56. {axis-60 → axis-61}/axis/models/applications/application.py +0 -0
  57. {axis-60 → axis-61}/axis/models/applications/fence_guard.py +0 -0
  58. {axis-60 → axis-61}/axis/models/applications/loitering_guard.py +0 -0
  59. {axis-60 → axis-61}/axis/models/applications/motion_guard.py +0 -0
  60. {axis-60 → axis-61}/axis/models/applications/object_analytics.py +0 -0
  61. {axis-60 → axis-61}/axis/models/applications/vmd4.py +0 -0
  62. {axis-60 → axis-61}/axis/models/basic_device_info.py +0 -0
  63. {axis-60 → axis-61}/axis/models/configuration.py +0 -0
  64. {axis-60 → axis-61}/axis/models/event.py +0 -0
  65. {axis-60 → axis-61}/axis/models/event_instance.py +0 -0
  66. {axis-60 → axis-61}/axis/models/light_control.py +0 -0
  67. {axis-60 → axis-61}/axis/models/parameters/__init__.py +0 -0
  68. {axis-60 → axis-61}/axis/models/parameters/brand.py +0 -0
  69. {axis-60 → axis-61}/axis/models/parameters/image.py +0 -0
  70. {axis-60 → axis-61}/axis/models/parameters/io_port.py +0 -0
  71. {axis-60 → axis-61}/axis/models/parameters/param_cgi.py +0 -0
  72. {axis-60 → axis-61}/axis/models/parameters/ptz.py +0 -0
  73. {axis-60 → axis-61}/axis/models/parameters/stream_profile.py +0 -0
  74. {axis-60 → axis-61}/axis/models/pir_sensor_configuration.py +0 -0
  75. {axis-60 → axis-61}/axis/models/port_cgi.py +0 -0
  76. {axis-60 → axis-61}/axis/models/port_management.py +0 -0
  77. {axis-60 → axis-61}/axis/models/ptz_cgi.py +0 -0
  78. {axis-60 → axis-61}/axis/models/pwdgrp_cgi.py +0 -0
  79. {axis-60 → axis-61}/axis/models/stream_profile.py +0 -0
  80. {axis-60 → axis-61}/axis/models/user_group.py +0 -0
  81. {axis-60 → axis-61}/axis/models/view_area.py +0 -0
  82. {axis-60 → axis-61}/axis/py.typed +0 -0
  83. {axis-60 → axis-61}/axis.egg-info/SOURCES.txt +0 -0
  84. {axis-60 → axis-61}/axis.egg-info/dependency_links.txt +0 -0
  85. {axis-60 → axis-61}/axis.egg-info/entry_points.txt +0 -0
  86. {axis-60 → axis-61}/axis.egg-info/top_level.txt +0 -0
  87. {axis-60 → axis-61}/setup.cfg +0 -0
  88. {axis-60 → axis-61}/tests/test_api_discovery.py +0 -0
  89. {axis-60 → axis-61}/tests/test_api_handler.py +0 -0
  90. {axis-60 → axis-61}/tests/test_basic_device_info.py +0 -0
  91. {axis-60 → axis-61}/tests/test_configuration.py +0 -0
  92. {axis-60 → axis-61}/tests/test_device.py +0 -0
  93. {axis-60 → axis-61}/tests/test_event.py +0 -0
  94. {axis-60 → axis-61}/tests/test_event_instances.py +0 -0
  95. {axis-60 → axis-61}/tests/test_event_stream.py +0 -0
  96. {axis-60 → axis-61}/tests/test_mqtt.py +0 -0
  97. {axis-60 → axis-61}/tests/test_pir_sensor_configuration.py +0 -0
  98. {axis-60 → axis-61}/tests/test_port_cgi.py +0 -0
  99. {axis-60 → axis-61}/tests/test_port_management.py +0 -0
  100. {axis-60 → axis-61}/tests/test_ptz.py +0 -0
  101. {axis-60 → axis-61}/tests/test_pwdgrp_cgi.py +0 -0
  102. {axis-60 → axis-61}/tests/test_rtsp.py +0 -0
  103. {axis-60 → axis-61}/tests/test_stream_manager.py +0 -0
  104. {axis-60 → axis-61}/tests/test_stream_profiles.py +0 -0
  105. {axis-60 → 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: 60
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
@@ -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.
@@ -9,11 +9,8 @@ from typing import TYPE_CHECKING, Any
9
9
  import httpx
10
10
 
11
11
  from ..errors import RequestError, raise_error
12
- from ..models.api import ApiRequest
13
12
  from ..models.pwdgrp_cgi import SecondaryGroup
14
- from ..models.stream_profile import StreamProfile
15
13
  from .api_discovery import ApiDiscoveryHandler
16
- from .api_handler import ApiHandler
17
14
  from .applications import (
18
15
  ApplicationsHandler,
19
16
  )
@@ -42,6 +39,9 @@ from .view_areas import ViewAreaHandler
42
39
 
43
40
  if TYPE_CHECKING:
44
41
  from ..device import AxisDevice
42
+ from ..models.api import ApiRequest
43
+ from ..models.stream_profile import StreamProfile
44
+ from .api_handler import ApiHandler
45
45
 
46
46
  LOGGER = logging.getLogger(__name__)
47
47
 
@@ -276,16 +276,19 @@ class Vapix:
276
276
  timeout=TIME_OUT,
277
277
  )
278
278
 
279
- except httpx.TimeoutException:
280
- raise RequestError("Timeout")
279
+ except httpx.TimeoutException as errt:
280
+ message = "Timeout"
281
+ raise RequestError(message) from errt
281
282
 
282
283
  except httpx.TransportError as errc:
283
284
  LOGGER.debug("%s", errc)
284
- raise RequestError(f"Connection error: {errc}")
285
+ message = f"Connection error: {errc}"
286
+ raise RequestError(message) from errc
285
287
 
286
288
  except httpx.RequestError as err:
287
289
  LOGGER.debug("%s", err)
288
- raise RequestError(f"Unknown error: {err}")
290
+ message = f"Unknown error: {err}"
291
+ raise RequestError(message) from err
289
292
 
290
293
  try:
291
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
@@ -341,7 +341,7 @@ class ClientConfig:
341
341
  """Specifies if a message should be sent when a connection is established.
342
342
 
343
343
  Contains options related to connect announcements.
344
- If this object is not defined this message wont be sent.
344
+ If this object is not defined this message won't be sent.
345
345
  """
346
346
  connect_timeout: int | None = None
347
347
  """The timed interval (in seconds) to allow a connect to finish.
@@ -359,7 +359,7 @@ class ClientConfig:
359
359
  """Specifies if a message should be sent when the client is manually disconnected.
360
360
 
361
361
  Contains options related to manual disconnect announcements.
362
- If this object is not defined this message wont be sent.
362
+ If this object is not defined this message won't be sent.
363
363
  This message should not be confused with LWT,
364
364
  as it is used when the connection is lost and managed by the broker.
365
365
  """
@@ -182,8 +182,8 @@ class PropertyParam(ParamItem):
182
182
  return cls(
183
183
  id="properties",
184
184
  api_http_version=data["API"]["HTTP"]["Version"],
185
- api_metadata=data["API"]["Metadata"]["Metadata"],
186
- api_metadata_version=data["API"]["Metadata"]["Version"],
185
+ api_metadata=data["API"].get("Metadata", {}).get("Metadata", "no"),
186
+ api_metadata_version=data["API"].get("Metadata", {}).get("Version", "0.0"),
187
187
  api_ptz_presets_version=data["API"]
188
188
  .get("PTZ", {})
189
189
  .get("Presets", {})
@@ -361,11 +361,11 @@ class RTSPSession:
361
361
  """RFC 2617."""
362
362
  from hashlib import md5
363
363
 
364
- ha1 = f"{self.username}:{self.realm}:{self.password}"
365
- HA1 = md5(ha1.encode("UTF-8")).hexdigest()
366
- ha2 = f"{self.method}:{self.url}"
367
- HA2 = md5(ha2.encode("UTF-8")).hexdigest()
368
- encrypt_response = f"{HA1}:{self.nonce}:{HA2}"
364
+ _ha1 = f"{self.username}:{self.realm}:{self.password}"
365
+ ha1 = md5(_ha1.encode("UTF-8")).hexdigest()
366
+ _ha2 = f"{self.method}:{self.url}"
367
+ ha2 = md5(_ha2.encode("UTF-8")).hexdigest()
368
+ encrypt_response = f"{ha1}:{self.nonce}:{ha2}"
369
369
  response = md5(encrypt_response.encode("UTF-8")).hexdigest()
370
370
 
371
371
  digest_auth = "Digest "
@@ -399,12 +399,12 @@ class RTSPMethods:
399
399
  """Define message methods."""
400
400
  self.session = session
401
401
  self.message_methods: dict[str, Callable[[], str]] = {
402
- "OPTIONS": self.OPTIONS,
403
- "DESCRIBE": self.DESCRIBE,
404
- "SETUP": self.SETUP,
405
- "PLAY": self.PLAY,
406
- "KEEP-ALIVE": self.KEEP_ALIVE,
407
- "TEARDOWN": self.TEARDOWN,
402
+ "OPTIONS": self.options,
403
+ "DESCRIBE": self.describe,
404
+ "SETUP": self.setup,
405
+ "PLAY": self.play,
406
+ "KEEP-ALIVE": self.keep_alive,
407
+ "TEARDOWN": self.teardown,
408
408
  }
409
409
 
410
410
  @property
@@ -414,11 +414,11 @@ class RTSPMethods:
414
414
  _LOGGER.debug(message)
415
415
  return message
416
416
 
417
- def KEEP_ALIVE(self) -> str:
417
+ def keep_alive(self) -> str:
418
418
  """Keep-Alive messages doesn't need authentication."""
419
- return self.OPTIONS(False)
419
+ return self.options(False)
420
420
 
421
- def OPTIONS(self, authenticate: bool = True) -> str:
421
+ def options(self, authenticate: bool = True) -> str:
422
422
  """Request options device supports."""
423
423
  message = f"OPTIONS {self.session.url} RTSP/1.0\r\n"
424
424
  message += self.sequence
@@ -428,7 +428,7 @@ class RTSPMethods:
428
428
  message += "\r\n"
429
429
  return message
430
430
 
431
- def DESCRIBE(self) -> str:
431
+ def describe(self) -> str:
432
432
  """Request description of what services RTSP server make available."""
433
433
  message = f"DESCRIBE {self.session.url} RTSP/1.0\r\n"
434
434
  message += self.sequence
@@ -438,7 +438,7 @@ class RTSPMethods:
438
438
  message += "\r\n"
439
439
  return message
440
440
 
441
- def SETUP(self) -> str:
441
+ def setup(self) -> str:
442
442
  """Set up stream transport."""
443
443
  message = f"SETUP {self.session.control_url} RTSP/1.0\r\n"
444
444
  message += self.sequence
@@ -448,7 +448,7 @@ class RTSPMethods:
448
448
  message += "\r\n"
449
449
  return message
450
450
 
451
- def PLAY(self) -> str:
451
+ def play(self) -> str:
452
452
  """RTSP session is ready to send data."""
453
453
  message = f"PLAY {self.session.url} RTSP/1.0\r\n"
454
454
  message += self.sequence
@@ -458,7 +458,7 @@ class RTSPMethods:
458
458
  message += "\r\n"
459
459
  return message
460
460
 
461
- def TEARDOWN(self) -> str:
461
+ def teardown(self) -> str:
462
462
  """Tell device to tear down session."""
463
463
  message = f"TEARDOWN {self.session.url} RTSP/1.0\r\n"
464
464
  message += self.sequence
@@ -471,7 +471,7 @@ class RTSPMethods:
471
471
  @property
472
472
  def sequence(self) -> str:
473
473
  """Generate sequence string."""
474
- return f"CSeq: {str(self.session.sequence)}\r\n"
474
+ return f"CSeq: {self.session.sequence}\r\n"
475
475
 
476
476
  @property
477
477
  def authentication(self) -> str:
@@ -1,13 +1,14 @@
1
1
  """Python library to enable Axis devices to integrate with Home Assistant."""
2
2
 
3
3
  import asyncio
4
- from collections.abc import Callable
5
4
  import logging
6
5
  from typing import TYPE_CHECKING
7
6
 
8
7
  from .rtsp import RTSPClient, Signal, State
9
8
 
10
9
  if TYPE_CHECKING:
10
+ from collections.abc import Callable
11
+
11
12
  from .device import AxisDevice
12
13
 
13
14
  _LOGGER = logging.getLogger(__name__)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: axis
3
- Version: 60
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
@@ -19,6 +19,6 @@ pytest-aiohttp==1.0.5
19
19
  pytest-asyncio==0.23.6
20
20
  pytest-cov==5.0.0
21
21
  respx==0.21.1
22
- ruff==0.3.4
22
+ ruff==0.3.5
23
23
  types-orjson==3.6.2
24
24
  types-xmltodict==v0.13.0.3