uiprotect 1.0.1__py3-none-any.whl → 1.2.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 +87 -62
- uiprotect/data/bootstrap.py +56 -69
- {uiprotect-1.0.1.dist-info → uiprotect-1.2.0.dist-info}/METADATA +1 -1
- {uiprotect-1.0.1.dist-info → uiprotect-1.2.0.dist-info}/RECORD +7 -7
- {uiprotect-1.0.1.dist-info → uiprotect-1.2.0.dist-info}/LICENSE +0 -0
- {uiprotect-1.0.1.dist-info → uiprotect-1.2.0.dist-info}/WHEEL +0 -0
- {uiprotect-1.0.1.dist-info → uiprotect-1.2.0.dist-info}/entry_points.txt +0 -0
uiprotect/data/base.py
CHANGED
|
@@ -141,26 +141,32 @@ class ProtectBaseObject(BaseModel):
|
|
|
141
141
|
api: ProtectApiClient | None = values.pop("api", None)
|
|
142
142
|
values_set = set(values)
|
|
143
143
|
|
|
144
|
-
unifi_objs
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
]
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
144
|
+
if (unifi_objs := cls._get_protect_objs()) and (
|
|
145
|
+
intersections := cls._get_protect_objs_set().intersection(values_set)
|
|
146
|
+
):
|
|
147
|
+
for key in intersections:
|
|
148
|
+
if isinstance(values[key], dict):
|
|
149
|
+
values[key] = unifi_objs[key].construct(**values[key])
|
|
150
|
+
|
|
151
|
+
if (unifi_lists := cls._get_protect_lists()) and (
|
|
152
|
+
intersections := cls._get_protect_lists_set().intersection(values_set)
|
|
153
|
+
):
|
|
154
|
+
for key in intersections:
|
|
155
|
+
if isinstance(values[key], list):
|
|
156
|
+
values[key] = [
|
|
157
|
+
unifi_lists[key].construct(**v) if isinstance(v, dict) else v
|
|
158
|
+
for v in values[key]
|
|
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
|
+
}
|
|
164
170
|
|
|
165
171
|
obj = super().construct(_fields_set=_fields_set, **values)
|
|
166
172
|
if api is not None:
|
|
@@ -336,11 +342,14 @@ class ProtectBaseObject(BaseModel):
|
|
|
336
342
|
)
|
|
337
343
|
|
|
338
344
|
# remap keys that will not be converted correctly by snake_case convert
|
|
339
|
-
remaps
|
|
340
|
-
|
|
341
|
-
|
|
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)
|
|
342
350
|
|
|
343
351
|
# convert to snake_case and remove extra fields
|
|
352
|
+
_fields = cls.__fields__
|
|
344
353
|
for key in list(data):
|
|
345
354
|
new_key = to_snake_case(key)
|
|
346
355
|
data[new_key] = data.pop(key)
|
|
@@ -349,35 +358,41 @@ class ProtectBaseObject(BaseModel):
|
|
|
349
358
|
if key == "api":
|
|
350
359
|
continue
|
|
351
360
|
|
|
352
|
-
if key not in
|
|
361
|
+
if key not in _fields:
|
|
353
362
|
del data[key]
|
|
354
363
|
continue
|
|
355
|
-
data[key] = convert_unifi_data(data[key],
|
|
364
|
+
data[key] = convert_unifi_data(data[key], _fields[key])
|
|
356
365
|
|
|
357
366
|
# clean child UFP objs
|
|
358
367
|
data_set = set(data)
|
|
359
368
|
|
|
360
|
-
unifi_objs
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
for key in cls._get_protect_lists_set().intersection(data_set):
|
|
366
|
-
if isinstance(data[key], list):
|
|
367
|
-
data[key] = cls._clean_protect_obj_list(
|
|
368
|
-
data[key],
|
|
369
|
-
unifi_lists[key],
|
|
370
|
-
api,
|
|
371
|
-
)
|
|
369
|
+
if (unifi_objs := cls._get_protect_objs()) and (
|
|
370
|
+
intersections := cls._get_protect_objs_set().intersection(data_set)
|
|
371
|
+
):
|
|
372
|
+
for key in intersections:
|
|
373
|
+
data[key] = cls._clean_protect_obj(data[key], unifi_objs[key], api)
|
|
372
374
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
375
|
+
if (unifi_lists := cls._get_protect_lists()) and (
|
|
376
|
+
intersections := cls._get_protect_lists_set().intersection(data_set)
|
|
377
|
+
):
|
|
378
|
+
for key in intersections:
|
|
379
|
+
if isinstance(data[key], list):
|
|
380
|
+
data[key] = cls._clean_protect_obj_list(
|
|
381
|
+
data[key],
|
|
382
|
+
unifi_lists[key],
|
|
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
|
+
)
|
|
381
396
|
|
|
382
397
|
return data
|
|
383
398
|
|
|
@@ -505,24 +520,34 @@ class ProtectBaseObject(BaseModel):
|
|
|
505
520
|
data["api"] = api
|
|
506
521
|
data_set = set(data)
|
|
507
522
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
523
|
+
if (unifi_objs_sets := self._get_protect_objs_set()) and (
|
|
524
|
+
intersections := unifi_objs_sets.intersection(data_set)
|
|
525
|
+
):
|
|
526
|
+
for key in intersections:
|
|
527
|
+
unifi_obj: Any | None = getattr(self, key)
|
|
528
|
+
if unifi_obj is not None and isinstance(unifi_obj, dict):
|
|
529
|
+
unifi_obj["api"] = api
|
|
512
530
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
531
|
+
if (unifi_lists_sets := self._get_protect_lists_set()) and (
|
|
532
|
+
intersections := unifi_lists_sets.intersection(data_set)
|
|
533
|
+
):
|
|
534
|
+
for key in intersections:
|
|
535
|
+
new_items = []
|
|
536
|
+
for item in data[key]:
|
|
537
|
+
if isinstance(item, dict):
|
|
538
|
+
item["api"] = api
|
|
539
|
+
new_items.append(item)
|
|
540
|
+
data[key] = new_items
|
|
541
|
+
|
|
542
|
+
if (unifi_dicts_sets := self._get_protect_dicts_set()) and (
|
|
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():
|
|
548
|
+
if isinstance(item, dict):
|
|
549
|
+
item["api"] = api
|
|
550
|
+
inner_dict[item_key] = item
|
|
526
551
|
|
|
527
552
|
return data
|
|
528
553
|
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -22,6 +22,7 @@ from ..utils import normalize_mac, utc_now
|
|
|
22
22
|
from .base import (
|
|
23
23
|
RECENT_EVENT_MAX,
|
|
24
24
|
ProtectBaseObject,
|
|
25
|
+
ProtectDeviceModel,
|
|
25
26
|
ProtectModel,
|
|
26
27
|
ProtectModelWithId,
|
|
27
28
|
)
|
|
@@ -65,6 +66,7 @@ STATS_KEYS = {
|
|
|
65
66
|
"recordingSchedules",
|
|
66
67
|
}
|
|
67
68
|
IGNORE_DEVICE_KEYS = {"nvrMac", "guid"}
|
|
69
|
+
STATS_AND_IGNORE_DEVICE_KEYS = STATS_KEYS | IGNORE_DEVICE_KEYS
|
|
68
70
|
|
|
69
71
|
CAMERA_EVENT_ATTR_MAP: dict[EventType, tuple[str, str]] = {
|
|
70
72
|
EventType.MOTION: ("last_motion", "last_motion_event_id"),
|
|
@@ -78,56 +80,36 @@ CAMERA_EVENT_ATTR_MAP: dict[EventType, tuple[str, str]] = {
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def _remove_stats_keys(data: dict[str, Any]) -> None:
|
|
82
|
-
for key in STATS_KEYS.intersection(data):
|
|
83
|
-
del data[key]
|
|
84
|
-
|
|
85
|
-
|
|
86
83
|
def _process_light_event(event: Event) -> None:
|
|
87
84
|
if event.light is None:
|
|
88
85
|
return
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
if dt is None or event.start >= dt or (event.end is not None and event.end >= dt):
|
|
87
|
+
if _event_is_in_range(event, event.light.last_motion):
|
|
92
88
|
event.light.last_motion_event_id = event.id
|
|
93
89
|
|
|
94
90
|
|
|
91
|
+
def _event_is_in_range(event: Event, dt: datetime | None) -> bool:
|
|
92
|
+
"""Check if event is in range of datetime."""
|
|
93
|
+
return (
|
|
94
|
+
dt is None or event.start >= dt or (event.end is not None and event.end >= dt)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
95
98
|
def _process_sensor_event(event: Event) -> None:
|
|
96
99
|
if event.sensor is None:
|
|
97
100
|
return
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
dt = event.sensor.motion_detected_at
|
|
101
|
-
if (
|
|
102
|
-
dt is None
|
|
103
|
-
or event.start >= dt
|
|
104
|
-
or (event.end is not None and event.end >= dt)
|
|
105
|
-
):
|
|
101
|
+
if event.type is EventType.MOTION_SENSOR:
|
|
102
|
+
if _event_is_in_range(event, event.sensor.motion_detected_at):
|
|
106
103
|
event.sensor.last_motion_event_id = event.id
|
|
107
104
|
elif event.type in {EventType.SENSOR_CLOSED, EventType.SENSOR_OPENED}:
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
dt is None
|
|
111
|
-
or event.start >= dt
|
|
112
|
-
or (event.end is not None and event.end >= dt)
|
|
113
|
-
):
|
|
105
|
+
if _event_is_in_range(event, event.sensor.open_status_changed_at):
|
|
114
106
|
event.sensor.last_contact_event_id = event.id
|
|
115
|
-
elif event.type
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
dt is None
|
|
119
|
-
or event.start >= dt
|
|
120
|
-
or (event.end is not None and event.end >= dt)
|
|
121
|
-
):
|
|
107
|
+
elif event.type is EventType.SENSOR_EXTREME_VALUE:
|
|
108
|
+
if _event_is_in_range(event, event.sensor.extreme_value_detected_at):
|
|
122
109
|
event.sensor.extreme_value_detected_at = event.end
|
|
123
110
|
event.sensor.last_value_event_id = event.id
|
|
124
|
-
elif event.type
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
dt is None
|
|
128
|
-
or event.start >= dt
|
|
129
|
-
or (event.end is not None and event.end >= dt)
|
|
130
|
-
):
|
|
111
|
+
elif event.type is EventType.SENSOR_ALARM:
|
|
112
|
+
if _event_is_in_range(event, event.sensor.alarm_triggered_at):
|
|
131
113
|
event.sensor.last_value_event_id = event.id
|
|
132
114
|
|
|
133
115
|
|
|
@@ -144,21 +126,25 @@ def _process_camera_event(event: Event) -> None:
|
|
|
144
126
|
|
|
145
127
|
event_type = event.type
|
|
146
128
|
dt_attr, event_attr = CAMERA_EVENT_ATTR_MAP[event_type]
|
|
147
|
-
dt = getattr(camera, dt_attr)
|
|
148
|
-
if
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
129
|
+
dt: datetime | None = getattr(camera, dt_attr)
|
|
130
|
+
if not _event_is_in_range(event, dt):
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
event_id = event.id
|
|
134
|
+
event_start = event.start
|
|
135
|
+
|
|
136
|
+
setattr(camera, event_attr, event_id)
|
|
137
|
+
setattr(camera, dt_attr, event_start)
|
|
138
|
+
if event_type in _CAMERA_SMART_AND_LINE_EVENTS:
|
|
139
|
+
for smart_type in event.smart_detect_types:
|
|
140
|
+
camera.last_smart_detect_event_ids[smart_type] = event_id
|
|
141
|
+
camera.last_smart_detects[smart_type] = event_start
|
|
142
|
+
elif event_type is _CAMERA_SMART_AUDIO_EVENT:
|
|
143
|
+
for smart_type in event.smart_detect_types:
|
|
144
|
+
if (audio_type := smart_type.audio_type) is None:
|
|
145
|
+
continue
|
|
146
|
+
camera.last_smart_audio_detect_event_ids[audio_type] = event_id
|
|
147
|
+
camera.last_smart_audio_detects[audio_type] = event_start
|
|
162
148
|
|
|
163
149
|
|
|
164
150
|
@dataclass
|
|
@@ -324,7 +310,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
324
310
|
if ref is None:
|
|
325
311
|
return None
|
|
326
312
|
|
|
327
|
-
devices = getattr(self, f"{ref.model.value}s")
|
|
313
|
+
devices: dict[str, ProtectModelWithId] = getattr(self, f"{ref.model.value}s")
|
|
328
314
|
return cast(ProtectAdoptableDeviceModel, devices.get(ref.id))
|
|
329
315
|
|
|
330
316
|
def get_device_from_id(self, device_id: str) -> ProtectAdoptableDeviceModel | None:
|
|
@@ -332,7 +318,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
332
318
|
ref = self.id_lookup.get(device_id)
|
|
333
319
|
if ref is None:
|
|
334
320
|
return None
|
|
335
|
-
devices = getattr(self, f"{ref.model.value}s")
|
|
321
|
+
devices: dict[str, ProtectModelWithId] = getattr(self, f"{ref.model.value}s")
|
|
336
322
|
return cast(ProtectAdoptableDeviceModel, devices.get(ref.id))
|
|
337
323
|
|
|
338
324
|
def process_event(self, event: Event) -> None:
|
|
@@ -363,14 +349,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
363
349
|
),
|
|
364
350
|
)
|
|
365
351
|
|
|
366
|
-
def _get_frame_data(
|
|
367
|
-
self,
|
|
368
|
-
packet: WSPacket,
|
|
369
|
-
) -> tuple[dict[str, Any], dict[str, Any] | None]:
|
|
370
|
-
if self.capture_ws_stats:
|
|
371
|
-
return deepcopy(packet.action_frame.data), deepcopy(packet.data_frame.data)
|
|
372
|
-
return packet.action_frame.data, packet.data_frame.data
|
|
373
|
-
|
|
374
352
|
def _process_add_packet(
|
|
375
353
|
self,
|
|
376
354
|
packet: WSPacket,
|
|
@@ -412,7 +390,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
412
390
|
|
|
413
391
|
def _process_remove_packet(self, packet: WSPacket) -> WSSubscriptionMessage | None:
|
|
414
392
|
model: str | None = packet.action_frame.data.get("modelKey")
|
|
415
|
-
devices = getattr(self, f"{model}s", None)
|
|
393
|
+
devices: dict[str, ProtectDeviceModel] | None = getattr(self, f"{model}s", None)
|
|
416
394
|
|
|
417
395
|
if devices is None:
|
|
418
396
|
return None
|
|
@@ -439,7 +417,8 @@ class Bootstrap(ProtectBaseObject):
|
|
|
439
417
|
ignore_stats: bool,
|
|
440
418
|
) -> WSSubscriptionMessage | None:
|
|
441
419
|
if ignore_stats:
|
|
442
|
-
|
|
420
|
+
for key in STATS_KEYS.intersection(data):
|
|
421
|
+
del data[key]
|
|
443
422
|
# nothing left to process
|
|
444
423
|
if not data:
|
|
445
424
|
self._create_stat(packet, None, True)
|
|
@@ -477,9 +456,10 @@ class Bootstrap(ProtectBaseObject):
|
|
|
477
456
|
ignore_stats: bool,
|
|
478
457
|
) -> WSSubscriptionMessage | None:
|
|
479
458
|
model_type = action["modelKey"]
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
459
|
+
remove_keys = (
|
|
460
|
+
STATS_AND_IGNORE_DEVICE_KEYS if ignore_stats else IGNORE_DEVICE_KEYS
|
|
461
|
+
)
|
|
462
|
+
for key in remove_keys.intersection(data):
|
|
483
463
|
del data[key]
|
|
484
464
|
# `last_motion` from cameras update every 100 milliseconds when a motion event is active
|
|
485
465
|
# this overrides the behavior to only update `last_motion` when a new event starts
|
|
@@ -491,12 +471,12 @@ class Bootstrap(ProtectBaseObject):
|
|
|
491
471
|
return None
|
|
492
472
|
|
|
493
473
|
key = f"{model_type}s"
|
|
494
|
-
devices = getattr(self, key)
|
|
474
|
+
devices: dict[str, ProtectModelWithId] = getattr(self, key)
|
|
495
475
|
action_id: str = action["id"]
|
|
496
476
|
if action_id in devices:
|
|
497
477
|
if action_id not in devices:
|
|
498
478
|
raise ValueError(f"Unknown device update for {model_type}: {action_id}")
|
|
499
|
-
obj
|
|
479
|
+
obj = devices[action_id]
|
|
500
480
|
data = obj.unifi_dict_to_dict(data)
|
|
501
481
|
old_obj = obj.copy()
|
|
502
482
|
obj = obj.update_from_dict(deepcopy(data))
|
|
@@ -542,7 +522,12 @@ class Bootstrap(ProtectBaseObject):
|
|
|
542
522
|
ignore_stats: bool = False,
|
|
543
523
|
) -> WSSubscriptionMessage | None:
|
|
544
524
|
"""Process a WS packet."""
|
|
545
|
-
action
|
|
525
|
+
action = packet.action_frame.data
|
|
526
|
+
data = packet.data_frame.data
|
|
527
|
+
if self.capture_ws_stats:
|
|
528
|
+
action = deepcopy(action)
|
|
529
|
+
data = deepcopy(data)
|
|
530
|
+
|
|
546
531
|
new_update_id: str = action["newUpdateId"]
|
|
547
532
|
if new_update_id is not None:
|
|
548
533
|
self.last_update_id = new_update_id
|
|
@@ -629,7 +614,9 @@ class Bootstrap(ProtectBaseObject):
|
|
|
629
614
|
if isinstance(device, NVR):
|
|
630
615
|
self.nvr = device
|
|
631
616
|
else:
|
|
632
|
-
devices = getattr(
|
|
617
|
+
devices: dict[str, ProtectModelWithId] = getattr(
|
|
618
|
+
self, f"{model_type.value}s"
|
|
619
|
+
)
|
|
633
620
|
devices[device.id] = device
|
|
634
621
|
_LOGGER.debug("Successfully refresh model: %s %s", model_type, device_id)
|
|
635
622
|
|
|
@@ -14,8 +14,8 @@ 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=
|
|
18
|
-
uiprotect/data/bootstrap.py,sha256=
|
|
17
|
+
uiprotect/data/base.py,sha256=kkPrRhvJV7igGyB3wv1fDvWH26Xzlbz94BjJkDiTvU4,38375
|
|
18
|
+
uiprotect/data/bootstrap.py,sha256=NH4jSEm44M0aijmViLHip2PKhVchuZJEeqWI3bcZwq0,21285
|
|
19
19
|
uiprotect/data/convert.py,sha256=rOQplUMIdTMD2SbAx_iI9BNPDscnhDvyRVLEMDhtADg,2047
|
|
20
20
|
uiprotect/data/devices.py,sha256=Nq3bOko5PFf5LvEBoD4JV8kmbq50laRdh3VHMWX7t-0,111809
|
|
21
21
|
uiprotect/data/nvr.py,sha256=aIi4d0KCJ55r1EP-33sBhvEa8lx-41jDGrDpAq5VnOE,47598
|
|
@@ -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.0.
|
|
34
|
-
uiprotect-1.0.
|
|
35
|
-
uiprotect-1.0.
|
|
36
|
-
uiprotect-1.0.
|
|
37
|
-
uiprotect-1.0.
|
|
33
|
+
uiprotect-1.2.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-1.2.0.dist-info/METADATA,sha256=N0K6ocu5Xe5y1qvGrBIqu00xfu0ZseOmTMTXccG3cnQ,10984
|
|
35
|
+
uiprotect-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-1.2.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-1.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|