uiprotect 7.0.2__py3-none-any.whl → 7.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/api.py +34 -3
- uiprotect/cli/__init__.py +2 -0
- uiprotect/cli/aiports.py +59 -0
- uiprotect/data/__init__.py +2 -0
- uiprotect/data/bootstrap.py +4 -0
- uiprotect/data/convert.py +2 -0
- uiprotect/data/devices.py +15 -0
- uiprotect/data/types.py +3 -0
- uiprotect/test_util/__init__.py +17 -0
- {uiprotect-7.0.2.dist-info → uiprotect-7.2.0.dist-info}/METADATA +1 -1
- {uiprotect-7.0.2.dist-info → uiprotect-7.2.0.dist-info}/RECORD +14 -13
- {uiprotect-7.0.2.dist-info → uiprotect-7.2.0.dist-info}/LICENSE +0 -0
- {uiprotect-7.0.2.dist-info → uiprotect-7.2.0.dist-info}/WHEEL +0 -0
- {uiprotect-7.0.2.dist-info → uiprotect-7.2.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -57,7 +57,7 @@ from .data import (
|
|
|
57
57
|
create_from_unifi_dict,
|
|
58
58
|
)
|
|
59
59
|
from .data.base import ProtectModelWithId
|
|
60
|
-
from .data.devices import Chime
|
|
60
|
+
from .data.devices import AiPort, Chime
|
|
61
61
|
from .data.types import IteratorCallback, ProgressCallback
|
|
62
62
|
from .exceptions import BadRequest, NotAuthorized, NvrError
|
|
63
63
|
from .utils import (
|
|
@@ -1268,6 +1268,14 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1268
1268
|
"""
|
|
1269
1269
|
return cast(list[Chime], await self.get_devices(ModelType.CHIME, Chime))
|
|
1270
1270
|
|
|
1271
|
+
async def get_aiports(self) -> list[AiPort]:
|
|
1272
|
+
"""
|
|
1273
|
+
Gets the list of aiports straight from the NVR.
|
|
1274
|
+
|
|
1275
|
+
The websocket is connected and running, you likely just want to use `self.bootstrap.aiports`
|
|
1276
|
+
"""
|
|
1277
|
+
return cast(list[AiPort], await self.get_devices(ModelType.AIPORT, AiPort))
|
|
1278
|
+
|
|
1271
1279
|
async def get_viewers(self) -> list[Viewer]:
|
|
1272
1280
|
"""
|
|
1273
1281
|
Gets the list of viewers straight from the NVR.
|
|
@@ -1386,6 +1394,14 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1386
1394
|
"""
|
|
1387
1395
|
return cast(Chime, await self.get_device(ModelType.CHIME, device_id, Chime))
|
|
1388
1396
|
|
|
1397
|
+
async def get_aiport(self, device_id: str) -> AiPort:
|
|
1398
|
+
"""
|
|
1399
|
+
Gets a AiPort straight from the NVR.
|
|
1400
|
+
|
|
1401
|
+
The websocket is connected and running, you likely just want to use `self.bootstrap.aiport[device_id]`
|
|
1402
|
+
"""
|
|
1403
|
+
return cast(AiPort, await self.get_device(ModelType.AIPORT, device_id, AiPort))
|
|
1404
|
+
|
|
1389
1405
|
async def get_viewer(self, device_id: str) -> Viewer:
|
|
1390
1406
|
"""
|
|
1391
1407
|
Gets a viewer straight from the NVR.
|
|
@@ -1790,10 +1806,12 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1790
1806
|
*,
|
|
1791
1807
|
volume: int | None = None,
|
|
1792
1808
|
repeat_times: int | None = None,
|
|
1809
|
+
ringtone_id: str | None = None,
|
|
1810
|
+
track_no: int | None = None,
|
|
1793
1811
|
) -> None:
|
|
1794
1812
|
"""Plays chime tones on a chime"""
|
|
1795
1813
|
data: dict[str, Any] | None = None
|
|
1796
|
-
if volume or repeat_times:
|
|
1814
|
+
if volume or repeat_times or ringtone_id or track_no:
|
|
1797
1815
|
chime = self.bootstrap.chimes.get(device_id)
|
|
1798
1816
|
if chime is None:
|
|
1799
1817
|
raise BadRequest("Invalid chime ID %s", device_id)
|
|
@@ -1801,8 +1819,11 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1801
1819
|
data = {
|
|
1802
1820
|
"volume": volume or chime.volume,
|
|
1803
1821
|
"repeatTimes": repeat_times or chime.repeat_times,
|
|
1804
|
-
"trackNo": chime.track_no,
|
|
1822
|
+
"trackNo": track_no or chime.track_no,
|
|
1805
1823
|
}
|
|
1824
|
+
if ringtone_id:
|
|
1825
|
+
data["ringtoneId"] = ringtone_id
|
|
1826
|
+
data.pop("trackNo", None)
|
|
1806
1827
|
|
|
1807
1828
|
await self.api_request(
|
|
1808
1829
|
f"chimes/{device_id}/play-speaker",
|
|
@@ -1814,6 +1835,16 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1814
1835
|
"""Plays chime tones on a chime"""
|
|
1815
1836
|
await self.api_request(f"chimes/{device_id}/play-buzzer", method="post")
|
|
1816
1837
|
|
|
1838
|
+
async def set_light_is_led_force_on(
|
|
1839
|
+
self, device_id: str, is_led_force_on: bool
|
|
1840
|
+
) -> None:
|
|
1841
|
+
"""Sets isLedForceOn for light.""" # workaround because forceOn doesnt work via websocket
|
|
1842
|
+
await self.api_request(
|
|
1843
|
+
f"lights/{device_id}",
|
|
1844
|
+
method="patch",
|
|
1845
|
+
json={"lightOnSettings": {"isLedForceOn": is_led_force_on}},
|
|
1846
|
+
)
|
|
1847
|
+
|
|
1817
1848
|
async def clear_tamper_sensor(self, device_id: str) -> None:
|
|
1818
1849
|
"""Clears tamper status for sensor"""
|
|
1819
1850
|
await self.api_request(f"sensors/{device_id}/clear-tamper-flag", method="post")
|
uiprotect/cli/__init__.py
CHANGED
|
@@ -17,6 +17,7 @@ from ..data import Version, WSPacket
|
|
|
17
17
|
from ..test_util import SampleDataGenerator
|
|
18
18
|
from ..utils import RELEASE_CACHE, get_local_timezone, run_async
|
|
19
19
|
from ..utils import profile_ws as profile_ws_job
|
|
20
|
+
from .aiports import app as aiports_app
|
|
20
21
|
from .base import CliContext, OutputFormatEnum
|
|
21
22
|
from .cameras import app as camera_app
|
|
22
23
|
from .chimes import app as chime_app
|
|
@@ -128,6 +129,7 @@ app.add_typer(doorlock_app, name="doorlocks")
|
|
|
128
129
|
app.add_typer(light_app, name="lights")
|
|
129
130
|
app.add_typer(sensor_app, name="sensors")
|
|
130
131
|
app.add_typer(viewer_app, name="viewers")
|
|
132
|
+
app.add_typer(aiports_app, name="aiports")
|
|
131
133
|
|
|
132
134
|
if backup_app is not None:
|
|
133
135
|
app.add_typer(backup_app, name="backup")
|
uiprotect/cli/aiports.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from ..api import ProtectApiClient
|
|
9
|
+
from ..cli import base
|
|
10
|
+
from ..data import AiPort
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(rich_markup_mode="rich")
|
|
13
|
+
|
|
14
|
+
ARG_DEVICE_ID = typer.Argument(
|
|
15
|
+
None, help="ID of AiPort device to select for subcommands"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class AiPortContext(base.CliContext):
|
|
21
|
+
devices: dict[str, AiPort]
|
|
22
|
+
device: AiPort | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@app.callback(invoke_without_command=True)
|
|
29
|
+
def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None:
|
|
30
|
+
"""
|
|
31
|
+
AiPort device CLI.
|
|
32
|
+
|
|
33
|
+
Returns full list of AiPorts without any arguments passed.
|
|
34
|
+
"""
|
|
35
|
+
protect: ProtectApiClient = ctx.obj.protect
|
|
36
|
+
context = AiPortContext(
|
|
37
|
+
protect=ctx.obj.protect,
|
|
38
|
+
device=None,
|
|
39
|
+
devices=protect.bootstrap.aiports,
|
|
40
|
+
output_format=ctx.obj.output_format,
|
|
41
|
+
)
|
|
42
|
+
ctx.obj = context
|
|
43
|
+
|
|
44
|
+
if device_id is not None and device_id not in ALL_COMMANDS:
|
|
45
|
+
if (device := protect.bootstrap.aiports.get(device_id)) is None:
|
|
46
|
+
typer.secho("Invalid aiport ID", fg="red")
|
|
47
|
+
raise typer.Exit(1)
|
|
48
|
+
ctx.obj.device = device
|
|
49
|
+
|
|
50
|
+
if not ctx.invoked_subcommand:
|
|
51
|
+
if device_id in ALL_COMMANDS:
|
|
52
|
+
ctx.invoke(ALL_COMMANDS[device_id], ctx)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
if ctx.obj.device is not None:
|
|
56
|
+
base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
base.print_unifi_dict(ctx.obj.devices)
|
uiprotect/data/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ from .base import (
|
|
|
10
10
|
from .bootstrap import Bootstrap
|
|
11
11
|
from .convert import create_from_unifi_dict
|
|
12
12
|
from .devices import (
|
|
13
|
+
AiPort,
|
|
13
14
|
Bridge,
|
|
14
15
|
Camera,
|
|
15
16
|
CameraChannel,
|
|
@@ -85,6 +86,7 @@ __all__ = [
|
|
|
85
86
|
"DEFAULT_TYPE",
|
|
86
87
|
"NVR",
|
|
87
88
|
"WS_HEADER_SIZE",
|
|
89
|
+
"AiPort",
|
|
88
90
|
"AnalyticsOption",
|
|
89
91
|
"AudioStyle",
|
|
90
92
|
"Bootstrap",
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -23,12 +23,14 @@ from .base import (
|
|
|
23
23
|
)
|
|
24
24
|
from .convert import MODEL_TO_CLASS, create_from_unifi_dict
|
|
25
25
|
from .devices import (
|
|
26
|
+
AiPort,
|
|
26
27
|
Bridge,
|
|
27
28
|
Camera,
|
|
28
29
|
Chime,
|
|
29
30
|
Doorlock,
|
|
30
31
|
Light,
|
|
31
32
|
ProtectAdoptableDeviceModel,
|
|
33
|
+
Ringtone,
|
|
32
34
|
Sensor,
|
|
33
35
|
Viewer,
|
|
34
36
|
)
|
|
@@ -181,6 +183,8 @@ class Bootstrap(ProtectBaseObject):
|
|
|
181
183
|
sensors: dict[str, Sensor]
|
|
182
184
|
doorlocks: dict[str, Doorlock]
|
|
183
185
|
chimes: dict[str, Chime]
|
|
186
|
+
aiports: dict[str, AiPort]
|
|
187
|
+
ringtones: list[Ringtone]
|
|
184
188
|
last_update_id: str
|
|
185
189
|
|
|
186
190
|
# TODO:
|
uiprotect/data/convert.py
CHANGED
|
@@ -8,6 +8,7 @@ from uiprotect.data.base import ProtectModelWithId
|
|
|
8
8
|
|
|
9
9
|
from ..exceptions import DataDecodeError
|
|
10
10
|
from .devices import (
|
|
11
|
+
AiPort,
|
|
11
12
|
Bridge,
|
|
12
13
|
Camera,
|
|
13
14
|
Chime,
|
|
@@ -40,6 +41,7 @@ MODEL_TO_CLASS: dict[str, type[ProtectModel]] = {
|
|
|
40
41
|
ModelType.SENSOR: Sensor,
|
|
41
42
|
ModelType.DOORLOCK: Doorlock,
|
|
42
43
|
ModelType.CHIME: Chime,
|
|
44
|
+
ModelType.AIPORT: AiPort,
|
|
43
45
|
ModelType.KEYRING: Keyring,
|
|
44
46
|
ModelType.ULP_USER: UlpUser,
|
|
45
47
|
}
|
uiprotect/data/devices.py
CHANGED
|
@@ -976,6 +976,8 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
976
976
|
audio_settings: CameraAudioSettings | None = None
|
|
977
977
|
# requires 5.0.33+
|
|
978
978
|
is_third_party_camera: bool | None = None
|
|
979
|
+
# requires 5.1.78+
|
|
980
|
+
is_paired_with_ai_port: bool | None = None
|
|
979
981
|
# TODO: used for adopting
|
|
980
982
|
# apMac read only
|
|
981
983
|
# apRssi read only
|
|
@@ -3382,3 +3384,16 @@ class Chime(ProtectAdoptableDeviceModel):
|
|
|
3382
3384
|
raise BadRequest("Camera %s is not paired with chime", camera.id)
|
|
3383
3385
|
|
|
3384
3386
|
await self.queue_update(callback)
|
|
3387
|
+
|
|
3388
|
+
|
|
3389
|
+
class AiPort(Camera):
|
|
3390
|
+
paired_cameras: list[str]
|
|
3391
|
+
|
|
3392
|
+
|
|
3393
|
+
class Ringtone(ProtectBaseObject):
|
|
3394
|
+
id: str
|
|
3395
|
+
name: str
|
|
3396
|
+
size: int
|
|
3397
|
+
is_default: bool
|
|
3398
|
+
nvr_mac: str
|
|
3399
|
+
model_key: str
|
uiprotect/data/types.py
CHANGED
|
@@ -124,9 +124,11 @@ class ModelType(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
124
124
|
DOORLOCK = "doorlock"
|
|
125
125
|
SCHEDULE = "schedule"
|
|
126
126
|
CHIME = "chime"
|
|
127
|
+
AIPORT = "aiport"
|
|
127
128
|
DEVICE_GROUP = "deviceGroup"
|
|
128
129
|
RECORDING_SCHEDULE = "recordingSchedule"
|
|
129
130
|
ULP_USER = "ulpUser"
|
|
131
|
+
RINGTONE = "ringtone"
|
|
130
132
|
KEYRING = "keyring"
|
|
131
133
|
UNKNOWN = "unknown"
|
|
132
134
|
|
|
@@ -173,6 +175,7 @@ class ModelType(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
173
175
|
ModelType.SENSOR,
|
|
174
176
|
ModelType.DOORLOCK,
|
|
175
177
|
ModelType.CHIME,
|
|
178
|
+
ModelType.AIPORT,
|
|
176
179
|
)
|
|
177
180
|
|
|
178
181
|
@classmethod
|
uiprotect/test_util/__init__.py
CHANGED
|
@@ -131,6 +131,7 @@ class SampleDataGenerator:
|
|
|
131
131
|
"sensor": len(bootstrap["sensors"]),
|
|
132
132
|
"doorlock": len(bootstrap["doorlocks"]),
|
|
133
133
|
"chime": len(bootstrap["chimes"]),
|
|
134
|
+
"aiport": len(bootstrap["aiports"]),
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
self.log("Generating event data...")
|
|
@@ -283,6 +284,7 @@ class SampleDataGenerator:
|
|
|
283
284
|
self.generate_sensor_data(),
|
|
284
285
|
self.generate_lock_data(),
|
|
285
286
|
self.generate_chime_data(),
|
|
287
|
+
self.generate_aiport_data(),
|
|
286
288
|
self.generate_bridge_data(),
|
|
287
289
|
self.generate_liveview_data(),
|
|
288
290
|
)
|
|
@@ -469,6 +471,21 @@ class SampleDataGenerator:
|
|
|
469
471
|
obj = await self.client.api_request_obj(f"chimes/{device_id}")
|
|
470
472
|
await self.write_json_file("sample_chime", obj)
|
|
471
473
|
|
|
474
|
+
async def generate_aiport_data(self) -> None:
|
|
475
|
+
objs = await self.client.api_request_list("aiports")
|
|
476
|
+
device_id: str | None = None
|
|
477
|
+
for obj_dict in objs:
|
|
478
|
+
device_id = obj_dict["id"]
|
|
479
|
+
if is_online(obj_dict):
|
|
480
|
+
break
|
|
481
|
+
|
|
482
|
+
if device_id is None:
|
|
483
|
+
self.log("No aiport found. Skipping aiport endpoints...")
|
|
484
|
+
return
|
|
485
|
+
|
|
486
|
+
obj = await self.client.api_request_obj(f"aiports/{device_id}")
|
|
487
|
+
await self.write_json_file("sample_aiport", obj)
|
|
488
|
+
|
|
472
489
|
async def generate_bridge_data(self) -> None:
|
|
473
490
|
objs = await self.client.api_request_list("bridges")
|
|
474
491
|
device_id: str | None = None
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
uiprotect/__init__.py,sha256=Oz6i1tonIz4QWVnEPkbielJDJ3WQdwZVgYtjY4IwGAQ,636
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
3
|
uiprotect/_compat.py,sha256=HThmb1zQZCEssCxYYbQzFhJq8zYYlVaSnIEZabKc-6U,302
|
|
4
|
-
uiprotect/api.py,sha256=
|
|
5
|
-
uiprotect/cli/__init__.py,sha256=
|
|
4
|
+
uiprotect/api.py,sha256=5oWPmVqMqNeug4eZFbyF0X7tq2pXseSLmKDeH3zrGso,70311
|
|
5
|
+
uiprotect/cli/__init__.py,sha256=gHo9G2WusvrWgnHhecT8Q2NLRXG0VS-rc9TKy4V5Kw8,8951
|
|
6
|
+
uiprotect/cli/aiports.py,sha256=wpEr2w_hY18CGpFiQM2Yc0FiVwG_1l2CzZhZLGNigvI,1576
|
|
6
7
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
7
8
|
uiprotect/cli/base.py,sha256=GVHQMrI3thQ-4ixJlunTCfEMF90xCnt-bvRPMDupDss,7588
|
|
8
9
|
uiprotect/cli/cameras.py,sha256=YvvMccQEYG3Wih0Ix8tan1R1vfaJ6cogg6YKWLzMUV8,16973
|
|
@@ -14,25 +15,25 @@ uiprotect/cli/liveviews.py,sha256=GU5z-ZLRBXHyspDKiJpiv-kbaBcvxK_-K70rPoqx2Ms,18
|
|
|
14
15
|
uiprotect/cli/nvr.py,sha256=TwxEg2XT8jXAbOqv6gc7KFXELKadeItEDYweSL4_-e8,4260
|
|
15
16
|
uiprotect/cli/sensors.py,sha256=fQtcDJCVxs4VbAqcavgBy2ABiVxAW3GXtna6_XFBp2k,8153
|
|
16
17
|
uiprotect/cli/viewers.py,sha256=2cyrp104ffIvgT0wYGIO0G35QMkEbFe7fSVqLwDXQYQ,2171
|
|
17
|
-
uiprotect/data/__init__.py,sha256=
|
|
18
|
+
uiprotect/data/__init__.py,sha256=audwJBjxRiYdNPeYlP6iofFIOq3gyQzh6VpDsOCM2dQ,2964
|
|
18
19
|
uiprotect/data/base.py,sha256=LRqmK60PKeDFgP2k5qpVj93AxEvdfC6kLk0Cqvt1W5k,35481
|
|
19
|
-
uiprotect/data/bootstrap.py,sha256=
|
|
20
|
-
uiprotect/data/convert.py,sha256=
|
|
21
|
-
uiprotect/data/devices.py,sha256=
|
|
20
|
+
uiprotect/data/bootstrap.py,sha256=Umija3z2SI4RPJmbzZNolm3jw-GSP4a2d9Fd0xcmf9c,23338
|
|
21
|
+
uiprotect/data/convert.py,sha256=xEN878_hm0HZZCVYGwJSxcSp2as9zpkvsemVIibReOA,2628
|
|
22
|
+
uiprotect/data/devices.py,sha256=ls-paEN3RITVASL0Nezfevpt5ROvxnhbBGYg8dPfAyM,114061
|
|
22
23
|
uiprotect/data/nvr.py,sha256=E18DgE0nXl9VZ_ULotTPcXSi3M1u3mWQsuZbY1gIajs,47490
|
|
23
|
-
uiprotect/data/types.py,sha256=
|
|
24
|
+
uiprotect/data/types.py,sha256=GVY6E88M41-_HjkIwVI7_EIObJSwH4c9BiunmEeo11Y,19164
|
|
24
25
|
uiprotect/data/user.py,sha256=Del5LUmt5uCfAQMI9-kl_GaKm085oTLjxmcCrlEKXxc,10526
|
|
25
26
|
uiprotect/data/websocket.py,sha256=m4EV1Qfh08eKOihy70ycViYgEQpeNSGZQJWdtGIYJDA,6791
|
|
26
27
|
uiprotect/exceptions.py,sha256=kgn0cRM6lTtgLza09SDa3ZiX6ue1QqHCOogQ4qu6KTQ,965
|
|
27
28
|
uiprotect/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
uiprotect/release_cache.json,sha256=NamnSFy78hOWY0DPO87J9ELFCAN6NnVquv8gQO75ZG4,386
|
|
29
30
|
uiprotect/stream.py,sha256=MWiTRFIhUfFLPA_csSrKl5-SkUbPZ2VhDu0XW2oVr-U,4800
|
|
30
|
-
uiprotect/test_util/__init__.py,sha256=
|
|
31
|
+
uiprotect/test_util/__init__.py,sha256=HlQBgIgdtrvT-gQ5OWP92LbgVr_YzsD5NFImLRonUZk,19320
|
|
31
32
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
32
33
|
uiprotect/utils.py,sha256=q2YYQfxr0b3QEWMSP7SdpcJZbB4huEhpo8PbtnTbEFI,20481
|
|
33
34
|
uiprotect/websocket.py,sha256=tEyenqblNXHcjWYuf4oRP1E7buNwx6zoECMwpBr-jig,8191
|
|
34
|
-
uiprotect-7.0.
|
|
35
|
-
uiprotect-7.0.
|
|
36
|
-
uiprotect-7.0.
|
|
37
|
-
uiprotect-7.0.
|
|
38
|
-
uiprotect-7.0.
|
|
35
|
+
uiprotect-7.2.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
36
|
+
uiprotect-7.2.0.dist-info/METADATA,sha256=_N35r5x3l7De3JMKKJv6kImajl786yjdJBMWHs8i_60,11142
|
|
37
|
+
uiprotect-7.2.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
38
|
+
uiprotect-7.2.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
39
|
+
uiprotect-7.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|