uiprotect 1.5.0__py3-none-any.whl → 1.7.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/data/base.py +67 -90
- uiprotect/data/nvr.py +19 -12
- {uiprotect-1.5.0.dist-info → uiprotect-1.7.0.dist-info}/METADATA +1 -1
- {uiprotect-1.5.0.dist-info → uiprotect-1.7.0.dist-info}/RECORD +7 -7
- {uiprotect-1.5.0.dist-info → uiprotect-1.7.0.dist-info}/LICENSE +0 -0
- {uiprotect-1.5.0.dist-info → uiprotect-1.7.0.dist-info}/WHEEL +0 -0
- {uiprotect-1.5.0.dist-info → uiprotect-1.7.0.dist-info}/entry_points.txt +0 -0
uiprotect/data/base.py
CHANGED
|
@@ -139,34 +139,25 @@ class ProtectBaseObject(BaseModel):
|
|
|
139
139
|
@classmethod
|
|
140
140
|
def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:
|
|
141
141
|
api: ProtectApiClient | None = values.pop("api", None)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (unifi_dicts := cls._get_protect_dicts()) and (
|
|
162
|
-
intersections := cls._get_protect_dicts_set().intersection(values_set)
|
|
163
|
-
):
|
|
164
|
-
for key in intersections:
|
|
165
|
-
if isinstance(values[key], dict):
|
|
166
|
-
values[key] = {
|
|
167
|
-
k: unifi_dicts[key].construct(**v) if isinstance(v, dict) else v
|
|
168
|
-
for k, v in values[key].items()
|
|
169
|
-
}
|
|
142
|
+
unifi_objs = cls._get_protect_objs()
|
|
143
|
+
has_unifi_objs = bool(unifi_objs)
|
|
144
|
+
unifi_lists = cls._get_protect_lists()
|
|
145
|
+
has_unifi_lists = bool(unifi_lists)
|
|
146
|
+
unifi_dicts = cls._get_protect_dicts()
|
|
147
|
+
has_unifi_dicts = bool(unifi_dicts)
|
|
148
|
+
for key, value in values.items():
|
|
149
|
+
if has_unifi_objs and key in unifi_objs and isinstance(value, dict):
|
|
150
|
+
values[key] = unifi_objs[key].construct(**value)
|
|
151
|
+
elif has_unifi_lists and key in unifi_lists and isinstance(value, list):
|
|
152
|
+
values[key] = [
|
|
153
|
+
unifi_lists[key].construct(**v) if isinstance(v, dict) else v
|
|
154
|
+
for v in value
|
|
155
|
+
]
|
|
156
|
+
elif has_unifi_dicts and key in unifi_dicts and isinstance(value, dict):
|
|
157
|
+
values[key] = {
|
|
158
|
+
k: unifi_dicts[key].construct(**v) if isinstance(v, dict) else v
|
|
159
|
+
for k, v in value.items()
|
|
160
|
+
}
|
|
170
161
|
|
|
171
162
|
obj = super().construct(_fields_set=_fields_set, **values)
|
|
172
163
|
if api is not None:
|
|
@@ -341,16 +332,16 @@ class ProtectBaseObject(BaseModel):
|
|
|
341
332
|
cls._api if isinstance(cls, ProtectBaseObject) else None
|
|
342
333
|
)
|
|
343
334
|
|
|
344
|
-
|
|
345
|
-
if (remaps := cls._get_unifi_remaps()) and (
|
|
346
|
-
intersections := cls._get_unifi_remaps_set().intersection(data)
|
|
347
|
-
):
|
|
348
|
-
for from_key in intersections:
|
|
349
|
-
data[remaps[from_key]] = data.pop(from_key)
|
|
350
|
-
|
|
335
|
+
remaps = cls._get_unifi_remaps()
|
|
351
336
|
# convert to snake_case and remove extra fields
|
|
352
337
|
_fields = cls.__fields__
|
|
353
338
|
for key in list(data):
|
|
339
|
+
if key in remaps:
|
|
340
|
+
# remap keys that will not be converted correctly by snake_case convert
|
|
341
|
+
remapped_key = remaps[key]
|
|
342
|
+
data[remapped_key] = data.pop(key)
|
|
343
|
+
key = remapped_key
|
|
344
|
+
|
|
354
345
|
new_key = to_snake_case(key)
|
|
355
346
|
data[new_key] = data.pop(key)
|
|
356
347
|
key = new_key
|
|
@@ -363,36 +354,23 @@ class ProtectBaseObject(BaseModel):
|
|
|
363
354
|
continue
|
|
364
355
|
data[key] = convert_unifi_data(data[key], _fields[key])
|
|
365
356
|
|
|
366
|
-
|
|
367
|
-
|
|
357
|
+
if not data:
|
|
358
|
+
return data
|
|
368
359
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
api,
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
if (unifi_dicts := cls._get_protect_dicts()) and (
|
|
387
|
-
intersections := cls._get_protect_dicts_set().intersection(data_set)
|
|
388
|
-
):
|
|
389
|
-
for key in intersections:
|
|
390
|
-
if isinstance(data[key], dict):
|
|
391
|
-
data[key] = cls._clean_protect_obj_dict(
|
|
392
|
-
data[key],
|
|
393
|
-
unifi_dicts[key],
|
|
394
|
-
api,
|
|
395
|
-
)
|
|
360
|
+
# clean child UFP objs
|
|
361
|
+
unifi_objs = cls._get_protect_objs()
|
|
362
|
+
has_unifi_objs = bool(unifi_objs)
|
|
363
|
+
unifi_lists = cls._get_protect_lists()
|
|
364
|
+
has_unifi_lists = bool(unifi_lists)
|
|
365
|
+
unifi_dicts = cls._get_protect_dicts()
|
|
366
|
+
has_unifi_dicts = bool(unifi_dicts)
|
|
367
|
+
for key, value in data.items():
|
|
368
|
+
if has_unifi_objs and key in unifi_objs:
|
|
369
|
+
data[key] = cls._clean_protect_obj(value, unifi_objs[key], api)
|
|
370
|
+
elif has_unifi_lists and key in unifi_lists and isinstance(value, list):
|
|
371
|
+
data[key] = cls._clean_protect_obj_list(value, unifi_lists[key], api)
|
|
372
|
+
elif has_unifi_dicts and key in unifi_dicts and isinstance(value, dict):
|
|
373
|
+
data[key] = cls._clean_protect_obj_dict(value, unifi_dicts[key], api)
|
|
396
374
|
|
|
397
375
|
return data
|
|
398
376
|
|
|
@@ -518,36 +496,27 @@ class ProtectBaseObject(BaseModel):
|
|
|
518
496
|
api: ProtectApiClient | None,
|
|
519
497
|
) -> dict[str, Any]:
|
|
520
498
|
data["api"] = api
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
for key in intersections:
|
|
535
|
-
new_items = []
|
|
536
|
-
for item in data[key]:
|
|
499
|
+
unifi_objs_sets = self._get_protect_objs_set()
|
|
500
|
+
has_unifi_objs = bool(unifi_objs_sets)
|
|
501
|
+
unifi_lists_sets = self._get_protect_lists_set()
|
|
502
|
+
has_unifi_lists = bool(unifi_lists_sets)
|
|
503
|
+
unifi_dicts_sets = self._get_protect_dicts_set()
|
|
504
|
+
has_unifi_dicts = bool(unifi_dicts_sets)
|
|
505
|
+
for key, value in data.items():
|
|
506
|
+
if has_unifi_objs and key in unifi_objs_sets and isinstance(value, dict):
|
|
507
|
+
value["api"] = api
|
|
508
|
+
elif (
|
|
509
|
+
has_unifi_lists and key in unifi_lists_sets and isinstance(value, list)
|
|
510
|
+
):
|
|
511
|
+
for item in value:
|
|
537
512
|
if isinstance(item, dict):
|
|
538
513
|
item["api"] = api
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
intersections := unifi_dicts_sets.intersection(data_set)
|
|
544
|
-
):
|
|
545
|
-
for key in intersections:
|
|
546
|
-
inner_dict: dict[str, Any] = data[key]
|
|
547
|
-
for item_key, item in inner_dict.items():
|
|
514
|
+
elif (
|
|
515
|
+
has_unifi_dicts and key in unifi_dicts_sets and isinstance(value, dict)
|
|
516
|
+
):
|
|
517
|
+
for item in value.values():
|
|
548
518
|
if isinstance(item, dict):
|
|
549
519
|
item["api"] = api
|
|
550
|
-
inner_dict[item_key] = item
|
|
551
520
|
|
|
552
521
|
return data
|
|
553
522
|
|
|
@@ -776,6 +745,14 @@ class ProtectModelWithId(ProtectModel):
|
|
|
776
745
|
revert_on_fail: bool = True,
|
|
777
746
|
) -> None:
|
|
778
747
|
"""Saves the current device changes to UFP."""
|
|
748
|
+
_LOGGER.debug(
|
|
749
|
+
"Saving device changes for %s (%s) data_before_changes=%s updated=%s",
|
|
750
|
+
self.id,
|
|
751
|
+
self.model,
|
|
752
|
+
data_before_changes,
|
|
753
|
+
updated,
|
|
754
|
+
)
|
|
755
|
+
|
|
779
756
|
assert (
|
|
780
757
|
self._update_lock.locked()
|
|
781
758
|
), "save_device_changes should only be called when the update lock is held"
|
uiprotect/data/nvr.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
7
|
import zoneinfo
|
|
8
|
+
from collections.abc import Callable
|
|
8
9
|
from datetime import datetime, timedelta, tzinfo
|
|
9
10
|
from functools import cache
|
|
10
11
|
from ipaddress import IPv4Address, IPv6Address
|
|
@@ -1169,26 +1170,32 @@ class NVR(ProtectDeviceModel):
|
|
|
1169
1170
|
if message in self.doorbell_settings.custom_messages:
|
|
1170
1171
|
raise BadRequest("Custom doorbell message already exists")
|
|
1171
1172
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
)
|
|
1176
|
-
|
|
1177
|
-
self.doorbell_settings.custom_messages.append(DoorbellText(message))
|
|
1178
|
-
await self.save_device(data_before_changes)
|
|
1179
|
-
self.update_all_messages()
|
|
1173
|
+
await self._update_doorbell_messages(
|
|
1174
|
+
lambda: self.doorbell_settings.custom_messages.append(
|
|
1175
|
+
DoorbellText(message)
|
|
1176
|
+
),
|
|
1177
|
+
)
|
|
1180
1178
|
|
|
1181
1179
|
async def remove_custom_doorbell_message(self, message: str) -> None:
|
|
1182
1180
|
"""Removes custom doorbell message"""
|
|
1183
1181
|
if message not in self.doorbell_settings.custom_messages:
|
|
1184
1182
|
raise BadRequest("Custom doorbell message does not exists")
|
|
1185
1183
|
|
|
1184
|
+
await self._update_doorbell_messages(
|
|
1185
|
+
lambda: self.doorbell_settings.custom_messages.remove(
|
|
1186
|
+
DoorbellText(message)
|
|
1187
|
+
),
|
|
1188
|
+
)
|
|
1189
|
+
|
|
1190
|
+
async def _update_doorbell_messages(
|
|
1191
|
+
self, update_callback: Callable[[], None]
|
|
1192
|
+
) -> None:
|
|
1193
|
+
"""Updates doorbell messages and saves to Protect."""
|
|
1186
1194
|
async with self._update_lock:
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
) # yield to the event loop once we have the look to ensure websocket updates are processed
|
|
1195
|
+
# yield to the event loop once we have the lock to ensure websocket updates are processed
|
|
1196
|
+
await asyncio.sleep(0)
|
|
1190
1197
|
data_before_changes = self.dict_with_excludes()
|
|
1191
|
-
|
|
1198
|
+
update_callback()
|
|
1192
1199
|
await self.save_device(data_before_changes)
|
|
1193
1200
|
self.update_all_messages()
|
|
1194
1201
|
|
|
@@ -14,11 +14,11 @@ 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=
|
|
17
|
+
uiprotect/data/base.py,sha256=McmQHwbb-gY2y9IjQ85HVBE6Vo0WkBzcXt6JDw5HijE,37708
|
|
18
18
|
uiprotect/data/bootstrap.py,sha256=INRUEbLWnsEJuzlcoFlm7RAT-WPx6odje3aD092QKGo,21465
|
|
19
19
|
uiprotect/data/convert.py,sha256=8h6Il_DhMkPRDPj9F_rA2UZIlTuchS3BQD24peKpk2A,2185
|
|
20
20
|
uiprotect/data/devices.py,sha256=Nq3bOko5PFf5LvEBoD4JV8kmbq50laRdh3VHMWX7t-0,111809
|
|
21
|
-
uiprotect/data/nvr.py,sha256=
|
|
21
|
+
uiprotect/data/nvr.py,sha256=XC4NO1c_Mom-hIpzj9ksKFcgKbHd6ToqWjkgzxJ1PJY,47636
|
|
22
22
|
uiprotect/data/types.py,sha256=-hhrnPZheC81eKIJOIZIGCb-A7jOBqa0zLWsmh9_vFQ,17220
|
|
23
23
|
uiprotect/data/user.py,sha256=Wb-ZWSwIJbyUbfVuENtUYbuW-uftHNDcoMH85dvEjkw,7071
|
|
24
24
|
uiprotect/data/websocket.py,sha256=WZJVA7EfYuKYMv-9jmvGgMWXKzE9ES25SKv1NQ2eHjc,6281
|
|
@@ -30,8 +30,8 @@ uiprotect/test_util/__init__.py,sha256=d2g7afa0LSdixQ0kjEDYwafDFME_UlW2LzxpamZ2B
|
|
|
30
30
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
31
31
|
uiprotect/utils.py,sha256=6OLY8hNiCzk418PjJJIlFW7jjPzVt1vxBKEzBSqMeTk,18418
|
|
32
32
|
uiprotect/websocket.py,sha256=IzDPyqbzrkOMREvahN-e2zdvVD0VABSCWy6jSoCwOT0,7299
|
|
33
|
-
uiprotect-1.
|
|
34
|
-
uiprotect-1.
|
|
35
|
-
uiprotect-1.
|
|
36
|
-
uiprotect-1.
|
|
37
|
-
uiprotect-1.
|
|
33
|
+
uiprotect-1.7.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-1.7.0.dist-info/METADATA,sha256=vzHyVx6DDhJCUu03RWBahUFumcx854dPYB_SQMwGztk,10984
|
|
35
|
+
uiprotect-1.7.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-1.7.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-1.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|