uiprotect 0.1.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.

@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from uiprotect.api import ProtectApiClient
9
+ from uiprotect.cli import base
10
+ from uiprotect.data import Liveview
11
+
12
+ app = typer.Typer(rich_markup_mode="rich")
13
+
14
+ ARG_DEVICE_ID = typer.Argument(None, help="ID of liveview to select for subcommands")
15
+ ALL_COMMANDS = {"list-ids": app.command(name="list-ids")(base.list_ids)}
16
+
17
+ app.command(name="protect-url")(base.protect_url)
18
+
19
+
20
+ @dataclass
21
+ class LiveviewContext(base.CliContext):
22
+ devices: dict[str, Liveview]
23
+ device: Liveview | None = None
24
+
25
+
26
+ @app.callback(invoke_without_command=True)
27
+ def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None:
28
+ """
29
+ Liveviews CLI.
30
+
31
+ Returns full list of Liveviews without any arguments passed.
32
+ """
33
+ protect: ProtectApiClient = ctx.obj.protect
34
+ context = LiveviewContext(
35
+ protect=ctx.obj.protect,
36
+ device=None,
37
+ devices=protect.bootstrap.liveviews,
38
+ output_format=ctx.obj.output_format,
39
+ )
40
+ ctx.obj = context
41
+
42
+ if device_id is not None and device_id not in ALL_COMMANDS:
43
+ if (device := protect.bootstrap.liveviews.get(device_id)) is None:
44
+ typer.secho("Invalid liveview ID", fg="red")
45
+ raise typer.Exit(1)
46
+ ctx.obj.device = device
47
+
48
+ if not ctx.invoked_subcommand:
49
+ if device_id in ALL_COMMANDS:
50
+ ctx.invoke(ALL_COMMANDS[device_id], ctx)
51
+ return
52
+
53
+ if ctx.obj.device is not None:
54
+ base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format)
55
+ return
56
+
57
+ base.print_unifi_dict(ctx.obj.devices)
58
+
59
+
60
+ @app.command()
61
+ def owner(ctx: typer.Context) -> None:
62
+ """Gets the owner for the liveview."""
63
+ base.require_device_id(ctx)
64
+ obj: Liveview = ctx.obj.device
65
+ base.print_unifi_obj(obj.owner, ctx.obj.output_format)
uiprotect/cli/nvr.py ADDED
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from datetime import timedelta
5
+
6
+ import orjson
7
+ import typer
8
+
9
+ from uiprotect.cli import base
10
+ from uiprotect.data import NVR, AnalyticsOption
11
+
12
+ app = typer.Typer(rich_markup_mode="rich")
13
+
14
+ ARG_TIMEOUT = typer.Argument(..., help="Timeout (in seconds)")
15
+ ARG_DOORBELL_MESSAGE = typer.Argument(..., help="ASCII only. Max length 30")
16
+ OPTION_ENABLE_SMART = typer.Option(
17
+ False,
18
+ "--enable-smart",
19
+ help="Automatically enable smart detections",
20
+ )
21
+
22
+
23
+ @dataclass
24
+ class NVRContext(base.CliContext):
25
+ device: NVR
26
+
27
+
28
+ @app.callback(invoke_without_command=True)
29
+ def main(ctx: typer.Context) -> None:
30
+ """
31
+ NVR device CLI.
32
+
33
+ Return NVR object without any arguments passed.
34
+ """
35
+ context = NVRContext(
36
+ protect=ctx.obj.protect,
37
+ device=ctx.obj.protect.bootstrap.nvr,
38
+ output_format=ctx.obj.output_format,
39
+ )
40
+ ctx.obj = context
41
+
42
+ if not ctx.invoked_subcommand:
43
+ base.print_unifi_obj(context.device, ctx.obj.output_format)
44
+
45
+
46
+ app.command(name="protect-url")(base.protect_url)
47
+ app.command(name="reboot")(base.reboot)
48
+ app.command(name="set-name")(base.set_name)
49
+
50
+
51
+ @app.command()
52
+ def set_analytics(ctx: typer.Context, value: AnalyticsOption) -> None:
53
+ """Sets analytics collection for NVR."""
54
+ nvr: NVR = ctx.obj.device
55
+ base.run(ctx, nvr.set_analytics(value))
56
+
57
+
58
+ @app.command()
59
+ def set_default_reset_timeout(ctx: typer.Context, timeout: int = ARG_TIMEOUT) -> None:
60
+ """
61
+ Sets default message reset timeout.
62
+
63
+ This is how long until a custom message is reset back to the default message if no
64
+ timeout is passed in when the custom message is set.
65
+ """
66
+ nvr: NVR = ctx.obj.device
67
+ base.run(ctx, nvr.set_default_reset_timeout(timedelta(seconds=timeout)))
68
+ base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format)
69
+
70
+
71
+ @app.command()
72
+ def set_default_doorbell_message(
73
+ ctx: typer.Context,
74
+ msg: str = ARG_DOORBELL_MESSAGE,
75
+ ) -> None:
76
+ """
77
+ Sets default message for doorbell.
78
+
79
+ This is the message that is set when a custom doorbell message times out or an empty
80
+ one is set.
81
+ """
82
+ nvr: NVR = ctx.obj.device
83
+ base.run(ctx, nvr.set_default_doorbell_message(msg))
84
+ base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format)
85
+
86
+
87
+ @app.command()
88
+ def add_custom_doorbell_message(
89
+ ctx: typer.Context,
90
+ msg: str = ARG_DOORBELL_MESSAGE,
91
+ ) -> None:
92
+ """Adds a custom doorbell message."""
93
+ nvr: NVR = ctx.obj.device
94
+ base.run(ctx, nvr.add_custom_doorbell_message(msg))
95
+ base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format)
96
+
97
+
98
+ @app.command()
99
+ def remove_custom_doorbell_message(
100
+ ctx: typer.Context,
101
+ msg: str = ARG_DOORBELL_MESSAGE,
102
+ ) -> None:
103
+ """Removes a custom doorbell message."""
104
+ nvr: NVR = ctx.obj.device
105
+ base.run(ctx, nvr.remove_custom_doorbell_message(msg))
106
+ base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format)
107
+
108
+
109
+ @app.command()
110
+ def update(ctx: typer.Context, data: str) -> None:
111
+ """Updates the NVR."""
112
+ nvr: NVR = ctx.obj.device
113
+ base.run(ctx, nvr.api.update_nvr(orjson.loads(data)))
114
+
115
+
116
+ @app.command()
117
+ def set_smart_detections(ctx: typer.Context, value: bool) -> None:
118
+ """Set if smart detections are globally enabled or not."""
119
+ nvr: NVR = ctx.obj.device
120
+ base.run(ctx, nvr.set_smart_detections(value))
121
+
122
+
123
+ @app.command()
124
+ def set_face_recognition(
125
+ ctx: typer.Context,
126
+ value: bool,
127
+ enable_smart: bool = OPTION_ENABLE_SMART,
128
+ ) -> None:
129
+ """Set if face detections is enabled. Requires smart detections to be enabled."""
130
+ nvr: NVR = ctx.obj.device
131
+
132
+ async def callback() -> None:
133
+ if enable_smart:
134
+ await nvr.set_smart_detections(True)
135
+ await nvr.set_face_recognition(value)
136
+
137
+ base.run(ctx, callback())
138
+
139
+
140
+ @app.command()
141
+ def set_license_plate_recognition(
142
+ ctx: typer.Context,
143
+ value: bool,
144
+ enable_smart: bool = OPTION_ENABLE_SMART,
145
+ ) -> None:
146
+ """Set if license plate detections is enabled. Requires smart detections to be enabled."""
147
+ nvr: NVR = ctx.obj.device
148
+
149
+ async def callback() -> None:
150
+ if enable_smart:
151
+ await nvr.set_smart_detections(True)
152
+ await nvr.set_license_plate_recognition(value)
153
+
154
+ base.run(ctx, callback())
@@ -0,0 +1,278 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from uiprotect.api import ProtectApiClient
9
+ from uiprotect.cli import base
10
+ from uiprotect.data import MountType, Sensor
11
+
12
+ app = typer.Typer(rich_markup_mode="rich")
13
+
14
+ ARG_DEVICE_ID = typer.Argument(None, help="ID of sensor to select for subcommands")
15
+
16
+
17
+ @dataclass
18
+ class SensorContext(base.CliContext):
19
+ devices: dict[str, Sensor]
20
+ device: Sensor | None = None
21
+
22
+
23
+ ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app)
24
+
25
+
26
+ @app.callback(invoke_without_command=True)
27
+ def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None:
28
+ """
29
+ Sensors device CLI.
30
+
31
+ Returns full list of Sensors without any arguments passed.
32
+ """
33
+ protect: ProtectApiClient = ctx.obj.protect
34
+ context = SensorContext(
35
+ protect=ctx.obj.protect,
36
+ device=None,
37
+ devices=protect.bootstrap.sensors,
38
+ output_format=ctx.obj.output_format,
39
+ )
40
+ ctx.obj = context
41
+
42
+ if device_id is not None and device_id not in ALL_COMMANDS:
43
+ if (device := protect.bootstrap.sensors.get(device_id)) is None:
44
+ typer.secho("Invalid sensor ID", fg="red")
45
+ raise typer.Exit(1)
46
+ ctx.obj.device = device
47
+
48
+ if not ctx.invoked_subcommand:
49
+ if device_id in ALL_COMMANDS:
50
+ ctx.invoke(ALL_COMMANDS[device_id], ctx)
51
+ return
52
+
53
+ if ctx.obj.device is not None:
54
+ base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format)
55
+ return
56
+
57
+ base.print_unifi_dict(ctx.obj.devices)
58
+
59
+
60
+ @app.command()
61
+ def camera(ctx: typer.Context, camera_id: Optional[str] = typer.Argument(None)) -> None:
62
+ """Returns or sets tha paired camera for a sensor."""
63
+ base.require_device_id(ctx)
64
+ obj: Sensor = ctx.obj.device
65
+
66
+ if camera_id is None:
67
+ base.print_unifi_obj(obj.camera, ctx.obj.output_format)
68
+ else:
69
+ protect: ProtectApiClient = ctx.obj.protect
70
+ if (camera_obj := protect.bootstrap.cameras.get(camera_id)) is None:
71
+ typer.secho("Invalid camera ID")
72
+ raise typer.Exit(1)
73
+ base.run(ctx, obj.set_paired_camera(camera_obj))
74
+
75
+
76
+ @app.command()
77
+ def is_tampering_detected(ctx: typer.Context) -> None:
78
+ """Returns if tampering is detected for sensor"""
79
+ base.require_device_id(ctx)
80
+ obj: Sensor = ctx.obj.device
81
+ base.json_output(obj.is_tampering_detected)
82
+
83
+
84
+ @app.command()
85
+ def is_alarm_detected(ctx: typer.Context) -> None:
86
+ """Returns if alarm is detected for sensor"""
87
+ base.require_device_id(ctx)
88
+ obj: Sensor = ctx.obj.device
89
+ base.json_output(obj.is_alarm_detected)
90
+
91
+
92
+ @app.command()
93
+ def is_contact_enabled(ctx: typer.Context) -> None:
94
+ """Returns if contact sensor is enabled for sensor"""
95
+ base.require_device_id(ctx)
96
+ obj: Sensor = ctx.obj.device
97
+ base.json_output(obj.is_contact_sensor_enabled)
98
+
99
+
100
+ @app.command()
101
+ def is_motion_enabled(ctx: typer.Context) -> None:
102
+ """Returns if motion sensor is enabled for sensor"""
103
+ base.require_device_id(ctx)
104
+ obj: Sensor = ctx.obj.device
105
+ base.json_output(obj.is_contact_sensor_enabled)
106
+
107
+
108
+ @app.command()
109
+ def is_alarm_enabled(ctx: typer.Context) -> None:
110
+ """Returns if alarm sensor is enabled for sensor"""
111
+ base.require_device_id(ctx)
112
+ obj: Sensor = ctx.obj.device
113
+ base.json_output(obj.is_alarm_sensor_enabled)
114
+
115
+
116
+ @app.command()
117
+ def is_light_enabled(ctx: typer.Context) -> None:
118
+ """Returns if light sensor is enabled for sensor"""
119
+ base.require_device_id(ctx)
120
+ obj: Sensor = ctx.obj.device
121
+ base.json_output(obj.is_light_sensor_enabled)
122
+
123
+
124
+ @app.command()
125
+ def is_temperature_enabled(ctx: typer.Context) -> None:
126
+ """Returns if temperature sensor is enabled for sensor"""
127
+ base.require_device_id(ctx)
128
+ obj: Sensor = ctx.obj.device
129
+ base.json_output(obj.is_temperature_sensor_enabled)
130
+
131
+
132
+ @app.command()
133
+ def is_humidity_enabled(ctx: typer.Context) -> None:
134
+ """Returns if humidity sensor is enabled for sensor"""
135
+ base.require_device_id(ctx)
136
+ obj: Sensor = ctx.obj.device
137
+ base.json_output(obj.is_humidity_sensor_enabled)
138
+
139
+
140
+ @app.command()
141
+ def set_status_light(ctx: typer.Context, enabled: bool) -> None:
142
+ """Sets status light for sensor device."""
143
+ base.require_device_id(ctx)
144
+ obj: Sensor = ctx.obj.device
145
+
146
+ base.run(ctx, obj.set_status_light(enabled))
147
+
148
+
149
+ @app.command()
150
+ def set_mount_type(ctx: typer.Context, mount_type: MountType) -> None:
151
+ """Sets mount type for sensor device."""
152
+ base.require_device_id(ctx)
153
+ obj: Sensor = ctx.obj.device
154
+
155
+ base.run(ctx, obj.set_mount_type(mount_type))
156
+
157
+
158
+ @app.command()
159
+ def set_motion(ctx: typer.Context, enabled: bool) -> None:
160
+ """Sets motion sensor status for sensor device."""
161
+ base.require_device_id(ctx)
162
+ obj: Sensor = ctx.obj.device
163
+
164
+ base.run(ctx, obj.set_motion_status(enabled))
165
+
166
+
167
+ @app.command()
168
+ def set_temperature(ctx: typer.Context, enabled: bool) -> None:
169
+ """Sets temperature sensor status for sensor device."""
170
+ base.require_device_id(ctx)
171
+ obj: Sensor = ctx.obj.device
172
+
173
+ base.run(ctx, obj.set_temperature_status(enabled))
174
+
175
+
176
+ @app.command()
177
+ def set_humidity(ctx: typer.Context, enabled: bool) -> None:
178
+ """Sets humidity sensor status for sensor device."""
179
+ base.require_device_id(ctx)
180
+ obj: Sensor = ctx.obj.device
181
+
182
+ base.run(ctx, obj.set_humidity_status(enabled))
183
+
184
+
185
+ @app.command()
186
+ def set_light(ctx: typer.Context, enabled: bool) -> None:
187
+ """Sets light sensor status for sensor device."""
188
+ base.require_device_id(ctx)
189
+ obj: Sensor = ctx.obj.device
190
+
191
+ base.run(ctx, obj.set_light_status(enabled))
192
+
193
+
194
+ @app.command()
195
+ def set_alarm(ctx: typer.Context, enabled: bool) -> None:
196
+ """Sets alarm sensor status for sensor device."""
197
+ base.require_device_id(ctx)
198
+ obj: Sensor = ctx.obj.device
199
+
200
+ base.run(ctx, obj.set_alarm_status(enabled))
201
+
202
+
203
+ @app.command()
204
+ def set_motion_sensitivity(
205
+ ctx: typer.Context,
206
+ sensitivity: int = typer.Argument(..., min=0, max=100),
207
+ ) -> None:
208
+ """Sets motion sensitivity for the sensor."""
209
+ base.require_device_id(ctx)
210
+ obj: Sensor = ctx.obj.device
211
+
212
+ base.run(ctx, obj.set_motion_sensitivity(sensitivity))
213
+
214
+
215
+ @app.command()
216
+ def set_temperature_range(
217
+ ctx: typer.Context,
218
+ low: float = typer.Argument(..., min=0, max=44),
219
+ high: float = typer.Argument(..., min=1, max=45),
220
+ ) -> None:
221
+ """Sets temperature safe range (in °C). Anything out side of range will trigger event."""
222
+ base.require_device_id(ctx)
223
+ obj: Sensor = ctx.obj.device
224
+
225
+ base.run(ctx, obj.set_temperature_safe_range(low, high))
226
+
227
+
228
+ @app.command()
229
+ def set_humidity_range(
230
+ ctx: typer.Context,
231
+ low: float = typer.Argument(..., min=1, max=98),
232
+ high: float = typer.Argument(..., min=2, max=99),
233
+ ) -> None:
234
+ """Sets humidity safe range (in relative % humidity). Anything out side of range will trigger event."""
235
+ base.require_device_id(ctx)
236
+ obj: Sensor = ctx.obj.device
237
+
238
+ base.run(ctx, obj.set_humidity_safe_range(low, high))
239
+
240
+
241
+ @app.command()
242
+ def set_light_range(
243
+ ctx: typer.Context,
244
+ low: float = typer.Argument(..., min=1, max=999),
245
+ high: float = typer.Argument(..., min=2, max=1000),
246
+ ) -> None:
247
+ """Sets light safe range (in lux). Anything out side of range will trigger event."""
248
+ base.require_device_id(ctx)
249
+ obj: Sensor = ctx.obj.device
250
+
251
+ base.run(ctx, obj.set_light_safe_range(low, high))
252
+
253
+
254
+ @app.command()
255
+ def remove_temperature_range(ctx: typer.Context) -> None:
256
+ """Removes temperature safe ranges so events will no longer fire."""
257
+ base.require_device_id(ctx)
258
+ obj: Sensor = ctx.obj.device
259
+
260
+ base.run(ctx, obj.remove_temperature_safe_range())
261
+
262
+
263
+ @app.command()
264
+ def remove_humidity_range(ctx: typer.Context) -> None:
265
+ """Removes humidity safe ranges so events will no longer fire."""
266
+ base.require_device_id(ctx)
267
+ obj: Sensor = ctx.obj.device
268
+
269
+ base.run(ctx, obj.remove_humidity_safe_range())
270
+
271
+
272
+ @app.command()
273
+ def remove_light_range(ctx: typer.Context) -> None:
274
+ """Removes light safe ranges so events will no longer fire."""
275
+ base.require_device_id(ctx)
276
+ obj: Sensor = ctx.obj.device
277
+
278
+ base.run(ctx, obj.remove_light_safe_range())
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+
6
+ import typer
7
+
8
+ from uiprotect.api import ProtectApiClient
9
+ from uiprotect.cli import base
10
+ from uiprotect.data import Viewer
11
+
12
+ app = typer.Typer(rich_markup_mode="rich")
13
+
14
+ ARG_DEVICE_ID = typer.Argument(None, help="ID of viewer to select for subcommands")
15
+
16
+
17
+ @dataclass
18
+ class ViewerContext(base.CliContext):
19
+ devices: dict[str, Viewer]
20
+ device: Viewer | None = None
21
+
22
+
23
+ ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app)
24
+
25
+
26
+ @app.callback(invoke_without_command=True)
27
+ def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None:
28
+ """
29
+ Viewers device CLI.
30
+
31
+ Returns full list of Viewers without any arguments passed.
32
+ """
33
+ protect: ProtectApiClient = ctx.obj.protect
34
+ context = ViewerContext(
35
+ protect=ctx.obj.protect,
36
+ device=None,
37
+ devices=protect.bootstrap.viewers,
38
+ output_format=ctx.obj.output_format,
39
+ )
40
+ ctx.obj = context
41
+
42
+ if device_id is not None and device_id not in ALL_COMMANDS:
43
+ if (device := protect.bootstrap.viewers.get(device_id)) is None:
44
+ typer.secho("Invalid viewer ID", fg="red")
45
+ raise typer.Exit(1)
46
+ ctx.obj.device = device
47
+
48
+ if not ctx.invoked_subcommand:
49
+ if device_id in ALL_COMMANDS:
50
+ ctx.invoke(ALL_COMMANDS[device_id], ctx)
51
+ return
52
+
53
+ if ctx.obj.device is not None:
54
+ base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format)
55
+ return
56
+
57
+ base.print_unifi_dict(ctx.obj.devices)
58
+
59
+
60
+ @app.command()
61
+ def liveview(
62
+ ctx: typer.Context,
63
+ liveview_id: Optional[str] = typer.Argument(None),
64
+ ) -> None:
65
+ """Returns or sets the current liveview."""
66
+ base.require_device_id(ctx)
67
+ obj: Viewer = ctx.obj.device
68
+
69
+ if liveview_id is None:
70
+ base.print_unifi_obj(obj.liveview, ctx.obj.output_format)
71
+ else:
72
+ protect: ProtectApiClient = ctx.obj.protect
73
+ if (liveview_obj := protect.bootstrap.liveviews.get(liveview_id)) is None:
74
+ typer.secho("Invalid liveview ID")
75
+ raise typer.Exit(1)
76
+ base.run(ctx, obj.set_liveview(liveview_obj))