uiprotect 1.12.1__py3-none-any.whl → 1.14.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 CHANGED
@@ -228,6 +228,7 @@ class BaseApiClient:
228
228
 
229
229
  @property
230
230
  def ws_url(self) -> str:
231
+ """Get Websocket URL."""
231
232
  url = f"wss://{self._host}"
232
233
  if self._port != 443:
233
234
  url += f":{self._port}"
@@ -267,7 +268,7 @@ class BaseApiClient:
267
268
 
268
269
  if self._websocket is None:
269
270
  self._websocket = Websocket(
270
- self.ws_url,
271
+ self.get_websocket_url,
271
272
  _auth,
272
273
  verify=self._verify_ssl,
273
274
  timeout=self._ws_timeout,
@@ -626,9 +627,6 @@ class BaseApiClient:
626
627
  self._websocket = None
627
628
 
628
629
  websocket = await self.get_websocket()
629
- # important to make sure WS URL is always current
630
- websocket.url = self.ws_url
631
-
632
630
  if not websocket.is_connected:
633
631
  self._last_ws_status = False
634
632
  with contextlib.suppress(
@@ -638,6 +636,10 @@ class BaseApiClient:
638
636
  ):
639
637
  await websocket.connect()
640
638
 
639
+ def get_websocket_url(self) -> str:
640
+ """Get Websocket URL."""
641
+ return self.ws_url
642
+
641
643
  async def async_disconnect_ws(self) -> None:
642
644
  """Disconnect from Websocket."""
643
645
  if self._websocket is None:
@@ -861,10 +863,6 @@ class ProtectApiClient(BaseApiClient):
861
863
  models=self._subscribed_models,
862
864
  ignore_stats=self._ignore_stats,
863
865
  )
864
- # update websocket URL after every message to ensure the latest last_update_id
865
- if self._websocket is not None:
866
- self._websocket.url = self.ws_url
867
-
868
866
  if processed_message is None:
869
867
  return
870
868
 
uiprotect/data/base.py CHANGED
@@ -485,68 +485,44 @@ class ProtectBaseObject(BaseModel):
485
485
 
486
486
  return new_data
487
487
 
488
- def _inject_api(
489
- self,
490
- data: dict[str, Any],
491
- api: ProtectApiClient | None,
492
- ) -> dict[str, Any]:
493
- data["api"] = api
494
- unifi_objs_sets = self._get_protect_objs_set()
495
- has_unifi_objs = bool(unifi_objs_sets)
496
- unifi_lists_sets = self._get_protect_lists_set()
497
- has_unifi_lists = bool(unifi_lists_sets)
498
- unifi_dicts_sets = self._get_protect_dicts_set()
499
- has_unifi_dicts = bool(unifi_dicts_sets)
500
- for key, value in data.items():
501
- if has_unifi_objs and key in unifi_objs_sets and isinstance(value, dict):
502
- value["api"] = api
503
- elif (
504
- has_unifi_lists and key in unifi_lists_sets and isinstance(value, list)
505
- ):
506
- for item in value:
507
- if isinstance(item, dict):
508
- item["api"] = api
509
- elif (
510
- has_unifi_dicts and key in unifi_dicts_sets and isinstance(value, dict)
511
- ):
512
- for item in value.values():
513
- if isinstance(item, dict):
514
- item["api"] = api
488
+ def update_from_dict(cls: ProtectObject, data: dict[str, Any]) -> ProtectObject:
489
+ """
490
+ Updates current object from a cleaned UFP JSON dict.
515
491
 
516
- return data
492
+ The api client is injected into each dict for any child
493
+ UFP objects that are detected.
494
+ """
495
+ unifi_objs = cls._get_protect_objs()
496
+ has_unifi_objs = bool(unifi_objs)
497
+ unifi_lists = cls._get_protect_lists()
498
+ has_unifi_lists = bool(unifi_lists)
517
499
 
