Full Code of briis/unifiprotect for AI

master 7ba7cdf52826 cached
48 files
310.2 KB
73.3k tokens
156 symbols
1 requests
Download .txt
Showing preview only (327K chars total). Download the full file or copy to clipboard to get everything.
Repository: briis/unifiprotect
Branch: master
Commit: 7ba7cdf52826
Files: 48
Total size: 310.2 KB

Directory structure:
gitextract_8gl2b350/

├── .devcontainer/
│   ├── Dockerfile
│   ├── automations.yaml
│   ├── configuration.yaml
│   └── devcontainer.json
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   └── feature-request.yml
│   └── workflows/
│       └── ci.yaml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── tasks.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── blueprints/
│   └── automation/
│       └── unifiprotect/
│           ├── dynamic_doorbell.yaml
│           ├── push_notification_doorbell_event.yaml
│           ├── push_notification_motion_event.yaml
│           └── push_notification_smart_event.yaml
├── custom_components/
│   └── unifiprotect/
│       ├── __init__.py
│       ├── binary_sensor.py
│       ├── button.py
│       ├── camera.py
│       ├── config_flow.py
│       ├── const.py
│       ├── data.py
│       ├── entity.py
│       ├── light.py
│       ├── manifest.json
│       ├── media_player.py
│       ├── models.py
│       ├── number.py
│       ├── select.py
│       ├── sensor.py
│       ├── services.py
│       ├── services.yaml
│       ├── strings.json
│       ├── switch.py
│       ├── translations/
│       │   ├── da.json
│       │   ├── de.json
│       │   ├── en.json
│       │   ├── fr.json
│       │   ├── nb.json
│       │   └── nl.json
│       └── utils.py
├── hacs.json
├── info.md
├── pylintrc
├── pyproject.toml
└── unifiprotect.markdown

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
FROM ghcr.io/ludeeus/devcontainer/integration:stable

RUN apt update \
    && sudo apt install -y libpcap-dev ffmpeg vim curl jq libturbojpeg0 \
    && mkdir -p /opt \
    && cd /opt \
    && git clone --depth=1 -b 2021.12.7 https://github.com/home-assistant/core.git hass \
    && python3 -m pip --disable-pip-version-check install --upgrade ./hass \
    && python3 -m pip install pyunifiprotect mypy black isort pyupgrade pylint pylint_strict_informational \
    && ln -s /workspaces/unifiprotect/custom_components/unifiprotect /opt/hass/homeassistant/components/unifiprotect


================================================
FILE: .devcontainer/automations.yaml
================================================


================================================
FILE: .devcontainer/configuration.yaml
================================================
default_config:

tts:
  - platform: google_translate

stream:
  ll_hls: true
  part_duration: 0.75
  segment_duration: 2

debugpy:
  start: false

http:
  server_port: 9123

logger:
  default: warning
  logs:
    pyunifiprotect: debug
    custom_components.unifiprotect: debug

automation: !include automations.yaml


================================================
FILE: .devcontainer/devcontainer.json
================================================
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
{
	"name": "HA unifiprotect",
	"dockerFile": "Dockerfile",
	"context": "..",
	"appPort": [
		"9123:9123"
	],
	"runArgs": [
		"-v",
		"${env:HOME}${env:USERPROFILE}/.ssh:/tmp/.ssh" // This is added so you can push from inside the container
	],
	"extensions": [
		"ms-python.python",
		"github.vscode-pull-request-github",
		"ryanluker.vscode-coverage-gutters",
		"ms-python.vscode-pylance",
		"bungcip.better-toml",
	],
	"mounts": [
		"type=volume,target=/config,src=vsc-dev-unifiprotect-ha-config,volume-driver=local"
	],
	"settings": {
		"files.eol": "\n",
		"editor.tabSize": 4,
		"terminal.integrated.defaultProfile.linux": "bash",
		"python.pythonPath": "/usr/local/python/bin/python",
		"python.analysis.autoSearchPaths": false,
		"python.formatting.blackArgs": [
			"--line-length",
			"88"
		],
		"python.formatting.provider": "black",
		"python.linting.banditEnabled": false,
		"python.linting.enabled": true,
		"python.linting.flake8Enabled": false,
		"python.linting.mypyEnabled": false,
		"python.linting.pylintEnabled": true,
		"python.linting.pylintArgs": [
			"--rcfile=${workspaceFolder}/pyproject.toml"
		],
		"python.linting.pylamaEnabled": false,
		"python.sortImports.args": [
			"--settings-path=${workspaceFolder}/pyproject.toml"
		],
		"editor.formatOnPaste": false,
		"editor.formatOnSave": true,
		"editor.formatOnType": true,
		"files.trimTrailingWhitespace": true,
		"yaml.customTags": [
			"!secret scalar"
		]
	}
}


================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.yml
================================================
name: Bug Report
description: File a bug report for the Home Assistant UniFi Protect Integration
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: input
    id: ha-version
    attributes:
      label: Home Assistant Version?
      description: What version of Home Assistant are you running?
      placeholder: "2021.10.6"
    validations:
      required: true
  - type: input
    id: ufp-version
    attributes:
      label: UniFi Protect Version?
      description: What version of UniFi Protect is installed?
      placeholder: "1.20.0"
    validations:
      required: true
  - type: input
    id: integration-version
    attributes:
      label: UniFi Protect HA Integration version?
      description: What version of Integration do you have installed in Home Assistant?
      placeholder: "0.10.0"
    validations:
      required: true
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Also tell us, what did you expect to happen?
      placeholder: Describe the bug
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
      render: shell
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: Feature Reuest
description: Ask for a new feature for the Home Assistant UniFi Protect Integration
labels: ["feature-request"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this feature request.
  - type: textarea
    id: new-feature
    attributes:
      label: New Feature
      description: Please describe in detail what feature you would like to have.
      placeholder: Describe the feature
    validations:
      required: true
  - type: textarea
    id: additional-context
    attributes:
      label: Additional context
      description: Add any other context or screenshots about the feature request here.
    validations:
      required: false


================================================
FILE: .github/workflows/ci.yaml
================================================
name: CI

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, labeled, unlabeled, synchronize, ready_for_review, converted_to_draft]
  schedule:
    - cron: "0 0 * * *"

jobs:
  validate:
    runs-on: "ubuntu-latest"
    steps:
      - uses: "actions/checkout@v2"

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: 3.9

      - name: Hassfest
        uses: home-assistant/actions/hassfest@master

      - name: Install Linting Dependencies
        run: |
          git clone --depth=1 -b dev https://github.com/home-assistant/core.git hass
          rm -rf ./hass/homeassistant/components/unifiprotect
          ln -s $GITHUB_WORKSPACE/custom_components/unifiprotect ./hass/homeassistant/components/unifiprotect

          python -m pip install --upgrade pip
          pip install pyunifiprotect mypy black isort pyupgrade pylint pylint_strict_informational
          pip install ./hass

      - name: isort
        run: isort --check-only --quiet custom_components/unifiprotect

      - name: black
        run: black --check custom_components/unifiprotect

      - name: mypy
        run: cd ./hass && mypy homeassistant/components/unifiprotect

      - name: pyupgrade
        run: find . ! -path "./hass/*" -name "*.py" | xargs pyupgrade

      - name: pylint
        run: pylint --rcfile pyproject.toml custom_components/unifiprotect


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/
unifiprotect.code-workspace
.DS_Store
.vscode/settings.json
met.no.json
custom_components/unifiprotect/test.py

.devcontainer/secrets.yaml
custom_components/unifiprotect/unifi_protect_server_new.py
custom_components/unifiprotect/unifi_protect_server_org.py
.devcontainer/secret.yaml
custom_components/unifiprotect/services-old.yaml
test.py


================================================
FILE: .vscode/launch.json
================================================
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        // Run "Start Home Assistant" task + `debugpy.start` HA service first
        {
            "name": "Python: Debug UniFi Protect",
            "type": "python",
            "request": "attach",
            "justMyCode": false,
            "port": 5678,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "/workspaces/unifiprotect",
                    "remoteRoot": "/config"
                }
            ]
        }
    ]
}


================================================
FILE: .vscode/tasks.json
================================================
{
	"version": "2.0.0",
	"tasks": [
		{
			"label": "Start Home Assistant",
			"type": "shell",
			"command": "container start",
			"problemMatcher": []
		}
		{
			"label": "Set Home Assistant Version",
			"type": "shell",
			"command": "container set-version",
			"problemMatcher": []
		},
		{
			"label": "Install Development Home Assistant Version",
			"type": "shell",
			"command": "container install",
			"problemMatcher": []
		}
		{
			"label": "Update Translations",
			"type": "shell",
			"command": "echo 'unifiprotect\n' | python3 -m script.translations develop",
			"options": {
				"cwd": "/opt/hass",
			},
			"problemMatcher": []
		}
	]
}


================================================
FILE: CHANGELOG.md
================================================
# // Changelog

## 0.12.0

0.12.0 was originally planned as a beta only release, but after giving it more thought, I figured it would be be great to mark it as stable for the folks that cannot upgrade to the HA core version in 2022.2.

This release is primarily fixes from the HA core process. There is also full support added for the G4 Doorbell Pro, the UP Sense.

This will be the **last** HACS release. After this point, the HACS repo is considered deprecated. We will still take issues in the repo as if people prefer to make them here instead of the HA core repo. But after a month or 2 we plan to archive the repo and have the integration removed from HACS.

### Differences between HACS version 0.12.0 and HA 2022.2.0b1 version:

#### HACS Only

* Migration code for updating from `0.10.x` or older still exists; this code has been _removed_ in the HA core version

#### HA Core Only

* Full language support. All of the languages HA core supports via Lokalise has been added to the ingration.

* Auto-discovery. If you have a Dream machine or a Cloud Key/UNVR on the same VLAN, the UniFi Protect integration will automatically be discovered and prompted for setup.

* UP Doorlock support. The HA core version has full support for the newly release EA UP Doorlock.

### Changes

* `CHANGE`: **BREAKING CHANGE** Removes all deprecations outlined in the 0.11.x release.

