Git Product home page Git Product logo

pyunifiprotect's People

Contributors

adrum avatar angellusmortis avatar bdraco avatar briis avatar cdce8p avatar cmsimike avatar cpressland avatar davidhariri avatar dependabot[bot] avatar jasperslits avatar julian0021 avatar mohlek avatar wmandra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pyunifiprotect's Issues

CLI Syntax Problem

Exact same problem as in the closed issue 240, recreated on WSL2 Ubuntu 20.04 / Python 3.10.7 / pyunifiprotect 4.3.3
(uprotect is a wrapper script setting envars).

$ ./uprotect chimes 62794a5802a3c003e7009af8 cameras 606f76fa00dbd903e7001197,606f6fee023cd903e700111e
Invalid camera ID: 606f76fa00dbd903e7001197,606f6fee023cd903e700111e
$ ./uprotect chimes 62794a5802a3c003e7009af8 cameras 606f76fa00dbd903e7001197 606f6fee023cd903e700111e
$

Discussion: Regrading Streaming and Talkback (Two Way Audio)

So thinking about trying to implement a WebRTC backend for a UFP camera or a UDP stream connection for Two Way Audio, where do we want to implement that? In pyunifiprotect or unifiprotect? I know HA already has some "streaming" logic in it, so it may make sense to put it to the HA integration, but then then if anyone wants to use the library outside of HA, they would need to implement their own solution.

If we do implement in the library, it will give the library a hard requirement of needing ffmpeg to manage the streams/transcodes/etc.

As for interface, I was thinking of something like this:

WebRTC:

def create_webrtc_stream(camera):
    ffmpeg_url = create_ffmpeg_transcode() # will spawn ffmpeg to create a video stream that transcodes the AAC audio into OPUS for WebRTC
    # some kind of singleton to prevent multiple streams for a single camera
    
    url = create_webrtc_backend(ffmpeg_url) # create WebRTC stream with aiortc
    return {url: url}

stream = create_webrtc_stream(camera)
stream.url # + whatever else HA needs to play the Web RTC stream 
stream.close() # maybe an automatic time out instead to close the session after X time of unuse? To keep from always spinning up

Playing audio (media player) / Two Way Audio:

def create_audio_stream(camera):
     ffmpeg_url = create_ffmpeg_stream() # Create a UDP connection to camera: https://github.com/hjdhjd/homebridge-unifi-protect/blob/f236df00a4a47ceb8ae5ca3a84305ff786fc4f23/src/protect-stream.ts#L487
     # again singleton of some kind

     retturn ffmpeg_url

steam = create_audio_stream(camera)
stream.write(raw_audio)
stream.close()  # maybe an automatic time out instead to close the session after X time of unuse? To keep from always spinning up

# some kind of wrapper class to one-off play audio to the camera to support the media_player interface

The Two Way Audio stuff would be "implemented, but unused" until HA got better two way audio support, but this is just discussing now where we would want it.

HA logs: AttributeError: 'UUID' object has no attribute 'replace'

Been seeing this exception spamming in the logs every few minutes. Latest HA version (2023.12.4), although I've been seeing it since adding the UniFi Protect HA integration.

Nothing unusual about my Protect setup that I can think of, other than using stacked NVRs which may be less common. Happy to debug further.

Logger: pyunifiprotect.websocket
Source: /usr/local/lib/python3.11/site-packages/pyunifiprotect/websocket.py:84
First occurred: December 28, 2023 at 10:36:47 PM (175 occurrences)
Last logged: 11:07:24 AM

Error processing websocket message
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/websocket.py", line 82, in _process_message
    sub(msg)
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 694, in _process_ws_message
    processed_message = self.bootstrap.process_ws_packet(
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/data/bootstrap.py", line 573, in process_ws_packet
    return self._process_nvr_update(packet, data, ignore_stats)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/data/bootstrap.py", line 447, in _process_nvr_update
    self.nvr = self.nvr.update_from_dict(deepcopy(data))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/data/base.py", line 546, in update_from_dict
    setattr(self, key, convert_unifi_data(data[key], self.__fields__[key]))
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/utils.py", line 238, in convert_unifi_data
    return type_(value)
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/uuid.py", line 175, in __init__
    hex = hex.replace('urn:', '').replace('uuid:', '')
          ^^^^^^^^^^^
AttributeError: 'UUID' object has no attribute 'replace'

Issue with CSRF token

hey there and thanks for this awesome library. I've encountered an issue in my particular setup:

  • I'm running Unifi Protect on Unifi OS
  • For Unifi OS machines, this library assumes that server responses include a x-csrf-token
  • There's a special case where this causes issues: If requests originate from localhost, Unifi OS will not include this header and does not require this header to be present.
  • If this header is missing in server responses, subsequent requests cause exceptions because this value is None on our side, e.g.:
  File "aiohttp/_http_writer.pyx", line 137, in aiohttp._http_writer._serialize_headers
  File "aiohttp/_http_writer.pyx", line 109, in aiohttp._http_writer.to_str
TypeError: Cannot serialize non-str key None

Therefore, I suggest adding a check in check_unifi_os() for this special case where localhost or 127.0.0.1 is present in self_base_url. The authenticate() method should then also not try to include this header.

Let me know :)

Thanks!

Uncontrolled Resource Consumption in pillow

I got a security alert from dependabot highlightning that pillow should be upgraded to minimum version 8.3.2. I can see that the two files requirements_all.txt and requirements_test.txt are autogenerated, so I am not exactly sure how to correct that. They both contain version 8.3.1 of pillow

@bdraco: Can you tell me how to fix this, or fix it?

ModuleNotFoundError: No module named 'pyunifiprotect.test_util'

I'm getting a ModuleNotFoundError when trying to run the command line program:

└─► unifi-protect --help
Traceback (most recent call last):
  File "/home/tanner/tmp/pyunifiprotect/env/bin/unifi-protect", line 5, in <module>
    from pyunifiprotect.__main__ import start
  File "/home/tanner/tmp/pyunifiprotect/env/lib/python3.9/site-packages/pyunifiprotect/__main__.py", line 5, in <module>
    from .cli import app
  File "/home/tanner/tmp/pyunifiprotect/env/lib/python3.9/site-packages/pyunifiprotect/cli.py", line 12, in <module>
    from .test_util import SampleDataGenerator
