uiprotect 2.2.0__py3-none-any.whl → 3.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of uiprotect might be problematic. Click here for more details.
- uiprotect/api.py +13 -27
- uiprotect/websocket.py +10 -6
- {uiprotect-2.2.0.dist-info → uiprotect-3.0.0.dist-info}/METADATA +1 -1
- {uiprotect-2.2.0.dist-info → uiprotect-3.0.0.dist-info}/RECORD +7 -7
- {uiprotect-2.2.0.dist-info → uiprotect-3.0.0.dist-info}/LICENSE +0 -0
- {uiprotect-2.2.0.dist-info → uiprotect-3.0.0.dist-info}/WHEEL +0 -0
- {uiprotect-2.2.0.dist-info → uiprotect-3.0.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -75,7 +75,6 @@ if sys.version_info[:2] < (3, 13):
|
|
|
75
75
|
|
|
76
76
|
TOKEN_COOKIE_MAX_EXP_SECONDS = 60
|
|
77
77
|
|
|
78
|
-
NEVER_RAN = -1000
|
|
79
78
|
# how many seconds before the bootstrap is refreshed from Protect
|
|
80
79
|
DEVICE_UPDATE_INTERVAL = 900
|
|
81
80
|
# retry timeout for thumbnails/heatmaps
|
|
@@ -164,7 +163,6 @@ class BaseApiClient:
|
|
|
164
163
|
_ws_timeout: int
|
|
165
164
|
|
|
166
165
|
_is_authenticated: bool = False
|
|
167
|
-
_last_update: float = NEVER_RAN
|
|
168
166
|
_last_ws_status: bool = False
|
|
169
167
|
_last_token_cookie: Morsel[str] | None = None
|
|
170
168
|
_last_token_cookie_decode: dict[str, Any] | None = None
|
|
@@ -289,7 +287,7 @@ class BaseApiClient:
|
|
|
289
287
|
# since the lastUpdateId is not valid anymore
|
|
290
288
|
if self._update_task and not self._update_task.done():
|
|
291
289
|
return
|
|
292
|
-
self._update_task = asyncio.create_task(self.update(
|
|
290
|
+
self._update_task = asyncio.create_task(self.update())
|
|
293
291
|
|
|
294
292
|
async def close_session(self) -> None:
|
|
295
293
|
"""Closing and deletes client session"""
|
|
@@ -699,7 +697,7 @@ class BaseApiClient:
|
|
|
699
697
|
def _get_last_update_id(self) -> str | None:
|
|
700
698
|
raise NotImplementedError
|
|
701
699
|
|
|
702
|
-
async def update(self
|
|
700
|
+
async def update(self) -> Bootstrap:
|
|
703
701
|
raise NotImplementedError
|
|
704
702
|
|
|
705
703
|
|
|
@@ -710,8 +708,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
710
708
|
UniFi Protect is a full async application. "normal" use of interacting with it is
|
|
711
709
|
to call `.update()` which will initialize the `.bootstrap` and create a Websocket
|
|
712
710
|
connection to UFP. This Websocket connection will emit messages that will automatically
|
|
713
|
-
update the `.bootstrap` over time.
|
|
714
|
-
verify the integry of the Websocket connection.
|
|
711
|
+
update the `.bootstrap` over time.
|
|
715
712
|
|
|
716
713
|
You can use the `.get_` methods to one off pull devices from the UFP API, but should
|
|
717
714
|
not be used for building an aplication on top of.
|
|
@@ -816,32 +813,21 @@ class ProtectApiClient(BaseApiClient):
|
|
|
816
813
|
|
|
817
814
|
return self._connection_host
|
|
818
815
|
|
|
819
|
-
async def update(self
|
|
816
|
+
async def update(self) -> Bootstrap:
|
|
820
817
|
"""
|
|
821
|
-
Updates the state of devices,
|
|
822
|
-
|
|
818
|
+
Updates the state of devices, initializes `.bootstrap`
|
|
819
|
+
|
|
820
|
+
The websocket is auto connected once there are any
|
|
821
|
+
subscriptions to it. update must be called at least
|
|
822
|
+
once before subscribing to the websocket.
|
|
823
823
|
|
|
824
824
|
You can use the various other `get_` methods if you need one off data from UFP
|
|
825
825
|
"""
|
|
826
826
|
async with self._update_lock:
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
bootstrap_updated = False
|
|
832
|
-
if (
|
|
833
|
-
self._bootstrap is None
|
|
834
|
-
or now - self._last_update > DEVICE_UPDATE_INTERVAL
|
|
835
|
-
):
|
|
836
|
-
bootstrap_updated = True
|
|
837
|
-
self._bootstrap = await self.get_bootstrap()
|
|
838
|
-
self.__dict__.pop("bootstrap", None)
|
|
839
|
-
self._last_update = now
|
|
840
|
-
|
|
841
|
-
if bootstrap_updated:
|
|
842
|
-
return None
|
|
843
|
-
self._last_update = now
|
|
844
|
-
return self._bootstrap
|
|
827
|
+
bootstrap = await self.get_bootstrap()
|
|
828
|
+
self.__dict__.pop("bootstrap", None)
|
|
829
|
+
self._bootstrap = bootstrap
|
|
830
|
+
return bootstrap
|
|
845
831
|
|
|
846
832
|
async def poll_events(self) -> None:
|
|
847
833
|
"""Poll for events."""
|
uiprotect/websocket.py
CHANGED
|
@@ -19,6 +19,8 @@ from aiohttp import (
|
|
|
19
19
|
)
|
|
20
20
|
from yarl import URL
|
|
21
21
|
|
|
22
|
+
from .exceptions import NotAuthorized, NvrError
|
|
23
|
+
|
|
22
24
|
_LOGGER = logging.getLogger(__name__)
|
|
23
25
|
AuthCallbackType = Callable[..., Coroutine[Any, Any, Optional[dict[str, str]]]]
|
|
24
26
|
GetSessionCallbackType = Callable[[], Awaitable[ClientSession]]
|
|
@@ -79,7 +81,7 @@ class Websocket:
|
|
|
79
81
|
_LOGGER.log(
|
|
80
82
|
level, "Websocket authentication error: %s: %s", url, ex
|
|
81
83
|
)
|
|
82
|
-
await self.
|
|
84
|
+
await self._attempt_auth(True)
|
|
83
85
|
else:
|
|
84
86
|
_LOGGER.log(level, "Websocket handshake error: %s: %s", url, ex)
|
|
85
87
|
else:
|
|
@@ -97,7 +99,7 @@ class Websocket:
|
|
|
97
99
|
|
|
98
100
|
async def _websocket_inner_loop(self, url: URL) -> None:
|
|
99
101
|
_LOGGER.debug("Connecting WS to %s", url)
|
|
100
|
-
|
|
102
|
+
await self._attempt_auth(False)
|
|
101
103
|
ssl = None if self.verify else False
|
|
102
104
|
msg: WSMessage | None = None
|
|
103
105
|
self._seen_non_close_message = False
|
|
@@ -140,12 +142,14 @@ class Websocket:
|
|
|
140
142
|
await self._ws_connection.close()
|
|
141
143
|
self._ws_connection = None
|
|
142
144
|
|
|
143
|
-
async def
|
|
144
|
-
"""Attempt to
|
|
145
|
+
async def _attempt_auth(self, force: bool) -> None:
|
|
146
|
+
"""Attempt to authenticate."""
|
|
145
147
|
try:
|
|
146
|
-
self._headers = await self._auth(
|
|
148
|
+
self._headers = await self._auth(force)
|
|
149
|
+
except (NotAuthorized, NvrError) as ex:
|
|
150
|
+
_LOGGER.debug("Error authenticating websocket: %s", ex)
|
|
147
151
|
except Exception:
|
|
148
|
-
_LOGGER.exception("
|
|
152
|
+
_LOGGER.exception("Unknown error authenticating websocket")
|
|
149
153
|
|
|
150
154
|
def start(self) -> None:
|
|
151
155
|
"""Start the websocket."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
uiprotect/__init__.py,sha256=UdpRSSLSy7pdDfTKf0zRIfy6KRGt_Jv-fMzYWgibbG4,686
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
|
-
uiprotect/api.py,sha256=
|
|
3
|
+
uiprotect/api.py,sha256=2IV1DjkWbv4aAesGnVJkzgT5Y9DdRmIAll2FB68Jf6Q,66830
|
|
4
4
|
uiprotect/cli/__init__.py,sha256=1MO8rJmjjAsfVx2x01gn5DJo8B64xdPGo6gRVJbWd18,8868
|
|
5
5
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
6
6
|
uiprotect/cli/base.py,sha256=k-_qGuNT7br0iV0KE5F4wYXF75iyLLjBEckTqxC71xM,7591
|
|
@@ -29,9 +29,9 @@ uiprotect/stream.py,sha256=McV3XymKyjn-1uV5jdQHcpaDjqLS4zWyMASQ8ubcyb4,4924
|
|
|
29
29
|
uiprotect/test_util/__init__.py,sha256=whiOUb5LfDLNT3AQG6ISiKtAqO2JnhCIdFavhWDK46M,18718
|
|
30
30
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
31
31
|
uiprotect/utils.py,sha256=3SJFF8qs1Jz8t3mD8qwc1hFSocolFjdXI_v4yVlC7o4,20088
|
|
32
|
-
uiprotect/websocket.py,sha256=
|
|
33
|
-
uiprotect-
|
|
34
|
-
uiprotect-
|
|
35
|
-
uiprotect-
|
|
36
|
-
uiprotect-
|
|
37
|
-
uiprotect-
|
|
32
|
+
uiprotect/websocket.py,sha256=wWcVhMST_hFamOIUQogUytMqwpsvKwKN73BYErYTrJU,6973
|
|
33
|
+
uiprotect-3.0.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-3.0.0.dist-info/METADATA,sha256=slp9fl5MLh2zPWhVqhgwXczlmNS2ZtN2cGHZUDkIfJA,10982
|
|
35
|
+
uiprotect-3.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-3.0.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|