518
- def update_from_dict(self: ProtectObject, data: dict[str, Any]) -> ProtectObject:
519
- """Updates current object from a cleaned UFP JSON dict"""
520
- data_set = set(data)
521
- for key in self._get_protect_objs_set().intersection(data_set):
522
- unifi_obj: Any | None = getattr(self, key)
523
- if unifi_obj is not None and isinstance(unifi_obj, ProtectBaseObject):
524
- item = data.pop(key)
525
- if item is not None:
526
- item = unifi_obj.update_from_dict(item)
527
- setattr(self, key, item)
528
-
529
- data = self._inject_api(data, self._api)
530
- unifi_lists = self._get_protect_lists()
531
- for key in self._get_protect_lists_set().intersection(data_set):
532
- if not isinstance(data[key], list):
533
- continue
534
- klass = unifi_lists[key]
535
- new_items = []
536
- for item in data.pop(key):
537
- if item is not None and isinstance(item, ProtectBaseObject):
538
- new_items.append(item)
539
- elif isinstance(item, dict):
540
- new_items.append(klass(**item))
541
- setattr(self, key, new_items)
542
-
543
- # Always injected above
544
- del data["api"]
545
-
546
- for key in data:
547
- setattr(self, key, convert_unifi_data(data[key], self.__fields__[key]))
548
-
549
- return self
500
+ api = cls._api
501
+ _fields = cls.__fields__
502
+ unifi_obj: ProtectBaseObject | None
503
+ if "api" in data:
504
+ del data["api"]
505
+ value: Any
506
+
507
+ for key, item in data.items():
508
+ if has_unifi_objs and key in unifi_objs and isinstance(item, dict):
509
+ if (unifi_obj := getattr(cls, key)) is not None:
510
+ value = unifi_obj.update_from_dict(item)
511
+ else:
512
+ value = None
513
+ elif has_unifi_lists and key in unifi_lists and isinstance(item, list):
514
+ klass = unifi_lists[key]
515
+ value = [
516
+ klass(**i, api=api) if isinstance(i, dict) else i
517
+ for i in item
518
+ if i is not None and isinstance(i, (dict, ProtectBaseObject))
519
+ ]
520
+ else:
521
+ value = convert_unifi_data(item, _fields[key])
522
+
523
+ setattr(cls, key, value)
524
+
525
+ return cls
550
526
 
551
527
  def dict_with_excludes(self) -> dict[str, Any]:
552
528
  """Returns a dict of the current object without any UFP objects converted to dicts."""
uiprotect/websocket.py CHANGED
@@ -45,7 +45,7 @@ class Websocket:
45
45
 
46
46
  def __init__(
47
47
  self,
48
- url: str,
48
+ get_url: Callable[[], str],
49
49
  auth_callback: CALLBACK_TYPE,
50
50
  *,
51
51
  timeout: int = 30,
@@ -53,7 +53,7 @@ class Websocket:
53
53
  verify: bool = True,
54
54
  ) -> None:
55
55
  """Init Websocket."""
56
- self.url = url
56
+ self.get_url = get_url
57
57
  self.timeout_interval = timeout
58
58
  self.backoff = backoff
59
59
  self.verify = verify
@@ -85,14 +85,15 @@ class Websocket:
85
85
  return True
86
86
 
87
87
  async def _websocket_loop(self, start_event: asyncio.Event) -> None:
88
- _LOGGER.debug("Connecting WS to %s", self.url)
88
+ url = self.get_url()
89
+ _LOGGER.debug("Connecting WS to %s", url)
89
90
  self._headers = await self._auth(self._should_reset_auth)
90
91
 
91
92
  session = self._get_session()
92
93
  # catch any and all errors for Websocket so we can clean up correctly
93
94
  try:
94
95
  self._ws_connection = await session.ws_connect(
95
- self.url,
96
+ url,
96
97
  ssl=None if self.verify else False,
97
98
  headers=self._headers,
98
99
  )
@@ -104,7 +105,7 @@ class Websocket:
104
105
  break
105
106
  self._reset_timeout()
106
107
  except ClientError:
107
- _LOGGER.exception("Websocket disconnect error: %s", self.url)
108
+ _LOGGER.exception("Websocket disconnect error: %s", url)
108
109
  finally:
109
110
  _LOGGER.debug("Websocket disconnected")
110
111
  self._increase_failure()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: uiprotect
3
- Version: 1.12.1
3
+ Version: 1.14.0
4
4
  Summary: Python API for Unifi Protect (Unofficial)