* `CHANGE`: **BREAKING CHANGE** The "Chime Duration" number entity has been replaced with a "Chime Type" select entity. This makes Home Assistant work the same way as UniFi Protect. (https://github.com/briis/unifiprotect/issues/451)

* `CHANGE`: **BREAKING CHANGE** Smart Sensor support has been overhauled and improved. If you have Smart Sensors, it is _highly recommended to delete your UniFi Protect integration config and re-add it_. Some of the categories for the sensors have changed and it is not easy to change those without re-adding the integration. The sensors for the Smart Sensor are may also appear unavaiable if that sensor is not configured to be abled. For example, if your have motion disabled on your Sensor in UniFi Protect, the motion sensor will be unavaiable in Home Assistnat. Full list of new Smart Sensor entites:

  * Alarm Sound and Tampering binary sensors
  * Motion Sensitivity number
  * Mount Type and Paired Camera selects
  * Status Light switch
  * Configuration switches for various sensors:
    * Motion Detection switch
    * Temperature Sensor switch
    * Humidity Sensor switch
    * Light Sensor switch
    * Alarm Sound Detection switch

* `CHANGE`: **BREAKING CHANGE** Removes `profile_ws` debug service. Core plans to add a more centralized way of getting debug information from an integration. This will be back in some form after that feature is added (estimate: 1-2 major core releases).

* `CHANGE`: **BREAKING CHANGE** Removes `event_thumbnail` attribute and associated `ThumbnailProxyView`. After a lot of discussion, core does not want to add more attributes with access tokens inside of attributes. We plan to add back event thumbnails in some form again. If you would like to follow along with the dicussion, checkout the [architecure dicussion for it](https://github.com/home-assistant/architecture/discussions/705).

* `CHANGE`: Switches Doorbell binary_sensor to use `is_ringing` attr, should great improve relaiability of the sensor

* `CHANGE`: Dynamic select options for Doorbell Text

* `CHANGE`: Improves names for a number of entities

* `CHANGE`: Adds a bunch of extra debug logging for entity updates

* `NEW`: Adds full support for the package camera for the G4 Doorbell Pro. It should now always be enabled by default (if you are upgrading from an older version, it will still be disabled). The snapshot for the Package Camera has also been fixed. Since the camera if only 2 FPS, _streaming is disabled_ to prevent buffering.

* `FIX`: Overhaul of the Websocket code. Websocket reconnects should be drastically improved. Hopefully all reconnnect issues should be gone now.

* `FIX`: Fixes NVR memory sensor if no data is reported

* `FIX`: Fixes spelling typo with Recording Capacity sensor (https://github.com/briis/unifiprotect/issues/440)

* `FIX`: Fixes `is_connected` check for cameras

* `FIX`: Adds back `last_trip_time` attribute to camera motion entity

* `FIX`: Fixes NVR memory sensor if no data is reported

* `FIX`: Fixes spelling typo with Recording Capacity sensor (https://github.com/briis/unifiprotect/issues/440)

* `FIX`: Further improves relibility of Doorbell binary_sensor

* `FIX`: Fixes voltage unit for doorbell voltage sensor

* `FIX`: Fixes `connection_host` for Cameras so it can have DNS hosts in addition to IPs.

* `FIX`: Improves relibility of entities when UniFi Protect goes offline and/or a device goes offline. Everything recovery seemlessly when UniFi Protect upgrades or firmware updates are applied (fixes https://github.com/briis/unifiprotect/issues/432).

* `FIX`: Improves relibility of `media_player` entities so they should report state better and be able to play longer audio clips.

* `FIX`: Fixes stopping in progress audio for `media_player` entities.

* `FIX`: Allows DNS hosts in addition to IP addresses (fixes https://github.com/briis/unifiprotect/issues/431).

* `FIX`: Fixes selection of default camera entity for when it is not the High Quality channel.

* `FIX`: Fixes https://github.com/briis/unifiprotect/issues/428. All string enums are now case insensitive.

* `FIX`: Fixes https://github.com/briis/unifiprotect/issues/427, affected cameras will automatically be converted to Detections recording mode.

## 0.12.0-beta10

This is the last planned release for the HACS version. This release primarily adds new features for the G4 Doorbell Pro and the Smart Sensor. This release does unfortunatly have a couple of breaking changes for people with doorbells and Smart Sensors which are avoidable due to how soon the Home Assistant core release is.

* `CHANGE`: **BREAKING CHANGE** The "Chime Duration" number entity has been replaced with a "Chime Type" select entity. This makes Home Assistant work the same way as UniFi Protect. (https://github.com/briis/unifiprotect/issues/451)

* `CHANGE`: **BREAKING CHANGE** Smart Sensor support has been overhauled and improved. If you have Smart Sensors, it is _highly recommended to delete your UniFi Protect integration config and re-add it_. Some of the categories for the sensors have changed and it is not easy to change those without re-adding the integration. The sensors for the Smart Sensor are may also appear unavaiable if that sensor is not configured to be abled. For example, if your have motion disabled on your Sensor in UniFi Protect, the motion sensor will be unavaiable in Home Assistnat. Full list of new Smart Sensor entites:

  * Alarm Sound and Tampering binary sensors
  * Motion Sensitivity number
  * Mount Type and Paired Camera selects
  * Status Light switch
  * Configuration switches for various sensors:
    * Motion Detection switch
    * Temperature Sensor switch
    * Humidity Sensor switch
    * Light Sensor switch
    * Alarm Sound Detection switch

* `CHANGE`: Adds full support for the package camera for the G4 Doorbell Pro. It should now always be enabled by default (if you are upgrading from an older version, it will still be disabled). The snapshot for the Package Camera has also been fixed. Since the camera if only 2 FPS, _streaming is disabled_ to prevent buffering.

* `FIX`: Overhaul of the Websocket code. Websocket reconnects should be drastically improved. Hopefully all reconnnect issues should be gone now.

## 0.12.0-beta9

Home Assistant core port complete! The version that is in `2022.2` will officially have all of the same features. This is the final backport version to make sure the two versions are equal. The only difference between `0.12.0-beta9` and the code in `2022.2` is

* Migration code from `< 0.11.x` has been dropped. You must be on at least `0.11.0` or newer to migrate to the Home Assistant core version.

Additionally, we could not add _every_ feature from the HACS version to the HA core version so there are 2 additional breaking changes in this release (sorry!):

* `CHANGE`: **BREAKING CHANGE** Removes `profile_ws` and `take_sample` debug services. Core plans to add a more centralized way of getting debug information from an integration. This will be back in some form after that feature is added (estimate: 1-2 major core releases).

* `CHANGE`: **BREAKING CHANGE** Removes `event_thumbnail` attribute and associated `ThumbnailProxyView`. After a lot of discussion, core does not want to add more attributes with access tokens inside of attributes. We plan to add back event thumbnails in some form again. If you would like to follow along with the dicussion, checkout the [architecure dicussion for it](https://github.com/home-assistant/architecture/discussions/705).

Going forward, there will be some new features for the 0.12.0-beta / core version that will be developed for core version and then be backported to the HACS version. These include improvements for the G4 Doorbell Pro and the UP Sense devices.

## 0.12.0-beta8

* `FIX`: Fixes NVR memory sensor if no data is reported

* `FIX`: Fixes spelling typo with Recording Capacity sensor (https://github.com/briis/unifiprotect/issues/440)

* `FIX`: Fixes `is_connected` check for cameras

* `FIX`: Adds back `last_trip_time` attribute to camera motion entity

## 0.12.0-beta7

* `FIX`: Fixes NVR memory sensor if no data is reported

* `FIX`: Fixes spelling typo with Recording Capacity sensor (https://github.com/briis/unifiprotect/issues/440)

* `FIX`: Fixes is_connected check for cameras

* `FIX`: Adds back `last_trip_time` attribute to camera motion entity

## 0.12.0-beta7

* `FIX`: Improve relibility of Websocket reconnects

* `FIX`: Further improves relibility of Doorbell binary_sensor

## 0.12.0-beta6

* `CHANGE`: Switches Doorbell binary_sensor to use `is_ringing` attr, should great improve relaiability of the sensor

* `NEW`: Adds `take_sample` service to help with debugging/issue reporting

* `FIX`: Fixes voltage unit for doorbell voltage sensor

Backports changes from Home Assistant core merge process:

* `CHANGE`: Dynamic select options for Doorbell Text

* `CHANGE`: Improves names for a number of entities

* `CHANGE`: Adds a bunch of extra debug logging for entity updates

## 0.12.0-beta5

* `FIX`: Fixes `connection_host` for Cameras so it can have DNS hosts in addition to IPs.

## 0.12.0-beta4

Backports fixes from Home Assistant core merge process:

* `FIX`: Improves relibility of entities when UniFi Protect goes offline and/or a device goes offline. Everything recovery seemlessly when UniFi Protect upgrades or firmware updates are applied (fixes https://github.com/briis/unifiprotect/issues/432).

* `FIX`: Improves relibility of `media_player` entities so they should report state better and be able to play longer audio clips.

* `FIX`: Fixes stopping in progress audio for `media_player` entities.

* `FIX`: Allows DNS hosts in addition to IP addresses (fixes https://github.com/briis/unifiprotect/issues/431).

* `FIX`: Fixes selection of default camera entity for when it is not the High Quality channel.

## 0.12.0-beta3

* `FIX`: Fixes https://github.com/briis/unifiprotect/issues/428. All string enums are now case insensitive.

## 0.12.0-beta2

* `FIX`: Fixes https://github.com/briis/unifiprotect/issues/427, affected cameras will automatically be converted to Detections recording mode.

## 0.12.0-beta1

The 0.12.0-beta is designed to be a "beta only" release. There will not be a stable release for it. It is designed to test the final changes needed to merge the unifiprotect into Home Assistant core.

* `CHANGE`: **BREAKING CHANGE** Removes all deprecations outlined in the 0.11.x release.

## 0.11.2

* `FIX`: Setting up camera entities will no longer error if a camera does not have a channel. Will now result in log and continue

* `FIX`: Unadopted entities are ignored (fixes #420)

* `FIX`: Event thumbnails now return instantly using newer endpoint from UniFi Protect. They appear to come back as a camera snapshot until after the events ends, but they should always return an image now.

## 0.11.1

### Deprecations

As an amended to the deprecations from 0.11.0, the `last_tripped_time` is _no longer_ deprecated as `last_changed` is not a full replacement (#411)

### Other changes

* `FIX`: Bumps version of `pyunifiprotect` to 1.3.4. This will fix talkback for all cameras that was not working as expected

## 0.11.0

### Deprecations

0.11 is last major release planned before we merge the `unifiprotect` integration into core. As a result, a number of features are being removed when we merged into core.

The following services will be removed in the next version:

* `unifiprotect.set_recording_mode` -- use the select introduced in 0.10 instead
* `unifiprotect.set_ir_mode` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_status_light` -- use the switch entity on the camera device instead
* `unifiprotect.set_hdr_mode` -- use the switch entity on the camera device instead
* `unifiprotect.set_highfps_video_mode` -- use the switch entity on the camera device instead
* `unifiprotect.set_doorbell_lcd_message` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_mic_volume` -- use the number entity introduced in 0.10 instead
* `unifiprotect.set_privacy_mode` -- use the switch entity introduced in 0.10 instead
* `unifiprotect.set_zoom_position` -- use the number entity introduced in 0.10 instead
* `unifiprotect.set_wdr_value` -- use the number entity introduced in 0.10 instead
* `unifiprotect.light_settings` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_viewport_view` -- use the select entity introduced in 0.10 instead

The following events will be removed in the next version:

* `unifiprotect_doorbell` -- use a State Changed event on "Doorbell" binary sensor on the device instead
* `unifiprotect_motion` -- use a State Changed event on the "Motion" binary sensor on the device instead

The following entities will be removed in the next version:

* The "Motion Recording" sensor for cameras (in favor of the "Recording Mode" select)
* The "Light Turn On" sensor for flood lights (in favor of the "Lighting" select)

All of following attributes should be duplicated data that can be gotten from other devices/entities and as such, they will be removed in the next version.

* `device_model` will be removed from all entities -- provided in the UI as part of the "Device Info"
* `last_tripped_time` will be removed from binary sensor entities -- use the `last_changed` value provided by the [HA state instead](https://www.home-assistant.io/docs/configuration/state_object/)
* `up_since` will be removed from camera and light entities -- now has its own sensor. The sensor is disabled by default so you will need to enable it if you want to use it.
* `enabled_at` will be removed from light entities -- now has its own sensor
* `camera_id` will be removed from camera entities -- no services need the camera ID anymore so it does not need to be exposed as an attribute. You can still get device IDs for testing/debugging from the Configuration URL in the "Device Info" section
* `chime_duration`, `is_dark`, `mic_sensitivity`, `privacy_mode`, `wdr_value`, and `zoom_position`  will be removed from camera entities -- all of them have now have their own sensors
* `event_object` will be removed from the Motion binary sensor. Use the dedicated Detected Object sensor.

### Breaking Changes in this release

* `CHANGE`: **BREAKING CHANGE** The internal name of the Privacy Zone controlled by the "Privacy Mode" switch has been changed. Make sure you turn off all of your privacy mode switches before upgrading. If you do not, you will need to manually delete the old Privacy Zone from your UniFi Protect app.

* `CHANGE`: **BREAKING CHANGE** WDR `number` entity has been removed from Cameras that have HDR. This is inline with changes made to Protect as you can no longer control WDR for cameras with HDR.

* `CHANGE`: **BREAKING CHANGE** the `event_length` attribute has been removed from the motion and door binary sensors. The value was previously calculated in memory and not reliable between restarts.

* `CHANGE`: **BREAKING CHANGE** the `event_object` attribute for binary motion sensors has changed the value for no object detected from "None Identified" (string) to "None" (NoneType/null)

* `CHANGE`: **BREAKING CHANGE** The Doorbell Text select entity for Doorbells has been overhauled. The Config Flow option for Doorbell Messages has been removed. You now can use the the  `unifiprotect.add_doorbell_text` and `unifiprotect.remove_doorbell_text` services to add/remove Doorbell messages. This will persist the messages in UniFi Protect and the choices will now be the same ones that appear in the UniFi Protect iOS/Android app. **NOTE**: After running one of these services, you must restart Home Assistant for the updated options to appear.

### Other Changes in this release

* `CHANGE`: Migrates `UpvServer` to new `ProtectApiClient` from `pyunifiprotect`.
    * This should lead to a number of behind-the-scenes reliability improvements.
      * Should fix/close the following issues: #248, #255, #297, #317, #341, and #360 (TODO: Verify)

* `CHANGE`: Overhaul Config Flow
    * Adds Reauthentication support
    * Adds "Verify SSL"
    * Updates Setup / Reauth / Options flows to pre-populate forms from existing settings
    * Removes changing username/password as part of the options flow as it is redundant with Reauthentication support
    * Removes Doorbell Text option since it is handled directly by UniFi Protect now
    * Adds new config option to update all metrics (storage stat usage, uptimes, CPU usage, etc.) in realtime. **WARNING**: Enabling this option will greatly increase your CPU usage. ~2x is what we were seeing in our testing. It is recommended to leave it disabled for now as we do not have a lot of diagnostic sensors using this data yet.

* `CHANGE`: The state of the camera entities now reflects on whether the camera is actually recording. If you set your Recording Mode to "Detections", your camera will switch back and forth between "Idle" and "Recording" based on if the camera is actually recording.
  * Closes #337

* `CHANGE`: Configuration URLs for UFP devices will now take you directly to the device in the UFP Web UI.

* `CHANGE`: Default names for all entities have been updated from `entity_name device_name` to `device_name entity_name` to match how Home Assistant expects them in 2021.11+

* `CHANGE`: The Bluetooth strength sensor for the UP Sense is now disabled by default (will not effect anyone that already has the sensor).

* `NEW`: Adds `unifiprotect.set_doorbell_message` service. This is just like the `unifiprotect.set_doorbell_lcd_message`, but it is not deprecated and it requires the Doorbell Text Select entity instead of the Camera entity. Should **only** be used to set dynamic doorbell text messages (i.e. setting the current outdoor temperate on your doorbell). If you want to use static custom messages, use the Doorbell Text Select entity and the `unifiprotect.add_doorbell_text` / `unifiprotect.remove_doorbell_text` service. `unifiprotect.set_doorbell_lcd_message` is still deprecated and will still be removed in the next release.
  * Closes #396

* `NEW`: Adds "Override Connection Host" config option. This will force your RTSP(S) connection IP address to be the same as everything else. Should only be used if you need to forcibly use a different IP address.
  * For sure closes #248

* `NEW`: Added Dark Mode brand images to https://github.com/home-assistant/brands.

* `NEW`: Adds `phy_rate` and `wifi_signal` sensors so all connection states (BLE, WiFi and Wired) should have a diagnostic sensor. Disabled by default. Requires "Realtime metrics" option to update in realtime.

* `NEW`: Added Detected Object sensor for cameras with smart detections. Values are `none`, `person` or `vehicle`. Contains `event_score` and `event_thumb` attributes.
  * Closes #342

* `NEW`: Adds Paired Camera select entity for Viewports

* `NEW`: Adds "Received Data", "Transferred Data", "Oldest Recording", "Storage Used", and "Disk Write Rate" sensors for cameras. Disabled by default. Requires "Realtime metrics" option to update in realtime.

* `NEW`: (requires UniFi Protect 1.20.1) Adds "Voltage" sensor for doorbells. Disabled by default.

* `NEW`: Adds "System Sounds" switch for cameras with speakers

* `NEW`: Adds switches to toggle overlay information for video feeds on all cameras

* `NEW`: Adds switches to toggle smart detection types on cameras with smart detections

* `NEW`: Adds event thumbnail proxy view.
  * URL is `/api/ufp/thumbnail/{thumb_id}`. `thumb_id` is the ID of the thumbnail from UniFi Protect.
  * `entity_id` is a required query parameters. `entity_id` be for an sensor that has event thumbnails on it (like the Motion binary sensor)
  * `token` is a required query parameter is you are _not_ authenticated. It is an attribute on the motion sensor for the Camera
  * `w` and `h` are optional query string params for thumbnail resizing.

* `NEW`: Adds `event_thumbnail` attribute to Motion binary sensor that uses above mentioned event thumbnail proxy view.

* `NEW`: Adds NVR sensors. All of them are disabled by default. All of the sensors will only update every ~15 minutes unless the "Realtime metrics" config option is turned on. List of all sensors:
    * Disk Health (one per disk)
    * System Info: CPU Temp, CPU, Memory and Storage Utilization
    * Uptime
    * Recording Capacity (in seconds)
    * Distributions of stored video for Resolution (4K/HD/Free)
    * Distributions of stored video for Type (Continuous/Detections/Timelapse)

* More clean up and improvements for upcoming Home Assistant core merge.

* Adds various new blueprints to help users automate UniFi Protect. New [Blueprints can be found in the README](https://github.com/briis/unifiprotect#automating-services)

## 0.11.0-beta.5

* `FIX`: Fixes motion events and sensors for UP-Sense devices (#405)

* `FIX`: Fixes error on start up for G4 Domes (#408)

## 0.11.0-beta.4

* `NEW`: Adds `unifiprotect.set_doorbell_message` service. This is just like the `unifiprotect.set_doorbell_lcd_message`, but it is not deprecated and it requires the Doorbell Text Select entity instead of the Camera entity. Should **only** be used to set dynamic doorbell text messages (i.e. setting the current outdoor temperate on your doorbell). If you want to use static custom messages, use the Doorbell Text Select entity and the `unifiprotect.add_doorbell_text` / `unifiprotect.remove_doorbell_text` service. `unifiprotect.set_doorbell_lcd_message` is still deprecated and will still be removed in the next release.
  * Closes #396

* `NEW`: Adds "Override Connection Host" config option. This will force your RTSP(S) connection IP address to be the same as everything else. Should only be used if you need to forcibly use a different IP address.
  * For sure closes #248

* `FIX`: Reset event_thumbnail attribute for Motion binary sensor after motion has ended

* `FIX`: Change unit for signal strength from db to dbm. (fixes Camera Wifi Signal Strength should be dBm not dB)
  * Closes #394

* `NEW`: Added Dark Mode brand images to https://github.com/home-assistant/brands.

## 0.11.0-beta.3

* `DEPRECATION`: The Motion binary sensor will stop showing details about smart detections in the next version. Use the new separate Detected Object sensor. `event_object` attribute will be removed as well.

* `NEW`: Adds `phy_rate` and `wifi_signal` sensors so all connection states (BLE, WiFi and Wired) should have a diagnostic sensor. Disabled by default. Requires "Realtime metrics" option to update in realtime.

* `NEW`: Added Detected Object sensor for cameras with smart detections. Values are `none`, `person` or `vehicle`. Contains `event_score` and `event_thumb` attributes.
  * Closes #342

* `NEW`: Adds Paired Camera select entity for Viewports

* `NEW`: Adds "Received Data", "Transferred Data", "Oldest Recording", "Storage Used", and "Disk Write Rate" sensors for cameras. Disabled by default. Requires "Realtime metrics" option to update in realtime.

* `NEW`: (requires UniFi Protect 1.20.1) Adds "Voltage" sensor for doorbells. Disabled by default.

* `NEW`: Adds "System Sounds" switch for cameras with speakers

* `NEW`: Adds switches to toggle overlay information for video feeds on all cameras

* `NEW`: Adds switches to toggle smart detection types on cameras with smart detections

## 0.11.0-beta.2

* `CHANGE`: Allows `device_id` parameter for global service calls to be any device from a UniFi Protect instance

* `NEW`: Adds event thumbnail proxy view.
  * URL is `/api/ufp/thumbnail/{thumb_id}`. `thumb_id` is the ID of the thumbnail from UniFi Protect.
  * `entity_id` is a required query parameters. `entity_id` be for an sensor that has event thumbnails on it (like the Motion binary sensor)
  * `token` is a required query parameter is you are _not_ authenticated. It is an attribute on the motion sensor for the Camera
  * `w` and `h` are optional query string params for thumbnail resizing.

* `NEW`: Adds `event_thumbnail` attribute to Motion binary sensor that uses above mentioned event thumbnail proxy view.

* `NEW`: Adds NVR sensors. All of them are disabled by default. All of the sensors will only update every ~15 minutes unless the "Realtime metrics" config option is turned on. List of all sensors:
    * Disk Health (one per disk)
    * System Info: CPU Temp, CPU, Memory and Storage Utilization
    * Uptime
    * Recording Capacity (in seconds)
    * Distributions of stored video for Resolution (4K/HD/Free)
    * Distributions of stored video for Type (Continuous/Detections/Timelapse)

* More clean up and improvements for upcoming Home Assistant core merge.

## 0.11.0-beta.1

### Deprecations

0.11 is last major release planned before we merge the `unifiprotect` integration into core. As a result, a number of features are being removed when we merged into core.

The following services will be removed in the next version:

* `unifiprotect.set_recording_mode` -- use the select introduced in 0.10 instead
* `unifiprotect.set_ir_mode` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_status_light` -- use the switch entity on the camera device instead
* `unifiprotect.set_hdr_mode` -- use the switch entity on the camera device instead
* `unifiprotect.set_highfps_video_mode` -- use the switch entity on the camera device instead
* `unifiprotect.set_doorbell_lcd_message` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_mic_volume` -- use the number entity introduced in 0.10 instead
* `unifiprotect.set_privacy_mode` -- use the switch entity introduced in 0.10 instead
* `unifiprotect.set_zoom_position` -- use the number entity introduced in 0.10 instead
* `unifiprotect.set_wdr_value` -- use the number entity introduced in 0.10 instead
* `unifiprotect.light_settings` -- use the select entity introduced in 0.10 instead
* `unifiprotect.set_viewport_view` -- use the select entity introduced in 0.10 instead

The following events will be removed in the next version:

* `unifiprotect_doorbell` -- use a State Changed event on "Doorbell" binary sensor on the device instead
* `unifiprotect_motion` -- use a State Changed event on the "Motion" binary sensor on the device instead

The following entities will be removed in the next version:

* The "Motion Recording" sensor for cameras (in favor of the "Recording Mode" select)
* The "Light Turn On" sensor for flood lights (in favor of the "Lighting" select)

All of following attributes should be duplicated data that can be gotten from other devices/entities and as such, they will be removed in the next version.

* `device_model` will be removed from all entities -- provided in the UI as part of the "Device Info"
* `last_tripped_time` will be removed from binary sensor entities -- use the `last_changed` value provided by the [HA state instead](https://www.home-assistant.io/docs/configuration/state_object/)
* `up_since` will be removed from camera and light entities -- now has its own sensor. The sensor is disabled by default so you will need to enable it if you want to use it.
* `enabled_at` will be removed from light entities -- now has its own sensor
* `camera_id` will be removed from camera entities -- no services need the camera ID anymore so it does not need to be exposed as an attribute. You can still get device IDs for testing/debugging from the Configuration URL in the "Device Info" section
* `chime_duration`, `is_dark`, `mic_sensitivity`, `privacy_mode`, `wdr_value`, and `zoom_position`  will be removed from camera entities -- all of them have now have their own sensors


### Breaking Changes in this release

* `CHANGE`: **BREAKING CHANGE** The internal name of the Privacy Zone controlled by the "Privacy Mode" switch has been changed. Make sure you turn off all of your privacy mode switches before upgrading. If you do not, you will need to manually delete the old Privacy Zone from your UniFi Protect app.

* `CHANGE`: **BREAKING CHANGE** WDR `number` entity has been removed from Cameras that have HDR. This is inline with changes made to Protect as you can no longer control WDR for cameras with HDR.

* `CHANGE`: **BREAKING CHANGE** the `event_length` attribute has been removed from the motion and door binary sensors. The value was previously calculated in memory and not reliable between restarts.

* `CHANGE`: **BREAKING CHANGE** the `event_object` attribute for binary motion sensors has changed the value for no object detected from "None Identified" (string) to "None" (NoneType/null)

* `CHANGE`: **BREAKING CHANGE** The Doorbell Text select entity for Doorbells has been overhauled. The Config Flow option for Doorbell Messages has been removed. You now can use the the  `unifiprotect.add_doorbell_text` and `unifiprotect.remove_doorbell_text` services to add/remove Doorbell messages. This will persist the messages in UniFi Protect and the choices will now be the same ones that appear in the UniFi Protect iOS/Android app. **NOTE**: After running one of these services, you must restart Home Assistant for the updated options to appear.

### Other Changes in this release

* `CHANGE`: Migrates `UpvServer` to new `ProtectApiClient` from `pyunifiprotect`.
    * This should lead to a number of behind-the-scenes reliability improvements.
      * Should fix/close the following issues: #248, #255, #297, #317, #341, and #360 (TODO: Verify)

* `CHANGE`: Overhaul Config Flow
    * Adds Reauthentication support
    * Adds "Verify SSL"
    * Updates Setup / Reauth / Options flows to pre-populate forms from existing settings
    * Removes changing username/password as part of the options flow as it is redundant with Reauthentication support
    * Removes Doorbell Text option since it is handled directly by UniFi Protect now
    * Adds new config option to update all metrics (storage stat usage, uptimes, CPU usage, etc.) in realtime. **WARNING**: Enabling this option will greatly increase your CPU usage. ~2x is what we were seeing in our testing. It is recommended to leave it disabled for now as we do not have a lot of diagnostic sensors using this data yet.

* `CHANGE`: The state of the camera entities now reflects on whether the camera is actually recording. If you set your Recording Mode to "Detections", your camera will switch back and forth between "Idle" and "Recording" based on if the camera is actually recording.
  * Closes #337

* `CHANGE`: Configuration URLs for UFP devices will now take you directly to the device in the UFP Web UI.

* `CHANGE`: Default names for all entities have been updated from `entity_name device_name` to `device_name entity_name` to match how Home Assistant expects them in 2021.11+

* `CHANGE`: The Bluetooth strength sensor for the UP Sense is now disabled by default (will not effect anyone that already has the sensor).

* `NEW`: Adds all of the possible enabled UFP Camera channels as different camera entities; only the highest resolution secure (RTSPS) one is enabled by default. If you need RTSP camera entities, you can enable one of the given insecure camera entities.

* `NEW`: Added the following attributes to Camera entity: `width`, `height`, `fps`, `bitrate` and `channel_id`

* `NEW`: Added status light switch for Flood Light devices

* `NEW`: Added "On Motion - When Dark" option for Flood Light Lighting switch

* `NEW`: Added "Auto-Shutoff Timer" number entity for Flood Lights

* `NEW`: Added "Motion Sensitivity" number entity for Flood Lights

* `NEW`: Added "Chime Duration" number entity for Doorbells

* `NEW`: Added "Uptime" sensor entity for all UniFi Protect adoptable devices. This is disabled by default.

* `NEW`: Added `unifiprotect.set_default_doorbell_text` service to allow you to set your default Doorbell message text. **NOTE**: After running this service, you must restart Home Assistant for the default to be reflected in the options.

* `NEW`: Added "SSH Enabled" switch for all adoptable UniFi Protect devices. This switch is disabled by default.

* `NEW`: (requires 2021.12+) Added "Reboot Device" button for all adoptable UniFi Protect devices. This button is disabled by default. Use with caution as there is no confirm. "Pressing" it instantly reboots your device.

* `NEW`: Added media player entity for cameras with speaker. Speaker will accept any ffmpeg playable audio file URI (URI must be accessible from _Home Assistant_, not your Camera). TTS works great!
  * TODO: Investigate for final release. This _may_ not work as expected on G4 Doorbells. Not sure yet if it is because of the recent Doorbell issues or because Doorbells are different.
  * Implements #304


## 0.10.0

Released: 2021-11-24

> **YOU MUST BE RUNNING V1.20.0 OF UNIFI PROTECT, TO USE THIS VERSION OF THE INTEGRATION. IF YOU ARE STILL ON 1.19.x STAY ON THE 0.9.2 RELEASE.

As UniFi Protect V1.20.0 is now released, we will also ship the final release of 0.10.0. If you were not on the beta, please read these Release Notes carefully, as there are many changes for this release, and many Breaking Changes.

### Supported Versions

This release requires the following minimum Software and Firmware version:

* **Home Assistant**: `2021.09.0`
* **UniFi Protect**: `1.20.0`

### Upgrade Instructions

> If you are already running V0.10.0-beta.3 or higher of this release, there should not be any breaking changes, and you should be able to do a normal upgrade from HACS.

Due to the many changes and entities that have been removed and replaced, we recommend the following process to upgrade from an earlier Beta or from an earlier release:

* Upgrade the Integration files, either through HACS (Recommended) or by copying the files manually to your `custom_components/unifiprotect` directory.
* Restart Home Assistant
* Remove the UniFi Protect Integration by going to the Integrations page, click the 3 dots in the lower right corner of the UniFi Protect Integration and select *Delete*
* While still on this page, click the `+ ADD INTEGRATION` button in the lower right corner, search for UnFi Protect, and start the installation, supplying your credentials.

### Changes in this release

* `CHANGE`: **BREAKING CHANGE** The support for *Anonymous Snapshots* has been removed as of this release. This has always been a workaround in a time where this did not work as well as it does now. If you have this flag set, you don't have to do anything, as snapshots are automatically moved to the supported method.

* `NEW`: **BREAKING CHANGE** Also as part of Home Assistant 2021.11 a new [Entity Category](https://www.home-assistant.io/blog/2021/11/03/release-202111/#entity-categorization) is introduced. This makes it possible to classify an entity as either `config` or `diagnostic`. A `config` entity is used for entities that can change the configuration of a device and a `diagnostic` entity is used for devices that report status, but does not allow changes. These two entity categories have been applied to selected entities in this Integration. If you are not on HA 2021.11+ then this will not have any effect on your installation.

* `CHANGE`: **BREAKING CHANGE** There has been a substansial rewite of the underlying IO API Module (`pyunifiprotect`) over the last few month. The structure is now much better and makes it easier to maintain going forward. It will take too long to list all the changes, but one important change is that we have removed the support for Non UnifiOS devices. These are CloudKey+ devices with a FW lower than 2.0.24. I want to give a big thank you to @AngellusMortis and @bdraco for making this happen.

* `CHANGE`: **BREAKING CHANGE** As this release has removed the support for Non UnifiOS devices, we could also remove the Polling function for Events as this is served through Websockets. This also means that the Scan Interval is no longer present in the Configuration.

* `CHANGE`: **BREAKING CHANGE** To future proof the Select entities, we had to change the the way the Unique ID is populated. The entity names are not changing, but the Unique ID's are If you have installed a previous beta of V0.10.0 you will get a duplicate of all Select entities, and the ones that were there before, will be marked as unavailable. You can either remove them manually from the Integration page, or even easier, just delete the UniFi Protect integration, and add it again. (The later is the recommended method)

* `CHANGE`: **BREAKING CHANGE** All switches called `switch.ir_active_CAMERANAME` have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.

* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.set_ir_mode` now supports the following values for ir_mode: `"auto, autoFilterOnly, on, off"`. This is a change from the previous valid options and if you have automations that uses this service you will need to make sure that you only use these supported modes.

* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.save_thumbnail_image` has been removed from the Integration. This service proved to be unreliable as the Thumbnail image very often was not available, when this service was called. Please use the service `camera.snapshot` instead.

* `CHANGE`: **BREAKING CHANGE** All switches called `switch.record_smart_CAMERANAME` and `switch.record_motion_CAMERANAME` have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.

* `CHANGE`: **BREAKING CHANGE** All switches for the *Floodlight devices* have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.

* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.set_recording_mode` now only supports the following values for recording_mode: `"never, detections, always"`. If you have automations that uses the recording_mode `smart` or `motion` you will have to change this to `detections`.

* `CHANGE`: Config Flow has been slimmed down so it will only ask for the minimum values we need during installation. If you would like to change this after that, you can use the Configure button on the Integration page.

* `CHANGE`: It is now possible to change the UFP Device username and password without removing and reinstalling the Integration. On the Home Assistant Integration page, select CONFIGURE in the lower left corner of the UniFi Protect integration, and you will have the option to enter a new username and/or password.

* `CHANGE`: We will now use RTSPS for displaying video. This is to further enhance security, and to ensure that the system will continue running if Ubiquiti decides to remove RTSP completely. This does not require any changes from your side.

* `NEW`: For each Camera there will be a binary sensor called `binary_sensor.is_dark_CAMERANAME`. This sensor will be on if the camera is perceiving it is as so dark that the Infrared lights will turn on (If enabled).

* `CHANGE`: A significant number of 'under the hood' changes have been made by @bdraco, to bring the Integration up to Home Assistant standards and to prepare for the integration in to HA Core. Thank you to @bdraco for all his advise, coding and review.

* `CHANGE`: `pyunifiprotect` is V1.0.4 and has been completely rewritten by @AngellusMortis, with the support of @bdraco and is now a much more structured and easier to maintain module. There has also been a few interesting additions to the module, which you will see the fruit of in a coming release. This version is not utilizing the new module yet, but stay tuned for the 0.11.0 release, which most likely also will be the last release before we try the move to HA Core.

* `NEW`: Device Configuration URL's are introduced in Home Assistant 2021.11. In this release we add URL Link to allow the user to visit the device for configuration or diagnostics from the *Devices* page. If you are not on HA 2021.11+ then this will not have any effect on your installation.

* `NEW`: A switch is being created to turn on and off the Privacy Mode for each Camera. This makes it possible to set the Privacy mode for a Camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_privacy_mode`

* `NEW`: Restarted the work on implementing the UFP Sense device. We don't have physical access to this device, but @Madbeefer is kindly enough to do all the testing.
  * The following new sensors will be created for each UFP Sense device: `Battery %`, `Ambient Light`, `Humidity`, `Temperature` and `BLE Signal Strength`.
  * The following binary sensors will be created for each UFP Sense device: `Motion`, `Open/Close` and `Battery Low`. **Note** as of this release, these sensors are not working correctly, this is still work in progress.

* `NEW`: For each Camera there will now be a `Select Entity` from where you can select the Infrared mode for each Camera. Valid options are `Auto, Always Enable, Auto (Filter Only, no LED's), Always Disable`. These are the same options you can use if you set this through the UniFi Protect App.

* `NEW`: Added a new `Number` entity called `number.wide_dynamic_range_CAMERANAME`. You can now set the Wide Dynamic Range for a camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_wdr_value`.

* `NEW`: Added `select.doorbell_text_DOORBELL_NAME` to be able to change the LCD Text on the Doorbell from the UI. In the configuration menu of the Integration there is now a field where you can type a list of Custom Texts that can be displayed on the Doorbell and then these options plus the two standard texts built-in to the Doorbell can now all be selected. The format of the custom text list has to ba a comma separated list, f.ex.: RING THE BELL, WE ARE SLEEPING, GO AWAY... etc.

* `NEW`: Added a new `Number` entity called `number.microphone_level_CAMERANAME`. From here you can set the Microphone Sensitivity Level for a camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_mic_volume`.

* `NEW`: Added a new `Number` entity called `number.zoom_position_CAMERANAME`. From here you can set the optical Zoom Position for a camera directly from the UI. This entity will only be added for Cameras that support optical zoom. This is a supplement to the already existing service `unifiprotect.set_zoom_position`.

* `NEW`: For each Camera there will now be a `Select Entity` from where you can select the recording mode for each Camera. Valid options are `Always, Never, Detections`. Detections is what you use to enable motion detection. Whether they do People and Vehicle detection, depends on the Camera Type and the settings in the UniFi Protect App. We might later on implement a new Select Entity from where you can set the the Smart Detection options. Until then, this needs to be done from the UniFi Protect App. (as is the case today)

* `NEW`: For each Floodlight there will now be a `Select Entity` from where you can select when the Light Turns on. This replaces the two switches that were in the earlier releases. Valid options are `On Motion, When Dark, Manual`.

* `NEW`: Added a new event `unifiprotect_motion` that triggers on motion. You can use this instead of the Binary Sensor to watch for a motion event on any motion enabled device. The output from the event will look similar tom the below

  ```json
  {
    "event_type": "unifiprotect_motion",
    "data": {
        "entity_id": "camera.outdoor",
        "smart_detect": [
            "person"
        ],
        "motion_on": true
    },
    "origin": "LOCAL",
    "time_fired": "2021-10-18T10:55:36.134535+00:00",
    "context": {
        "id": "b3723102b4fb71a758a423d0f3a04ba6",
        "parent_id": null,
        "user_id": null
    }
  }
  ```


## 0.10.0 Beta 5 Hotfix 1

Released: November 13th, 2021

### Supported Versions

This release requires the following minimum Software and Firmware version:

* **Home Assistant**: `2021.09.0`
* **UniFi Protect**: `1.20.0-beta.7`

### Upgrade Instructions

Due to the many changes and entities that have been removed and replaced, we recommend the following process to upgrade from an earlier Beta or from an earlier release:

* Upgrade the Integration files, either through HACS (Recommended) or by copying the files manually to your `custom_components/unifiprotect` directory.
* Restart Home Assistant
* Remove the UniFi Protect Integration by going to the Integrations page, click the 3 dots in the lower right corner of the UniFi Protect Integration and select *Delete*
* While still on this page, click the `+ ADD INTEGRATION` button in the lower right corner, search for UnFi Protect, and start the installation, supplying your credentials.

### Changes in this release

* `CHANGE`: Updated `pyunifiprotect` to 1.0.2. Fixing errors that can occur when using Python 3.9 - Home Assistant uses that.

## 0.10.0 Beta 5

Released: November 13th, 2021

### Supported Versions

This release requires the following minimum Software and Firmware version:

* **Home Assistant**: `2021.09.0`
* **UniFi Protect**: `1.20.0-beta.7`

### Upgrade Instructions

Due to the many changes and entities that have been removed and replaced, we recommend the following process to upgrade from an earlier Beta or from an earlier release:

* Upgrade the Integration files, either through HACS (Recommended) or by copying the files manually to your `custom_components/unifiprotect` directory.
* Restart Home Assistant
* Remove the UniFi Protect Integration by going to the Integrations page, click the 3 dots in the lower right corner of the UniFi Protect Integration and select *Delete*
* While still on this page, click the `+ ADD INTEGRATION` button in the lower right corner, search for UnFi Protect, and start the installation, supplying your credentials.

### Changes in this release

As there were still some changes we wanted to do before releasing this, we decided to do one more Beta, before freezing.

* `CHANGE`: The support for *Anonymous Snapshots* has been removed as of this release. This had always been a workaround in a time where this did not work as well as it does now. If you have this flag set, you don't have to do anything, as snapshots are automatically moved to the supported method.
* `CHANGE`: Config Flow has been slimmed down so it will only ask for the minimum values we need during installation. If you would like to change this after that, you can use the Configure button on the Integration page.
* `CHANGE`: It is now possible to change the UFP Device username and password without removing and reinstalling the Integration. On the Home Assistant Integration page, select CONFIGURE in the lower left corner of the UniFi Protect integration, and you will have the option to enter a new username and/or password.
* `NEW`: For each Camera there will be a binary sensor called `binary_sensor.is_dark_CAMERANAME`. This sensor will be on if the camera is perceiving it is as so dark that the Infrared lights will turn on (If enabled).
* `CHANGE`: A significant number of 'under the hood' changes have been made, to bring the Integration up to Home Assistant standards and to prepare for the integration in to HA Core. Thank you to @bdraco for all his advise, coding and review.
* `CHANGE`: `pyunifiprotect` has been completely rewritten by @AngellusMortis, with the support of @bdraco and is now a much more structured and easier to maintain module. There has also been a few interesting additions to the module, which you will see the fruit of in a coming release. This version is not utilizing the new module yet, but stay tuned for the 0.11.0 release, which most likely also will be the last release before we try the move to HA Core.

## 0.10.0 Beta 4

Released: November 4th, 2021

**REMINDER** This version is only valid for **V1.20.0-beta.2** or higher of UniFi Protect. If you are not on that version, stick with V0.9.1.

### Upgrade Instructions

Due to the many changes and entities that have been removed and replaced, we recommend the following process to upgrade from an earlier Beta or from an earlier release:

* Upgrade the Integration files, either through HACS (Recommended) or by copying the files manually to your `custom_components/unifiprotect` directory.
* Restart Home Assistant
* Remove the UniFi Protect Integration by going to the Integrations page, click the 3 dots in the lower right corner of the UniFi Protect Integration and select *Delete*
* While still on this page, click the `+ ADD INTEGRATION` button in the lower right corner, search for UnFi Protect, and start the installation, supplying your credentials.

### Changes in this release

This will be the last beta with functional changes, so after this release it will only be bug fixes. The final release will come out when 1.20 of UniFi Protect is officially launched. Everything from Beta 1, 2 and 3 is included here, plus the following:

* `NEW`: Device Configuration URL's are introduced in Home Assistant 2021.11. In this release we add URL Link to allow the user to visit the device for configuration or diagnostics from the *Devices* page. If you are not on HA 2021.11+ then this will not have any effect on your installation.
* `NEW`: **BREAKING CHANGE** Also as part of Home Assistant 2021.11 a new [Entity Category](https://www.home-assistant.io/blog/2021/11/03/release-202111/#entity-categorization) is introduced. This makes it possible to classify an entity as either `config` or `diagnostic`. A `config` entity is used for entities that can change the configuration of a device and a `diagnostic` entity is used for devices that report status, but does not allow changes. These two entity categories have been applied to selected entities in this Integration. If you are not on HA 2021.11+ then this will not have any effect on your installation.<br>
We would like to have feedback from people on this choice. Have we categorized too many entities, should we not use this at all. Please come with the feedback.<br>
Entities which have the entity_category set:
  * Are not included in a service call targetting a whole device or area.
  * Are, by default, not exposed to Google Assistant or Alexa. If entities are already exposed, there will be no change.
  * Are shown on a separate card on the device configuration page.
  * Do not show up on the automatically generated Lovelace Dashboards.
* `NEW`: A switch is being created to turn on and off the Privacy Mode for each Camera. This makes it possible to set the Privacy mode for a Camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_privacy_mode`
* `NEW`: Restarted the work on implementing the UFP Sense device. We don't have physical access to this device, but @Madbeefer is kindly enough to do all the testing.
  * The following new sensors will be created for each UFP Sense device: `Battery %`, `Ambient Light`, `Humidity`, `Temperature` and `BLE Signal Strength`.
  * The following binary sensors will be created for each UFP Sense device: `Motion`, `Open/Close` and `Battery Low`. **Note** as of this beta, these sensors are not working correctly, this is still work in progress.


## 0.10.0 Beta 3

Released: October 27th, 2021

**REMINDER** This version is only valid for **V1.20.0-beta.2** or higher of UniFi Protect. If you are not on that version, stick with V0.9.1.

### Upgrade Instructions

Due to the many changes and entities that have been removed and replaced, we recommend the following process to upgrade from an earlier Beta or from an earlier release:

* Upgrade the Integration files, either through HACS (Recommended) or by copying the files manually to your `custom_components/unifiprotect` directory.
* Restart Home Assistant
* Remove the UniFi Protect Integration by going to the Integrations page, click the 3 dots in the lower right corner of the UniFi Protect Integration and select *Delete*
* While still on this page, click the `+ ADD INTEGRATION` button in the lower right corner, search for UnFi Protect, and start the installation, supplying your credentials.

### Changes in this release

Everything from Beta 1 and 2 is included here, plus the following:

* `CHANGE`: **BREAKING CHANGE** There has been a substansial rewite of the underlying IO API Module (`pyunifiprotect`) over the last few month. The structure is now much better and makes it easier to maintain going forward. It will take too long to list all the changes, but one important change is that we have removed the support for Non UnifiOS devices. These are CloudKey+ devices with a FW lower than 2.0.24. I want to give a big thank you to @AngellusMortis and @bdraco for making this happen.
* `CHANGE`: **BREAKING CHANGE** As this release has removed the support for Non UnifiOS devices, we could also remove the Polling function for Events as this is served through Websockets. This also means that the Scan Interval is no longer present in the Configuration.
* `CHANGE`: **BREAKING CHANGE** To future proof the Select entities, we had to change the the way the Unique ID is populated. The entity names are not changing, but the Unique ID's are If you have installed a previous beta of V0.10.0 you will get a duplicate of all Select entities, and the ones that were there before, will be marked as unavailable. You can either remove them manually from the Integration page, or even easier, just delete the UniFi Protect integration, and add it again. (The later is the recommended method)
* `CHANGE`: **BREAKING CHANGE** All switches called `switch.ir_active_CAMERANAME` have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.
* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.set_ir_mode` now supports the following values for ir_mode: `"auto, autoFilterOnly, on, off"`. This is a change from the previous valid options and if you have automations that uses this service you will need to make sure that you only use these supported modes.
* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.save_thumbnail_image` has been removed from the Integration. This service proved to be unreliable as the Thumbnail image very often was not available, when this service was called. Please use the service `camera.snapshot` instead.
* `NEW`: For each Camera there will now be a `Select Entity` from where you can select the Infrared mode for each Camera. Valid options are `Auto, Always Enable, Auto (Filter Only, no LED's), Always Disable`. These are the same options you can use if you set this through the UniFi Protect App.
* `NEW`: Added a new `Number` entity called `number.wide_dynamic_range_CAMERANAME`. You can now set the Wide Dynamic Range for a camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_wdr_value`.
* `NEW`: Added `select.doorbell_text_DOORBELL_NAME` to be able to change the LCD Text on the Doorbell from the UI. In the configuration menu of the Integration there is now a field where you can type a list of Custom Texts that can be displayed on the Doorbell and then these options plus the two standard texts built-in to the Doorbell can now all be selected. The format of the custom text list has to ba a comma separated list, f.ex.: RING THE BELL, WE ARE SLEEPING, GO AWAY... etc.
* `NEW`: Added a new `Number` entity called `number.microphone_level_CAMERANAME`. From here you can set the Microphone Sensitivity Level for a camera directly from the UI. This is a supplement to the already existing service `unifiprotect.set_mic_volume`.
* `NEW`: Added a new `Number` entity called `number.zoom_position_CAMERANAME`. From here you can set the optical Zoom Position for a camera directly from the UI. This entity will only be added for Cameras that support optical zoom. This is a supplement to the already existing service `unifiprotect.set_zoom_position`.

## 0.10.0 Beta 2

Released: October 24th, 2021

Everything from Beta 1 is included here, plus the following:

`CHANGE`: Changes to the underlying `pyunifiprotect` module done by @AngellusMortis to ensure all tests are passing and adding new functionality to be used in a later release.
`NEW`: Added a new event `unifiprotect_motion` that triggers on motion. You can use this instead of the Binary Sensor to watch for a motion event on any motion enabled device. The output from the event will look similar tom the below

  ```json
  {
    "event_type": "unifiprotect_motion",
    "data": {
        "entity_id": "camera.outdoor",
        "smart_detect": [
            "person"
        ],
        "motion_on": true
    },
    "origin": "LOCAL",
    "time_fired": "2021-10-18T10:55:36.134535+00:00",
    "context": {
        "id": "b3723102b4fb71a758a423d0f3a04ba6",
        "parent_id": null,
        "user_id": null
    }
  }
  ```

## 0.10.0 Beta 1

Released: October 17th, 2021

This is the first Beta release that will support **UniFi Protect 1.20.0**. There have been a few changes to the Protect API, that requires us to change this Integration. Unfortunately it cannot be avoided that these are Breaking Changes, so please read carefully below before you do the upgrade.

When reading the Release Notes for UniFi Protect 1.20.0-beta.2 the following changes are directly affecting this Integration:

* Integrate “Smart detections” and “Motion Detections” into “Detections”.
* Generate only RTSPS links for better security. (RTSP streams are still available by removing S from RTSPS and by changing port 7441 to 7447.

#### Changes implemented in this version:
* `CHANGE`: **IMPORTANT** You MUST have at least UniFi Protect **V1.20.0-beta.1** installed for this Integration to work. There are checks on both new installations and upgraded installations to see if your UniFi Protect App is at the right version number. Please consult the HA Logfile for more information if something does not work.
If you are not running the 1.20.0 beta, DO NOT UPGRADE. If you did anyway, you can just uninstall and select the 0.9.1 release from HACS and all should be running again.
* `CHANGE`: **BREAKING CHANGE** All switches called `switch.record_smart_CAMERANAME` and `switch.record_motion_CAMERANAME` have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.
* `CHANGE`: **BREAKING CHANGE** All switches for the *Floodlight devices* have been removed from the system. They are being migrated to a `Select Entity` which you can read more about below. If you have automations that turns these switches on and off, you will have to replace this with the `select.select_option` service, using the valid options described below for the `option` data.
* `CHANGE`: **BREAKING CHANGE** The Service `unifiprotect.set_recording_mode` now only supports the following values for recording_mode: `"never, detections, always"`. If you have automations that uses the recording_mode `smart` or `motion` you will have to change this to `detections`.
* `NEW`: For each Camera there will now be a `Select Entity` from where you can select the recording mode for each Camera. Valid options are `Always, Never, Detections`. Detections is what you use to enable motion detection. Whether they do People and Vehicle detection, depends on the Camera Type and the settings in the UniFi Protect App. We might later on implement a new Select Entity from where you can set the the Smart Detection options. Until then, this needs to be done from the UniFi Protect App. (as is the case today)
* `NEW`: For each Floodlight there will now be a `Select Entity` from where you can select when the Light Turns on. This replaces the two switches that were in the earlier releases. Valid options are `On Motion, When Dark, Manual`.
* `CHANGE`: We will now use RTSPS for displaying video. This is to further enhance security, and to ensure that the system will continue running if Ubiquiti decides to remove RTSP completely. This does not require any changes from your side.

## 0.9.1

Released: October 17th, 2021

This will be the final release for devices not running the UnifiOS. With the next official release, there will no longer be support for the CloudKey+ running a firmware lover than 2.0.
**NOTE** This release does not support UniFi Protect 1.20.0+. This will be supported in the next Beta release.

* `FIX`: Issue #297. Improves determining reason for bad responses.

## 0.9.0

Released: August 29th, 2021

* `NEW`: This release adds support for the UFP Viewport device. This is done by adding the `select` platform, from where the views defined in Unifi Protect can be selected. When changing the selection, the Viewport will change it's current view to the selected item. The `select` platform will only be setup if UFP Viewports are found in Unfi Protect. When you create a view in Unifi Protect, you must check the box *Shared with Others* in order to use the view in this integration.<br>
**NOTE**: This new entity requires a minimum of Home Assistant 2021.7. If you are on an older version, the Integration will still work, but you will get an error during startup.
* `NEW`: As part of the support for the UFP Viewport, there also a new service being created, called `unifiprotect.set_viewport_view`. This service requires two parameters: The `entity_id` of the Viewport and the `view_id` of the View you want to set. `view_id` is a long string, but you can find the id number when looking at the Attributes for the `select` entity.
* `FIX`: Issue #264, missing image_width variable is fixed in this release.
* `CHANGE`: PR #276, Ensure setup is retried later when device is rebooting. Thanks to @bdraco
* `CHANGE`: PR #271. Updated README, to ensure proper capitalization. Thanks to @jonbloom
* `CHANGE`: PR #278. Allow requesting a custom snapshot width and height, to support 2021.9 release. Thank to @bdraco. Fixing Issue #282

## 0.9.0 Beta 2

Released: July 17th, 2021

* `BREAKING`: If you installed Beta 1, then you will have a media_player entity that is no longer used. You can disable it, or reinstall the Integration to get completely rid of it.
* `NEW`: This release adds support for the UFP Viewport device. This is done by adding the `select` platform, from where the views defined in Unifi Protect can be selected. When changing the selection, the Viewport will change it's current view to the selected item. The `select` platform will only be setup if UFP Viewports are found in Unfi Protect. When you create a view in Unifi Protect, you must check the box *Shared with Others* in order to use the view in this integration.<br>
**NOTE**: This new entity requires a minimum of Home Assistant 2021.7

* `NEW`: As part of the support for the UFP Viewport, there also a new service being created, called `unifiprotect.set_viewport_view`. This service requires two parameters: The `entity_id` of the Viewport and the `view_id` of the View you want to set. `view_id` is a long string, but you can find the id number when looking at the Attributes for the `select` entity.
* `FIX`: Issue #264, missing image_width variable is fixed in this release.

## 0.9.0 Beta 1

Released: July 6th, 2021

* `NEW`: This release adds support for the UFP Viewport device. This is done by adding the `media_player` platform, from where the views defined in Unifi Protect can be selected as source. When selecting the source, the Viewport will change it's current view to the selected source. The `media_player` platform will only be setup if UFP Viewports are found in Unfi Protect.
* `NEW`: As part of the support for the UFP Viewport, there also a new service being created, called `unifiprotect.set_viewport_view`. This service requires two parameters: The `entity_id` of the Viewport and the `view_id` of the View you want to set. `view_id` is a long string, but you can find the id number when looking at the Attributes for the media_player.

## 0.8.9

Released: June 29th, 2021

* `FIXED`: During startup of the Integration, it would sometimes log `Error Code: 500 - Error Status: Internal Server Error`. (Issue #249) This was caused by some values not being available at startup.
* `CHANGE`: The service `unifiprotect.save_thumbnail_image` now creates the directories in the filename if they do not exist. Issue #250.
* `FIX`: We have started the integration of the new UFP-Sense devices, but these are not available in Europe yet, so the integration is not completed, and will not be, before I can get my hands on one of these devices. Some users with the devices, got a crash when running the latest version, which is now fixed. The integration is not completed, this fix, just removes the errors that were logged. Thanks to @michaeladam for finding this.
* `NEW`: When the doorbell is pressed, the integration now fires an event with the type `unifiprotect_doorbell`. You can use this in automations instead of monitoring the binary sensor. The event will look like below and only fire when the doorbell is pressed, so there will be no `false`event. If you have multiple doorbells you use the `entity_id` value in the `data` section to check which doorbell was pressed.

  ```json
  {
      "event_type": "unifiprotect_doorbell",
      "data": {
          "ring": true,
          "entity_id": "binary_sensor.doorbell_kamera_doerklokke"
      },
      "origin": "LOCAL",
      "time_fired": "2021-06-26T08:16:58.882088+00:00",
      "context": {
          "id": "6b8cbcecb61d75cbaa5035e2624a3051",
          "parent_id": null,
          "user_id": null
      }
  }
  ```

## 0.8.8

Released: May 22nd, 2021

* `NEW`: As stated a few times, there is a delay of 10-20 seconds in the Live Stream from UniFi Protect. There is not much this integration can do about it, but what we can do is, to disable the RTSP Stream, so that JPEG push is used instead. This gives an almost realtime experience, with the cost of NO AUDIO. As of this version you can disable the RTSP Stream from the Config menu.
* `FIXED`: Issue #235, where the aspect ratio of the Doorbell image was wrong when displayed in Lovelace or in Notifications. Now the aspect ratio is read from the camera, so all cameras should have a correct ratio.


## 0.8.7

Released: May 4th, 2021

* `CHANGED`: Added **iot_class** to `manifest.json` as per HA requirements
* `FIXED`: Ensure the event_object is not cleared too soon, when a smart detect event occurs. Issue #225. Thanks to @bdraco for the fix.
* `CHANGED`: Updated README.md with information on how to turn on Debug logger. Thank you @blaines


## 0.8.6

Released: April 25th, 2021

* `FIXED`: If authentication failed during setup or startup of the Integration it did not return the proper boolean, and did not close the session properly.
* `CHANGED`: Stop updates on stop event to prevent shutdown delay.
* `CHANGED`: Updated several files to ensure compatability with 2021.5+ of Home Assistant. Thanks to @bdraco for the fix.

## 0.8.5

Released: March 30th, 2021

* `ADDED`: Have you ever wanted to silence your doorbell chime when you go to bed, or you put your child to sleep? - Now this is possible. A new service to enable/disable the attached Doorbel Chime is delivered with this release. The service is called `unifiprotect.set_doorbell_chime_duration` and takes two parameters: Entity ID of the Doorbell, Duration in milliseconds which is a number between 0 and 10000. 0 equals no chime. 300 is the standard for mechanical chimes and 10000 is only used in combination with a digital chime. The function does not really exist in the API, so this is a workaround. Let me know what values are best for on with the different chimes. You might still hear a micro second of a ding, but nothing that should wake anyone up. Fixing issue #211

## 0.8.4

Released: March 18th, 2021

* `FIXED`: Issues when activating Services that required an Integer as value, and using a Template to supply that value. Services Schemas have now been converted to use `vol.Coerce(int)` instead of just `int`.
* `CHANGED`: All Services definitions have now been rewritten to use the new format introduced with the March 2021 Home Assistant release. **NOTE**: You might need to do a Hard Refresh of your browser to see the new Services UI.
* `FIXED`: When using the switches or service to change recording mode for a camera, the recording settings where reset to default values. This is now fixed, so the settings you do in the App are not modfied by activating the Service or Recording mode switches.

## 0.8.3

Released: March 3rd, 2021

* `ADDED`: New service `unifiprotect.set_wdr_value` which can set the Wide Dynamic Range of a camera to an integer between 0 and 4. Where 0 is disabled and 4 is full.
## 0.8.2

Released: February 4th, 2021

* `FIXED`: Use the UniFi Servers MAc address as unique ID to ensure that it never changes. Previously we used the name, and that can be changed by the user. This will help with stability and prevent integrations from suddenly stop working if the name of the UDMP, UNVR4 or CKP was changed.
* `FIXED`: Further enhance the fix applied in 0.8.1 to ensure the Integration loads even if the first update fails. Thanks to @bdraco for implementing this.
* `FIXED`: Sometimes we would be missing event_on or event_ring_on if the websocket connected before the integration setup the binary sensor. We now always return the full processed data, eliminating this error. Another fix by @bdraco

## 0.8.1

Released: January 28th, 2021

* `FIXED`: The service `unifiprotect.set_status_light` did not function, as it was renamed in the IO module. This has now been fixed so that both the service and the Switch work again.
* `FIXED`: Issue #181, Add a retry if the first update request fails on load of the Integration.

## 0.8.0

Released: January 8th, 2021

This release adds support for the new Ubiquiti Floodlight device. If found on your Protect Server, it will add a new entity type `light`, that will expose the Floodlight as a light entity and add support for turning on and off, plus adjustment of brightness.

There will also be support for the PIR motion sensor built-in to the Floodlight, and you will be able to adjust PIR settings and when to detect motion.

You must have UniFi Protect V1.17.0-beta.10+ installed for Floodlight Support. Below that version, you cannot add the Floodlight as a device to UniFi Protect.

THANK YOU again to @bdraco for helping with some of the code and for extensive code review. Without you, a lot of this would not have been possible.


* `ADDED`: New `light` entity for each Floodlight found. You can use the normal *light* services to turn on and off. Be aware that *brightness* in the Protect App only accepts a number from 1-6, so when you adjust brightness from Lovelace or the Service, the number here will be converted to a number between 1 and 6.
* `ADDED`: A Motion Sensor is created for each Floodlight attached. It will trigger motion despite the state of the Light. It will however not re-trigger until the time set in the *Auto Shutoff Timer* has passed.
* `ADDED`: New service `unifiprotect.light_settings`. Please see the README file for details on this Service.
* `FIXED`: Missing " in the Services description, prevented message to be displayed to the user. Thank you to @MarcJenningsUK for spotting and fixing this.
* `CHANGED`: Bumped `pyunifiprotect` to 0.28.8

**IMPORTANT**: With the official FW 2.0.24 for the CloudKey+ all UniFi Protect Servers are now migrated to UniFiOS. So as of this release, there will be no more development on the Non UniFiOS features. What is there will still be working, but new features will only be tested on UniFiOS. We only have access to very limited HW to test on, so it is not possible to maintain HW for backwards compatability testing.

#### This release is tested on:

*Tested* means that either new features work on the below versions or they don't introduce breaking changes.

* CloudKey+ G2: FW Version 2.0.24 with Unifi Protect V1.17.0-beta.13
* UDMP: FW Version 1.18.5 with Unifi Protect V1.17.0-beta.13

## Release 0.7.1

Released: January 3rd, 2021

* `ADDED`: New service `unifiprotect.set_zoom_position` to set the optical zoom level of a Camera. This only works on Cameras that support optical zoom.

  The services takes two parameters: **entity_id** of the camera, **position** which can be between 0 and 100 where 0 is no zoom and 100 is maximum zoom.

  A new attribue called `zoom_position` is added to each camera, showing the current zoom position. For cameras that does not support setting optical zoom, this will always be 0.

#### This release is tested on:

*Tested* means that either new features work on the below versions or they don't introduce breaking changes.

* CloudKey+ G2: FW Version 2.0.24 with Unifi Protect V1.16.9
* UDMP: FW Version 1.18.5 with Unifi Protect V1.17.0-beta.10
## Release 0.7.0

Released: December 20th, 2020

* `ADDED`: New service `unifiprotect.set_privacy_mode` to enable or disable a Privacy Zone, that blacks-out the camera. The effect is that you cannot view anything on screen. If recording is enabled, the camera will still record, but the only thing you will get is a black screen. You can enable/disable the microphone and set recording mode from this service, by specifying the values you see below.
If the camera already has one or more Privacy Zones set up, they will not be overwritten, and will still be there when you turn of this.
Use this instead of physically turning the camera off or on.

  The services takes four parameters: **entity_id** of the camera, **privacy_mode** which can be true or false, **mic_level** which can be between 0 and 100 and **recording_mode** which can be never, motion, always or smart.

  Also a new attribute called `privacy_mode` is added to each camera, that shows if this mode is enabled or not. (Issue #159)

* `CHANGED`: Some users are getting a warning that *verify_sll* is deprecated and should be replaced with *ssl*. We changed the pyunifiportect module to use `ssl` instead of `verify_sll` (Issue #160)

* `ADDED`: Dutch translation to Config Flow is now added. Thank you to @copperek for doing it.

* `FIXED`: KeyError: 'server_id' during startup of Unifi Protect. This error poped up occasionally during startup of Home Assistant. Thank you to @bdraco for fixing this. (Issue #147)

* `FIXED`: From V1.17.x of UniFi Protect, Non Adopted Cameras would be created as camera.none and creating all kinds of errors. Now these cameras will be ignored, until they are properly adopted by the NVR. Thank you to @bdraco for helping fixing this.

#### This release is tested on:

*Tested* means that either new features work on the below versions or they don't introduce breaking changes.

* CloudKey+ G2: FW Version 1.1.13 with Unifi Protect V1.13.37
* UDMP: FW Version 1.18.4-3 with Unifi Protect V1.17.0-beta.6

## Release 0.6.7

Released: December 15th, 2020

`ADDED`: New attribute on each camera called `is_dark`. This attribute is true if the camera sees the surroundings as dark. If infrared mode is set to *auto*, then infrared mode would be turned on when this changes to true.

`ADDED`: New Service `unifiprotect.set_mic_volume` to set the Sensitivity of the built-in Microphone on each Camera. Requires two parameters: *Camera Entity* and *level*, where level is a number between 0 and 100. If level is set to 0, the Camera will not react on Audio Events.
On each camera there is also now a new attribute called `mic_sensitivity` which displayes the current value.

See [README.md](https://github.com/briis/unifiprotect#create-input-slider-for-microphone-sensitivity) for instructions on how to setup an Input Slider in Lovelace to adjust the value.

`CHANGED`: Updated the README.md documentation and added more information and a TOC.

#### This release is tested on:

*Tested* means that either new features work on the below versions or they don't introduce breaking changes.

* CloudKey+ G2: FW Version 1.1.13 with Unifi Protect V1.13.37
* UDMP: FW Version 1.18.3 with Unifi Protect V1.17.0-beta.6
## Release 0.6.6

With the release of Unifi Protect V1.7.0-Beta 1, there is now the option of detecting Vehicles on top of the Person detection that is allready there. This is what Ubiquiti calls *Smart Detection*. Also you can now set recording mode to only look for Smart Detection events, meaning that motion is only activated if a person or a vehicle is detected on the cameras. Smart Detection requires a G4-Series camera and a UnifiOS device.

**NOTE**: If you are not running Unifi Protect V1.17.x then the new features introduced here will not apply to your system. It has been tested on older versions of Unifi Protect, and should not break any existing installations.

* **New** For all G4 Cameras, a new Switch will be created called *Record Smart*, where you can activate or de-active Smart Recording on the camera
* **New** The service `unifiprotect.set_recording_mode` now has a new option for `recording_mode` called *smart*. This will turn on Smart Recording for the selected Camera. Please note this will only work on G4-Series cameras.
* **Fix** When the G4 Doorbell disconnected or restarted, the Ring Sensor was triggered. This fix now ensures that this does not happen.

### This release is tested on:

*Tested* means that either new features work on the below versions or they don't introduce breaking changes.

* CloudKey+ G2: FW Version 1.1.13 with Unifi Protect V1.13.37
* UDMP: FW Version 1.18.3-5 with Unifi Protect V1.17.0-beta.1
* UNVR: FW Version 1.3.15 with Unifi Protect V1.15.0

## Release 0.6.5

* **Hotfix** The recording of motion score and motion length got out of sync with the motion detections on/off state. With this fix, motion score and length are now updated together with the off state of the binary motion sensors. This was only an issue for Non UnifiOS devices (*CloudKey+ users with the latest original firmware version or below*).

*This release is tested on*:
* CloudKey+ G2: FW Version 1.1.13 with Unifi Protect V1.13.37
* UDMP: FW Version 1.18.3-5 with Unifi Protect V1.16.8

## Release 0.6.4

* **Hotfix** for those who experience that motion sensors no longer work after upgrading to 0.6.3. Users affected will be those who are running a version of Unifi Protect that does not support SmartDetection.

*This release is tested on*:
* CloudKey+ G2: FW Version 1.1.13 with Unifi Protect V1.13.37
* UDMP: FW Version 1.18.3-4 with Unifi Protect V1.16.7
* UDMP FW Version 1.18.0 with Unifi Protect V1.14.11

## Release 0.6.3

@bdraco made some serious changes to the underlying IO module, that gives the following new features:

* When running UnifiOS on the Ubiquiti Device, events are now fully constructed from Websockets.
* Motion Events are now triggered regardless of the Recording Mode, meaning you can use your cameras as Motion Detectors. **Object detection** still requires that the Cameras recording mode is enabled (Motion or Always) as this information is only passed back when either of these are on.

  **BREAKING** If your Automations trigger on Motion Detection from a Camera, and you assume that Recording is enabled on a camera then you now need to make a check for that in the Condition Section of your automation.
* Bumped pyunifiprotect to 0.24.3

## Release 0.6.2

* Changed text for Config Flow, to differ between UnifiOS and NON UNifiOS devices, instead of CloudKey and UDMP. This makes more sense, now that CloudKey+ also can run UnifiOS.
* Changed the default port in Config Flow, from 7443 to 443, as this will be the most used port with the update to CloudKey+
* Added a Debug option to Config Flow, so that we can capture the actual error message when trying to Authenticate.

## Release 0.6.1
@bdraco strikes again and fixed the following problems:

* If the system is loaded, we miss events because the time has already passed.
* If the doorbell is rung at the same time as motion, we don't see the ring event because the motion event obscures it.
* If the hass clock and unifi clock are out of sync, we see the wrong events. (Still recommend to ensure that unifi and hass clocks are synchronized.)
* The Doorbell is now correctly mapped as DEVICE_CLASS_OCCUPANCY.

## Release 0.6.0
The Integration has now been rewritten to use **Websockets** for updating events, giving a lot of benefits:

* Motion and doorbell updates should now happen right away
* Reduces the amount of entity updates since we now only update cameras that change when we poll instead of them all.
* Reduce the overall load on Home Assistant.

Unfortunately, Websockets are **only available for UnifiOS** powered devices (UDMP & UNVR), so this will not apply to people running on the CloudKey. Here we will still need to do polling. Hopefully Ubiquity, will soon move the CloudKey to UnifiOS or add Websockets to this device also.

All Credits for this rewrite goes to:
* @bdraco, who did the rewrite of both the IO module and the Integration
* @adrum for the initial work on the Websocket support
* @hjdhjd for reverse engineering the Websocket API and writing up the description

This could not have been done without all your work.

### Other changes

* When setting the LCD text on the Doorbell, this is now truncated to 30 Characters, as this is the maximum supported Characters. Thanks to @hjdhjd for documenting this.
* Fixed an error were sometimes the External IP of the Server was used for the Internal Stream. Thanks to @adrum for fixing this.
* Added Switch for changing HDR mode from Lovelace (Issue #128). This switch will only be created for Cameras that support HDR mode.
* Added Switch for changing High FPS mode from Lovelace (Issue #128). This switch will only be created for Cameras that support High FPS mode.
* Improved error handling.
* Added German translation for Config Flow. Thank you @SeraphimSerapis

## Release 0.5.8
Object Detection was introduced with 1.14 of Unifi Protect for the UDMP/UNVR with the G4 series of Cameras. (I am unsure about the CloudKey+, but this release should not break on the CloudKey+ even without object detection). This release now adds a new Attribute to the Binary Motion Sensors that will display the object detected. I have currently only seen `person` being detected, but I am happy to hear if anyone finds other objects. See below on how this could be used.
This release also introduces a few new Services, as per user request. Please note that HDR and High FPS Services will require a version of Unifi Protect greater than 1.13.x. You will still be able to upgrade, but the functions might not work.

* **New feature**: Turn HDR mode on or off, asked for in Issue #119. Only selected Cameras support HDR mode, but for those cameras that support it, you can now switch this on or off by calling the service: `unifiprotect.set_hdr_mode`. Please note that when you use this Service the stream will reset, so expect a drop out in the stream for a little while.
* **New feature**: Turn High FPS video mode on or off. The G4 Cameras support High FPS video mode. With this release there is now a service to turn this on or off. Call the service `unifiprotect.set_highfps_video_mode`.
* **New feature**: Set the LCD Message on the G4 Doorbell. There is now a new service called `unifiprotect.set_doorbell_lcd_message` from where you can set a Custom Text for the LCD. Closing Issue #104
* **New attribute** `event_object` that will add the object detected when Motion occurs. It will contain the string `None Identified` if no specific object is detected. If a human is detected it will return `person` in this attribute, which you can test for in an automation. (See README.md for an example)

## Release 0.5.6
New feature: Turn the Status Light on or off, asked for in Issue #102. With this release there is now the possibility to turn the Status light on each camera On or Off. This can be done in two ways:
1. Use the service `unifiprotect.set_status_light`
2. Use the new switch that will be created for each camera.

Disabled the Websocket update, that was introduced in 0.5.5, as it is currently not being used, and caused error messages when HA was closing down, due to not being stopped.


## Release 0.5.5

The latest beta of Unifi Protect includes the start of Ubiquiti's version of AI, and introduces a concept called Smart Detect, which currently can identify People on specific Camera models. When this is enabled on a Camera, the event type changes from a *motion* event to a *smartdetect* event, and as such these cameras will no longer trigger motion events.

This release is a quick fix for the people who have upgraded to the latest Unifi Protect Beta version. I will later introduce more Integration features based on these new Unifi Protect features.

## Release 0.5.4

A more permanent fix for Issue #88, where the snapshot images did not always get the current image. The API call has now been modified, so that it forces a refresh of the image when pulling it from the camera. Thank you to @rajeevan for finding the solution.
If you installed release 0.5.3 AND enabled *Anonymous Snapshots* you can now deselect that option again, and you will not have to enable the Anonymous Snapshot on each Camera.

## Release 0.5.3

Fix for Issue #88 - The function for saving a Camera Snapshot works fine for most people, but it turns out that the image it saves is only refreshed every 10-15 seconds. There might be a way to force a new image, but as the Protect API is not documented I have not found this yet. If you need the guaranteed latest image from the Camera, there is a way around it, and that is to enable Anonymous Snapshots on each Camera, as this function always gets the latest image directly from the Camera.

This version introduces a new option where you can enable or disable anonymous snapshots in the Unifi Integration. If enabled, it will use a different function than if disabled, but it will only work if you login to each of your Cameras and enable the *Anonymous Snapshot*.

To use the Anonymous Snapshot, after this update has been installed, do the following:

1. Login to each of your Cameras by going to http://CAMERA_IP. The Username is *ubnt* and the Camera Password can be found in Unifi Protect under *Settings*.
2. If you have never logged in to the Camera before, it might take you through a Setup procedure - just make sure to keep it in *Unifi Video* mode, so that it is managed by Unifi Protect.
3. Once you are logged in, you will see an option on the Front page for enabling Anonymous Snapshots. Make sure this is checked, and then press the *Save Changes* button.
4. Repeat step 3 for each of your Cameras.
5. Now go to the Integrations page in Home Assistant. Find the Unifi Protect Widget and press options.
6. Select the checkbox *Use Anonymous Snapshots* and press *Submit*

Now the Unfi Protect Integration will use the direct Snapshot from the Camera, without going through Unfi Protect first.

## Release 0.5.2

* Added exception handling when the http connection is dropped on a persistent connection. The Integration will now throw a `ConfigEntryNotReady` instead and retry.

## Release 0.5.1

Two fixes are implemented in this release:
1. Basic data for Cameras were pulled at the same interval as the Events for Motion and Doorbell. This caused an unnecessary load on the CPU. Now the base Camera data is only pulled every 60 seconds, to minimize that load.
2. A user reported that when having more than 20 Cameras attached, the Binary Sensors stayed in an unavailable state. This was caused by the fact that a poll interval of 2 seconds for the events, was not enough to go through all cameras, so the state was never reported back to Home Assistant. With this release there is now an option on the *Integration Widget* to change the Scan Interval to a value between 2 and 30 seconds. You **ONLY** have to make this adjustment if you experience that the sensors stay unavailable - so typically if you have many Cameras attached. Default is still 2 seconds.
3. The same user mentioned above, is running Unifi Protect on the new NVR4 device, and the Integration seems to work fine on this new platform. I have not heard from anyone else on this, but at least one user has success with that.
4. Bumped pyunifiprotect to v0.16 which fixes the problem mentioned in point 1 above. Thank you to @bdraco for the fix.

## Release 0.5 - Fully Integration based

The Beta release 0.4 has been out for a while now, and I belive we are at a stage where I will release it officially, and it will be called Version 0.5.

After the conversion to use all the Async Libraries, it became obvious to also move away from *Yaml Configuration*, to the fully UI based Integration. As I wrote in the Tester Notes, I know there are some people with strong feelings about this, but I made the decision to make the move, and going forward **only** to support this way of adding Unifi Protect to Home Assistant.

### ***** BREAKING CHANGES *****
Once setup, the base functionality will be the same as before, with the addition of a few minor changes. But behind the scene there are many changes in all modules, which also makes this a lot more ready for becoming an official Integration in Home Assistant Core.

I want to send a BIG THANK YOU to @bdraco who has made a lot of code review, and helped me shape this to conform to Home Assistant standards. I learned so much from your suggestions and advice, so thank you!

Here are the Breaking changes:

1. Configuration can only be done from the *Integration* menu on the *Configuration* tab. So you will have to remove all references to *unifiprotect* from your configuration files.
2. All entities will get the `unifiprotect_` prefix removed from them, so you will have to change automations and scripts where you use these entities. This is done to make sure that entities have a Unique Id and as such can be renamed from the UI as required by Home Assistant. I will give a 99% guarantee, that we do not need to change entity names again.

### Upgrading and Installing
If you have not used Unifi Protect before, go to step 4.

If you are already runing a version of *Unifi Protect* with version 0.3.x or lower:

1. Remove the previous installation
 * If you have installed through HACS, then go to HACS and remove the Custom Component
 * If you manually copied the files to your system, go to the `custom_components` directory and delete the `unifiprotect` directory.
* Edit `configuration.yaml` and remove all references to *unifiprotect*. Some have split the setup in to multiple files, so remember to remove references to unifiprotect from these files also.
* I recommend to restart Home Assistant at this point, but in theory it should not be necessary.

4. Install the new version
 * If you use HACS, go there, and add Unifi Protect V0.5 or later.
 * If you do it manually, go to [Github](https://github.com/briis/unifiprotect/tree/master/custom_components/unifiprotect) and copy the files to `custom_components/unifiprotect`. Remember to include the `translations` directory and the files in here.
* Restart Home Assistant
* Now go to the *Integration* menu on the *Configuration* tab, and search for *Unifi Protect*. If it does not show up, try and clear your browser cache, and refresh your browser.
* From there, it should be self explanatory.

I will leave Release 0.3.4 as an option in HACS, so if you want to stick with the Yaml version, feel free to do so, but please note that I will not make any changes to this version going forward.

## Version 0.3.2

**NOTE** When upgrading Home Assistant to +0.110 you will receive warnings during startup about deprecated BinaryDevice and SwitchDevice. There has been a change to SwitchEntity and BinaryEntity in HA 0.110. For now this will not be changed, as not everybody is on 0.110 and if changing it this Component will break for users not on that version as it is not backwards compatible.

With this release the following items are new or have been fixed:

* **BREAKING** Attribute `last_motion` has been replaced with `last_tripped_time` and attribute `motion_score` has been replaced with `event_score`. So if you use any of these attributes in an automation you will need to change the automation.
* **NEW** There is now support for the Unifi Doorbell. If a Doorbell is discovered there will be an extra Binary Sensor created for each Doorbell, so a Doorbell Device will have both a Motion Binary Sensor and a Ring Binary Sensor. The later, turns True if the doorbell is pressed.<br>
**BREAKING** As part of this implementation, it is no longer possible to define which binary sensors to load - all motion and doorbell *binary sensors* found are loaded. So the `monitored_condition` parameter is removed from the configuration for `binary_sensor` and needs to be removed from your `configuration.yaml` file if present.
* **FIX** The Switch Integration was missing a Unique_ID


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Bjarne Riis

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# // UniFi Protect for Home Assistant


## THIS REPOSITORY IS NOW ARCHIEVED AND READONLY.
The `unifiprotect` integration is now in Home Assistant core, so no more updates will be made to this repository. Please visit the official documentation for [UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect/) to read more.

-----
## ⚠️ ⚠️ WARNING ABOUT Home Assistant v2022.2
The `unifiprotect` integration will be in Home Assistant core v2022.2. If you are running **0.10.x or older** of the HACS integration, **do not install v2022.2.x of Home Assistant core**.

If you are running 0.11.x or the 0.12.0, you should be safe to delete the HACS version as part of your upgrade. The 0.11.x branch is designed to be compatible with the 0.12.0-beta and the HA core version. The latest version of 0.12.0-beta will be the version of `unifiprotect` in HA core in v2022.0.

This repo is now **deprecated** in favor of the Home Assistant core version. This repo will be archived and removed from HACS after the 2022.4 release of Home Assistant.

### Reporting Issues

We have disable reporting issues to the HACS Github repo for the `unifiprotect` integration. If you have an issue you would like to report for the `unifiprotect` integration, please make you are running the HA core version of the integration provided by 2022.2.0 or new and then report your issue on the [HA core repo](https://github.com/home-assistant/core/issues/new/choose). 

If you would still like to discuss the HACS version of the `unifiprotect` integration, feel free to use the [dicussions section](https://github.com/briis/unifiprotect/discussions) or the [HA Community forums thread](https://community.home-assistant.io/t/custom-component-unifi-protect/158041/865).

### Migration to HA Core Version Steps

If you have Smart Sensor devices and you are **not** running `0.12.0-beta10` or newer, it is recommended you just delete your UniFi Protect integration config and re-add it. If you do not have Smart Sensor devices, you can migrate to the Home Assistant core version by following the steps below:

1. Upgrade to the 0.12.0 version for the HACS unifiprotect integration and restart Home Assistant.
2. Remove your HACS `unifiprotect` integration from HACS (do not remove your `unifiprotect` config entry). It is safe to ignore the warning about needing to remove your config first.
3. Do *not* restart HA yet.
4. Upgrade to Home Assistant 2022.2.x

You **must** remove the HACS integration efore upgrading to 2022.2.0 first to prevent a conflicting version of `pyunifiprotect` from being installed.

### Differences between HACS version 0.12.0 and HA 2022.2.0b1 version:

#### HACS Only

* Migration code for updating from `0.10.x` or older still exists; this code has been _removed_ in the HA core version

#### HA Core Only

* Full language support. All of the languages HA core supports via Lokalise has been added to the ingration.

* Auto-discovery. If you have a Dream machine or a Cloud Key/UNVR on the same VLAN, the UniFi Protect integration will automatically be discovered and prompted for setup.

* UP Doorlock support. The HA core version has full support for the newly release EA UP Doorlock.

-----

![GitHub release (latest by date)](https://img.shields.io/github/v/release/briis/unifiprotect?style=flat-square) [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=flat-square)](https://github.com/custom-components/hacs) [![](https://img.shields.io/badge/COMMUNITY-FORUM-success?style=flat-square)](https://community.home-assistant.io/t/custom-component-unifi-protect/158041)

The UniFi Protect Integration adds support for retrieving Camera feeds and Sensor data from a UniFi Protect installation on either an Ubiquiti CloudKey+, Ubiquiti UniFi Dream Machine Pro or UniFi Protect Network Video Recorder.

There is support for the following device types within Home Assistant:
* Camera
  * A camera entity for each camera channel and RTSP(S) combination found on the NVR device will be created
* Sensor
  * **Cameras**: (only for cameras with Smart Detections) Currently detected object
  * **Sensors**: Sensors for battery level, light level, humidity and temperate
  * **All Devices** (Disabled by default): a sensor for uptime, BLE signal (only for bluetooth devices), link speed (only for wired devices), WiFi signal (only for WiFi devices)
  * **Cameras** (Disabled by default): sensors for bytes transferred, bytes received, oldest recording, storage used by camera recordings, write rate for camera recordings
  * **Doorbells** (Disabled by default, requires UniFi Protect 1.20.1+) current voltage sensor
  * **NVR** (Disabled by default): sensors for uptime, CPU utilization, CPU temp, memory utilization, storage utilization, percent distribution of timelapse, continuos, and detections video on disk, percentage of HD video, 4K video and free space of disk, estimated recording capacity
* Binary Sensor
  * **Cameras** and **Flood Lights**: sensors for if it is dark, if motion is detected
  * **Doorbells**: sensor if the doorbell is currently being rung
  * **Sensors**: sensors for if the door is open, battery is low and if motion is detected
  * **NVR** (Disabled by default): a sensor for the disk health for each disk
    * **NOTE**: The disk numbers here are _not guaranteed to match up to the disk numbers shown in UniFiOS_
* Switch
  * **Cameras**: switches to enabled/disable status light, HDR, High FPS mode, "Privacy Mode", System Sounds (if the camera has speakers), toggles for the Overlay information, toggles for smart detections objects (if the camera has smart detections)
    * **Privacy Mode**: Turning on Privacy Mode adds a privacy zone that blacks out the camera so nothing can be seen, turn microphone sensitivity to 0 and turns off recording
  * **Flood Lights**: switch to enable/disable status light
  * **All Devices** (Disabled by default): Switch to enable/disable SSH access
* Light
  * A light entity will be created for each UniFi Floodlight found. This works as a normal light entity, and has a brightness scale also.
* Select
  * **Cameras**: selects to choose between the recording mode and the current infrared settings (if the camera has IR LEDs)
  * **Doorbells**: select to choose between the currently disable text options on the LCD screen
  * **Flood Lights**: select to choose between the light turn on mode and the paired camera (used for motion detections)
  * **Viewports**: select to choose between the currently active Liveview display on the Viewport
* Number
  * **Cameras**: number entities for the current WDR setting (only if the camera does not have HDR), current microphone sensitivity level, current optical zoom level (if camera has optical zoom),
  * **Doorbells**: number entity for the current chime duration
  * **Flood Lights**: number entities for the current motion sensitivity level and auto-shutdown duration after the light triggers on
* Media Player
  * A media player entity is added for any camera that has speakers that allow talkback
* Button
  * A button entity is added for every adoptable device (anything except the UniFiOS console) to allow you to reboot the device

It supports both regular Ubiquiti Cameras and the UniFi Doorbell. Camera feeds, Motion Sensors, Doorbell Sensors, Motion Setting Sensors and Switches will be created automatically for each Camera found, once the Integration has been configured.

## Table of Contents

1. [UniFi Protect Support](#unifi-protect-support)
2. [Hardware Support](#hardware-support)
3. [Prerequisites](#prerequisites)
4. [Installation](#installation)
5. [UniFi Protect Services](#special-unifi-protect-services)
6. [UniFi Protect Events](#unifi-protect-events)
7. [Automating Services](#automating-services)
    * [Send a notification when the doorbell is pressed](#send-a-notification-when-the-doorbell-is-pressed)
    * [Person Detection](#automate-person-detection)
    * [Input Slider for Doorbell Chime Duration](#create-input-slider-for-doorbell-chime-duration)
8. [Enable Debug Logging](#enable-debug-logging)
9. [Contribute to Development](#contribute-to-the-project-and-developing-with-a-devcontainer)

## UniFi Protect Support

In general, stable/beta version of this integration mirror stable/beta versions of UniFi Protect. That means:

**Stable versions of this integration require the latest stable version of UniFi Protect to run.**

**Beta versions / `master` branch of this integration require the latest beta version of UniFi Protect to run (or the latest stable if there is no beta)**

We try our best to avoid breaking changes so you may need to use older versions of UniFi Protect with newer versions of the integration. Just keep in mind, we may not be able to support you if you do.

## Docs for Old Versions

If you are not using the latest beta of the integration, you can view old versions of this README at any time in GitHub at `https://github.com/briis/unifiprotect/tree/{VERSION}`. Example, docs for v0.9.1 can be found at [https://github.com/briis/unifiprotect/tree/v0.9.1](https://github.com/briis/unifiprotect/tree/v0.9.1)

## Minimal Versions

As of v0.10 of the integration, the following versions of HA and UniFi Protect are _required_ to even install the integration:

* UniFi Protect minimum version is **1.20.0**
* Home Assistant minimum version is **2021.11.0**

## Hardware Support

This Integration supports all UniFiOS Consoles that can run UniFi Protect. Currently this includes:

* UniFi Protect Network Video Recorder (**UNVR**)
* UniFi Protect Network Video Recorder Pro (**UNVRPRO**)
* UniFi Dream Machine Pro (**UDMP**)
* UniFi Cloud Key Gen2 Plus (**CKGP**) firmware version v2.0.24+

Ubiquity released V2.0.24 as an official firmware release for the CloudKey+, and it is recommended that people upgrade to this UniFiOS based firmware for their CloudKey+, as this gives a much better realtime experience.

CKGP with Firmware V1.x **do NOT run UniFiOS**, you must upgrade to firmware v2.0.24 or newer.

**NOTE**: If you are still running a version of UniFi Protect without a UniFiOS Console, you can use a V0.8.x as it is the last version fully supported by NON UniFiOS devices. However, please note NON UniFiOS devices are not supported by us anymore.

## Prerequisites

Before you install this Integration you need to ensure that the following two settings are applied in UniFi Protect:

1. **Local User**
    * Login to your *Local Portal* on your UniFiOS device, and click on *Users*
    * In the upper right corner, click on *Add User*
    * Click *Add Admin*, and fill out the form. Specific Fields to pay attention to:
      * Role: Must be *Limited Admin*
      * Account Type: *Local Access Only*
      * CONTROLLER PERMISSIONS - Under UniFi Protect, select Administrators.
    * Click *Add* in at the bottom Right.

    **HINT**: A few users have reported that they had to restart their UDMP device after creating the local user for it to work. So if you get some kind of *Error 500* when setting up the Integration, try restart the UDMP.

    ![ADMIN_UNIFIOS](https://github.com/briis/unifiprotect/blob/master/images/screenshots/unifi_os_admin.png)

2. **RTSP Stream**

    The Integration uses the RTSP Stream as the Live Feed source, so this needs to be enabled on each camera. With the latest versions of UniFi Protect, the stream is enabled per default, but it is recommended to just check that this is done. To check and enable the the feature
    * open UniFi Protect and click on *Devices*
    * Select *Manage* in the Menu bar at the top
    * Click on the + Sign next to RTSP
    * Enable minimum 1 stream out of the 3 available. UniFi Protect will select the Stream with the Highest resolution

## Installation

This Integration is part of the default HACS store. Search for *unifi protect* under *Integrations* and install from there. After the installation of the files you must restart Home Assistant, or else you will not be able to add UniFi Protect from the Integration Page.

If you are not familiar with HACS, or haven't installed it, I would recommend to [look through the HACS documentation](https://hacs.xyz/), before continuing. Even though you can install the Integration manually, I would recommend using HACS, as you would always be reminded when a new release is published.

**Please note**: All HACS does, is copying the needed files to Home Assistant, and placing them in the right directory. To get the Integration to work, you now need to go through the steps in the *Configuration* section.

Before you restart Home Assistant, make sure that the stream component is enabled. Open `configuration.yaml` and look for *stream:*. If not found add `stream:` somewhere in the file and save it.

## Configuration

To add *UniFi Protect* to your Home Assistant installation, go to the Integrations page inside the configuration panel, click on `+ ADD INTEGRATION`, find *UniFi Protect*, and add your UniFi Protect server by providing the Host IP, Port Number, Username and Password.

**Note**: If you can't find the *UniFi Protect* integration, hard refresh your browser, when you are on the Integrations page.

If the UniFi Protect Server is found on the network it will be added to your installation. After that, you can add more UniFi Protect Servers, should you have more than one installed.

**You can only add UniFi Protect through the Integration page, Yaml configuration is no longer supported.**

### MIGRATING FROM CLOUDKEY+ V1.x

When you upgrade your CloudKey+ from FW V1.x to 2.x, your CK wil move to UniFiOS as core operating system. That also means that where you previously used port 7443 you now need to use port 443. There are two ways to fix this:

* Delete the UniFi Protect Integration and re-add it, using port 443.
* Edit the file `.storage/core.config_entries` in your Home Assistant instance. Search for UniFi Protect and change port 7443 to 443. Restart Home Assistant. (Make a backup first)

### CONFIGURATION VARIABLES

**host**:<br>
  *(string)(Required)*<br>
  Type the IP address of your *UniFi Protect NVR*. Example: `192.168.1.1`

**port**:<br>
  *(int)(Optional)*<br>
  The port used to communicate with the NVR. Default is 443.

**username**:<br>
  *(string)(Required)*<br>
  The local username you setup under the *Prerequisites* section.

**password**:<br>
  *(string)(Required)*<br>
  The local password you setup under the *Prerequisites* section.

**verify ssl**:<br>
  *(bool)(Required)*<br>
  If your UniFi Protect instance has a value HTTPS cert, you can enforce validation of the cert

**deactivate rtsp stream**<br>
  *(bool)Optional*<br>
  If this box is checked, the camera stream will not use the RTSP stream, but instead jpeg push. This gives a realtime stream, but does not include Audio.

**realtime metrics**<br>
  *(bool)Optional*<br>
  Enable processing of all Websocket events from UniFi Protect. This enables realtime updates for many sensors that are disabled by default. If this is disabled, those sensors will only update once every 15 minutes. **Will greatly increase CPU usage**, do not enable unless you plan to use it.

**override connection host**
  *(bool)Optional*<br>
  By default uses the connection host provided by your UniFi Protect instance for connecting to cameras for RTSP(S) streams. If you would like to force the integration to use the same IP address you provided above, set this to true.

## Special UniFi Protect Services

The Integration adds specific *UniFi Protect* services and supports the standard camera services. Below is a list of the *UniFi Protect* specific services:

Service | Parameters | Description
:------------ | :------------ | :-------------
`unifiprotect.add_doorbell_text` | `device_id` - A device for your current UniFi Protect instance (in case you have multiple).<br>`message` - custom message text to add| Adds a new custom message for Doorbells.\*
`unifiprotect.remove_doorbell_text` | `device_id` - A device for your current UniFi Protect instance (in case you have multiple).<br>`message` - custom message text to remove| Remove an existing custom message for Doorbells.\*
`unifiprotect.set_default_doorbell_text` | `device_id` - A device for your current UniFi Protect instance (in case you have multiple).<br>`message` - default text for doorbell| Sets the "default" text for when a message is reset or none is set.\*
`unifiprotect.set_doorbell_message` | `device_id` - A device for your current UniFi Protect instance (in case you have multiple).<br>`message` - text for doorbell| Dynamically sets text for doorbell.\*\*
`unifiprotect.profile_ws_messages` | `device_id` - A device for your current UniFi Protect instance (in case you have multiple).<br>`duration` - how long to provide| Debug service to help profile the processing of Websocket messages from UniFi Protect.

\*: Adding, removing or changing a doorbell text option requires you to restart your Home Assistant instance to be able to use the new ones. This is a limitation of how downstream entities and integrations subscribe to options for select entities. They cannot be dynamic.

\*\*: The `unifiprotect.set_doorbell_message` service should _only_ be used for setting the text of your doorbell dynamically. i.e. if you want to set the current time or outdoor temp on it. If you want to set a static message, use the select entity already provided. See the [Dynamic Doorbell](#dynamic-doorbell-messages) blueprint for an example.

## Automating Services

As part of the integration, we provide a couple of blueprints that you can use or extend to automate stuff.

### Doorbell Notifications

[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fraw.githubusercontent.com%2Fbriis%2Funifiprotect%2Fmaster%2Fblueprints%2Fautomation%2Funifiprotect%2Fpush_notification_doorbell_event.yaml)

### Motion Notifications

[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fraw.githubusercontent.com%2Fbriis%2Funifiprotect%2Fmaster%2Fblueprints%2Fautomation%2Funifiprotect%2Fpush_notification_motion_event.yaml)

### Smart Detection Notifications

[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fraw.githubusercontent.com%2Fbriis%2Funifiprotect%2Fmaster%2Fblueprints%2Fautomation%2Funifiprotect%2Fpush_notification_smart_event.yaml)

### Dynamic Doorbell Messages

[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fraw.githubusercontent.com%2Fbriis%2Funifiprotect%2Fmaster%2Fblueprints%2Fautomation%2Funifiprotect%2Fdynamic_doorbell.yaml)

### Enable Debug Logging

If logs are needed for debugging or reporting an issue, use the following configuration.yaml:

```yaml
logger:
  default: error
  logs:
    pyunifiprotect: debug
    custom_components.unifiprotect: debug
```

### CONTRIBUTE TO THE PROJECT AND DEVELOPING WITH A DEVCONTAINER

1. Fork and clone the repository.

2. Open in VSCode and choose to open in devcontainer. Must have VSCode devcontainer prerequisites.

3. Run the command container start from VSCode terminal

4. A fresh Home Assistant test instance will install and will eventually be running on port 9123 with this integration running

5. When the container is running, go to http://localhost:9123 and the add UniFi Protect from the Integration Page.


================================================
FILE: blueprints/automation/unifiprotect/dynamic_doorbell.yaml
================================================
blueprint:
  name: UniFi Protect Dynamic Doorbell
  description: |
    ## UniFi Protect Dynamic Doorbell

    This blueprint will dynamically update the text on the LCD display for your UniFi Protect Doorbell. Will automatically run once per minute and update your Doorbell LCD Screen.

    For this automation to run, you need to ensure your doorbell LCD display is set to the "Default Message" option. This is to still allow you to set static custom messages like "LEAVE PACKAGE AT THE DOOR" without being overridden by the automation. This behavior can be disabled in the options.

    ### Required Settings

      - UniFi Protect Doorbell Sensor

    ### Optional Settings

      - Text template format you want to display (see more below)
      - Temperature sensor entity so you can display temperature on screen
      - Time formatting string for formatting `{ctime}` template (see more below)

    ### Requirements

    To take full effect of this automation blueprint, your Home Assistant instance needs some setup beforehand.

    - A UniFi Protect NVR running on a UDM Pro, UNVR or other Protect Console
    - The [unifiprotect][1] integration version 0.11-beta4 or newer
    - A UniFi G4 Doorbell

    ### Text Template

    The text that is display on your doorbell is configurable using the templating engine. Any [Home Assistant Templating][2] _should_ work in the template.

    The follow variables will injected and replaced at render time as well (note the _single_ curly bracket, not 2):

      - `{ctime}` -- current time, formatting according to "Time Format String" option
      - `{temp}` -- current temperature from the "Temperature Sensor Entity" option

    [1]: https://community.home-assistant.io/t/custom-component-unifi-protect/158041
    [2]: https://www.home-assistant.io/docs/configuration/templating/
  domain: automation
  input:
    doorbell:
      name: Doorbell Entity
      description: >
        The doorbell sensor you want to trigger notifications for.
      selector:
        entity:
          integration: unifiprotect
          domain: select
    temp_entity:
      name: (Optional) Temperature Sensor Entity
      description: Temperature sensor to use. Adds `{temp}` var to template.
      default: ""
      selector:
        entity:
          domain: sensor
          device_class: temperature
    text_template:
      name: (Optional) Text Template
      description: >
        Message template to display on doorbell. Can be any HA template string.
        Final generated string must be 30 characters or less.
      default: "Welcome | {ctime}"
      selector:
        text:
    always_run:
      name: (Optional) Ignore current state?
      description: >
        Ignore the current state of the doorbell text message. If true, will always run every
        minute, potentially resetting a custom static message you set. If false, will only
        run every minute if the current text is the "Default Message" option or the
        "unknown" (meaning it is already set to a dynamic value).
      default: false
      selector:
        boolean:
    time_format:
      name: (Optional) Time Format String
      description: >
        Python datetime format code string for the `{ctime}` variable.
        https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
      default: "%I:%M %p"
      selector:
        text:

mode: single
max_exceeded: silent

variables:
  # input vars
  input_doorbell: !input doorbell
  input_time_format: !input time_format
  input_text_template: !input text_template
  input_temp_entity: !input temp_entity
  input_always_run: !input always_run

trigger:
  - platform: time_pattern
    minutes: "*"

condition:
  - "{{ input_always_run or is_state(input_doorbell, 'unknown') or states[input_doorbell].state.startswith('Default Message') }}"

action:
  - service: unifiprotect.set_doorbell_message
    data:
      entity_id: !input doorbell
      message: |
        {%- set ctime=as_local(now()).strftime(input_time_format) -%}
        {%- if input_temp_entity != "" -%}
          {%- if is_state(input_temp_entity, 'unavailable') -%}
            {%- set temp="" %}
          {%- else -%}
            {%- set temp="{}{}".format(int(states[input_temp_entity].state), state_attr(input_temp_entity, "unit_of_measurement")) -%}
          {%- endif -%}
        {%- endif -%}
        {{ input_text_template.replace("{ctime}", ctime).replace("{temp}", temp) }}


================================================
FILE: blueprints/automation/unifiprotect/push_notification_doorbell_event.yaml
================================================
blueprint:
  name: UniFi Protect Doorbell Notifications
  description: |
    ## UniFi Protect Doorbell Notifications

    This blueprint will send push notifications to desktop browser / mobile Home Assistant apps / Telegram when a UniFi Chime is rung.

    ### Required Settings

      - UniFi Protect Doorbell Sensor

    ### Optional Settings

      - [HTML5 Push Notification Target][1] and/or [Mobile App Notification Target][2]
      - Notification targets and toggles for following notifications types:
        - [HTML5 Push Notification][1]
        - [Mobile App Notification][2]
        - [Telegram Notification][9]
      - Time formatting strings. Timestamp is injected into the notification in case the notification is delay.
      - Cooldown before sending another notification
      - Silence timer for muting notifications via Actionable Notification (docs: [HTML5][6], [Mobile][7])
      - Configurable HA Internal / External Base URLs
      - Configurable lovelace view from notification
      - Optional Actionable Notification to unlock door entity for camera
      - Optional TTS messages (requires [TTS integration][10] to be configured):
        - TTS message to play through the doorbell when unlocking door
        - Actionable Notification to play audio message through doorbell to respond to guest.

    ### Requirements

    To take full effect of this automation blueprint, your Home Assistant instance needs some setup beforehand.

    - A UniFi Protect NVR running on a UDM Pro, UNVR or other Protect Console
    - The [unifiprotect][8] integration (requires version 0.11.1 or newer for TTS)
    - A UniFi camera pair with your NVR that has a chime (like the G4 Doorbell)
    - A valid HTTPS certificate and public facing Home Assistant instance
      - If you do not have these, the actionable notifications and images will not appear in the notifications.
      - You do not need your _whole_ Home Assistant to be publicly accessible. Only the paths `/api/camera_proxy/*` and `/api/webhook/*` need to be accessible outside of your network.
    - TTS messages require Home Assistant to be able to communicate to your cameras directly (**not** to your NVR). It will _not_ work if the internal LAN IP of the camera is not routable by Home Assistant.

    ### Caveat About Actionable Notifications Limits

    HTML5 Push notification can only have a max of 2 actionable notifications. If you enable Slience, Unlock Door and Respond, Slience will not appear.

    Android Push notifications likewise have a limit of 3 actionable notifications. So if you have Open Camera, Slience, Unlock Door and Respond, Slience will not appear.

    iOS is not affected by the limit (limit = 10).

    [1]: https://www.home-assistant.io/integrations/html5
    [2]: https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
    [3]: https://www.home-assistant.io/integrations/html5#tag
    [4]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#notification-channels
    [5]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#replacing
    [6]: https://www.home-assistant.io/integrations/html5#actions
    [7]: https://companion.home-assistant.io/docs/notifications/actionable-notifications/
    [8]: https://community.home-assistant.io/t/custom-component-unifi-protect/158041
    [9]: https://www.home-assistant.io/integrations/telegram/
    [10]: https://www.home-assistant.io/integrations/tts/
  domain: automation
  input:
    doorbell:
      name: Doorbell Entity
      description: >
        The doorbell sensor you want to trigger notifications for.
      selector:
        entity:
          integration: unifiprotect
          domain: binary_sensor
          device_class: occupancy
    lock_entity:
      name: (Optional) Door Lock Entity
      description: >
        The Lock entity to provide an actionable notification to unlock on doorbell
        ring. The time interval you have to respond to the unlock action is controlled 
        by "Cooldown". Short Cooldown timers may prevent you from unlocking the door.
      default: ""
      selector:
        entity:
          domain: lock
    tts_target:
      name: (Optional) TTS Service
      description: >
        The TTS service you want to use to generate TTS messages
        https://www.home-assistant.io/integrations/tts/
      default: ""
      selector:
        text:
    lock_tts:
      name: (Optional) Unlock TTS message
      description: >
        TTS Message for play through doorbell when door is unlocked.
      default: ""
      selector:
        text:
    wait_tts:
      name: (Optional) TTS message
      description: >
        Adds actionable notification to play TTS message to respond to guest. The time 
        interval you have to respond to the TTS action is controlled by "Cooldown". 
        Short Cooldown timers may prevent you from sending a TTS message the door.
      default: ""
      selector:
        text:
    send_mobile:
      name: (Optional) Send Mobile App Notifications
      description: Send mobile app push notifications
      default: true
      selector:
        boolean:
    notify_target_app:
      name: (Optional) Notification Target (Mobile App)
      description: >
        The notification target for mobile apps notifications. Should be only the
        specific service name in the notify domain.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
      default: notify
      selector:
        text:
    send_html5:
      name: (Optional) Send HTML5 Notifications
      description: >
        Send HTML5 push notifications. Requires you to have configured push
        notifications on at least one device.
      default: false
      selector:
        boolean:
    notify_target_html5:
      name: (Optional) Notification Target (HTML5 Push)
      description: >
        The notification target for HTML5 push notifications. Should be only the
        specific service name in the notify domain.
        https://www.home-assistant.io/integrations/html5
      default: push_notification
      selector:
        text:
    channel:
      name: (Optional) Notification Channel
      description: >
        Notification channel/tag to use. Will automatically be prepended with
        "Manual " if action is triggered manually.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#notification-channels
      default: Doorbell
      selector:
        text:
    send_telegram:
      name: (Optional) Telegram Notification
      description: >
        Send a notification via Telegram. Telegram notification will not have a link to Home Assistant like the mobile apps.
      default: false
      selector:
        boolean:
    notify_telegram:
      name: (Optional) Notification Target (Telegram)
      description: >
        The notification target for Telegram notifications. Should be name of the Telegram bot you have configured.
        https://www.home-assistant.io/integrations/telegram/
      default: telegrambot
      selector:
        text:
    time_format:
      name: (Optional) Time Format String
      description: >
        Python datetime format code string for the event trigger time. This string is
        the actual time the doorbell event was triggered in case the automation or
        notification is delayed. Manual triggers will cause this to always be the time
        of the previous event.
        https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
      default: "%I:%M %p"
      selector:
        text:
    cooldown:
      name: (Optional) Cooldown
      description: >
        Delay before sending another notification for this camera after the last event.
        Is also the interval you have to respond to actions in notification.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: seconds
    silence_timer:
      name: (Optional) Silence Notifications
      description: >
        How long to silence notifications for this camera when requested as part of the
        actionable notification. The time interval you have to respond to the slient 
        action is controlled by "Cooldown". Short Cooldown timers may prevent you from 
        silencing.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: minutes
    base_ha_url:
      name: (Optional) Base Home Assistant URL
      description: Base URL to use for opening HA links in HTML5 push notifications.
      default: http://homeassistant.local:8123
      selector:
        text:
    base_image_url:
      name: (Optional) Base Image URL
      description: >
        Publicly accessible base URL for your Home Assistant instance. If you are using
        Nabu Casa, it should be that URL. May be different from your Base Home Assistant
        URL if your HA instance not publicly accessible.
        Must be an HTTPS URL with a valid certificate.
      default: ""
      selector:
        text:
    lovelace_view:
      name: (Optional) Lovelace View
      description: |
        Home Assistant Lovelace view to open when clicking notification.
        If left blank, URI Notification actions will not be generated.
      default: ""
      selector:
        text:
    debug_enabled:
      name: (Optional) Debug
      description: >
        Enable debugging for automation. If enabled, will send persistent notifications
        with extra data.
      default: false
      selector:
        boolean:

mode: single
max_exceeded: silent

variables:
  # input vars
  input_doorbell: !input doorbell
  input_channel: !input channel
  input_base_image_url: !input base_image_url
  input_base_ha_url: !input base_ha_url
  input_lovelace_view: !input lovelace_view
  input_debug_enabled: !input debug_enabled
  input_notify_target_app: !input notify_target_app
  input_notify_target_html5: !input notify_target_html5
  input_notify_telegram: !input notify_telegram
  input_silence_timer: !input silence_timer
  input_lock_entity: !input lock_entity
  input_send_mobile: !input send_mobile
  input_send_html5: !input send_html5
  input_send_telegram: !input send_telegram
  input_time_format: !input time_format
  input_lock_tts: !input lock_tts
  input_wait_tts: !input wait_tts
  input_tts_target: !input tts_target
  # automation data
  camera_entities: '[{% for eid in device_entities(device_id(input_doorbell)) %}{%if eid.startswith(''camera'') and not is_state(eid, ''unavailable'') %}"{{ eid }}",{% endif %}{% endfor %}]'
  media_entities: '[{% for eid in device_entities(device_id(input_doorbell)) %}{%if eid.startswith(''media_player'') and not is_state(eid, ''unavailable'') %}"{{ eid }}",{% endif %}{% endfor %}]'
  # automation variables
  lovelace_view: "{{ input_lovelace_view | trim }}"
  camera_entity_id: "{{ camera_entities | default([None]) | first }}"
  media_entity_id: "{{ media_entities | default([None]) | first }}"
  lock_entity_id: "{{ input_lock_entity or '' }}"
  trigger_time: |
    {% if states[input_doorbell] == None %}
      None
    {% else %}
      {{ as_local(states[input_doorbell].last_changed).strftime(input_time_format) }}
    {% endif %}
  notification_channel: |
    {% if "from_state" in trigger %}
      {{ input_channel }}
    {% else %}
      Manual {{ input_channel }}
    {% endif %}
  notification_tag: "{{ notification_channel.lower().replace(' ', '-') }}"
  notification_title: "{{ device_attr(input_doorbell, 'name') }}"
  notification_url: |
    {% if lovelace_view == "" %}
      None
    {% else %}
      {{ input_base_ha_url | trim }}{{ lovelace_view }}
    {% endif %}
  notification_message: "Someone rang {{ notification_title }}{% if trigger_time != None %} at {{ trigger_time }}{% endif %}."
  notification_message_html5: |
    {{ notification_message }}{% if notification_url != None %}

    Tap to open camera in Home Assistant.
    {% endif %}
  notification_image: |
    {% if camera_entity_id == None or input_base_image_url == "" %}
      None
    {% else %}
      {{ input_base_image_url | trim }}{{ state_attr(camera_entity_id, 'entity_picture') }}
    {% endif %}
  silence_action: "silence-{{ input_doorbell }}"
  unlock_action: "unlock-{{ lock_entity_id }}"
  tts_action: "tts-{{ input_doorbell }}"
  lock_tts_enabled: "{{ input_tts_target != '' and input_lock_tts != '' and media_entity_id != None }}"
  wait_tts_enabled: "{{ input_tts_target != '' and input_wait_tts != '' and media_entity_id != None }}"

trigger:
  - platform: state
    entity_id: !input doorbell
    from: "off"
    to: "on"

action:
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: |
                Entity ID: `{{ input_doorbell }}`
                Camera: `{{ camera_entity_id }}`
                Media Player: `{{ media_entity_id }}`
                Lock: `{{ lock_entity_id }}`
                Lock TTS: `{{ input_lock_tts }}` | `{{ lock_tts_enabled }}`
                Wait TTS: `{{ input_wait_tts }}` | `{{ wait_tts_enabled }}`
                TTS Service: `{{ input_tts_target }}`
                Notification Service (Mobile): `notify.{{ input_notify_target_app }}`
                Notification Service (HTML5): `notify.{{ input_notify_target_html5 }}`
                Notification Service (Telegram): `notify.{{ input_notify_telegram }}`

                Channel: {{ notification_channel }}
                Tag: {{ notification_tag }}
                Message: {{ notification_message }}
                Image: {{ notification_image }}
                URL: {{ notification_url }}

  - choose:
      - conditions: "{{ input_send_mobile }}"
        sequence:
          - service: notify.{{ input_notify_target_app }}
            data:
              message: "{{ notification_message }}"
              title: "{{ notification_title }}"
              data:
                # Android/iOS notification tag
                tag: "{{ notification_tag }}"
                # Android notification Channel
                channel: "{{ notification_channel }}"
                # Android high prority
                ttl: 0
                priority: high
                # iOS high prority
                time-sensitive: 1
                # Android image
                image: "{{ notification_image }}"
                # iOS image
                attachment:
                  url: "{{ notification_image }}"
                actions: >
                  [
                  {% if notification_url != None %}
                  { "action": "URI", "title": "Open Camera", "uri": "{{ lovelace_view }}" },
                  {% endif %}
                  {% if lock_entity_id != "" %}
                  { "action": "{{ unlock_action }}", "title": "Unlock Door" },
                  {% endif %}
                  {% if wait_tts_enabled %}
                  { "action": "{{ tts_action }}", "title": "Respond" },
                  {% endif %}
                  {% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence", "destructive": True },
                  {% endif %}
                  ]
  - choose:
      - conditions: "{{ input_send_html5 }}"
        sequence:
          - service: notify.{{ input_notify_target_html5 }}
            data:
              message: "{{ notification_message_html5 }}"
              title: "{{ notification_title }}"
              data:
                # HTML5 Notification tag
                tag: "{{ notification_tag }}"
                image: "{{ notification_image }}"
                url: "{{ notification_url }}"
                actions: >
                  [
                  {% if lock_entity_id != "" %}
                  { "action": "{{ unlock_action }}", "title": "Unlock Door" },
                  {% endif %}
                  {% if wait_tts_enabled %}
                  { "action": "{{ tts_action }}", "title": "Respond" },
                  {% endif %}
                  {% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence" },
                  {% endif %}
                  ]
  - choose:
      - conditions: "{{ input_send_telegram }}"
        sequence:
          - service: notify.{{ input_notify_telegram }}
            data:
              title: "{{ notification_title }}"
              message: "{{ notification_message }}"
              data: >
                {
                  {%- if notification_image != None -%}
                  "photo": {
                    "url": "{{ notification_image }}",
                    "caption": "{{ notification_message }}",
                    "message_tag": "{{ notification_tag }}",
                  },
                  {%- endif -%}
                  "inline_keyboard": [
                  {% if lock_entity_id != "" %}
                    "Unlock Door:/{{ unlock_action }}",
                  {% endif %}
                  {% if wait_tts_enabled %}
                    "Respond:/{{ tts_action }}",
                  {% endif %}
                  {%- if input_silence_timer > 0 -%}
                    "Silence:/{{ silence_action }}"
                  {%- endif -%}
                  ]
                }
  - wait_for_trigger:
      - platform: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: html5_notification.clicked
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: telegram_callback
        event_data:
          data: "/{{ silence_action }}"
      - platform: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ unlock_action }}"
      - platform: event
        event_type: html5_notification.clicked
        event_data:
          action: "{{ unlock_action }}"
      - platform: event
        event_type: telegram_callback
        event_data:
          data: "/{{ unlock_action }}"
      - platform: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ tts_action }}"
      - platform: event
        event_type: html5_notification.clicked
        event_data:
          action: "{{ tts_action }}"
      - platform: event
        event_type: telegram_callback
        event_data:
          data: "/{{ tts_action }}"
    timeout:
      seconds: !input cooldown
    continue_on_timeout: false
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: |
                Callback: `{{ wait.trigger.event.event_type }}`
                Action : `{{ wait.trigger.event.data.action | default("") }}`
                Action (Telegram): `{{ wait.trigger.event.data.data | default("") }}`
  - choose:
      - conditions: "{{ (wait.trigger.event.data.action == unlock_action or wait.trigger.event.data.data.endswith(unlock_action)) and lock_entity_id != '' }}"
        sequence:
          - choose:
              - conditions: "{{ input_debug_enabled }}"
                sequence:
                  - service: notify.persistent_notification
                    data:
                      title: "Debug: {{ notification_title }}"
                      message: "Unlock `{{ lock_entity_id }}`"
          - service: lock.unlock
            data:
              entity_id: "{{ lock_entity_id }}"
          - choose:
              - conditions: "{{ lock_tts_enabled }}"
                sequence:
                  - choose:
                      - conditions: "{{ input_debug_enabled }}"
                        sequence:
                          - service: notify.persistent_notification
                            data:
                              title: "Debug: {{ notification_title }}"
                              message: "Sending TTS: {{ input_lock_tts }}."
                  - service: tts.{{ input_tts_target }}
                    data:
                      entity_id: "{{ media_entity_id }}"
                      message: "{{ input_lock_tts }}"
          - choose:
              - conditions: "{{ input_send_telegram }}"
                sequence:
                  - service: telegram_bot.answer_callback_query
                    data:
                      message: 'Unlocked door{% if lock_tts_enabled %} and played "{{ input_lock_tts }}" TTS message{% endif %}'
                      callback_query_id: "{{ wait.trigger.event.data.id }}"
      - conditions: "{{ (wait.trigger.event.data.action == silence_action or wait.trigger.event.data.data.endswith(silence_action)) }}"
        sequence:
          - choose:
              - conditions: "{{ input_send_telegram }}"
                sequence:
                  - service: telegram_bot.answer_callback_query
                    data:
                      message: "Doorbell notifications silenced for {{ input_silence_timer }} minutes"
                      callback_query_id: "{{ wait.trigger.event.data.id }}"
          - choose:
              - conditions: "{{ input_debug_enabled }}"
                sequence:
                  - service: notify.persistent_notification
                    data:
                      title: "Debug: {{ notification_title }}"
                      message: Silence started.
          - delay:
              minutes: "{{ input_silence_timer }}"
          - choose:
              - conditions: "{{ input_debug_enabled }}"
                sequence:
                  - service: notify.persistent_notification
                    data:
                      title: "Debug: {{ notification_title }}"
                      message: Silence ended.
      - conditions: "{{ (wait.trigger.event.data.action == tts_action or wait.trigger.event.data.data.endswith(tts_action)) and wait_tts_enabled }}"
        sequence:
          - choose:
              - conditions: "{{ input_debug_enabled }}"
                sequence:
                  - service: notify.persistent_notification
                    data:
                      title: "Debug: {{ notification_title }}"
                      message: "Sending TTS: {{ input_wait_tts }}."
          - service: tts.{{ input_tts_target }}
            data:
              entity_id: "{{ media_entity_id }}"
              message: "{{ input_wait_tts }}"
          - choose:
              - conditions: "{{ input_send_telegram }}"
                sequence:
                  - service: telegram_bot.answer_callback_query
                    data:
                      message: 'Played "{{ input_wait_tts }}" TTS message'
                      callback_query_id: "{{ wait.trigger.event.data.id }}"


================================================
FILE: blueprints/automation/unifiprotect/push_notification_motion_event.yaml
================================================
blueprint:
  name: UniFi Protect Motion Notifications
  description: |
    ## UniFi Protect Motion Notifications

    This blueprint will send push notifications to desktop browser / mobile Home Assistant / Telegram apps when a motion event is fired.

    This blueprint is _only_ for **motion** events, not smart detections.

    ### Required Settings

      - UniFi Protect Motion Sensor

    ### Optional Settings

      - Precense filter for only sending notifications for when you are not home
      - Notification targets and toggles for following notifications types:
        - [HTML5 Push Notification][1]
        - [Mobile App Notification][2]
        - [Telegram Notification][9]
      - Time formatting strings. Timestamp is injected into the notification in case the notification is delay.
      - Notification Channel / Tag (docs: [HTML5 Tag][3], [Android Channels][4], [Mobile Tag][5])
      - Cooldown before sending another notification
      - Silence timer for muting notifications via Actionable Notification (docs: [HTML5][6], [Mobile][7])
      - Configurable HA Internal / External Base URLs
      - Configurable Lovelace view from notification

    ### Requirements

    To take full effect of this automation blueprint, your Home Assistant instance needs some setup beforehand.

    - A UniFi Protect NVR running on a UDM Pro, UNVR or other Protect Console
    - The [unifiprotect][8] integration version 0.11.0 or newer
    - A UniFi camera pair with your NVR
    - A valid HTTPS certificate and public facing Home Assistant instance
      - If you do not have these, the actionable notifications and images will not appear in the notifications.
      - You do not need your _whole_ Home Assistant to be publicly accessible. Only the paths `/api/camera_proxy/*` and`/api/webhook/*` need to be accessible outside of your network.

    [1]: https://www.home-assistant.io/integrations/html5
    [2]: https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
    [3]: https://www.home-assistant.io/integrations/html5#tag
    [4]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#notification-channels
    [5]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#replacing
    [6]: https://www.home-assistant.io/integrations/html5#actions
    [7]: https://companion.home-assistant.io/docs/notifications/actionable-notifications/
    [8]: https://community.home-assistant.io/t/custom-component-unifi-protect/158041
    [9]: https://www.home-assistant.io/integrations/telegram/
  domain: automation
  input:
    motion_entity:
      name: Motion Entity
      description: >
        The camera motion sensor for you want to fire events for.
      selector:
        entity:
          integration: unifiprotect
          domain: binary_sensor
          device_class: motion
    presence_filter:
      name: (Optional) Presence Filter
      description: Only notify if selected presence entity is not "home".
      default: ""
      selector:
        entity:
    send_mobile:
      name: (Optional) Send Mobile App Notifications
      description: Send mobile app push notifications
      default: true
      selector:
        boolean:
    notify_target_app:
      name: (Optional) Notification Target (Mobile App)
      description: >
        The notification target for mobile apps notifications. Should be only the
        specific service name in the notify domain.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
      default: notify
      selector:
        text:
    send_html5:
      name: (Optional) Send HTML5 Notifications
      description: >
        Send HTML5 push notifications. Requires you to have configured push
        notifications on at least one device.
      default: false
      selector:
        boolean:
    notify_target_html5:
      name: (Optional) Notification Target (HTML5 Push)
      description: >
        The notification target for HTML5 push notifications. Should be only the
        specific service name in the notify domain.
        https://www.home-assistant.io/integrations/html5
      default: push_notification
      selector:
        text:
    channel:
      name: (Optional) Notification Channel
      description: >
        Notification channel/tag to use. Will automatically be prepended with
        "Manual " if action is triggered manually.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#notification-channels
      default: Motion
      selector:
        text:
    send_telegram:
      name: (Optional) Telegram Notification
      description: >
        Send a notification via Telegram. Telegram notification will not have a link to Home Assistant like the mobile apps.
      default: false
      selector:
        boolean:
    notify_telegram:
      name: (Optional) Notification Target (Telegram)
      description: >
        The notification target for Telegram notifications. Should be name of the Telegram bot you have configured.
        https://www.home-assistant.io/integrations/telegram/
      default: telegrambot
      selector:
        text:
    time_format:
      name: (Optional) Time Format String
      description: >
        Python datetime format code string for the event trigger time. This string is
        the actual time the motion event was triggered in case the automation or
        notification is delayed. Manual triggers will cause this to always be the time
        of the previous event.
        https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
      default: "%I:%M %p"
      selector:
        text:
    cooldown:
      name: (Optional) Cooldown
      description: >
        Delay before sending another notification for this camera after the last event.
        Is also the interval you have to respond to actions in notification.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: seconds
    silence_timer:
      name: (Optional) Silence Notifications
      description: >
        How long to silence notifications for this camera when requested as part of the
        actionable notification. The time interval you have to respond to the slient 
        action is controlled by "Cooldown". Short Cooldown timers may prevent you from 
        silencing.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: minutes
    base_ha_url:
      name: (Optional) Base Home Assistant URL
      description: Base URL to use for opening HA links in HTML5 push notifications.
      default: http://homeassistant.local:8123
      selector:
        text:
    base_image_url:
      name: (Optional) Base Image URL
      description: >
        Publicly accessible base URL for your Home Assistant instance. If you are using
        Nabu Casa, it should be that URL. May be different from your Base Home Assistant
        URL if your HA instance not publicly accessible.
        Must be an HTTPS URL with a valid certificate.
      default: ""
      selector:
        text:
    lovelace_view:
      name: (Optional) Lovelace View
      description: |
        Home Assistant Lovelace view to open when clicking notification.
        If left blank, URI Notification actions will not be generated.
      default: ""
      selector:
        text:
    debug_enabled:
      name: (Optional) Debug
      description: >
        Enable debugging for automation. If enabled, will send persistent notifications
        with extra data.
      default: false
      selector:
        boolean:

mode: single
max_exceeded: silent

variables:
  # input vars
  input_motion_entity: !input motion_entity
  input_channel: !input channel
  input_base_image_url: !input base_image_url
  input_base_ha_url: !input base_ha_url
  input_lovelace_view: !input lovelace_view
  input_debug_enabled: !input debug_enabled
  input_notify_target_app: !input notify_target_app
  input_notify_target_html5: !input notify_target_html5
  input_notify_telegram: !input notify_telegram
  input_silence_timer: !input silence_timer
  input_send_mobile: !input send_mobile
  input_send_html5: !input send_html5
  input_send_telegram: !input send_telegram
  input_time_format: !input time_format
  input_presence_filter: !input presence_filter
  # automation data
  camera_entities: '[{% for eid in device_entities(device_id(input_motion_entity)) %}{%if eid.startswith(''camera'') and not is_state(eid, ''unavailable'') %}"{{ eid }}",{% endif %}{% endfor %}]'
  # automation variables
  lovelace_view: "{{ input_lovelace_view | trim }}"
  camera_entity_id: "{{ camera_entities | default([None]) | first }}"
  trigger_time: |
    {% if states[input_motion_entity] == None %}
      None
    {% else %}
      {{ as_local(states[input_motion_entity].last_changed).strftime(input_time_format) }}
    {% endif %}
  notification_channel: |
    {% if "from_state" in trigger %}
      {{ input_channel }}
    {% else %}
      Manual {{ input_channel }}
    {% endif %}
  notification_tag: "{{ notification_channel.lower().replace(' ', '-') }}"
  notification_title: "{{ device_attr(input_motion_entity, 'name') }}"
  notification_url: |
    {% if lovelace_view == "" %}
      None
    {% else %}
      {{ input_base_ha_url | trim }}{{ lovelace_view }}
    {% endif %}
  notification_message: "Motion detected by {{ notification_title }}{% if trigger_time != None %} at {{ trigger_time }}{% endif %}."
  notification_message_html5: |
    {{ notification_message }}{% if notification_url != None %}

    Tap to open camera in Home Assistant.
    {% endif %}
  notification_image: |
    {% if camera_entity_id == None or input_base_image_url == "" %}
      None
    {% else %}
      {{ input_base_image_url | trim }}{{ state_attr(camera_entity_id, 'entity_picture') }}
    {% endif %}
  silence_action: "silence-{{ input_motion_entity }}"

trigger:
  - platform: state
    entity_id: !input motion_entity
    from: "off"
    to: "on"

condition:
  - "{{ not input_presence_filter or not is_state(input_presence_filter, 'home') }}"

action:
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: |
                Entity ID: `{{ input_motion_entity }}`
                Camera: `{{ camera_entity_id }}`
                Notification Service (Mobile): `notify.{{ input_notify_target_app }}`
                Notification Service (HTML5): `notify.{{ input_notify_target_html5 }}`
                Notification Service (Telegram): `notify.{{ input_notify_telegram }}`

                Channel: {{ notification_channel }}
                Tag: {{ notification_tag }}
                Message: {{ notification_message }}
                Image: {{ notification_image }}
                URL: {{ notification_url }}

  - choose:
      - conditions: "{{ input_send_mobile }}"
        sequence:
          - service: notify.{{ input_notify_target_app }}
            data:
              message: "{{ notification_message }}"
              title: "{{ notification_title }}"
              data:
                # Android/iOS notification tag
                tag: "{{ notification_tag }}"
                # Android notification Channel
                channel: "{{ notification_channel }}"
                # Android high prority
                ttl: 0
                priority: high
                # iOS high prority
                time-sensitive: 1
                # Android image
                image: "{{ notification_image }}"
                # iOS image
                attachment:
                  url: "{{ notification_image }}"
                actions: >
                  [{% if notification_url != None %}
                  { "action": "URI", "title": "Open Camera", "uri": "{{ lovelace_view }}" },
                  {% endif %}
                  {% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence", "destructive": True },
                  {% endif %}]
  - choose:
      - conditions: "{{ input_send_html5 }}"
        sequence:
          - service: notify.{{ input_notify_target_html5 }}
            data:
              message: "{{ notification_message_html5 }}"
              title: "{{ notification_title }}"
              data:
                # HTML5 Notification tag
                tag: "{{ notification_tag }}"
                image: "{{ notification_image }}"
                url: "{{ notification_url }}"
                actions: >
                  [{% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence" },
                  {% endif %}]
  - choose:
      - conditions: "{{ input_send_telegram }}"
        sequence:
          - service: notify.{{ input_notify_telegram }}
            data:
              title: "{{ notification_title }}"
              message: "{{ notification_message }}"
              data: >
                {
                  {%- if notification_image != None -%}
                  "photo": {
                    "url": "{{ notification_image }}",
                    "caption": "{{ notification_message }}",
                    "message_tag": "{{ notification_tag }}",
                  },
                  {%- endif -%}
                  {%- if input_silence_timer > 0 -%}
                  "inline_keyboard": ["Silence:/{{ silence_action }}"],
                  {%- endif -%}
                }
  - wait_for_trigger:
      - platform: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: html5_notification.clicked
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: telegram_callback
        event_data:
          data: "/{{ silence_action }}"
    timeout:
      seconds: !input cooldown
    continue_on_timeout: false
  - choose:
      - conditions: "{{ input_send_telegram }}"
        sequence:
          - service: telegram_bot.answer_callback_query
            data:
              message: "Motion notifications silenced for {{ input_silence_timer }} minutes"
              callback_query_id: "{{ wait.trigger.event.data.id }}"
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: Silence started.
  - delay:
      minutes: "{{ input_silence_timer }}"
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: Silence ended.


================================================
FILE: blueprints/automation/unifiprotect/push_notification_smart_event.yaml
================================================
blueprint:
  name: UniFi Protect Smart Detection Notifications
  description: |
    ## UniFi Protect Smart Detection Notifications

    This blueprint will send push notifications to desktop browser / mobile Home Assistant / Telegram apps when a smart detection event is fired.

    This blueprint is _only_ for **smart detection**, not motion.

    ### Required Settings

      - UniFi Protect Smart Detection Sensor
      - What Smart Detection(s) you want to trigger on

    ### Optional Settings

      - Precense filter for only sending notifications for when you are not home
      - Notification targets and toggles for following notifications types:
        - [HTML5 Push Notification][1]
        - [Mobile App Notification][2]
        - [Telegram Notification][9]
      - Time formatting strings. Timestamp is injected into the notification in case the notification is delay.
      - Notification Channel / Tag (docs: [HTML5 Tag][3], [Android Channels][4], [Mobile Tag][5])
      - Cooldown before sending another notification
      - Silence timer for muting notifications via Actionable Notification (docs: [HTML5][6], [Mobile][7])
      - Configurable HA Internal / External Base URLs
      - Configurable Lovelace view from notification

    ### Requirements

    To take full effect of this automation blueprint, your Home Assistant instance needs some setup beforehand.

    - A UniFi Protect NVR running on a UDM Pro, UNVR or other Protect Console
    - The [unifiprotect][8] integration version 0.11.0 or newer
    - A UniFi camera pair with your NVR that has Smart Detections. This is any G4 series camera _except_ the EA G4 Instant.
    - A valid HTTPS certificate and public facing Home Assistant instance
      - If you do not have these, the actionable notifications and images will not appear in the notifications.
      - You do not need your _whole_ Home Assistant to be publicly accessible. Only the paths `/api/camera_proxy/*` and`/api/webhook/*` need to be accessible outside of your network.

    [1]: https://www.home-assistant.io/integrations/html5
    [2]: https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
    [3]: https://www.home-assistant.io/integrations/html5#tag
    [4]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#notification-channels
    [5]: https://companion.home-assistant.io/docs/notifications/notifications-basic/#replacing
    [6]: https://www.home-assistant.io/integrations/html5#actions
    [7]: https://companion.home-assistant.io/docs/notifications/actionable-notifications/
    [8]: https://community.home-assistant.io/t/custom-component-unifi-protect/158041
    [9]: https://www.home-assistant.io/integrations/telegram/
  domain: automation
  input:
    smart_entity:
      name: Smart Dection Entity
      description: >
        The smart detection sensor for you want to fire events for.
      selector:
        entity:
          integration: unifiprotect
          domain: sensor
          device_class: occupancy
    objects:
      name: (Optional) Smart Detections
      description: >
        Smart Detections to filter on. List should be comma separated.
        Possible objects: person, vehicle
      default: person,vehicle
      selector:
        text:
    presence_filter:
      name: (Optional) Presence Filter
      description: Only notify if selected presence entity is not "home".
      default: ""
      selector:
        entity:
    send_mobile:
      name: (Optional) Send Mobile App Notifications
      description: Send mobile app push notifications
      default: true
      selector:
        boolean:
    notify_target_app:
      name: (Optional) Notification Target (Mobile App)
      description: >
        The notification target for mobile apps notifications. Should be only the
        specific service name in the notify domain.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices
      default: notify
      selector:
        text:
    send_html5:
      name: (Optional) Send HTML5 Notifications
      description: >
        Send HTML5 push notifications. Requires you to have configured push
        notifications on at least one device.
      default: false
      selector:
        boolean:
    notify_target_html5:
      name: (Optional) Notification Target (HTML5 Push)
      description: >
        The notification target for HTML5 push notifications. Should be only the
        specific service name in the notify domain.
        https://www.home-assistant.io/integrations/html5
      default: push_notification
      selector:
        text:
    channel:
      name: (Optional) Notification Channel
      description: >
        Notification channel/tag to use. Will automatically be prepended with
        "Manual " if action is triggered manually.
        https://companion.home-assistant.io/docs/notifications/notifications-basic#notification-channels
      default: Smart Detection
      selector:
        text:
    send_telegram:
      name: (Optional) Telegram Notification
      description: >
        Send a notification via Telegram. Telegram notification will not have a link to Home Assistant like the mobile apps.
      default: false
      selector:
        boolean:
    notify_telegram:
      name: (Optional) Notification Target (Telegram)
      description: >
        The notification target for Telegram notifications. Should be name of the Telegram bot you have configured.
        https://www.home-assistant.io/integrations/telegram/
      default: telegrambot
      selector:
        text:
    time_format:
      name: (Optional) Time Format String
      description: >
        Python datetime format code string for the event trigger time. This string is
        the actual time the motion event was triggered in case the automation or
        notification is delayed. Manual triggers will cause this to always be the time
        of the previous event.
        https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
      default: "%I:%M %p"
      selector:
        text:
    cooldown:
      name: (Optional) Cooldown
      description: >
        Delay before sending another notification for this camera after the last event.
        Is also the interval you have to respond to actions in notification.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: seconds
    silence_timer:
      name: (Optional) Silence Notifications
      description: >
        How long to silence notifications for this camera when requested as part of the
        actionable notification. The time interval you have to respond to the slient
        action is controlled by "Cooldown". Short Cooldown timers may prevent you from
        silencing.
      default: 30
      selector:
        number:
          max: 300
          min: 0
          unit_of_measurement: minutes
    base_ha_url:
      name: (Optional) Base Home Assistant URL
      description: Base URL to use for opening HA links in HTML5 push notifications.
      default: http://homeassistant.local:8123
      selector:
        text:
    base_image_url:
      name: (Optional) Base Image URL
      description: >
        Publicly accessible base URL for your Home Assistant instance. If you are using
        Nabu Casa, it should be that URL. May be different from your Base Home Assistant
        URL if your HA instance not publicly accessible.
        Must be an HTTPS URL with a valid certificate.
      default: ""
      selector:
        text:
    lovelace_view:
      name: (Optional) Lovelace View
      description: |
        Home Assistant Lovelace view to open when clicking notification.
        If left blank, URI Notification actions will not be generated.
      default: ""
      selector:
        text:
    debug_enabled:
      name: (Optional) Debug
      description: >
        Enable debugging for automation. If enabled, will send persistent notifications
        with extra data.
      default: false
      selector:
        boolean:

mode: single
max_exceeded: silent

variables:
  # input vars
  input_smart_entity: !input smart_entity
  input_objects: !input objects
  input_channel: !input channel
  input_base_image_url: !input base_image_url
  input_base_ha_url: !input base_ha_url
  input_lovelace_view: !input lovelace_view
  input_debug_enabled: !input debug_enabled
  input_notify_target_app: !input notify_target_app
  input_notify_target_html5: !input notify_target_html5
  input_notify_telegram: !input notify_telegram
  input_silence_timer: !input silence_timer
  input_send_mobile: !input send_mobile
  input_send_html5: !input send_html5
  input_send_telegram: !input send_telegram
  input_time_format: !input time_format
  input_presence_filter: !input presence_filter
  # automation data
  camera_entities: '[{% for eid in device_entities(device_id(input_smart_entity)) %}{%if eid.startswith(''camera'') and not is_state(eid, ''unavailable'') %}"{{ eid }}",{% endif %}{% endfor %}]'
  smart_entities: '[{% for eid in device_entities(device_id(input_smart_entity)) %}{%if eid.startswith(''sensor'') and is_state_attr(eid, "device_class", "occupancy") %}"{{ eid }}",{% endif %}{% endfor %}]'
  smart_detect_objs: "{{ (input_objects | lower).split(',') | map('trim') | list | select('in', ['person', 'vehicle']) | list }}"
  # automation variables
  lovelace_view: "{{ input_lovelace_view | trim }}"
  camera_entity_id: "{{ camera_entities | default([None]) | first }}"
  trigger_object: "{{ states[input_smart_entity].state }}"
  trigger_time: |
    {% if states[input_smart_entity] == None %}
      None
    {% else %}
      {{ as_local(states[input_smart_entity].last_changed).strftime(input_time_format) }}
    {% endif %}
  notification_channel: |
    {% if "from_state" in trigger %}
      {{ input_channel }}
    {% else %}
      Manual {{ input_channel }}
    {% endif %}
  notification_tag: "{{ notification_channel.lower().replace(' ', '-') }}"
  notification_title: "{{ device_attr(input_smart_entity, 'name') }}"
  notification_url: |
    {% if lovelace_view == "" %}
      None
    {% else %}
      {{ input_base_ha_url | trim }}{{ lovelace_view }}
    {% endif %}
  notification_message: "{{ trigger_object.title() }} detected by {{ notification_title }}{% if trigger_time != None %} at {{ trigger_time }}{% endif %}."
  notification_message_html5: |
    {{ notification_message }}{% if notification_url != None %}

    Tap to open camera in Home Assistant.
    {% endif %}
  notification_image: |
    {% if camera_entity_id == None or input_base_image_url == "" %}
      None
    {% else %}
      {{ input_base_image_url | trim }}{{ state_attr(camera_entity_id, 'entity_picture') }}
    {% endif %}
  silence_action: "silence-{{ input_smart_entity }}"

trigger:
  - platform: state
    entity_id: !input smart_entity
    from: "none"

condition:
  - "{{ not input_presence_filter or not is_state(input_presence_filter, 'home') }}"
  - "{{ trigger_object in smart_detect_objs }}"

action:
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: |
                Entity ID: `{{ input_smart_entity }}`
                Camera: `{{ camera_entity_id }}`
                Object: `{{ trigger_object }}`
                Notification Service (Mobile): `notify.{{ input_notify_target_app }}`
                Notification Service (HTML5): `notify.{{ input_notify_target_html5 }}`
                Notification Service (Telegram): `notify.{{ input_notify_telegram }}`

                Channel: {{ notification_channel }}
                Tag: {{ notification_tag }}
                Message: {{ notification_message }}
                Image: {{ notification_image }}
                URL: {{ notification_url }}
  - choose:
      - conditions: "{{ input_send_mobile }}"
        sequence:
          - service: notify.{{ input_notify_target_app }}
            data:
              message: "{{ notification_message }}"
              title: "{{ notification_title }}"
              data:
                # Android/iOS notification tag
                tag: "{{ notification_tag }}"
                # Android notification Channel
                channel: "{{ notification_channel }}"
                # Android high prority
                ttl: 0
                priority: high
                # iOS high prority
                time-sensitive: 1
                # Android image
                image: "{{ notification_image }}"
                # iOS image
                attachment:
                  url: "{{ notification_image }}"
                actions: >
                  [{% if notification_url != None %}
                  { "action": "URI", "title": "Open Camera", "uri": "{{ lovelace_view }}" },
                  {% endif %}
                  {% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence", "destructive": True },
                  {% endif %}]
  - choose:
      - conditions: "{{ input_send_html5 }}"
        sequence:
          - service: notify.{{ input_notify_target_html5 }}
            data:
              message: "{{ notification_message_html5 }}"
              title: "{{ notification_title }}"
              data:
                # HTML5 Notification tag
                tag: "{{ notification_tag }}"
                image: "{{ notification_image }}"
                url: "{{ notification_url }}"
                actions: >
                  [{% if input_silence_timer > 0 %}
                  { "action": "{{ silence_action }}", "title": "Silence" },
                  {% endif %}]
  - choose:
      - conditions: "{{ input_send_telegram }}"
        sequence:
          - service: notify.{{ input_notify_telegram }}
            data:
              title: "{{ notification_title }}"
              message: "{{ notification_message }}"
              data: >
                {
                  {%- if notification_image != None -%}
                  "photo": {
                    "url": "{{ notification_image }}",
                    "caption": "{{ notification_message }}",
                    "message_tag": "{{ notification_tag }}",
                  },
                  {%- endif -%}
                  {%- if input_silence_timer > 0 -%}
                  "inline_keyboard": ["Silence:/{{ silence_action }}"],
                  {%- endif -%}
                }
  - wait_for_trigger:
      - platform: event
        event_type: mobile_app_notification_action
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: html5_notification.clicked
        event_data:
          action: "{{ silence_action }}"
      - platform: event
        event_type: telegram_callback
        event_data:
          data: "/{{ silence_action }}"
    timeout:
      seconds: !input cooldown
    continue_on_timeout: false
  - choose:
      - conditions: "{{ input_send_telegram }}"
        sequence:
          - service: telegram_bot.answer_callback_query
            data:
              message: "Smart Detections notifications silenced for {{ input_silence_timer }} minutes"
              callback_query_id: "{{ wait.trigger.event.data.id }}"
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: Silence started.
  - delay:
      minutes: "{{ input_silence_timer }}"
  - choose:
      - conditions: "{{ input_debug_enabled }}"
        sequence:
          - service: notify.persistent_notification
            data:
              title: "Debug: {{ notification_title }}"
              message: Silence ended.


================================================
FILE: custom_components/unifiprotect/__init__.py
================================================
"""UniFi Protect Platform."""
from __future__ import annotations

import asyncio
from datetime import timedelta
import logging

from aiohttp import CookieJar
from aiohttp.client_exceptions import ServerDisconnectedError
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    CONF_HOST,
    CONF_PASSWORD,
    CONF_PORT,
    CONF_USERNAME,
    CONF_VERIFY_SSL,
    EVENT_HOMEASSISTANT_STOP,
    Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
from pyunifiprotect.data import ModelType

from .const import (
    CONF_ALL_UPDATES,
    CONF_DOORBELL_TEXT,
    CONF_OVERRIDE_CHOST,
    CONFIG_OPTIONS,
    DEFAULT_SCAN_INTERVAL,
    DEVICES_FOR_SUBSCRIBE,
    DEVICES_THAT_ADOPT,
    DOMAIN,
    MIN_REQUIRED_PROTECT_V,
    OUTDATED_LOG_MESSAGE,
    PLATFORMS,
)
from .data import ProtectData
from .services import async_cleanup_services, async_setup_services

_LOGGER = logging.getLogger(__name__)

SCAN_INTERVAL = timedelta(seconds=DEFAULT_SCAN_INTERVAL)


@callback
async def _async_migrate_data(
    hass: HomeAssistant, entry: ConfigEntry, protect: ProtectApiClient
) -> None:
    # already up to date, skip
    if CONF_ALL_UPDATES in entry.options:
        return

    _LOGGER.info("Starting entity migration...")

    # migrate entry
    options = dict(entry.options)
    data = dict(entry.data)
    options[CONF_ALL_UPDATES] = False
    if CONF_DOORBELL_TEXT in options:
        del options[CONF_DOORBELL_TEXT]
    hass.config_entries.async_update_entry(entry, data=data, options=options)

    # migrate entities
    registry = er.async_get(hass)
    mac_to_id: dict[str, str] = {}
    mac_to_channel_id: dict[str, str] = {}
    bootstrap = await protect.get_bootstrap()
    for model in DEVICES_THAT_ADOPT:
        attr = model.value + "s"
        for device in getattr(bootstrap, attr).values():
            mac_to_id[device.mac] = device.id
            if model != ModelType.CAMERA:
                continue

            for channel in device.channels:
                channel_id = str(channel.id)
                if channel.is_rtsp_enabled:
                    break
            mac_to_channel_id[device.mac] = channel_id

    count = 0
    entities = er.async_entries_for_config_entry(registry, entry.entry_id)
    for entity in entities:
        new_unique_id: str | None = None
        if entity.domain != Platform.CAMERA.value:
            parts = entity.unique_id.split("_")
            if len(parts) >= 2:
                device_or_key = "_".join(parts[:-1])
                mac = parts[-1]

                device_id = mac_to_id[mac]
                if device_or_key == device_id:
                    new_unique_id = device_id
                else:
                    new_unique_id = f"{device_id}_{device_or_key}"
        else:
            parts = entity.unique_id.split("_")
            if len(parts) == 2:
                mac = parts[1]
                device_id = mac_to_id[mac]
                channel_id = mac_to_channel_id[mac]
                new_unique_id = f"{device_id}_{channel_id}"
            else:
                device_id = parts[0]
                channel_id = parts[2]
                extra = "" if len(parts) == 3 else "_insecure"
                new_unique_id = f"{device_id}_{channel_id}{extra}"

        if new_unique_id is None:
            continue

        _LOGGER.debug(
            "Migrating entity %s (old unique_id: %s, new unique_id: %s)",
            entity.entity_id,
            entity.unique_id,
            new_unique_id,
        )
        try:
            registry.async_update_entity(entity.entity_id, new_unique_id=new_unique_id)
        except ValueError:
            _LOGGER.warning(
                "Could not migrate entity %s (old unique_id: %s, new unique_id: %s)",
                entity.entity_id,
                entity.unique_id,
                new_unique_id,
            )
        else:
            count += 1

    _LOGGER.info("Migrated %s entities", count)
    if count != len(entities):
        _LOGGER.warning("%s entities not migrated", len(entities) - count)


@callback
def _async_import_options_from_data_if_missing(
    hass: HomeAssistant, entry: ConfigEntry
) -> None:
    options = dict(entry.options)
    data = dict(entry.data)
    modified = False
    for importable_option in CONFIG_OPTIONS:
        if importable_option not in entry.options and importable_option in entry.data:
            options[importable_option] = entry.data[importable_option]
            del data[importable_option]
            modified = True

    if modified:
        hass.config_entries.async_update_entry(entry, data=data, options=options)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up the UniFi Protect config entries."""
    _async_import_options_from_data_if_missing(hass, entry)

    session = async_create_clientsession(hass, cookie_jar=CookieJar(unsafe=True))
    protect = ProtectApiClient(
        host=entry.data[CONF_HOST],
        port=entry.data[CONF_PORT],
        username=entry.data[CONF_USERNAME],
        password=entry.data[CONF_PASSWORD],
        verify_ssl=entry.data[CONF_VERIFY_SSL],
        session=session,
        subscribed_models=DEVICES_FOR_SUBSCRIBE,
        override_connection_host=entry.options.get(CONF_OVERRIDE_CHOST, False),
        ignore_stats=not entry.options.get(CONF_ALL_UPDATES, False),
    )
    _LOGGER.debug("Connect to UniFi Protect")
    data_service = ProtectData(hass, protect, SCAN_INTERVAL, entry)

    try:
        nvr_info = await protect.get_nvr()
    except NotAuthorized as err:
        raise ConfigEntryAuthFailed(err) from err
    except (asyncio.TimeoutError, NvrError, ServerDisconnectedError) as err:
        raise ConfigEntryNotReady from err

    if nvr_info.version < MIN_REQUIRED_PROTECT_V:
        _LOGGER.error(
            OUTDATED_LOG_MESSAGE,
            nvr_info.version,
            MIN_REQUIRED_PROTECT_V,
        )
        return False

    await _async_migrate_data(hass, entry, protect)
    if entry.unique_id is None:
        hass.config_entries.async_update_entry(entry, unique_id=nvr_info.mac)

    await data_service.async_setup()
    if not data_service.last_update_success:
        raise ConfigEntryNotReady

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = data_service
    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    async_setup_services(hass)

    entry.async_on_unload(entry.add_update_listener(_async_options_updated))
    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, data_service.async_stop)
    )

    return True


async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry) -> None:
    """Update options."""
    await hass.config_entries.async_reload(entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload UniFi Protect config entry."""
    if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
        data: ProtectData = hass.data[DOMAIN][entry.entry_id]
        await data.async_stop()
        hass.data[DOMAIN].pop(entry.entry_id)
        async_cleanup_services(hass)

    return bool(unload_ok)


async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Migrate old entry."""
    _LOGGER.debug("Migrating from version %s", config_entry.version)

    if config_entry.version == 1:
        new = {**config_entry.data}
        # keep verify SSL false for anyone migrating to maintain backwards compatibility
        new[CONF_VERIFY_SSL] = False
        if CONF_DOORBELL_TEXT in new:
            del new[CONF_DOORBELL_TEXT]

        config_entry.version = 2
        hass.config_entries.async_update_entry(config_entry, data=new)

    _LOGGER.info("Migration to version %s successful", config_entry.version)

    return True


================================================
FILE: custom_components/unifiprotect/binary_sensor.py
================================================
"""This component provides binary sensors for UniFi Protect."""
from __future__ import annotations

from copy import copy
from dataclasses import dataclass
import logging

from homeassistant.components.binary_sensor import (
    BinarySensorDeviceClass,
    BinarySensorEntity,
    BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_LAST_TRIP_TIME, ATTR_MODEL
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from pyunifiprotect.data import NVR, Camera, Event, Light, MountType, Sensor

from .const import DOMAIN
from .data import ProtectData
from .entity import (
    EventThumbnailMixin,
    ProtectDeviceEntity,
    ProtectNVREntity,
    async_all_device_entities,
)
from .models import ProtectRequiredKeysMixin
from .utils import get_nested_attr

_LOGGER = logging.getLogger(__name__)
_KEY_DOOR = "door"


@dataclass
class ProtectBinaryEntityDescription(
    ProtectRequiredKeysMixin, BinarySensorEntityDescription
):
    """Describes UniFi Protect Binary Sensor entity."""

    ufp_last_trip_value: str | None = None


MOUNT_DEVICE_CLASS_MAP = {
    MountType.GARAGE: BinarySensorDeviceClass.GARAGE_DOOR,
    MountType.WINDOW: BinarySensorDeviceClass.WINDOW,
    MountType.DOOR: BinarySensorDeviceClass.DOOR,
}


CAMERA_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
    ProtectBinaryEntityDescription(
        key="doorbell",
        name="Doorbell",
        device_class=BinarySensorDeviceClass.OCCUPANCY,
        icon="mdi:doorbell-video",
        ufp_required_field="feature_flags.has_chime",
        ufp_value="is_ringing",
        ufp_last_trip_value="last_ring",
    ),
    ProtectBinaryEntityDescription(
        key="dark",
        name="Is Dark",
        icon="mdi:brightness-6",
        ufp_value="is_dark",
    ),
)

LIGHT_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
    ProtectBinaryEntityDescription(
        key="dark",
        name="Is Dark",
        icon="mdi:brightness-6",
        ufp_value="is_dark",
    ),
    ProtectBinaryEntityDescription(
        key="motion",
        name="Motion Detected",
        device_class=BinarySensorDeviceClass.MOTION,
        ufp_value="is_pir_motion_detected",
        ufp_last_trip_value="last_motion",
    ),
)

SENSE_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
    ProtectBinaryEntityDescription(
        key=_KEY_DOOR,
        name="Contact",
        device_class=BinarySensorDeviceClass.DOOR,
        ufp_value="is_opened",
        ufp_last_trip_value="open_status_changed_at",
        ufp_enabled="is_contact_sensor_enabled",
    ),
    ProtectBinaryEntityDescription(
        key="battery_low",
        name="Battery low",
        device_class=BinarySensorDeviceClass.BATTERY,
        entity_category=EntityCategory.DIAGNOSTIC,
        ufp_value="battery_status.is_low",
    ),
    ProtectBinaryEntityDescription(
        key="motion",
        name="Motion Detected",
        device_class=BinarySensorDeviceClass.MOTION,
        ufp_value="is_motion_detected",
        ufp_last_trip_value="motion_detected_at",
        ufp_enabled="is_motion_sensor_enabled",
    ),
    ProtectBinaryEntityDescription(
        key="tampering",
        name="Tampering Detected",
        device_class=BinarySensorDeviceClass.TAMPER,
        ufp_value="is_tampering_detected",
        ufp_last_trip_value="tampering_detected_at",
    ),
)

MOTION_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
    ProtectBinaryEntityDescription(
        key="motion",
        name="Motion",
        device_class=BinarySensorDeviceClass.MOTION,
        ufp_value="is_motion_detected",
        ufp_last_trip_value="last_motion",
    ),
)


DISK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
    ProtectBinaryEntityDescription(
        key="disk_health",
        name="Disk {index} Health",
        device_class=BinarySensorDeviceClass.PROBLEM,
        entity_category=EntityCategory.DIAGNOSTIC,
    ),
)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up binary sensors for UniFi Protect integration."""
    data: ProtectData = hass.data[DOMAIN][entry.entry_id]
    entities: list[ProtectDeviceEntity] = async_all_device_entities(
        data,
        ProtectDeviceBinarySensor,
        camera_descs=CAMERA_SENSORS,
        light_descs=LIGHT_SENSORS,
        sense_descs=SENSE_SENSORS,
    )
    entities += _async_motion_entities(data)
    entities += _async_nvr_entities(data)

    async_add_entities(entities)


@callback
def _async_motion_entities(
    data: ProtectData,
) -> list[ProtectDeviceEntity]:
Download .txt
gitextract_8gl2b350/

├── .devcontainer/
│   ├── Dockerfile
│   ├── automations.yaml
│   ├── configuration.yaml
│   └── devcontainer.json
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.yml
│   │   └── feature-request.yml
│   └── workflows/
│       └── ci.yaml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   └── tasks.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── blueprints/
│   └── automation/
│       └── unifiprotect/
│           ├── dynamic_doorbell.yaml
│           ├── push_notification_doorbell_event.yaml
│           ├── push_notification_motion_event.yaml
│           └── push_notification_smart_event.yaml
├── custom_components/
│   └── unifiprotect/
│       ├── __init__.py
│       ├── binary_sensor.py
│       ├── button.py
│       ├── camera.py
│       ├── config_flow.py
│       ├── const.py
│       ├── data.py
│       ├── entity.py
│       ├── light.py
│       ├── manifest.json
│       ├── media_player.py
│       ├── models.py
│       ├── number.py
│       ├── select.py
│       ├── sensor.py
│       ├── services.py
│       ├── services.yaml
│       ├── strings.json
│       ├── switch.py
│       ├── translations/
│       │   ├── da.json
│       │   ├── de.json
│       │   ├── en.json
│       │   ├── fr.json
│       │   ├── nb.json
│       │   └── nl.json
│       └── utils.py
├── hacs.json
├── info.md
├── pylintrc
├── pyproject.toml
└── unifiprotect.markdown
Download .txt
SYMBOL INDEX (156 symbols across 16 files)

FILE: custom_components/unifiprotect/__init__.py
  function _async_migrate_data (line 49) | async def _async_migrate_data(
  function _async_import_options_from_data_if_missing (line 139) | def _async_import_options_from_data_if_missing(
  function async_setup_entry (line 155) | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> ...
  function _async_options_updated (line 209) | async def _async_options_updated(hass: HomeAssistant, entry: ConfigEntry...
  function async_unload_entry (line 214) | async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) ->...
  function async_migrate_entry (line 225) | async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigE...

FILE: custom_components/unifiprotect/binary_sensor.py
  class ProtectBinaryEntityDescription (line 36) | class ProtectBinaryEntityDescription(
  function async_setup_entry (line 139) | async def async_setup_entry(
  function _async_motion_entities (line 160) | def _async_motion_entities(
  function _async_nvr_entities (line 177) | def _async_nvr_entities(
  class ProtectDeviceBinarySensor (line 195) | class ProtectDeviceBinarySensor(ProtectDeviceEntity, BinarySensorEntity):
    method _async_update_device_from_protect (line 202) | def _async_update_device_from_protect(self) -> None:
  class ProtectDiskBinarySensor (line 230) | class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
    method __init__ (line 235) | def __init__(
    method _async_update_device_from_protect (line 250) | def _async_update_device_from_protect(self) -> None:
  class ProtectEventBinarySensor (line 262) | class ProtectEventBinarySensor(EventThumbnailMixin, ProtectDeviceBinaryS...
    method _async_get_event (line 268) | def _async_get_event(self) -> Event | None:

FILE: custom_components/unifiprotect/button.py
  function async_setup_entry (line 19) | async def async_setup_entry(
  class ProtectButton (line 38) | class ProtectButton(ProtectDeviceEntity, ButtonEntity):
    method __init__ (line 44) | def __init__(
    method async_press (line 53) | async def async_press(self) -> None:

FILE: custom_components/unifiprotect/camera.py
  function get_camera_channels (line 29) | def get_camera_channels(
  function async_setup_entry (line 53) | async def async_setup_entry(
  class ProtectCamera (line 91) | class ProtectCamera(ProtectDeviceEntity, Camera):
    method __init__ (line 96) | def __init__(
    method _async_set_stream_source (line 122) | def _async_set_stream_source(self) -> None:
    method _async_update_device_from_protect (line 140) | def _async_update_device_from_protect(self) -> None:
    method async_camera_image (line 160) | async def async_camera_image(
    method stream_source (line 171) | async def stream_source(self) -> str | None:

FILE: custom_components/unifiprotect/config_flow.py
  class ProtectFlowHandler (line 38) | class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
    method __init__ (line 43) | def __init__(self) -> None:
    method async_get_options_flow (line 51) | def async_get_options_flow(
    method _async_create_entry (line 58) | def _async_create_entry(self, title: str, data: dict[str, Any]) -> Flo...
    method _async_get_nvr_data (line 69) | async def _async_get_nvr_data(
    method async_step_reauth (line 111) | async def async_step_reauth(self, user_input: dict[str, Any]) -> FlowR...
    method async_step_reauth_confirm (line 117) | async def async_step_reauth_confirm(
    method async_step_user (line 149) | async def async_step_user(
  class OptionsFlowHandler (line 187) | class OptionsFlowHandler(config_entries.OptionsFlow):
    method __init__ (line 190) | def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
    method async_step_init (line 194) | async def async_step_init(

FILE: custom_components/unifiprotect/data.py
  class ProtectData (line 27) | class ProtectData:
    method __init__ (line 30) | def __init__(
    method disable_stream (line 52) | def disable_stream(self) -> bool:
    method get_by_types (line 56) | def get_by_types(
    method async_setup (line 68) | async def async_setup(self) -> None:
    method async_stop (line 75) | async def async_stop(self, *args: Any) -> None:
    method async_refresh (line 85) | async def async_refresh(self, *_: Any, force: bool = False) -> None:
    method _async_process_ws_message (line 110) | def _async_process_ws_message(self, message: WSSubscriptionMessage) ->...
    method _async_process_updates (line 139) | def _async_process_updates(self, updates: Bootstrap | None) -> None:
    method async_subscribe_device_id (line 154) | def async_subscribe_device_id(
    method async_unsubscribe_device_id (line 170) | def async_unsubscribe_device_id(
    method async_signal_device_id_update (line 182) | def async_signal_device_id_update(self, device_id: str) -> None:

FILE: custom_components/unifiprotect/entity.py
  function _async_device_entities (line 32) | def _async_device_entities(
  function async_all_device_entities (line 69) | def async_all_device_entities(
  class ProtectDeviceEntity (line 93) | class ProtectDeviceEntity(Entity):
    method __init__ (line 100) | def __init__(
    method async_update (line 124) | async def async_update(self) -> None:
    method _async_set_device_info (line 132) | def _async_set_device_info(self) -> None:
    method _async_update_device_from_protect (line 144) | def _async_update_device_from_protect(self) -> None:
    method _async_updated_event (line 166) | def _async_updated_event(self) -> None:
    method async_added_to_hass (line 171) | async def async_added_to_hass(self) -> None:
  class ProtectNVREntity (line 181) | class ProtectNVREntity(ProtectDeviceEntity):
    method __init__ (line 187) | def __init__(
    method _async_set_device_info (line 197) | def _async_set_device_info(self) -> None:
    method _async_update_device_from_protect (line 209) | def _async_update_device_from_protect(self) -> None:
  class EventThumbnailMixin (line 216) | class EventThumbnailMixin(ProtectDeviceEntity):
    method __init__ (line 219) | def __init__(self, *args: Any, **kwarg: Any) -> None:
    method _async_get_event (line 225) | def _async_get_event(self) -> Event | None:
    method _async_thumbnail_extra_attrs (line 233) | def _async_thumbnail_extra_attrs(self) -> dict[str, Any]:
    method _async_update_device_from_protect (line 246) | def _async_update_device_from_protect(self) -> None:

FILE: custom_components/unifiprotect/light.py
  function async_setup_entry (line 24) | async def async_setup_entry(
  function unifi_brightness_to_hass (line 45) | def unifi_brightness_to_hass(value: int) -> int:
  function hass_to_unifi_brightness (line 50) | def hass_to_unifi_brightness(value: int) -> int:
  class ProtectLight (line 55) | class ProtectLight(ProtectDeviceEntity, LightEntity):
    method _async_update_device_from_protect (line 64) | def _async_update_device_from_protect(self) -> None:
    method async_turn_on (line 71) | async def async_turn_on(self, **kwargs: Any) -> None:
    method async_turn_off (line 79) | async def async_turn_off(self, **kwargs: Any) -> None:

FILE: custom_components/unifiprotect/media_player.py
  function async_setup_entry (line 34) | async def async_setup_entry(
  class ProtectMediaPlayer (line 54) | class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
    method __init__ (line 60) | def __init__(
    method _async_update_device_from_protect (line 81) | def _async_update_device_from_protect(self) -> None:
    method async_set_volume_level (line 93) | async def async_set_volume_level(self, volume: float) -> None:
    method async_media_stop (line 99) | async def async_media_stop(self) -> None:
    method async_play_media (line 110) | async def async_play_media(

FILE: custom_components/unifiprotect/models.py
  class ProtectRequiredKeysMixin (line 18) | class ProtectRequiredKeysMixin:
    method get_ufp_value (line 26) | def get_ufp_value(self, obj: ProtectAdoptableDeviceModel | NVR) -> Any:
    method get_ufp_enabled (line 38) | def get_ufp_enabled(self, obj: ProtectAdoptableDeviceModel | NVR) -> b...
  class ProtectSetableKeysMixin (line 46) | class ProtectSetableKeysMixin(ProtectRequiredKeysMixin):
    method ufp_set (line 54) | async def ufp_set(self, obj: ProtectAdoptableDeviceModel, value: Any) ...

FILE: custom_components/unifiprotect/number.py
  class NumberKeysMixin (line 22) | class NumberKeysMixin:
  class ProtectNumberEntityDescription (line 31) | class ProtectNumberEntityDescription(
  function _get_pir_duration (line 37) | def _get_pir_duration(obj: Any) -> int:
  function _set_pir_duration (line 42) | async def _set_pir_duration(obj: Any, value: float) -> None:
  function async_setup_entry (line 129) | async def async_setup_entry(
  class ProtectNumbers (line 147) | class ProtectNumbers(ProtectDeviceEntity, NumberEntity):
    method __init__ (line 153) | def __init__(
    method _async_update_device_from_protect (line 166) | def _async_update_device_from_protect(self) -> None:
    method async_set_value (line 170) | async def async_set_value(self, value: float) -> None:

FILE: custom_components/unifiprotect/select.py
  class ProtectSelectEntityDescription (line 106) | class ProtectSelectEntityDescription(ProtectSetableKeysMixin, SelectEnti...
  function _get_viewer_options (line 117) | def _get_viewer_options(api: ProtectApiClient) -> list[dict[str, Any]]:
  function _get_doorbell_options (line 123) | def _get_doorbell_options(api: ProtectApiClient) -> list[dict[str, Any]]:
  function _get_paired_camera_options (line 134) | def _get_paired_camera_options(api: ProtectApiClient) -> list[dict[str, ...
  function _get_viewer_current (line 142) | def _get_viewer_current(obj: Any) -> str:
  function _get_light_motion_current (line 147) | def _get_light_motion_current(obj: Any) -> str:
  function _get_doorbell_current (line 158) | def _get_doorbell_current(obj: Any) -> str | None:
  function _set_light_mode (line 165) | async def _set_light_mode(obj: Any, mode: str) -> None:
  function _set_paired_camera (line 174) | async def _set_paired_camera(
  function _set_doorbell_message (line 185) | async def _set_doorbell_message(obj: Any, message: str) -> None:
  function _set_liveview (line 195) | async def _set_liveview(obj: Any, liveview_id: str) -> None:
  function async_setup_entry (line 303) | async def async_setup_entry(
  class ProtectSelects (line 328) | class ProtectSelects(ProtectDeviceEntity, SelectEntity):
    method __init__ (line 334) | def __init__(
    method _async_update_device_from_protect (line 346) | def _async_update_device_from_protect(self) -> None:
    method _async_set_options (line 360) | def _async_set_options(self) -> None:
    method current_option (line 374) | def current_option(self) -> str:
    method async_select_option (line 382) | async def async_select_option(self, option: str) -> None:
    method async_set_doorbell_message (line 396) | async def async_set_doorbell_message(self, message: str, duration: str...

FILE: custom_components/unifiprotect/sensor.py
  class ProtectSensorEntityDescription (line 50) | class ProtectSensorEntityDescription(ProtectRequiredKeysMixin, SensorEnt...
    method get_ufp_value (line 55) | def get_ufp_value(self, obj: ProtectAdoptableDeviceModel | NVR) -> Any:
  function _get_uptime (line 64) | def _get_uptime(obj: ProtectAdoptableDeviceModel | NVR) -> datetime | None:
  function _get_nvr_recording_capacity (line 73) | def _get_nvr_recording_capacity(obj: Any) -> int:
  function _get_nvr_memory (line 82) | def _get_nvr_memory(obj: Any) -> float | None:
  function _get_alarm_sound (line 91) | def _get_alarm_sound(obj: ProtectAdoptableDeviceModel | NVR) -> str:
  function async_setup_entry (line 387) | async def async_setup_entry(
  function _async_motion_entities (line 408) | def _async_motion_entities(
  function _async_nvr_entities (line 428) | def _async_nvr_entities(
  class ProtectDeviceSensor (line 440) | class ProtectDeviceSensor(ProtectDeviceEntity, SensorEntity):
    method __init__ (line 445) | def __init__(
    method _async_update_device_from_protect (line 455) | def _async_update_device_from_protect(self) -> None:
  class ProtectNVRSensor (line 460) | class ProtectNVRSensor(ProtectNVREntity, SensorEntity):
    method __init__ (line 465) | def __init__(
    method _async_update_device_from_protect (line 475) | def _async_update_device_from_protect(self) -> None:
  class ProtectEventSensor (line 480) | class ProtectEventSensor(ProtectDeviceSensor, EventThumbnailMixin):
    method _async_get_event (line 486) | def _async_get_event(self) -> Event | None:
    method _async_update_device_from_protect (line 500) | def _async_update_device_from_protect(self) -> None:

FILE: custom_components/unifiprotect/services.py
  function _async_all_ufp_instances (line 43) | def _async_all_ufp_instances(hass: HomeAssistant) -> list[ProtectApiClie...
  function _async_unifi_mac_from_hass (line 51) | def _async_unifi_mac_from_hass(mac: str) -> str:
  function _async_get_macs_for_device (line 57) | def _async_get_macs_for_device(device_entry: dr.DeviceEntry) -> list[str]:
  function _async_get_ufp_instances (line 66) | def _async_get_ufp_instances(
  function _async_get_protect_from_call (line 91) | def _async_get_protect_from_call(
  function _async_call_nvr (line 103) | async def _async_call_nvr(
  function add_doorbell_text (line 117) | async def add_doorbell_text(hass: HomeAssistant, call: ServiceCall) -> N...
  function remove_doorbell_text (line 124) | async def remove_doorbell_text(hass: HomeAssistant, call: ServiceCall) -...
  function set_default_doorbell_text (line 131) | async def set_default_doorbell_text(hass: HomeAssistant, call: ServiceCa...
  function async_setup_services (line 138) | def async_setup_services(hass: HomeAssistant) -> None:
  function async_cleanup_services (line 163) | def async_cleanup_services(hass: HomeAssistant) -> None:

FILE: custom_components/unifiprotect/switch.py
  class ProtectSwitchEntityDescription (line 25) | class ProtectSwitchEntityDescription(ProtectSetableKeysMixin, SwitchEnti...
  function _get_is_highfps (line 32) | def _get_is_highfps(obj: Any) -> bool:
  function _set_highfps (line 37) | async def _set_highfps(obj: Any, value: bool) -> None:
  function async_setup_entry (line 217) | async def async_setup_entry(
  class ProtectSwitch (line 235) | class ProtectSwitch(ProtectDeviceEntity, SwitchEntity):
    method __init__ (line 240) | def __init__(
    method is_on (line 263) | def is_on(self) -> bool:
    method async_turn_on (line 267) | async def async_turn_on(self, **kwargs: Any) -> None:
    method async_turn_off (line 277) | async def async_turn_off(self, **kwargs: Any) -> None:

FILE: custom_components/unifiprotect/utils.py
  function get_nested_attr (line 8) | def get_nested_attr(obj: Any, attr: str) -> Any:
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (332K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 578,
    "preview": "FROM ghcr.io/ludeeus/devcontainer/integration:stable\n\nRUN apt update \\\n    && sudo apt install -y libpcap-dev ffmpeg vim"
  },
  {
    "path": ".devcontainer/automations.yaml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".devcontainer/configuration.yaml",
    "chars": 316,
    "preview": "default_config:\n\ntts:\n  - platform: google_translate\n\nstream:\n  ll_hls: true\n  part_duration: 0.75\n  segment_duration: 2"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 1517,
    "preview": "// See https://aka.ms/vscode-remote/devcontainer.json for format details.\n{\n\t\"name\": \"HA unifiprotect\",\n\t\"dockerFile\": \""
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "chars": 1442,
    "preview": "name: Bug Report\ndescription: File a bug report for the Home Assistant UniFi Protect Integration\nlabels: [\"bug\"]\nbody:\n "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.yml",
    "chars": 719,
    "preview": "name: Feature Reuest\ndescription: Ask for a new feature for the Home Assistant UniFi Protect Integration\nlabels: [\"featu"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 1449,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    types: [opened, labeled, unlabeled, synchronize, "
  },
  {
    "path": ".gitignore",
    "chars": 2139,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 748,
    "preview": "{\n    // Use IntelliSense to learn about possible attributes.\n    // Hover to view descriptions of existing attributes.\n"
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 653,
    "preview": "{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": [\n\t\t{\n\t\t\t\"label\": \"Start Home Assistant\",\n\t\t\t\"type\": \"shell\",\n\t\t\t\"command\": \"container "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 95120,
    "preview": "# // Changelog\n\n## 0.12.0\n\n0.12.0 was originally planned as a beta only release, but after giving it more thought, I fig"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2019 Bjarne Riis\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 20008,
    "preview": "# // UniFi Protect for Home Assistant\n\n\n## THIS REPOSITORY IS NOW ARCHIEVED AND READONLY.\nThe `unifiprotect` integration"
  },
  {
    "path": "blueprints/automation/unifiprotect/dynamic_doorbell.yaml",
    "chars": 4462,
    "preview": "blueprint:\n  name: UniFi Protect Dynamic Doorbell\n  description: |\n    ## UniFi Protect Dynamic Doorbell\n\n    This bluep"
  },
  {
    "path": "blueprints/automation/unifiprotect/push_notification_doorbell_event.yaml",
    "chars": 23069,
    "preview": "blueprint:\n  name: UniFi Protect Doorbell Notifications\n  description: |\n    ## UniFi Protect Doorbell Notifications\n\n  "
  },
  {
    "path": "blueprints/automation/unifiprotect/push_notification_motion_event.yaml",
    "chars": 15057,
    "preview": "blueprint:\n  name: UniFi Protect Motion Notifications\n  description: |\n    ## UniFi Protect Motion Notifications\n\n    Th"
  },
  {
    "path": "blueprints/automation/unifiprotect/push_notification_smart_event.yaml",
    "chars": 16015,
    "preview": "blueprint:\n  name: UniFi Protect Smart Detection Notifications\n  description: |\n    ## UniFi Protect Smart Detection Not"
  },
  {
    "path": "custom_components/unifiprotect/__init__.py",
    "chars": 8164,
    "preview": "\"\"\"UniFi Protect Platform.\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nfrom datetime import timedelta\nimport l"
  },
  {
    "path": "custom_components/unifiprotect/binary_sensor.py",
    "chars": 8820,
    "preview": "\"\"\"This component provides binary sensors for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nfrom copy import cop"
  },
  {
    "path": "custom_components/unifiprotect/button.py",
    "chars": 1679,
    "preview": "\"\"\"Support for Ubiquiti's UniFi Protect NVR.\"\"\"\nfrom __future__ import annotations\n\nimport logging\n\nfrom homeassistant.c"
  },
  {
    "path": "custom_components/unifiprotect/camera.py",
    "chars": 5703,
    "preview": "\"\"\"Support for Ubiquiti's UniFi Protect NVR.\"\"\"\nfrom __future__ import annotations\n\nfrom collections.abc import Generato"
  },
  {
    "path": "custom_components/unifiprotect/config_flow.py",
    "chars": 7208,
    "preview": "\"\"\"Config Flow to configure UniFi Protect Integration.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nfrom typing"
  },
  {
    "path": "custom_components/unifiprotect/const.py",
    "chars": 1484,
    "preview": "\"\"\"Constant definitions for UniFi Protect Integration.\"\"\"\n\nfrom homeassistant.const import Platform\nfrom pyunifiprotect."
  },
  {
    "path": "custom_components/unifiprotect/data.py",
    "chars": 7138,
    "preview": "\"\"\"Base class for protect data.\"\"\"\nfrom __future__ import annotations\n\nfrom collections.abc import Generator, Iterable\nf"
  },
  {
    "path": "custom_components/unifiprotect/entity.py",
    "chars": 8344,
    "preview": "\"\"\"Shared Entity definition for UniFi Protect Integration.\"\"\"\nfrom __future__ import annotations\n\nfrom collections.abc i"
  },
  {
    "path": "custom_components/unifiprotect/light.py",
    "chars": 2443,
    "preview": "\"\"\"This component provides Lights for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nfrom typing i"
  },
  {
    "path": "custom_components/unifiprotect/manifest.json",
    "chars": 430,
    "preview": "{\n  \"domain\": \"unifiprotect\",\n  \"name\": \"UniFi Protect\",\n  \"documentation\": \"https://github.com/briis/unifiprotect\",\n  \""
  },
  {
    "path": "custom_components/unifiprotect/media_player.py",
    "chars": 4079,
    "preview": "\"\"\"Support for Ubiquiti's UniFi Protect NVR.\"\"\"\nfrom __future__ import annotations\n\nimport logging\nfrom typing import An"
  },
  {
    "path": "custom_components/unifiprotect/models.py",
    "chars": 2147,
    "preview": "\"\"\"The unifiprotect integration models.\"\"\"\nfrom __future__ import annotations\n\nfrom collections.abc import Callable, Cor"
  },
  {
    "path": "custom_components/unifiprotect/number.py",
    "chars": 5252,
    "preview": "\"\"\"This component provides number entities for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses im"
  },
  {
    "path": "custom_components/unifiprotect/select.py",
    "chars": 14391,
    "preview": "\"\"\"This component provides select entities for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nfrom collections.ab"
  },
  {
    "path": "custom_components/unifiprotect/sensor.py",
    "chars": 17274,
    "preview": "\"\"\"This component provides sensors for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import dat"
  },
  {
    "path": "custom_components/unifiprotect/services.py",
    "chars": 5707,
    "preview": "\"\"\"UniFi Protect Integration services.\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport functools\nfrom typin"
  },
  {
    "path": "custom_components/unifiprotect/services.yaml",
    "chars": 3117,
    "preview": "add_doorbell_text:\n  name: Add Custom Doorbell Text\n  description: Adds a new custom message for Doorbells.\n  fields:\n  "
  },
  {
    "path": "custom_components/unifiprotect/strings.json",
    "chars": 2176,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"UniFi Protect Server already configured.\",\n          "
  },
  {
    "path": "custom_components/unifiprotect/switch.py",
    "chars": 9756,
    "preview": "\"\"\"This component provides Switches for UniFi Protect.\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import da"
  },
  {
    "path": "custom_components/unifiprotect/translations/da.json",
    "chars": 1286,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"Denne UniFi Protect Server er allerede konfigureret.\""
  },
  {
    "path": "custom_components/unifiprotect/translations/de.json",
    "chars": 1270,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"UniFi Protect Server bereits konfiguriert.\"\n        }"
  },
  {
    "path": "custom_components/unifiprotect/translations/en.json",
    "chars": 1895,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"reauth_successful\": \"Re-authentication was successful\",\n            \"s"
  },
  {
    "path": "custom_components/unifiprotect/translations/fr.json",
    "chars": 1330,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"Le server UniFi Protect est déjà configuré.\"\n        "
  },
  {
    "path": "custom_components/unifiprotect/translations/nb.json",
    "chars": 1269,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"UniFi Protect Server er allerede konfigurert.\"\n      "
  },
  {
    "path": "custom_components/unifiprotect/translations/nl.json",
    "chars": 1304,
    "preview": "{\n    \"config\": {\n        \"abort\": {\n            \"server_exists\": \"UniFi Protect Server is al geconfigureerd.\"\n        }"
  },
  {
    "path": "custom_components/unifiprotect/utils.py",
    "chars": 449,
    "preview": "\"\"\"UniFi Protect Integration utils.\"\"\"\nfrom __future__ import annotations\n\nfrom enum import Enum\nfrom typing import Any\n"
  },
  {
    "path": "hacs.json",
    "chars": 280,
    "preview": "{\n    \"name\": \"UniFi Protect Integration\",\n    \"domains\": [\n        \"binary_sensor\",\n        \"sensor\",\n        \"camera\","
  },
  {
    "path": "info.md",
    "chars": 1431,
    "preview": "# // UniFi Protect for Home Assistant\n\n![GitHub release (latest by date including pre-releases)](https://img.shields.io/"
  },
  {
    "path": "pylintrc",
    "chars": 1286,
    "preview": "[MASTER]\nreports=no\n\n# Reasons disabled:\n# format - handled by black\n# locally-disabled - it spams too much\n# duplicate-"
  },
  {
    "path": "pyproject.toml",
    "chars": 2723,
    "preview": "[tool.black]\ntarget-version = [\"py38\"]\nexclude = 'generated'\n\n[tool.isort]\n# https://github.com/PyCQA/isort/wiki/isort-S"
  },
  {
    "path": "unifiprotect.markdown",
    "chars": 3470,
    "preview": "---\ntitle: Ubiquiti UniFi Protect\ndescription: Instructions on how to configure UniFi Protect integration by Ubiquiti.\nh"
  }
]

About this extraction

This page contains the full source code of the briis/unifiprotect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (310.2 KB), approximately 73.3k tokens, and a symbol index with 156 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!