ModuleNotFoundError: No module named 'pyunifiprotect.test_util'

Steps to reproduce:

$ git clone https://github.com/briis/pyunifiprotect.git
$ cd pyunifiprotect/
$ virtualenv -p python3 env
$ . env/bin/activate
(env) $ pip install .
(env) $ unifi-protect --help

I'm on Debian 11 with Python 3.9.2 if that matters.

Do you know what I'm doing wrong? Thanks!

[Early Access] Protect 2.9.16 includes breaking changes: pyunifiprotect.exceptions.DataDecodeError: No modelKey

This issue is absolutely due to running an Early Access build of Unifi Protect. I'm creating this issue to track the changes that are showing up in the EA version of Protect so they can get addressed for the eventual release. I plan on digging into this error and possibly creating a PR.

2023-09-06 07:59:44.687 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: sport
2023-09-06 07:59:44.697 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: slowShutter
2023-09-06 07:59:44.700 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: sport
2023-09-06 07:59:44.701 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: slowShutter
2023-09-06 07:59:44.702 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: sport
2023-09-06 07:59:44.703 WARNING (MainThread) [pyunifiprotect.utils] Unknown video mode: slowShutter
2023-09-06 07:59:46.127 ERROR (MainThread) [homeassistant.components.unifiprotect] Error setting up UniFi Protect integration: No modelKey
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 105, in async_setup_entry
await _async_setup_entry(hass, entry, data_service)
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 137, in _async_setup_entry
await data_service.async_setup()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 114, in async_setup
await self.async_refresh()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 134, in async_refresh
updates = await self.api.update(force=force)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 612, in update
events = await self.get_events(start=self._last_update_dt or max_event_dt, end=now_dt)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 731, in get_events
event = create_from_unifi_dict(event_dict, api=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/data/convert.py", line 61, in create_from_unifi_dict
raise DataDecodeError("No modelKey")
pyunifiprotect.exceptions.DataDecodeError: No modelKey
2023-09-06 07:59:46.131 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry Layer 1 - Dream Machine SE for unifiprotect
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/config_entries.py", line 387, in async_setup
result = await component.async_setup_entry(hass, self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 105, in async_setup_entry
await _async_setup_entry(hass, entry, data_service)
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 137, in _async_setup_entry
await data_service.async_setup()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 114, in async_setup
await self.async_refresh()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 134, in async_refresh
updates = await self.api.update(force=force)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 612, in update
events = await self.get_events(start=self._last_update_dt or max_event_dt, end=now_dt)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 731, in get_events
event = create_from_unifi_dict(event_dict, api=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/data/convert.py", line 61, in create_from_unifi_dict
raise DataDecodeError("No modelKey")
pyunifiprotect.exceptions.DataDecodeError: No modelKey

_process_nvr_update updating bootstrap.nvr with the wrong nvr data (when the NVR's are stacked)

When processing events from the websocket the nvr.stacked events cause the bootstrap.nvr information to get overwritten with the nvr.stacked data; the bootstrap.nvr object should be the primary nvr and should not be updated with data from the stacked unit.

diff --git a/pyunifiprotect/data/bootstrap.py b/pyunifiprotect/data/bootstrap.py
index 97f021c..5c666fd 100644
--- a/pyunifiprotect/data/bootstrap.py
+++ b/pyunifiprotect/data/bootstrap.py
@@ -428,6 +428,7 @@ class Bootstrap(ProtectBaseObject):
         packet: WSPacket,
         data: dict[str, Any],
         ignore_stats: bool,
+        id: str = None,
     ) -> Optional[WSSubscriptionMessage]:
         if ignore_stats:
             _remove_stats_keys(data)
@@ -442,6 +443,9 @@ class Bootstrap(ProtectBaseObject):
             self._create_stat(packet, [], True)
             return None
 
+        if id and not self.nvr.id == id:
+            return None
+
         old_nvr = self.nvr.copy()
         self.nvr = self.nvr.update_from_dict(deepcopy(data))
 
@@ -569,7 +573,9 @@ class Bootstrap(ProtectBaseObject):
 
             if action["action"] == "update":
                 if action["modelKey"] == ModelType.NVR.value:
-                    return self._process_nvr_update(packet, data, ignore_stats)
+                    return self._process_nvr_update(
+                        packet, data, ignore_stats, action["id"]
+                    )
                 if (
                     action["modelKey"] in ModelType.bootstrap_models()
                     or action["modelKey"] == ModelType.EVENT.value

Long TTS media is garbeled

When long TTS messages are played from two way cameras the sound seemed to be garbeled and becomes gibberish and unclear. I am not sure how better to explain it besides for that. Its not clear to me where the issue is(HA, UI Cameras, or this package).

Links to similar reports:
https://community.ui.com/questions/TTS-is-stuttering-with-G4-Doorbell/3bf84a96-f2d5-4109-9737-4535f72fae9f
https://community.home-assistant.io/t/tts-problem-with-unifi-doorbell-and-camera/685506/7

get_camera_snapshot is only returning a 360p image

I suddenly started getting 360p images when I was getting 1080p. I thought it was an issue with protect, but when I requested the endpoint in a browser, I get the full 4K image. Can you please figure out what is happening here?

ValueError: 'UNKNOWN' is not a valid StorageType

I'm using the docker container (image hash 0824181c17ef) to run the tool, and I suddenly started getting this error when I try to access protect (2.1.1), specifically showing one or all cameras:

Traceback (most recent call last):
  File "/usr/local/bin/unifi-protect", line 8, in <module>
    sys.exit(start())
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/__main__.py", line 18, in start
    app()
  File "/usr/local/lib/python3.10/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1654, in invoke
    super().invoke(ctx)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/typer/main.py", line 500, in wrapper
    return callback(**use_params)  # type: ignore
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/cli/__init__.py", line 121, in main
    loop.run_until_complete(update())
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/cli/__init__.py", line 117, in update
    protect._bootstrap = await protect.get_bootstrap()  # pylint: disable=protected-access
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/api.py", line 781, in get_bootstrap
    return Bootstrap.from_unifi_dict(**data, api=self)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 114, in from_unifi_dict
    data = cls.unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/bootstrap.py", line 184, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 336, in unifi_dict_to_dict
    data[key] = cls._clean_protect_obj(data[key], unifi_objs[key], api)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 275, in _clean_protect_obj
    return klass.unifi_dict_to_dict(data=data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/nvr.py", line 762, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 715, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 336, in unifi_dict_to_dict
    data[key] = cls._clean_protect_obj(data[key], unifi_objs[key], api)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 275, in _clean_protect_obj
    return klass.unifi_dict_to_dict(data=data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 336, in unifi_dict_to_dict
    data[key] = cls._clean_protect_obj(data[key], unifi_objs[key], api)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 275, in _clean_protect_obj
    return klass.unifi_dict_to_dict(data=data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 329, in unifi_dict_to_dict
    data[key] = convert_unifi_data(data[key], cls.__fields__[key])
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/utils.py", line 189, in convert_unifi_data
    value = field.type_(value)
  File "/usr/local/lib/python3.10/enum.py", line 385, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/lib/python3.10/enum.py", line 710, in __new__
    raise ve_exc
ValueError: 'UNKNOWN' is not a valid StorageType

Connection Issue with UniFi Protect in Docker Environment

Issue Description

I am just creating this issue for documentation purposes. I encountered an error while trying to use the UniFi Protect backup. This documentation is intended to help others who might face a similar issue.

Error Details

Received the following error message:

NvrError: Error requesting data from https://192.168.0.95: Cannot connect to host https:443 ssl:False [Name or service not known]

Steps to Reproduce

  1. Ran the Docker container with the following command:
docker run --rm -it \
  -e UFP_USERNAME=USERNAME \
  -e UFP_PASSWORD=PASSWORD \
  -e UFP_ADDRESS=https://192.168.0.95 \
  -e UFP_PORT=443 \
  -e UFP_SSL_VERIFY=False \
  -e TZ=America/Chicago \
  -v $PWD:/data ghcr.io/angellusmortis/pyunifiprotect:latest nvr
  1. Noticed an error in the log suggesting a URL formatting issue.
url = URL('https://https/api/auth/login')

Resolution

Removed 'https://' from the UFP_ADDRESS environment variable in the Docker command:

docker run --rm -it \
  -e UFP_USERNAME=USERNAME \
  -e UFP_PASSWORD=PASSWORD \
  -e UFP_ADDRESS=192.168.0.95 \
  -e UFP_PORT=443 \
  -e UFP_SSL_VERIFY=False \
  -e TZ=America/Chicago \
  -v $PWD:/data ghcr.io/angellusmortis/pyunifiprotect:latest nvr

Motion detection broken while recording disabled

After upgrading to HA 2023.2.x, motion entities are unavailable when recording is disabled. Motion entities work just fine while recording is disabled prior to this.

I see home-assistant/core#86329 bumped the pyunifiprotect dependency to change this behavior and attempt to avoid confusion where it wasn't working, but it seems the change took away functionality where it did previously work, as well.

The only instance when I've seen motion detection not work is when the privacy toggle is on in HA, due to the privacy mask, presumably.

Devices: G3 Flex, G4 Dome, G4 Doorbell
Protect version 2.7.18 on a UNVR with 3.0.16

AttributeError: 'URL' object has no attribute 'joinpath'

Looks like d8f92dc has introduced an issue as reported here:

ep1cman/unifi-protect-backup#130 (comment)

2024-02-17 13:56:16 [  WARNING  ] unifi_protect_backup.unifi_protect_backup_core :  Failed to connect to UniFi Protect, retrying in 2s...
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/unifi_protect_backup/unifi_protect_backup_core.py", line 190, in start
    await self._protect.update()
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 673, in update
    self._bootstrap = await self.get_bootstrap()
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 991, in get_bootstrap
    data = await self.api_request_obj("bootstrap")
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 379, in api_request_obj
    data = await self.api_request(
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 358, in api_request
    data = await self.api_request_raw(
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 319, in api_request_raw
    response = await self.request(
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 255, in request
    await self.ensure_authenticated()
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 416, in ensure_authenticated
    await self.authenticate()
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 441, in authenticate
    response = await self.request("post", url=url, json=auth)
  File "/usr/local/lib/python3.10/dist-packages/pyunifiprotect/api.py", line 257, in request
    request_url = self._url.joinpath(url[1:])
AttributeError: 'URL' object has no attribute 'joinpath'

WS.

@briis I see you've added support for the websockets interface...glancing at your code, I assume you've had a look at mine. I spent a few weeks reverse engineering it all and making it work. There are definitely some quirks...if you run into them and you'd like to bounce some ideas, please reach out over on my UniFi Protect project.

If I can answer any questions, don't hesitate to reach out. Welcome the collaboration.

Best of luck!

Error when calling service unifiprotect.add_doorbell_text

Hi Chris,
I tried to add some text to my Doorbell, and got the below error. I checked the service again, and figured that I selected the Doorbell Camera as device and I should have used the UDMP. When I did that it worked fine.

So would it be possible to limit the devices we get listed here, to only the NVR's or at least make the text more clear on that this is the device to use.

2021-12-04 15:38:00 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Error for call_service at pos 1: No UniFi Protect NVR found for device ID: 8a835d359fb9eced47980daa34baf360
2021-12-04 15:38:00 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [139831577205776] Error handling message: No UniFi Protect NVR found for device ID: 8a835d359fb9eced47980daa34baf360
2021-12-04 15:38:10 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Error for call_service at pos 1: No UniFi Protect NVR found for device ID: 8a835d359fb9eced47980daa34baf360

Doorbells and smart chimes not found

running 4.20.0 (also had same issues with 4.19.0) on Ubuntu

My doorbells (2) are not being enumerated. I had two G4 doorbells running with no problem on earlier versions (using unifi-protect-backup to offload image files). One doorbell died, taking down the transformer with it. This week I received a G4 Pro to replace the failed G4, and a new transformer. All is working from the Unifi Protect perspective, but neither the old doorbell nor the new one appears in the output of unifi-protect cameras or under the shell interface using for camera in protect.bootstrap.cameras.values():

I also do not see the smart chime enumerated (using for camera in protect.bootstrap.chimes.values():).

Please advise. Happy to provide additional information.

--
Rik

How to get live alerts?

Hello,
Do anobody have a sample code for having live alerts? or subscribe to alerts ?
Regards

Hold a strong ref to asyncio.Task objects until they are complete

python/cpython#88831

./lib/python3.10/site-packages/pyunifiprotect/data/bootstrap.py:                asyncio.create_task(self.refresh_device(model_type, device_id))
./lib/python3.10/site-packages/pyunifiprotect/data/base.py:        loop.call_later(EVENT_PING_INTERVAL.total_seconds(), asyncio.create_task, self.emit_message({}))
./lib/python3.10/site-packages/pyunifiprotect/websocket.py:        asyncio.create_task(self._websocket_loop(start_event))


Looks like we have 3 to cleanup.

Local variable `image_width` referenced before assignment

I think this still appears to be an issue for me when running via the UnifiProtect Home Assistant custom integration. I'm running the master version of the UnfiProtect, which appears to be using the latest version of this repo, 0.30.18, seen here. Although the error message is surfacing in the UnifiProtect custom integration, I think the root cause is coming from this repo's code base.

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 249, in async_setup
    result = await component.async_setup_entry(hass, self)  # type: ignore
  File "/home-automation/k8s/home-assistant/home-assistant-config/custom_components/unifiprotect/__init__.py", line 99, in async_setup_entry
    await protect_data.async_setup()
  File "/home-automation/k8s/home-assistant/home-assistant-config/custom_components/unifiprotect/data.py", line 32, in async_setup
    await self.async_refresh()
  File "/home-automation/k8s/home-assistant/home-assistant-config/custom_components/unifiprotect/data.py", line 48, in async_refresh
    await self._protectserver.update(
  File "/usr/local/lib/python3.8/site-packages/pyunifiprotect/unifi_protect_server.py", line 123, in update
    await self._get_device_list(not self.ws_connection)
  File "/usr/local/lib/python3.8/site-packages/pyunifiprotect/unifi_protect_server.py", line 314, in _get_device_list
    self._process_cameras_json(json_response, server_id, include_events)
  File "/usr/local/lib/python3.8/site-packages/pyunifiprotect/unifi_protect_server.py", line 335, in _process_cameras_json
    process_camera(
  File "/usr/local/lib/python3.8/site-packages/pyunifiprotect/unifi_data.py", line 349, in process_camera
    "image_width": image_width,
UnboundLocalError: local variable 'image_width' referenced before assignment

_process_nvr_update updating bootstrap.nvr with the wrong nvr data

I'm not familiar with this code base; but would be happy to push a pull request if requested.

my HomeAssistant is not able to browse media source / events and continuously gets the following error in the UI:

Unexpected identifier: <nvr_id.stacked>:browse:<camera>

I have 2 NVR pro's stacked.

I thought there was a bug in the media source; but it appears that the following call stack overwrites my primary nvr information with the stacked nvr information.
ProtectData -> async_setup() -> async_refresh() -> async_connect_ws(force) -> _process_nvr_update()

ProtectData->api.bootstrap.nvr

I think we need some sort of check before swapping the information during the _process_nvr_update().

here is a suggested fix:

diff --git a/pyunifiprotect/data/bootstrap.py b/pyunifiprotect/data/bootstrap.py
index 97f021c..5c666fd 100644
--- a/pyunifiprotect/data/bootstrap.py
+++ b/pyunifiprotect/data/bootstrap.py
@@ -428,6 +428,7 @@ class Bootstrap(ProtectBaseObject):
         packet: WSPacket,
         data: dict[str, Any],
         ignore_stats: bool,
+        id: str = None,
     ) -> Optional[WSSubscriptionMessage]:
         if ignore_stats:
             _remove_stats_keys(data)
@@ -442,6 +443,9 @@ class Bootstrap(ProtectBaseObject):
             self._create_stat(packet, [], True)
             return None
 
+        if id and not self.nvr.id == id:
+            return None
+
         old_nvr = self.nvr.copy()
         self.nvr = self.nvr.update_from_dict(deepcopy(data))
 
@@ -569,7 +573,9 @@ class Bootstrap(ProtectBaseObject):
 
             if action["action"] == "update":
                 if action["modelKey"] == ModelType.NVR.value:
-                    return self._process_nvr_update(packet, data, ignore_stats)
+                    return self._process_nvr_update(
+                        packet, data, ignore_stats, action["id"]
+                    )
                 if (
                     action["modelKey"] in ModelType.bootstrap_models()
                     or action["modelKey"] == ModelType.EVENT.value

The above patch addresses my home assistant issue; but it's not clear if that's the correct fix or not.

HA 2022.7.0b0: Platform unifiprotect does not generate unique IDs.

Hi,

I get the following error with the new HA Beta:
I don't think I got this error before...

Logger: homeassistant.components.binary_sensor
Source: helpers/entity_platform.py:620
Integration: Binärsensor ([documentation](https://rc.home-assistant.io/integrations/binary_sensor), [issues](https://github.com/home-assistant/home-assistant/issues?q=is%3Aissue+is%3Aopen+label%3A%22integration%3A+binary_sensor%22))
First occurred: 22:51:19 (1 occurrences)
Last logged: 22:51:19

Platform unifiprotect does not generate unique IDs. ID 74ACB9005A9A_motion already exists - ignoring binary_sensor.carport_motion

[Bug] Unexpected datetime.timedelta object being put through from_js_time() function

I know I'm using an Early Access build of UniFi Protect, but it's worked fine up until I upgraded Home Assistant to 2022.12.0b1 this morning.

Somehow, a datetime.timedelta object is making its way through to the from_js_time() function in /usr/local/lib/python3.10/site-packages/pyunifiprotect/utils.py.

  • UniFi console: UniFi Dream Router
  • UniFi OS: UDR v3.0.13
  • UniFi Protect: 2.7.5
  • Home Assistant: 2022.12.0b1
Traceback:
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 93, in async_setup_entry
    await _async_setup_entry(hass, entry, data_service)
  File "/usr/src/homeassistant/homeassistant/components/unifiprotect/__init__.py", line 125, in _async_setup_entry
    await data_service.async_setup()
  File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 109, in async_setup
    await self.async_refresh()
  File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 129, in async_refresh
    updates = await self.api.update(force=force)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/api.py", line 596, in update
    self._bootstrap = await self.get_bootstrap()
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/api.py", line 772, in get_bootstrap
    return Bootstrap.from_unifi_dict(**data, api=self)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 115, in from_unifi_dict
    data = cls.unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/bootstrap.py", line 190, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 346, in unifi_dict_to_dict
    data[key] = cls._clean_protect_obj_dict(data[key], unifi_dicts[key], api)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 292, in _clean_protect_obj_dict
    items[key] = cls._clean_protect_obj(value, klass, api)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 276, in _clean_protect_obj
    return klass.unifi_dict_to_dict(data=data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/devices.py", line 835, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 752, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/data/base.py", line 329, in unifi_dict_to_dict
    data[key] = convert_unifi_data(data[key], cls.__fields__[key])
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/utils.py", line 199, in convert_unifi_data
    value = from_js_time(value)
  File "/usr/local/lib/python3.10/site-packages/pyunifiprotect/utils.py", line 131, in from_js_time
    return datetime.fromtimestamp(int(num) / 1000, tz=timezone.utc)
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'datetime.timedelta'

License plate detection - Pass the plate number to HA

Feature request. Currently the License plate detection for the AI cameras(AI Bullet and AI 360 Panoramic) shows the license plate number overlaid on the video and send via the ubiquity app notification. In Home Assistant the notification is just a sensor with a binary "License Plate Detected" Yes/No.

I do not know if it is possible to pull this information, however it would be extremely useful to have the plate number inside of Home Assistant.

IPv6 Handling for `wan_ip` causing crash

When running app with a UniFI Console with IPv6 enabled WAN we get the following exception:

Traceback (most recent call last):
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/bin/unifi-protect", line 8, in <module>
    sys.exit(start())
             ^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/__main__.py", line 18, in start
    app()
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/typer/main.py", line 328, in __call__
    raise e
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/typer/main.py", line 311, in __call__
    return get_command(self)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/typer/core.py", line 778, in main
    return _main(
           ^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/typer/core.py", line 216, in _main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/click/core.py", line 1654, in invoke
    super().invoke(ctx)
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/.local/share/virtualenvs/pyunifiprotect-V1bbUH0L/lib/python3.11/site-packages/typer/main.py", line 683, in wrapper
    return callback(**use_params)  # type: ignore
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/cli/__init__.py", line 131, in main
    loop.run_until_complete(update())
  File "/opt/homebrew/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/cli/__init__.py", line 127, in update
    protect._bootstrap = await protect.get_bootstrap()  # pylint: disable=protected-access
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/api.py", line 776, in get_bootstrap
    return Bootstrap.from_unifi_dict(**data, api=self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/base.py", line 116, in from_unifi_dict
    data = cls.unifi_dict_to_dict(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/bootstrap.py", line 187, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/base.py", line 338, in unifi_dict_to_dict
    data[key] = cls._clean_protect_obj(data[key], unifi_objs[key], api)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/base.py", line 278, in _clean_protect_obj
    return klass.unifi_dict_to_dict(data=data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/nvr.py", line 845, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/base.py", line 780, in unifi_dict_to_dict
    return super().unifi_dict_to_dict(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/data/base.py", line 331, in unifi_dict_to_dict
    data[key] = convert_unifi_data(data[key], cls.__fields__[key])
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cpressland/pyunifiprotect/pyunifiprotect/utils.py", line 205, in convert_unifi_data
    value = field.type_(value)
            ^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ipaddress.py", line 1315, in __init__
    self._ip = self._ip_int_from_string(addr_str)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/ipaddress.py", line 1202, in _ip_int_from_string
    raise AddressValueError("Expected 4 octets in %r" % ip_str)
ipaddress.AddressValueError: Expected 4 octets in '2001:<snipped>:3e9'
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x10d8550d0>
Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x10d8bef90>, 713697.718996708)]']
connector: <aiohttp.connector.TCPConnector object at 0x10d855190>

Two-Way Audio

From this thread I understand that there had been a discussion about two-way audio support a while ago, but it was not yet supported by Home Assistant itself.

However, looks like the Frigate card got two-way audio support added in the meantime:
https://github.com/dermotduffy/frigate-hass-card#using-2-way-audio

I've got a Unify Protect AI 360 camera here which does include a microphone as well as a speaker.

Is it possible now to use this device with the Unify Protect integration including two-way audio?

Thanks in advance
Andreas

Adds Smart Track for Smart Detection events

I figured out you can get the smart detect data for an event at /proxy/protect/api/events/{event_id}/smartDetectTrack. I would like to use this to build in new features for smart detection events.

  • New class for Smart Detect Tracks
  • New API client endpoint / get_ on Event to get the smart detect tracks (cached on event after first call)
  • Helper method on Event to get the triggering zones for smart detection
  • Helper method on Event to take first frame of smart detect track and render rectangle for smart detect track (as an alternative to the event thumbnail) Result will look something like this:
    image

unifi-protect cameras save-video exporting empty file

Not sure what I am doing wrong, but save-video cli command is exporting a blank corrupt mp4 file

unifi-protect cameras save-video export.mp4 2023-09-29T08:11:31 2023-09-29T08:13:04

the snapshot one works perfectly.

I have also checked the timestamp video exists in the HDD, and I can export it from the web UI.

Bootstrap resync can block the event loop for ~0.5s

2022-09-11 15:52:08.743 WARNING (MainThread) [asyncio] Executing <Task pending name='Task-18790' coro=<ProtectData.async_refresh() running at /usr/src/homeassistant/homeassistant/components/unifiprotect/data.py:129> wait_for=<Future pending cb=[Task.task_wakeup()] created at /usr/local/lib/python3.10/asyncio/base_events.py:429> created at /usr/src/homeassistant/homeassistant/core.py:473> took 0.507 seconds
2022-09-11 16:07:08.891 WARNING (MainThread) [asyncio] Executing <Task pending name='Task-23637' coro=<ProtectData.async_refresh() running at /usr/src/homeassistant/homeassistant/components/unifiprotect/data.py:129> wait_for=<Future pending cb=[Task.task_wakeup()] created at /usr/local/lib/python3.10/asyncio/base_events.py:429> created at /usr/src/homeassistant/homeassistant/core.py:473> took 0.500 seconds

Error setting up UniFi Protect integration: invalid literal for int() with base 16

Yes, I know I am running EA Protect - v2.10.10

I am getting an error when trying to initialize Protect after updating HA to 2023.11.03

https://gist.github.com/grbray/af2cfcd2dd43b1c3ebab0f405f674da4#file-gistfile1-txt-L24

ValueError: invalid literal for int() with base 16: '00000000000000 0 000000000000000'
2023-12-02 19:32:30.340 ERROR (MainThread) [homeassistant.helpers.integration_platform] Error processing platform unifiprotect.media_source
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/integration_platform.py", line 61, in _async_process_single_integration_platform_component
await integration_platform.process_platform(hass, component_name, platform)
File "/usr/src/homeassistant/homeassistant/components/media_source/init.py", line 92, in _process_media_source_platform
hass.data[DOMAIN][domain] = await platform.async_get_media_source(hass)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/media_source.py", line 93, in async_get_media_source
data_sources[data.api.bootstrap.nvr.id] = data
^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 556, in bootstrap
raise BadRequest("Client not initalized, run update first")
pyunifiprotect.exceptions.BadRequest: Client not initalized, run update first
2023-12-02 19:32:53.146 ERROR (MainThread) [homeassistant.components.unifiprotect] Error setting up UniFi Protect integration: invalid literal for int() with base 16: '00000000000000 0 000000000000000'
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/init.py", line 105, in async_setup_entry
await _async_setup_entry(hass, entry, data_service)
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/init.py", line 137, in _async_setup_entry
await data_service.async_setup()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 114, in async_setup
await self.async_refresh()
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 134, in async_refresh
updates = await self.api.update(force=force)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Error when running unifi-protect sensor

After the latest updates to the module, I get the below error when issuing the command unifi-protect sensor. It does display the data first, but then it closes up with this error.

Task exception was never retrieved
future: <Task finished name='Task-5' coro=<BaseApiClient._setup_websocket() done, defined at /workspaces/pyunifiprotect/pyunifiprotect/unifi_protect_server.py:335> exception=RuntimeError('Session is closed')>
Traceback (most recent call last):
  File "/workspaces/pyunifiprotect/pyunifiprotect/unifi_protect_server.py", line 336, in _setup_websocket
    await self.ensure_authenticated()
  File "/workspaces/pyunifiprotect/pyunifiprotect/unifi_protect_server.py", line 258, in ensure_authenticated
    await self.authenticate()
  File "/workspaces/pyunifiprotect/pyunifiprotect/unifi_protect_server.py", line 272, in authenticate
    response = await self.request("post", url=url, json=auth)
  File "/workspaces/pyunifiprotect/pyunifiprotect/unifi_protect_server.py", line 143, in request
    response = await req_context.__aenter__()
  File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1117, in __aenter__
    self._resp = await self._coro
  File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 381, in _request
    raise RuntimeError("Session is closed")
RuntimeError: Session is closed

Request thumbnail for usage with notifications

Hi all,

First, thank you for this! Really happy with it in Home Assistant.

I was looking into setting up notification and noticed this wasn't available yet. I could find anything related to it in Home Assistant and found a mention of this in a TODO: here; So I'm guessing we don't have that option here yet right?

So my question is, since I saw the TODO, where is this on the roadmap, and is there anything I can do to help get this working?

I've been trying to help with other components and didn't have much luck with the repository owners, so I thought I'd see if i can help you guys out. It's for my benefit as well. 😄

Toggling "Color Night Vision" on G5 Pro cameras with "Vision Enhancer"

Hi,

I just picked up a couple G5 Pros with Vision Enhancers and going it doesn't seem possible to control whether or not I can enable "Color night vision"
image

With the vision enhancer attached, a flood light turns on when the camera detects motion and it's dark. This settings enables or disables that feature.
It would be ideal if I can manually turn the light on/off but that functionality doesn't seem to exist :(

NVR reboot does not work + Add shutdown

I'm experimenting with the NVR reboot endpoint and it seems the current implementation of reboot_nvr() does not work. The correct target path for rebooting the NVR is {root}/api/system/reboot. The only way I was able to accomplish this was by setting the api_path property to /api/ and then executing r = await protect.api_request("system/reboot", method="post", data={}, raise_exception=False). I have to modify the api_path property due to api_request_raw prefixing api_path to any url passed to this function. The static value of /proxy/protect/api/ does not work for the reboot endpoint. By setting api_path to /api/, the NVR successfully reboots. Also, the NVR returns code 204. While this is the expected return (as confirmed from dev tools on Chrome), the api_request_raw function should be modified to allow for code 204 (No Content).

As an enhancement request, could you add a shutdown_nvr() function? I believe the path for this is {root}/api/system/shutdown. We occasionally get power outages where I live and I'd like to build some automation to shut down the NVR and preserve backup power for critical operations such as modem and router.

Complete reboot code:

import asyncio
from pyunifiprotect import ProtectApiClient

host = '{NVR_IP}'
port = '{NVR_PORT}'
username = '{NVR_USER}'
password = '{NVR_PASSWORD}'

async def reboot():
    connection_opened = False
    try:
        protect = ProtectApiClient(host, port, username, password, verify_ssl=False)
        await protect.update()
        connection_opened = True
        # protect.reboot_nvr()
        protect.api_path = '/api/'
        r = await protect.api_request("system/reboot", method="post", data={}, raise_exception=False)
        # print (r.status_code)
    except Exception as ex:
        print (f'Error with reboot(): {ex}')
    finally:
        if connection_opened:
            await protect.close_session()

if __name__ == '__main__':
    asyncio.run(reboot())

Thank you!

Websocket loses connection silently

After a couple days of running, pyunificonnect loses websocket connection to Unifi Protect but doesn't log any error messages.

These are the last few things logged: https://txt.t0.vc/VLFO

There are no more messages after, and updates stop coming through.

This happens on both briis/pyunifiprotect:master and AngellusMortis/pyunifiprotect:feature/data-models.

Any idea why? Is there anything I can do to help debug this? For reference, here's the code I'm using:

https://git.tannercollin.com/tanner/doorbelldingdongringringdoorbell/src/branch/master/main.py#L59

pyunifiprotect.exceptions.NvrError: Error requesting data from.id.ui.direct: Cannot connect to host.id.ui.direct:443 ssl:default [Name does not resolve]

Logger: homeassistant.components.unifiprotect.data
Source: components/unifiprotect/data.py:134
Integration: UniFi Protect (documentation, issues)
First occurred: 01:49:41 (1 occurrences)
Last logged: 01:49:41

Error while updating
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1173, in _create_direct_connection
hosts = await asyncio.shield(host_resolved)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 884, in _resolve_host
addrs = await self._resolver.resolve(host, port, family=self._family)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/resolver.py", line 33, in resolve
infos = await self._loop.getaddrinfo(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/asyncio/base_events.py", line 867, in getaddrinfo
return await self.run_in_executor(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/socket.py", line 962, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name does not resolve

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 256, in request
response = await req_context.aenter()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/client.py", line 1187, in aenter
self._resp = await self._coro
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/client.py", line 574, in _request
conn = await self._connector.connect(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 544, in connect
proto = await self._create_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 911, in _create_connection
_, proto = await self._create_direct_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1187, in _create_direct_connection
raise ClientConnectorError(req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host 70a741654aad066c773306b959e506285ffdd.id.ui.direct:443 ssl:default [Name does not resolve]

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 134, in async_refresh
updates = await self.api.update(force=force)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 654, in update
self._bootstrap = await self.get_bootstrap()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 830, in get_bootstrap
data = await self.api_request_obj("bootstrap")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 363, in api_request_obj
data = await self.api_request(
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 342, in api_request
data = await self.api_request_raw(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 303, in api_request_raw
response = await self.request(
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 285, in request
raise NvrError(
pyunifiprotect.exceptions.NvrError: Error requesting data from 70a741654aad066c773306b959e506285ffdd.id.ui.direct: Cannot connect to host 70a741654aad066c773306b959e506285ffdd.id.ui.direct:443 ssl:default [Name does not resolve]

i'm a noob, so don't know if this is home assistant or pyunifiprotect .. but to me looks more like an pyunifiprotect issue?
came out of nothing

or is it something with my network and resolving my own dns ? because homeassistant is also not ssl protected (which i maybe should do, but it's also not exposed to outside)

thanks

Authentication errors every 2h

With the risk of this being closed / rejected asa it's not the right place.

I am using:
Home Assistant 2023.11.0
Unifi protect 2.9.42
UDM PRO with UniFi OS 3.2.6 running on 192.168.178.1
pyunifiprotect 4.21.0 (manually updated to get rid of some HA warnings related to sport).

I am seeing seeing "2023-11-20 18:18:47.292 ERROR (MainThread) [homeassistant.components.unifiprotect.data] Auth error while updating" every 2 hours in the logs since recently, I think since 2.9.4x

Upon turning debugging on for pyunifiprotect, the following logs are obtained.

2023-11-20 18:18:00.960 DEBUG (MainThread) [pyunifiprotect.websocket] WS Timeout loop sleep 29.841506746946834
2023-11-20 18:18:02.260 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:07.261 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:12.262 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:17.262 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:22.262 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:27.264 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:30.802 DEBUG (MainThread) [pyunifiprotect.websocket] WS Timeout loop sleep 26.708600973011926
2023-11-20 18:18:32.265 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:37.265 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:42.266 DEBUG (MainThread) [pyunifiprotect.api] Skipping update since websocket is active
2023-11-20 18:18:47.267 DEBUG (MainThread) [pyunifiprotect.api] Request url: https://192.168.178.1/api/auth/login
2023-11-20 18:18:47.283 DEBUG (MainThread) [pyunifiprotect.api] 401 application/json <ClientResponse(https://192.168.178.1/api/auth/login) [401 Unauthorized]>
<CIMultiDictProxy('Server': 'nginx', 'Date': 'Mon, 20 Nov 2023 17:18:47 GMT', 'Content-Type': 'application/json', 'Content-Length': '47', 'Connection': 'keep-alive', 'Referrer-Policy': 'no-referrer', 'Strict-Transport-Security': 'max-age=15552000; includeSubDomains', 'X-Content-Type-Options': 'nosniff', 'X-DNS-Prefetch-Control': 'off', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'Set-Cookie': 'TOKEN=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; samesite=none; secure; httponly')>

2023-11-20 18:18:47.283 DEBUG (MainThread) [pyunifiprotect.api] Authenticated successfully!
2023-11-20 18:18:47.283 DEBUG (MainThread) [pyunifiprotect.api] Request url: https://192.168.178.1/proxy/protect/api/bootstrap
2023-11-20 18:18:47.292 ERROR (MainThread) [homeassistant.components.unifiprotect.data] Auth error while updating
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/unifiprotect/data.py", line 134, in async_refresh
    updates = await self.api.update(force=force)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 600, in update
    self._bootstrap = await self.get_bootstrap()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 763, in get_bootstrap
    data = await self.api_request_obj("bootstrap")
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 328, in api_request_obj
    data = await self.api_request(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 311, in api_request
    data = await self.api_request_raw(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pyunifiprotect/api.py", line 286, in api_request_raw
    raise NotAuthorized(msg % (url, response.status, reason))
pyunifiprotect.exceptions.NotAuthorized: Request failed: /proxy/protect/api/bootstrap - Status: 401 - Reason: {'code': 401, 'message': 'Unauthorized'}
2023-11-20 18:18:52.269 DEBUG (MainThread) [pyunifiprotect.utils] Authentication token decode error: Not enough segments
2023-11-20 18:18:52.269 DEBUG (MainThread) [pyunifiprotect.api] Request url: https://192.168.178.1/api/auth/login
2023-11-20 18:18:52.653 DEBUG (MainThread) [pyunifiprotect.api] 200 application/json <ClientResponse(https://192.168.178.1/api/auth/login) [200 OK]>
<CIMultiDictProxy('Server': 'nginx', 'Date': 'Mon, 20 Nov 2023 17:18:52 GMT', 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': '3379', 'Connection': 'keep-alive', 'Set-Cookie': 'TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1N2E1MWZhYi05ZGJkLTRiOTEtOTA2NC01ZDNkMzNiODc1ZTQiLCJwYXNzd29yZFJldmlzaW9uIjoxNzAwMjk3Mzc5LCJpc1JlbWVtYmVyZWQiOmZhbHNlLCJjc3JmVG9rZW4iOiI0YzczOTU1Zi04MTU0LTRmZDgtOTY3Ny1jZWE2ODA1MDVlMWEiLCJpYXQiOjE3MDA1MDA3MzIsImV4cCI6MTcwMDUwNzkzMiwianRpIjoiNzY1NmJhMjUtNjJiYi00YTdmLWI2MjEtM2FlMTkxYjA3YjQyIn0.JeMTQndsZsxsiRMkE1ITTjGslFe7742CsDtb_2zzIIU; path=/; expires=Mon, 20 Nov 2023 19:18:52 GMT; samesite=none; secure; httponly', 'x-updated-csrf-token': '4c73955f-8154-4fd8-9677-cea680505e1a', 'x-token-expire-time': '1700507932470', 'X-Response-Time': '378ms', 'Access-Control-Allow-Credentials': 'false', 'Access-Control-Expose-Headers': 'Content-Disposition, Content-Range, Filename, Location, Range, Upload-Length, Upload-Offset, X-Connection-Type, X-Csrf-Token, X-File-Id, X-Token-Expire-Time, X-Updated-Csrf-Token', 'Referrer-Policy': 'no-referrer', 'Strict-Transport-Security': 'max-age=15552000; includeSubDomains', 'X-Content-Type-Options': 'nosniff', 'X-DNS-Prefetch-Control': 'off', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1; mode=block', 'X-Csrf-Token': '4c73955f-8154-4fd8-9677-cea680505e1a')>

2023-11-20 18:18:52.653 DEBUG (MainThread) [pyunifiprotect.api] Authenticated successfully!
2023-11-20 18:18:52.653 DEBUG (MainThread) [pyunifiprotect.api] Request url: https://192.168.178.1/proxy/protect/api/bootstrap
2023-11-20 18:18:52.700 DEBUG (MainThread) [pyunifiprotect.websocket] Disconnecting websocket...
2023-11-20 18:18:52.700 DEBUG (MainThread) [pyunifiprotect.websocket] Websocket disconnected
2023-11-20 18:18:52.700 DEBUG (MainThread) [pyunifiprotect.websocket] Adding subscription: <bound method ProtectApiClient._process_ws_message of <pyunifiprotect.api.ProtectApiClient object at 0x7f53dfd92f50>>
2023-11-20 18:18:52.700 DEBUG (MainThread) [pyunifiprotect.websocket] Scheduling WS connect...
2023-11-20 18:18:52.701 DEBUG (MainThread) [pyunifiprotect.websocket] Connecting WS to wss://192.168.178.1/proxy/protect/ws/updates?lastUpdateId=fd091387-c9ab-4b8d-83f3-5eeb91282f2e
2023-11-20 18:18:52.714 DEBUG (MainThread) [pyunifiprotect.websocket] Connected to Websocket successfully
2023-11-20 18:18:52.714 INFO (MainThread) [pyunifiprotect.api] Websocket re-connected successfully

The JWT from 2023-11-20 18:18:52.653 decodes to:

{
  "userId": "57a51fab-9dbd-4b91-9064-5d3d33b875e4",
  "passwordRevision": 1700297379,
  "isRemembered": false,
  "csrfToken": "4c73955f-8154-4fd8-9677-cea680505e1a",
  "iat": 1700500732,
  "exp": 1700507932,
  "jti": "7656ba25-62bb-4a7f-b621-3ae191b07b42"
}

Exp 1700507932 translates to Monday, 20 November 2023 20:18:52 [GMT+01:00] so that's exactly 2 hours from iat.

I dont know if there's been a change in Unifi's login handling since UniFi OS 3.2.6 or with Protect but this always worked fine for a year+ when running beta versions. I haven't seen any other reports in Home Assistant's git repo but seeing it's coming from pyunifiprotect, I thought this is an appropriate place.

As re-authentication takes place, it's just noise in the logs every 2hours and doesnt break anything (camera, doorbell notification).

CLI syntax question

In assigning multiple doorbell cameras to a chime, I received an error following the outlined syntax:

> uprotect chimes 62794a5802a3c003e7009af8 cameras 606f76fa00dbd903e7001197,606f6fee023cd903e700111e
Invalid camera ID: 606f76fa00dbd903e7001197,606f6fee023cd903e700111e

After replacing the comma with a space, the command completed without error and a test determined that both doorbells now ring the chime.

I have not tested to see if other multiple-device commands have the same parse issue.

Is there a way to assign different chime tones to each camera?

Note that I am running on Windows 11. I see that it is not supported, so feel free to ignore the report.

Snapshot quality

Hello,

I'm trying to make snapshots and looking at the quality it's blurry as if quality is not:

❯ unifi-protect cameras 655f4d7401f6bd13e40003ef save-snapshot  ~/Downloads/out.jpeg
❯ file ~/Downloads/out.jpeg
/Users/zp/Downloads/out.jpeg: JPEG image data, baseline, precision 8, 2688x1512, components 3

When downloading a snapshot from the UI stats are like this:

❯ file  "/Users/zp/Downloads/cam.jpg"
/Users/zp/Downloads/cam.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 1280x720, components 3

Any idea why this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.