5
5
  Home-page: https://github.com/uilibs/uiprotect
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  uiprotect/__init__.py,sha256=llnQNtiBfwQG8IkQXovvFz4LZeFjrJx7XdmmUhu3a9E,289
2
2
  uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
3
- uiprotect/api.py,sha256=t3E1wMja0av6ENtvXJHGv70WC6NCCWbF_wFIJxOLS0I,66463
3
+ uiprotect/api.py,sha256=zVKTMieBqx3EM1sex5zzAj2J2k4pUlQQ_xF8nE6AS5s,66339
4
4
  uiprotect/cli/__init__.py,sha256=sSLW9keVQOkgFcMW18HTDjRrt9sJ0KWjn9DJDA6f9Pc,8658
5
5
  uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
6
6
  uiprotect/cli/base.py,sha256=zpTm2kyJe_GLixnv3Uadke__iRLh64AEwQzp-2hqS7g,7730
@@ -14,7 +14,7 @@ uiprotect/cli/nvr.py,sha256=TwxEg2XT8jXAbOqv6gc7KFXELKadeItEDYweSL4_-e8,4260
14
14
  uiprotect/cli/sensors.py,sha256=fQtcDJCVxs4VbAqcavgBy2ABiVxAW3GXtna6_XFBp2k,8153
15
15
  uiprotect/cli/viewers.py,sha256=2cyrp104ffIvgT0wYGIO0G35QMkEbFe7fSVqLwDXQYQ,2171
16
16
  uiprotect/data/__init__.py,sha256=OcfuJl2qXfHcj_mdnrHhzZ5tEIZrw8auziX5IE7dn-I,2938
17
- uiprotect/data/base.py,sha256=yzGm5RCeo9AqmgArWC8O7LhBp_Qxz1XSel3xRyhNAbM,37718
17
+ uiprotect/data/base.py,sha256=OWKeMWk7n5VaHF_meTt2Ykw7H9zx6U47u-P2vLNDWzE,36627
18
18
  uiprotect/data/bootstrap.py,sha256=wjRFVrv1rOFVDcij6ZYm7dzMe0iz6AqYciQozC-J2TI,21893
19
19
  uiprotect/data/convert.py,sha256=8h6Il_DhMkPRDPj9F_rA2UZIlTuchS3BQD24peKpk2A,2185
20
20
  uiprotect/data/devices.py,sha256=Nq3bOko5PFf5LvEBoD4JV8kmbq50laRdh3VHMWX7t-0,111809
@@ -29,9 +29,9 @@ uiprotect/stream.py,sha256=McV3XymKyjn-1uV5jdQHcpaDjqLS4zWyMASQ8ubcyb4,4924
29
29
  uiprotect/test_util/__init__.py,sha256=d2g7afa0LSdixQ0kjEDYwafDFME_UlW2LzxpamZ2BC0,18556
30
30
  uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
31
31
  uiprotect/utils.py,sha256=6OLY8hNiCzk418PjJJIlFW7jjPzVt1vxBKEzBSqMeTk,18418
32
- uiprotect/websocket.py,sha256=IzDPyqbzrkOMREvahN-e2zdvVD0VABSCWy6jSoCwOT0,7299
33
- uiprotect-1.12.1.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
34
- uiprotect-1.12.1.dist-info/METADATA,sha256=tQiyGoMyj_g6AQLes0kHr4TWIXfENceZ_ksLycnREqI,10985
35
- uiprotect-1.12.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
- uiprotect-1.12.1.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
37
- uiprotect-1.12.1.dist-info/RECORD,,
32
+ uiprotect/websocket.py,sha256=JHI_2EZeRPqPyQopsBZS0dr3tu0HaTiqeLazfBXhW_8,7339
33
+ uiprotect-1.14.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
34
+ uiprotect-1.14.0.dist-info/METADATA,sha256=3CmROnNdNIJkOFAIRb0Ha13iqu67iKaJY0jKIcJdbcQ,10985
35
+ uiprotect-1.14.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
+ uiprotect-1.14.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
37
+ uiprotect-1.14.0.dist-info/RECORD,,