uiprotect 7.8.0__tar.gz → 7.9.0__tar.gz

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.

Files changed (38) hide show
  1. {uiprotect-7.8.0 → uiprotect-7.9.0}/PKG-INFO +1 -1
  2. {uiprotect-7.8.0 → uiprotect-7.9.0}/pyproject.toml +1 -1
  3. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/api.py +35 -2
  4. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/__init__.py +18 -0
  5. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/base.py +0 -1
  6. {uiprotect-7.8.0 → uiprotect-7.9.0}/LICENSE +0 -0
  7. {uiprotect-7.8.0 → uiprotect-7.9.0}/README.md +0 -0
  8. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/__init__.py +0 -0
  9. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/__main__.py +0 -0
  10. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/_compat.py +0 -0
  11. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/aiports.py +0 -0
  12. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/backup.py +0 -0
  13. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/cameras.py +0 -0
  14. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/chimes.py +0 -0
  15. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/doorlocks.py +0 -0
  16. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/events.py +0 -0
  17. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/lights.py +0 -0
  18. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/liveviews.py +0 -0
  19. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/nvr.py +0 -0
  20. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/sensors.py +0 -0
  21. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/cli/viewers.py +0 -0
  22. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/__init__.py +0 -0
  23. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/base.py +0 -0
  24. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/bootstrap.py +0 -0
  25. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/convert.py +0 -0
  26. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/devices.py +0 -0
  27. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/nvr.py +0 -0
  28. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/types.py +0 -0
  29. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/user.py +0 -0
  30. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/data/websocket.py +0 -0
  31. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/exceptions.py +0 -0
  32. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/py.typed +0 -0
  33. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/release_cache.json +0 -0
  34. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/stream.py +0 -0
  35. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/test_util/__init__.py +0 -0
  36. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/test_util/anonymize.py +0 -0
  37. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/utils.py +0 -0
  38. {uiprotect-7.8.0 → uiprotect-7.9.0}/src/uiprotect/websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: uiprotect
3
- Version: 7.8.0
3
+ Version: 7.9.0
4
4
  Summary: Python API for Unifi Protect (Unofficial)
5
5
  License: MIT
6
6
  Author: UI Protect Maintainers
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uiprotect"
3
- version = "7.8.0"
3
+ version = "7.9.0"
4
4
  license = "MIT"
5
5
  description = "Python API for Unifi Protect (Unofficial)"
6
6
  authors = [{ name = "UI Protect Maintainers", email = "ui@koston.org" }]
@@ -390,12 +390,15 @@ class BaseApiClient:
390
390
  method: str = "get",
391
391
  require_auth: bool = True,
392
392
  raise_exception: bool = True,
393
+ api_path: str | None = None,
393
394
  **kwargs: Any,
394
395
  ) -> bytes | None:
395
- """Make a request to UniFi Protect API"""
396
+ """Make a API request"""
397
+ path = api_path if api_path is not None else self.api_path
398
+
396
399
  response = await self.request(
397
400
  method,
398
- f"{self.api_path}{url}",
401
+ f"{path}{url}",
399
402
  require_auth=require_auth,
400
403
  auto_close=False,
401
404
  **kwargs,
@@ -449,6 +452,7 @@ class BaseApiClient:
449
452
  method: str = "get",
450
453
  require_auth: bool = True,
451
454
  raise_exception: bool = True,
455
+ api_path: str | None = None,
452
456
  **kwargs: Any,
453
457
  ) -> list[Any] | dict[str, Any] | None:
454
458
  data = await self.api_request_raw(
@@ -456,6 +460,7 @@ class BaseApiClient:
456
460
  method=method,
457
461
  require_auth=require_auth,
458
462
  raise_exception=raise_exception,
463
+ api_path=api_path,
459
464
  **kwargs,
460
465
  )
461
466
 
@@ -2038,3 +2043,31 @@ class ProtectApiClient(BaseApiClient):
2038
2043
  method="post",
2039
2044
  )
2040
2045
  return PTZPreset(**preset)
2046
+
2047
+ async def create_api_key(self, name: str) -> str:
2048
+ """Create an API key with the given name and return the full API key."""
2049
+ if not name:
2050
+ raise BadRequest("API key name cannot be empty")
2051
+
2052
+ user_id = None
2053
+ if self._last_token_cookie_decode is not None:
2054
+ user_id = self._last_token_cookie_decode.get("userId")
2055
+ if not user_id:
2056
+ raise BadRequest("User ID not available for API key creation")
2057
+
2058
+ response = await self.api_request(
2059
+ api_path="/proxy/users/api/v2",
2060
+ url=f"/user/{user_id}/keys",
2061
+ method="post",
2062
+ json={"name": name},
2063
+ )
2064
+
2065
+ if (
2066
+ not isinstance(response, dict)
2067
+ or "data" not in response
2068
+ or not isinstance(response["data"], dict)
2069
+ or "full_api_key" not in response["data"]
2070
+ ):
2071
+ raise BadRequest("Failed to create API key")
2072
+
2073
+ return response["data"]["full_api_key"]
@@ -320,3 +320,21 @@ def release_versions(ctx: typer.Context) -> None:
320
320
 
321
321
  Path(RELEASE_CACHE).write_bytes(output)
322
322
  typer.echo(output.decode("utf-8"))
323
+
324
+
325
+ @app.command()
326
+ def create_api_key(
327
+ ctx: typer.Context,
328
+ name: str = typer.Argument(..., help="Name for the API key"),
329
+ ) -> None:
330
+ """Create a new API key for the current user."""
331
+ protect = cast(ProtectApiClient, ctx.obj.protect)
332
+
333
+ async def callback() -> str:
334
+ api_key = await protect.create_api_key(name)
335
+ await protect.close_session()
336
+ return api_key
337
+
338
+ _setup_logger()
339
+ result = run_async(callback())
340
+ typer.echo(result)
@@ -223,7 +223,6 @@ def init_common_commands(
223
223
  device_commands: dict[str, Callable[..., Any]] = {}
224
224
 
225
225
  deviceless_commands["list-ids"] = app.command()(list_ids)
226
- device_commands["protect-url"] = app.command()(protect_url)
227
226
  device_commands["is-wired"] = app.command()(is_wired)
228
227
  device_commands["is-wifi"] = app.command()(is_wifi)
229
228
  device_commands["is-bluetooth"] = app.command()(is_bluetooth)
File without changes
File without changes