pax_global_header00006660000000000000000000000064146731022020014507gustar00rootroot0000000000000052 comment=6e2b47bf346481f4dd7eb5761c2d06bc68bebb26 uiprotect-6.1.0/000077500000000000000000000000001467310220200135315ustar00rootroot00000000000000uiprotect-6.1.0/.all-contributorsrc000066400000000000000000000004451467310220200173650ustar00rootroot00000000000000{ "projectName": "uiprotect", "projectOwner": "uilibs", "repoType": "github", "repoHost": "https://github.com", "files": [ "README.md" ], "imageSize": 80, "commit": true, "commitConvention": "angular", "contributors": [], "contributorsPerLine": 7, "skipCi": true } uiprotect-6.1.0/.bin/000077500000000000000000000000001467310220200143575ustar00rootroot00000000000000uiprotect-6.1.0/.bin/lib/000077500000000000000000000000001467310220200151255ustar00rootroot00000000000000uiprotect-6.1.0/.bin/lib/common.sh000066400000000000000000000012101467310220200167430ustar00rootroot00000000000000#!/bin/bash function setRoot() { ROOT_PATH=$PWD while [[ $ROOT_PATH != / ]]; do output=$(find "$ROOT_PATH" -maxdepth 1 -mindepth 1 -name "pyproject.toml") if [[ -n $output ]]; then break fi # Note: if you want to ignore symlinks, use "$(realpath -s "$path"/..)" ROOT_PATH="$(readlink -f "$ROOT_PATH"/..)" done if [[ $ROOT_PATH == / ]]; then ROOT_PATH=$( realpath $( dirname "${BASH_SOURCE[0]}" )/../../ ) echo "Could not find \`pyproject.toml\`, following back to $( basename $ROOT_PATH )" else echo "Using project $( basename $ROOT_PATH )" fi } uiprotect-6.1.0/.bin/run-mypy000077500000000000000000000000511467310220200161010ustar00rootroot00000000000000#!/bin/sh poetry run mypy src/uiprotect uiprotect-6.1.0/.bin/test-code000077500000000000000000000011771467310220200162020ustar00rootroot00000000000000#!/bin/bash set -o errexit set -o pipefail set -o nounset BASE_DIR=$( realpath $( dirname "${BASH_SOURCE[0]}" )/../ ) source "$BASE_DIR/.bin/lib/common.sh" setRoot WS_TIMEOUT="${WS_TIMEOUT:-40}" PYTEST_EXTRA_ARGS="${PYTEST_EXTRA_ARGS:-}" pushd "$ROOT_PATH" 2>&1 >/dev/null rm -rf .coverage.* .coverage echo -e "\nRunning tests (no benchmarks)..." poetry run pytest --timeout=10 --color=yes --cov-report=xml --benchmark-skip --maxfail=10 $PYTEST_EXTRA_ARGS echo -e "\nRunning benchmark tests..." poetry run pytest --timeout=$WS_TIMEOUT --cov-report=term --color=yes --benchmark-only -n=0 -rP $PYTEST_EXTRA_ARGS popd 2>&1 >/dev/null uiprotect-6.1.0/.bin/update-release-cache000077500000000000000000000001611467310220200202440ustar00rootroot00000000000000#!/bin/bash set -o errexit set -o pipefail set -o nounset poetry install poetry run uiprotect release-versions uiprotect-6.1.0/.codespellrc000066400000000000000000000000571467310220200160330ustar00rootroot00000000000000[codespell] ignore-words-list = socio-economic uiprotect-6.1.0/.copier-answers.yml000066400000000000000000000010441467310220200172720ustar00rootroot00000000000000# Changes here will be overwritten by Copier _commit: 368c483 _src_path: gh:browniebroke/pypackage-template add_me_as_contributor: false cli_name: uiprotect copyright_year: '2024' documentation: true email: ui@koston.org full_name: UI Protect Maintainers github_username: uilibs has_cli: true initial_commit: true open_source_license: MIT package_name: uiprotect project_name: uiprotect project_short_description: Python API for Unifi Protect (Unofficial) project_slug: uiprotect run_poetry_install: true setup_github: true setup_pre_commit: true uiprotect-6.1.0/.coveragerc000066400000000000000000000014171467310220200156550ustar00rootroot00000000000000[run] source = src/uiprotect omit = site/* src/uiprotect/cli/* src/uiprotect/test_util/* [report] omit = site/* src/uiprotect/cli/* src/uiprotect/test_util/* # Regexes for lines to exclude from consideration exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about missing debug-only code: def __repr__ # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError raise exceptions.NotSupportedError # TYPE_CHECKING and @overload blocks are never executed during pytest run # except ImportError: are never executed as well if TYPE_CHECKING: @overload except ImportError: if _LOGGER.isEnabledFor(logging.DEBUG): uiprotect-6.1.0/.devcontainer/000077500000000000000000000000001467310220200162705ustar00rootroot00000000000000uiprotect-6.1.0/.devcontainer/devcontainer.json000066400000000000000000000116751467310220200216560ustar00rootroot00000000000000{ "name": "uiprotect", "build": { "dockerfile": "../Dockerfile", "context": "..", "target": "dev" }, "updateRemoteUserUID": true, "containerUser": "app", "mounts": [ "source=${localEnv:HOME}${localEnv:USERPROFILE}/.cache,target=/home/app/.cache,type=bind,consistency=cached" ], "initializeCommand": { "mkdir-posix": "mkdir -p $HOME/.cache $HOME/.cache $HOME/.cache || true" }, "onCreateCommand": [ "/bin/bash", "-c", "sudo -E HOME=/root uv pip install -e '.[dev]' && /usr/local/bin/uiprotect --install-completion bash && docker-fix" ], "updateContentCommand": [ "/bin/bash", "-c", "sudo -E HOME=/root uv pip sync requirements.txt dev-requirements.txt" ], "postAttachCommand": [ "/bin/bash", "-c", "test -f /.codespaces && echo -e '\\e[1;31mYou will not be able to connect to a UniFi Protect instance inside of Codespaces.' || true" ], "features": { "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {} }, "hostRequirements": { "cpus": 2, "memory": "8gb", "storage": "32gb" }, "customizations": { "vscode": { "extensions": [ "eamodio.gitlens", "github.codespaces", "github.vscode-github-actions", "gitHub.vscode-pull-request-github", "jasonnutter.vscode-codeowners", "mikestead.dotenv", "ms-azuretools.vscode-docker", "ms-vsliveshare.vsliveshare", "streetsidesoftware.code-spell-checker", "tamasfe.even-better-toml", "ryanluker.vscode-coverage-gutters", "charliermarsh.ruff", "ms-python.black-formatter", "ms-python.isort", "ms-python.mypy-type-checker", "ms-python.python", "ms-python.debugpy", "ms-python.vscode-pylance", "njpwerner.autodocstring", "samuelcolvin.jinjahtml" ], "settings": { "editor.formatOnSave": true, "editor.formatOnSaveTimeout": 3000, "editor.insertSpaces": true, "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, "[toml]": { "editor.formatOnSave": false }, "[python]": { "editor.defaultFormatter": "ms-python.black-formatter", "editor.codeActionsOnSave": { "source.organizeImports": true, "source.fixAll": true } }, "python.envFile": "${workspaceFolder}/.env", "python.defaultInterpreterPath": "/usr/local/bin/python", "python.pythonPath": "/usr/local/bin/python", "python.formatting.provider": "none", "python.linting.enabled": true, "black.args": [ "--config=/workspaces/uiprotect/pyproject.toml" ], "isort.args": [ "--settings-path=/workspaces/uiprotect/pyproject.toml" ], "mypy-type-checker.interpreter": [ "/usr/local/bin/python" ], "mypy-type-checker.args": [ "--config-file=/workspaces/uiprotect/pyproject.toml" ], "mypy-type-checker.severity": { "error": "Error", "note": "Warning" }, "ruff.lint.args": [ "--preview", "--config=/workspaces/uiprotect/pyproject.toml" ], "ruff.fixAll": true, "ruff.organizeImports": false, "coverage-gutters.xmlname": "coverage.xml", "coverage-gutters.customizable.status-bar-toggler-watchCoverageAndVisibleEditors-enabled": true, "coverage-gutters.showLineCoverage": true, "python.testing.promptToConfigure": false, "python.testing.pytestEnabled": true, "python.testing.unittestEnabled": false, "python.testing.nosetestsEnabled": false, "python.testing.pytestArgs": [ "--cov-report=xml", "--color=no" ], "cSpell.allowCompoundWords": true, "cSpell.dictionaries": [ "en_US", "en-gb", "companies", "softwareTerms", "misc", "python", "html", "bash", "fonts", "filetypes" ] } } } } uiprotect-6.1.0/.docker/000077500000000000000000000000001467310220200150565ustar00rootroot00000000000000uiprotect-6.1.0/.docker/bashrc000066400000000000000000000013301467310220200162400ustar00rootroot00000000000000BOLD="\[$(tput bold)\]" BLACK="\[$(tput setaf 0)\]" RED="\[$(tput setaf 1)\]" GREEN="\[$(tput setaf 2)\]" YELLOW="\[$(tput setaf 3)\]" BLUE="\[$(tput setaf 4)\]" MAGENTA="\[$(tput setaf 5)\]" CYAN="\[$(tput setaf 6)\]" WHITE="\[$(tput setaf 7)\]" RESET="\[$(tput sgr0)\]" function prompt_command { RET=$? if [[ "$(id -u)" -eq 0 ]]; then PS1="$BOLD$RED" else PS1="$GREEN" fi branch="$(git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/')" PS1+="\u$RESET:$YELLOW\w$RESET$CYAN$branch$RESET " if [[ "$RET" -eq 0 ]]; then PS1+="$BOLD$GREEN" else PS1+="$RET $BOLD$RED" fi PS1+="\\$ $RESET" export PS1 } export PROMPT_COMMAND=prompt_command uiprotect-6.1.0/.docker/docker-fix.sh000066400000000000000000000014541467310220200174510ustar00rootroot00000000000000#!/bin/bash set -o errexit set -o pipefail set -o nounset DOCKER_SOCK="" if [[ -e /var/run/docker-host.sock ]]; then DOCKER_SOCK="/var/run/docker-host.sock" else if [[ -e /var/run/docker.sock ]]; then DOCKER_SOCK="/var/run/docker.sock" fi fi # fix the group ID of the docker group so it can write to /var/run/docker.sock if [[ -n "$DOCKER_SOCK" ]]; then DOCKER_GID=$(ls -la $DOCKER_SOCK | awk '{print $4}') if [[ $DOCKER_GID != 'docker' ]]; then sudo groupmod -g $DOCKER_GID docker if [[ -f '/.codespaces' ]]; then echo -e '\e[1;31mYou must stop and restart the Codespace to be able to access docker properly' else echo -e '\e[1;31mYou must run the `Reload Window` command for be able to access docker properly' fi fi fi uiprotect-6.1.0/.docker/entrypoint.sh000066400000000000000000000001321467310220200176210ustar00rootroot00000000000000#!/bin/bash set -o errexit set -o pipefail set -o nounset /usr/local/bin/uiprotect "$@" uiprotect-6.1.0/.dockerignore000066400000000000000000000001651467310220200162070ustar00rootroot00000000000000.vscode test-data ufp-data *.mp3 *.mp4 .* *.xml Dockerfile LICENSE *.md !README.md tests/** *.egg-info *.js !.docker uiprotect-6.1.0/.editorconfig000066400000000000000000000004441467310220200162100ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.bat] indent_style = tab end_of_line = crlf [LICENSE] insert_final_newline = false [Makefile] indent_style = tab uiprotect-6.1.0/.github/000077500000000000000000000000001467310220200150715ustar00rootroot00000000000000uiprotect-6.1.0/.github/CODE_OF_CONDUCT.md000066400000000000000000000120561467310220200176740ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting @uilibs. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. uiprotect-6.1.0/.github/FUNDING.yml000066400000000000000000000000231467310220200167010ustar00rootroot00000000000000github: ["uilibs"] uiprotect-6.1.0/.github/ISSUE_TEMPLATE/000077500000000000000000000000001467310220200172545ustar00rootroot00000000000000uiprotect-6.1.0/.github/ISSUE_TEMPLATE/1-bug_report.yml000066400000000000000000000035311467310220200223070ustar00rootroot00000000000000name: Bug report description: Create a report to help us improve labels: [bug] body: - type: textarea id: description attributes: label: Describe the bug description: A clear and concise description of what the bug is. placeholder: Describe the bug validations: required: true - type: textarea id: reproduce attributes: label: To Reproduce description: Steps to reproduce the behavior. placeholder: To Reproduce validations: required: true - type: textarea id: context attributes: label: Additional context description: Add any other context about the problem here. placeholder: Additional context - type: input id: version attributes: label: Version description: Version of the project. placeholder: Version validations: required: true - type: input id: platform attributes: label: Platform description: Platform where the bug was found. placeholder: "Example: Windows 11 / macOS 12.0.1 / Ubuntu 20.04" validations: required: true - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/uilibs/uiprotect/blob/main/.github/CODE_OF_CONDUCT.md). options: - label: I agree to follow this project's Code of Conduct. required: true - type: checkboxes id: no-duplicate attributes: label: No Duplicate description: Please check [existing issues](https://github.com/uilibs/uiprotect/issues) to avoid duplicates. options: - label: I have checked existing issues to avoid duplicates. required: true - type: markdown attributes: value: 👋 Have a great day and thank you for the bug report! uiprotect-6.1.0/.github/ISSUE_TEMPLATE/2-feature-request.yml000066400000000000000000000036101467310220200232570ustar00rootroot00000000000000name: Feature request description: Suggest an idea for this project labels: [enhancement] body: - type: textarea id: description attributes: label: Is your feature request related to a problem? Please describe. description: A clear and concise description of what the problem is. value: I'm always frustrated when validations: required: true - type: textarea id: solution attributes: label: Describe alternatives you've considered description: A clear and concise description of any alternative solutions or features you've considered. placeholder: Describe alternatives you've considered validations: required: true - type: textarea id: context attributes: label: Additional context description: Add any other context or screenshots about the feature request here. placeholder: Additional context - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/uilibs/uiprotect/blob/main/.github/CODE_OF_CONDUCT.md). options: - label: I agree to follow this project's Code of Conduct required: true - type: checkboxes id: willing attributes: label: Are you willing to resolve this issue by submitting a Pull Request? description: Remember that first-time contributors are welcome! 🙌 options: - label: Yes, I have the time, and I know how to start. - label: Yes, I have the time, but I don't know how to start. I would need guidance. - label: No, I don't have the time, although I believe I could do it if I had the time... - label: No, I don't have the time and I wouldn't even know how to start. - type: markdown attributes: value: 👋 Have a great day and thank you for the feature request! uiprotect-6.1.0/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000002701467310220200212430ustar00rootroot00000000000000blank_issues_enabled: false contact_links: - name: Questions url: https://github.com/uilibs/uiprotect/discussions/categories/q-a about: Please ask and answer questions here. uiprotect-6.1.0/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000034601467310220200206750ustar00rootroot00000000000000 ### Description of change ### Pull-Request Checklist - [ ] Code is up-to-date with the `main` branch - [ ] This pull request follows the [contributing guidelines](https://github.com/uilibs/uiprotect/blob/main/CONTRIBUTING.md). - [ ] This pull request links relevant issues as `Fixes #0000` - [ ] There are new or updated unit tests validating the change - [ ] Documentation has been updated to reflect this change - [ ] The new commits follow conventions outlined in the [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/), such as "fix(api): prevent racing of requests". > - If pre-commit.ci is failing, try `pre-commit run -a` for further information. > - If CI / test is failing, try `poetry run pytest` for further information. uiprotect-6.1.0/.github/dependabot.yml000066400000000000000000000010151467310220200177160ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" uiprotect-6.1.0/.github/labels.toml000066400000000000000000000035151467310220200172340ustar00rootroot00000000000000[breaking] color = "ffcc00" name = "breaking" description = "Breaking change." [bug] color = "d73a4a" name = "bug" description = "Something isn't working" [dependencies] color = "0366d6" name = "dependencies" description = "Pull requests that update a dependency file" [github_actions] color = "000000" name = "github_actions" description = "Update of github actions" [documentation] color = "1bc4a5" name = "documentation" description = "Improvements or additions to documentation" [duplicate] color = "cfd3d7" name = "duplicate" description = "This issue or pull request already exists" [enhancement] color = "a2eeef" name = "enhancement" description = "New feature or request" ["good first issue"] color = "7057ff" name = "good first issue" description = "Good for newcomers" ["help wanted"] color = "008672" name = "help wanted" description = "Extra attention is needed" [invalid] color = "e4e669" name = "invalid" description = "This doesn't seem right" [nochangelog] color = "555555" name = "nochangelog" description = "Exclude pull requests from changelog" [question] color = "d876e3" name = "question" description = "Further information is requested" [removed] color = "e99695" name = "removed" description = "Removed piece of functionalities." [tests] color = "bfd4f2" name = "tests" description = "CI, CD and testing related changes" [wontfix] color = "ffffff" name = "wontfix" description = "This will not be worked on" [discussion] color = "c2e0c6" name = "discussion" description = "Some discussion around the project" [hacktoberfest] color = "ffa663" name = "hacktoberfest" description = "Good issues for Hacktoberfest" [answered] color = "0ee2b6" name = "answered" description = "Automatically closes as answered after a delay" [waiting] color = "5f7972" name = "waiting" description = "Automatically closes if no answer after a delay" uiprotect-6.1.0/.github/workflows/000077500000000000000000000000001467310220200171265ustar00rootroot00000000000000uiprotect-6.1.0/.github/workflows/ci.yml000066400000000000000000000063671467310220200202600ustar00rootroot00000000000000name: CI on: push: branches: - main pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install poetry run: pipx install poetry - uses: actions/setup-python@v5 with: python-version: 3.x cache: "poetry" - name: Install Dependencies run: | poetry install - uses: pre-commit/action@v3.0.1 # Make sure commit messages follow the conventional commits convention: # https://www.conventionalcommits.org commitlint: name: Lint Commit Messages runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v6.0.1 test: strategy: fail-fast: false matrix: python-version: - "3.10" - "3.11" - "3.12" - "3.13" os: - ubuntu-latest pydantic: - "1" - "2" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Install poetry run: pipx install poetry - name: Set up Python uses: actions/setup-python@v5 id: setup-python with: python-version: ${{ matrix.python-version }} cache: "poetry" allow-prereleases: true - run: echo "Cache hit:${{ steps.setup-python.outputs.cache-hit }}" # true if cache-hit occurred on the primary key - name: Install Dependencies run: | sudo apt update sudo apt install -y ffmpeg poetry install - name: Downgrade to Pydantic 1.x if: matrix.pydantic == '1' run: | poetry add 'pydantic>=1.0,<2.0,!=1.10.16' - name: Test with Pytest run: ./.bin/test-code shell: bash - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} release: needs: - test - lint - commitlint runs-on: ubuntu-latest environment: release concurrency: release permissions: id-token: write contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.head_ref || github.ref_name }} # Do a dry run of PSR - name: Test release uses: python-semantic-release/python-semantic-release@v9.8.8 if: github.ref_name != 'main' with: root_options: --noop # On main branch: actual PSR + upload to PyPI & GitHub - name: Release uses: python-semantic-release/python-semantic-release@v9.8.8 id: release if: github.ref_name == 'main' with: github_token: ${{ secrets.GITHUB_TOKEN }} - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 if: steps.release.outputs.released == 'true' - name: Publish package distributions to GitHub Releases uses: python-semantic-release/upload-to-gh-release@main if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} uiprotect-6.1.0/.github/workflows/docker.yml000066400000000000000000000053611467310220200211250ustar00rootroot00000000000000name: CD - Build Docker Image on: release: types: [published] workflow_dispatch: inputs: rebuild: description: "Rebuild tag?" required: true default: "no" type: choice options: - "no" - "yes" concurrency: group: docker-${{ github.event.workflow_run.head_branch || github.ref }} cancel-in-progress: true permissions: packages: write env: DEFAULT_PYTHON: "3.12" jobs: docker: name: Build Docker Image runs-on: ubuntu-latest environment: name: release steps: - name: Check repo uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install poetry run: pipx install poetry - name: Set up Python uses: actions/setup-python@v5 with: python-version: "${{ env.DEFAULT_PYTHON }}" cache: "poetry" - name: Install dependencies run: | poetry install - name: Get current version (rebuild) if: ${{ inputs.rebuild == 'yes' }} run: | UIPROTECT_VERSION=$(git describe --tags --abbrev=0) echo "UIPROTECT_VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "DOCKER_TAGS=ghcr.io/uilibs/uiprotect:dev,ghcr.io/uilibs/uiprotect:$UIPROTECT_VERSION" >> $GITHUB_ENV - name: Get current version (no rebuild) if: ${{ inputs.rebuild != 'yes' }} run: | UIPROTECT_VERSION=v$(python -c 'from importlib.metadata import version; print(version("uiprotect"))') echo "UIPROTECT_VERSION=$UIPROTECT_VERSION" >> $GITHUB_ENV echo "DOCKER_TAGS=ghcr.io/uilibs/uiprotect:dev,ghcr.io/uilibs/uiprotect:$(echo $UIPROTECT_VERSION | tr "+" -)" >> $GITHUB_ENV - name: Add Latest Docker Tag run: | if [[ ! "$UIPROTECT_VERSION" == *"dev"* ]]; then echo "DOCKER_TAGS=ghcr.io/uilibs/uiprotect:latest,$DOCKER_TAGS" >> $GITHUB_ENV fi - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and Push uses: docker/build-push-action@v3 with: context: . platforms: linux/amd64,linux/arm64 target: prod push: true build-args: | UIPROTECT_VERSION=${{ env.UIPROTECT_VERSION }} cache-from: ghcr.io/uilibs/uiprotect:buildcache cache-to: type=registry,ref=ghcr.io/uilibs/uiprotect:buildcache,mode=max tags: ${{ env.DOCKER_TAGS }} uiprotect-6.1.0/.github/workflows/issue-manager.yml000066400000000000000000000013401467310220200224070ustar00rootroot00000000000000name: Issue Manager on: schedule: - cron: "0 0 * * *" issue_comment: types: - created issues: types: - labeled pull_request_target: types: - labeled workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - uses: tiangolo/issue-manager@0.5.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > { "answered": { "message": "Assuming the original issue was solved, it will be automatically closed now." }, "waiting": { "message": "Automatically closing. To re-open, please provide the additional information requested." } } uiprotect-6.1.0/.github/workflows/poetry-upgrade.yml000066400000000000000000000003401467310220200226150ustar00rootroot00000000000000name: Upgrader on: workflow_dispatch: schedule: - cron: "36 13 6 * *" jobs: upgrade: uses: browniebroke/github-actions/.github/workflows/poetry-upgrade.yml@v1 secrets: gh_pat: ${{ secrets.GH_PAT }} uiprotect-6.1.0/.gitignore000066400000000000000000000054711467310220200155300ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) ### Python template # settings.json is user-specific overrides for devcontainer.json .vscode/settings.json test-data ufp-data *.mp3 *.mp4 *.js *.json *.csv backup .benchmarks .ruff_cache # 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/ cover/ # 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 .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version .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 {{package_name}} settings .spyderproject .spyproject # Rope {{package_name}} settings # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ .DS_Store test.py camerasnapshot.py dumpeventdata.py eventdata.json dumpeventdata.py heatmap_snapshot.py smartdetect.json dumpcameradata.py cameras.json setstatuslight.py # websocket.py test_function.py test_event.py events.json event_obj.json src/uiprotect/unifi_protect_server_ws.py websocket_test.py test_ws.py camera_cloudkey.json camera_cloudkey_privacy.json cameras_upsense.json event.json test_websocket.py test_raw.py rawdata.json # IDE settings .vscode/ .idea/ uiprotect-6.1.0/.gitpod.yml000066400000000000000000000003061467310220200156170ustar00rootroot00000000000000tasks: - command: | pip install poetry PIP_USER=false poetry install - command: | pip install pre-commit pre-commit install PIP_USER=false pre-commit install-hooks uiprotect-6.1.0/.pre-commit-config.yaml000066400000000000000000000027131467310220200200150ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks exclude: "CHANGELOG.md|.copier-answers.yml|.all-contributorsrc" default_stages: [commit] ci: autofix_commit_msg: "chore(pre-commit.ci): auto fixes" autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate" skip: [mypy] repos: - repo: https://github.com/commitizen-tools/commitizen rev: v3.29.0 hooks: - id: commitizen stages: [commit-msg] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - id: debug-statements - id: check-builtin-literals - id: check-case-conflict - id: check-docstring-first - id: check-toml - id: check-xml - id: detect-private-key - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/python-poetry/poetry rev: 1.8.0 hooks: - id: poetry-check - repo: https://github.com/pre-commit/mirrors-prettier rev: v4.0.0-alpha.8 hooks: - id: prettier args: ["--tab-width", "2"] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.5 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - id: ruff-format - repo: local hooks: - id: mypy name: mypy language: script entry: ./.bin/run-mypy types_or: [python, pyi] require_serial: true files: ^(src/uiprotect)/.+\.(py|pyi)$ uiprotect-6.1.0/.readthedocs.yml000066400000000000000000000011111467310220200166110ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.12" jobs: post_create_environment: # Install poetry - python -m pip install poetry post_install: # Install dependencies, reusing RTD virtualenv - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs # Build documentation in the docs directory with mkdocs mkdocs: configuration: mkdocs.yml uiprotect-6.1.0/.vscode/000077500000000000000000000000001467310220200150725ustar00rootroot00000000000000uiprotect-6.1.0/.vscode/launch.json000066400000000000000000000021541467310220200172410ustar00rootroot00000000000000{ "version": "0.2.0", "configurations": [ { "name": "Run Subcommand: shell", "type": "debugpy", "request": "launch", "module": "uiprotect", "args": [ "-u", "shell", ] }, { "name": "Run Subcommand: generate-sample-data", "type": "debugpy", "request": "launch", "module": "uiprotect", "args": [ "generate-sample-data", "-w", "30", "--actual", ], "env": { "UFP_SAMPLE_DIR": "${workspaceFolder}/test-data" } }, { "name": "Python: Debug Tests", "type": "debugpy", "request": "launch", "program": "${file}", "purpose": [ "debug-test" ], "console": "integratedTerminal", "justMyCode": false, "env": { "PYTEST_ADDOPTS": "-n=0 --no-cov -vv" } } ] } uiprotect-6.1.0/.vscode/tasks.json000066400000000000000000000041071467310220200171140ustar00rootroot00000000000000{ "version": "2.0.0", "tasks": [ { "label": "Test Code", "type": "shell", "command": "${workspaceFolder}/.bin/test-code", "problemMatcher": [] }, { "label": "Update requirements", "type": "shell", "command": "${workspaceFolder}/.bin/update-requirements", "problemMatcher": [] }, { "label": "MkDocs: Serve", "type": "shell", "command": "mkdocs serve", "problemMatcher": [] }, { "label": "MkDocs: Build", "type": "shell", "command": "mkdocs build", "problemMatcher": [] }, { "label": "Generate Sample Data", "type": "shell", "command": "uiprotect generate-sample-data -w ${input:sampleTime} ${input:sampleAnonymize}", "problemMatcher": [], "options": { "env": { "UFP_SAMPLE_DIR": "${workspaceFolder}/${input:sampleLocation}" } } }, { "label": "Regenerate Release Cache", "type": "shell", "command": "uiprotect release-versions", "problemMatcher": [], }, ], "inputs": [ { "id": "sampleLocation", "description": "Location to generate sample data in", "default": "test-data", "type": "pickString", "options": [ "test-data", "tests/sample_data", ], }, { "id": "sampleTime", "description": "Length of time to generate sample data", "default": "30", "type": "promptString", }, { "id": "sampleAnonymize", "description": "Anonymize parameter for generate sample data", "default": "", "type": "pickString", "options": [ "", "--actual", ], }, ] } uiprotect-6.1.0/CHANGELOG.md000066400000000000000000000622601467310220200153500ustar00rootroot00000000000000# Changelog ## v6.1.0 (2024-09-19) ### Fix - Add additional types to device_events (#213) ([`072bc7c`](https://github.com/uilibs/uiprotect/commit/072bc7cbc6a8af634f4638ac79658715cb31379a)) - Bump psr to 9.8.8 to fix release process (#221) ([`b109433`](https://github.com/uilibs/uiprotect/commit/b1094333c8767dd7588fe0d0f97f4c711b7e2595)) ### Feature - Speed up url joins (#220) ([`a10fc5a`](https://github.com/uilibs/uiprotect/commit/a10fc5adc88a1cf78199f5ca2e4a995032f58743)) ## v6.0.2 (2024-08-13) ### Fix - Bump aiofiles requirement to >=24 (#182) ([`1eb9ea7`](https://github.com/uilibs/uiprotect/commit/1eb9ea7c5fb2036ad0af42eb607604652d1b0210)) ## v6.0.1 (2024-08-09) ### Fix - Simplify ssl verify flag in websocket class (#175) ([`c36e19a`](https://github.com/uilibs/uiprotect/commit/c36e19a549c78f4fd123b89f562669fdaa5f78a5)) ## v6.0.0 (2024-08-08) ### Breaking - Remove default websocket receive timeout (#173) ([`8b0b303`](https://github.com/uilibs/uiprotect/commit/8b0b3033880532ddbf00cb59df881100db273dcb)) ## v5.4.0 (2024-07-20) ### Feature - Improve performance of convert_unifi_data (#153) ([`45f66b4`](https://github.com/uilibs/uiprotect/commit/45f66b4d6f35cbd02abae21f0905089b0e329d59)) ## v5.3.0 (2024-07-16) ### Feature - Speed up camera snapshots (#152) ([`d333865`](https://github.com/uilibs/uiprotect/commit/d3338658c2fa714e993c3d668945b44a1e7ebd27)) ## v5.2.2 (2024-07-04) ### Fix - Reflection of chime duration seconds (#142) ([`0266b8e`](https://github.com/uilibs/uiprotect/commit/0266b8e2470084df63422d4971c04354710b1ae8)) ## v5.2.1 (2024-07-04) ### Fix - Avoid reflecting back smoke_cmonx when changing smart audio (#141) ([`7270a5c`](https://github.com/uilibs/uiprotect/commit/7270a5cb40ed9c83db353677abc0496dc7b59f9e)) ## v5.2.0 (2024-07-03) ### Feature - Remove deepcopy before calling update_from_dict (#140) ([`23bc68f`](https://github.com/uilibs/uiprotect/commit/23bc68f2ca31c06e224cb5f5600ce87e1c842ec6)) ## v5.1.0 (2024-07-03) ### Feature - Small cleanups to smart detect lookups (#139) ([`ef21763`](https://github.com/uilibs/uiprotect/commit/ef217638129bc48fb67d9e60fe828f78daf2a017)) ## v5.0.0 (2024-07-02) ### Breaking - Do not auto convert enums to values for fetching attrs (#138) ([`f6d7ead`](https://github.com/uilibs/uiprotect/commit/f6d7eade0e2b1dc4073b5e45f7f2a75909180a30)) ## v4.2.0 (2024-06-27) ### Feature - Replace manual dict deletes with convertertools (#131) ([`22f7df8`](https://github.com/uilibs/uiprotect/commit/22f7df8852d5dcb252337a3f4620932619b6c5be)) ## v4.1.0 (2024-06-27) ### Feature - Avoid the need to deepcopy in the ws stats (#130) ([`5318b02`](https://github.com/uilibs/uiprotect/commit/5318b0219c89a1183218c94525fe08319208bc30)) ## v4.0.0 (2024-06-26) ### Breaking - Remove is_ringing property and ring ping back from camera (#125) ([`b400435`](https://github.com/uilibs/uiprotect/commit/b400435366c859d0350a9095ae6e9136afb2b08a)) ## v3.8.0 (2024-06-26) ### Fix - Use id checks for type compares (#126) ([`0e54ac6`](https://github.com/uilibs/uiprotect/commit/0e54ac6d82e010a6553c7ee7d42d884e8ec0bbd3)) - Do not swallow asyncio.cancellederror (#129) ([`09bc38b`](https://github.com/uilibs/uiprotect/commit/09bc38b419b26c00363b47c5ae8ce0e6a7280133)) ### Feature - Improve websocket error handling (#128) ([`b70d071`](https://github.com/uilibs/uiprotect/commit/b70d071dc52fa179710134e023c34ac0c8caebbe)) ## v3.7.0 (2024-06-25) ### Feature - Small cleanups to packet packing/unpacking (#122) ([`00cb125`](https://github.com/uilibs/uiprotect/commit/00cb125e89f5f43f7c759719d5fc581fb631af3c)) - Small cleanups to devices (#124) ([`1b64a8e`](https://github.com/uilibs/uiprotect/commit/1b64a8e89259e9d791a9c9703ced088e4fc7622c)) - Cleanup some additional dupe attr lookups (#123) ([`24849d8`](https://github.com/uilibs/uiprotect/commit/24849d819cfbba582a0f21c975de895d3754ef3b)) ## v3.6.0 (2024-06-25) ### Feature - Reduce some duplicate attr lookups in devices (#121) ([`8ea72ea`](https://github.com/uilibs/uiprotect/commit/8ea72eae1c8c0e37206a1268937287b0b1f29b28)) ## v3.5.0 (2024-06-25) ### Feature - Use more list/dict comps where possible (#120) ([`9c1ef3f`](https://github.com/uilibs/uiprotect/commit/9c1ef3f30b8e1c01edb5a6d44b0126edd9e3610d)) ## v3.4.0 (2024-06-25) ### Feature - Reduce duplicate code to do unifi_dict_to_dict conversions (#119) ([`f616c52`](https://github.com/uilibs/uiprotect/commit/f616c528cc94a313dd2ac0ba7e302bfcfca4afde)) ## v3.3.1 (2024-06-24) ### Fix - License classifier (#116) ([`ac048d7`](https://github.com/uilibs/uiprotect/commit/ac048d7325529823ab7d2840dc63aaa822008b32)) ## v3.3.0 (2024-06-24) ### Feature - Skip empty models in unifi_dict (#115) ([`d42023f`](https://github.com/uilibs/uiprotect/commit/d42023f9f07d3bdf097669637e1ad754a70ea0b7)) ## v3.2.0 (2024-06-24) ### Feature - Refactor internal object tracking (#114) ([`ad1b2b4`](https://github.com/uilibs/uiprotect/commit/ad1b2b45f3d72243ca8cb24c326b4f0fcd0bd71f)) ## v3.1.9 (2024-06-24) ### Fix - Remove event is in range check (#92) ([`2847f40`](https://github.com/uilibs/uiprotect/commit/2847f402a19655e9dee1d596b331e70b25bf3da3)) ## v3.1.8 (2024-06-23) ### Fix - Small tweaks to compact code (#113) ([`aa136ba`](https://github.com/uilibs/uiprotect/commit/aa136badd8ff7dbad6b74fcd1418de5f8ca04d73)) ## v3.1.7 (2024-06-23) ### Fix - Remove unreachable code in the websocket decoder (#112) ([`235cdef`](https://github.com/uilibs/uiprotect/commit/235cdef8bf930fc7b86084fc44cccea96fb316ef)) ## v3.1.6 (2024-06-23) ### Fix - Remove unreachable api in data checks (#110) ([`c7772a9`](https://github.com/uilibs/uiprotect/commit/c7772a9ecdf8d29290d0ba84e31a6f104fcb1dd1)) - Make creation of update sync primitives lazy (#111) ([`b05af57`](https://github.com/uilibs/uiprotect/commit/b05af578a1ed9b30a1c986a13d006fbaf89b760f)) ## v3.1.5 (2024-06-23) ### Fix - Exclude_fields would mutate the classvar (#109) ([`1c461e1`](https://github.com/uilibs/uiprotect/commit/1c461e1a481eb1c022c1dc5aa09529fc1abfec0e)) ## v3.1.4 (2024-06-23) ### Fix - Ensure test harness does not delete coveragerc (#108) ([`02bd064`](https://github.com/uilibs/uiprotect/commit/02bd0640fc6ce917db180a410ab0d102b6c8c73a)) ## v3.1.3 (2024-06-23) ### Fix - Add test coverage for updating to none (#107) ([`b2adeac`](https://github.com/uilibs/uiprotect/commit/b2adeac94fcef09bac8fe06c9795c8a41694ff95)) ## v3.1.2 (2024-06-23) ### Fix - Coveragerc fails to omit cli and tests (#106) ([`d1a4052`](https://github.com/uilibs/uiprotect/commit/d1a4052984e8545b5ac876337909ae235813db7f)) ## v3.1.1 (2024-06-22) ### Fix - _raise_for_status when raise_exception is not set (#105) ([`0a6ff9e`](https://github.com/uilibs/uiprotect/commit/0a6ff9e358e66058f2f7ca3bff12925f3b1d4e90)) ## v3.1.0 (2024-06-22) ### Feature - Add websocket state subscription (#104) ([`d7083ab`](https://github.com/uilibs/uiprotect/commit/d7083ab8ced2dc3cc65dcaf6ea2dd8c869e70a96)) ## v3.0.0 (2024-06-22) ### Breaking - Remove the force flag from update (#103) ([`0bee3e6`](https://github.com/uilibs/uiprotect/commit/0bee3e64d8f1a540e6bfde7b3ab282bc26e6f150)) ## v2.3.0 (2024-06-22) ### Feature - Handle websocket auth errors on restart (#102) ([`7026491`](https://github.com/uilibs/uiprotect/commit/7026491ac909cb2ed2bf3d9457cf86a1a44de025)) ## v2.2.0 (2024-06-22) ### Feature - Decrease websocket logging for known errors (#101) ([`05df499`](https://github.com/uilibs/uiprotect/commit/05df499863006b8d66d2ca0e3c76c639730e30de)) ## v2.1.0 (2024-06-22) ### Feature - Improve websocket error handling (#100) ([`813ac9c`](https://github.com/uilibs/uiprotect/commit/813ac9ca2eaefa2623b15f43d9cdf4f3fab31bcb)) ## v2.0.0 (2024-06-22) ### Breaking - Rework websocket (#96) ([`574a846`](https://github.com/uilibs/uiprotect/commit/574a846ff4e34737169b49ec418b4a112fa12f3e)) ## v1.20.0 (2024-06-21) ### Feature - Include getter builder utils for fetching ufp object values (#95) ([`9056edf`](https://github.com/uilibs/uiprotect/commit/9056edf85ecf8cd59d053411ae18f1d05093d9e5)) ## v1.19.3 (2024-06-21) ### Fix - Pin and drop pydantic compat imports now that pydantic is fixed (#94) ([`00adc2c`](https://github.com/uilibs/uiprotect/commit/00adc2cc39cf004e93952a8ef489ef1051c1fb83)) ## v1.19.2 (2024-06-20) ### Fix - Ensure update_from_dict creates the object is it was previously none (#93) ([`f268c01`](https://github.com/uilibs/uiprotect/commit/f268c01bac2b9969f10de70dae2295ce87a6f70b)) ## v1.19.1 (2024-06-19) ### Fix - Update broken documentation readme link (#90) ([`1580c04`](https://github.com/uilibs/uiprotect/commit/1580c042d04d989e1ebe4b919df3d232ae4e8ae9)) ## v1.19.0 (2024-06-17) ### Feature - Simplify websocket stats logic (#88) ([`5b01f34`](https://github.com/uilibs/uiprotect/commit/5b01f34b9c5cc8bcb3cae9f274acd687870a4091)) ### Fix - Refactoring error in 83 (#89) ([`ed477c2`](https://github.com/uilibs/uiprotect/commit/ed477c288047fd1fba39f51d6e695adb6a72ba08)) ## v1.18.1 (2024-06-17) ### Fix - Ensure camera and chime keys are not included in the base ignored set (#86) ([`02ab5f6`](https://github.com/uilibs/uiprotect/commit/02ab5f696db9497610ec6b34739452abdfe6ca68)) - Ignore cameraids for chime updates (#85) ([`3a7e48d`](https://github.com/uilibs/uiprotect/commit/3a7e48dea4111eb6b0a6012ffe08cafcd66cf4d6)) ## v1.18.0 (2024-06-17) ### Feature - Add repr for websocket packets (#84) ([`60dd356`](https://github.com/uilibs/uiprotect/commit/60dd356a233ab183c31375417ded3f6e53427e5d)) ### Refactor - Avoid writing out some more key converts (#83) ([`851c798`](https://github.com/uilibs/uiprotect/commit/851c7987b772a185fd4c448dddd9e180fd4f16da)) ## v1.17.0 (2024-06-17) ### Feature - Improve performance of websocket packet processing (#82) ([`58df1c3`](https://github.com/uilibs/uiprotect/commit/58df1c3ac1c050c418d6ea6255ce18ad64422168)) ### Refactor - Remove and consolidate unused code in base (#81) ([`523d931`](https://github.com/uilibs/uiprotect/commit/523d931f6a06b7c66fc7af7cdfac2abf8ebaa737)) - Use tuples for all the delete iterators (#80) ([`9ec88ce`](https://github.com/uilibs/uiprotect/commit/9ec88ce68ab5c0d9f6cb30175eb4ffd9b4a47d43)) - Cleanup debug (#79) ([`7883c24`](https://github.com/uilibs/uiprotect/commit/7883c24c9b9a08e41ec044e943e6fab3b66a56f1)) - Reduce code to remove keys (#78) ([`7b496cb`](https://github.com/uilibs/uiprotect/commit/7b496cb72b3b5efffad18bb86f58355e910122e7)) ## v1.16.0 (2024-06-17) ### Feature - Refactor protect obj methods to use comprehensions (#77) ([`ae4cdb9`](https://github.com/uilibs/uiprotect/commit/ae4cdb914b162c756f8384c0c25f256fbaa634d7)) ## v1.15.0 (2024-06-17) ### Feature - Small cleanup to get device functions (#76) ([`86f18d8`](https://github.com/uilibs/uiprotect/commit/86f18d8901d8fd9b6e2ebfa9c3926ed1d1d0e45c)) ## v1.14.0 (2024-06-17) ### Feature - Optimize update_from_dict (#75) ([`1b8ed6d`](https://github.com/uilibs/uiprotect/commit/1b8ed6dc146c0351927eeb15c47373481b3ad40e)) ## v1.13.0 (2024-06-16) ### Feature - Improve performance of processing websocket messages (#74) ([`84277cb`](https://github.com/uilibs/uiprotect/commit/84277cb3ac8b47e8d6b483ace8e31c0d9b07baad)) ## v1.12.1 (2024-06-16) ### Fix - Ensure ping back messages are called back and empty updates excluded (#62) ([`b319dba`](https://github.com/uilibs/uiprotect/commit/b319dba4b88e0a7d7b237ec57f2e89ca46c1cc6c)) ## v1.12.0 (2024-06-16) ### Fix - Add missing eventstats key to stats_keys (#73) ([`6c8be31`](https://github.com/uilibs/uiprotect/commit/6c8be3129c763d6ade16c57df01cc79d57190fef)) ### Feature - Small cleanups to bootstrap code (#72) ([`78e6dbb`](https://github.com/uilibs/uiprotect/commit/78e6dbb8165b97522b7f42d8f9e885f0e23cd1eb)) ## v1.11.1 (2024-06-16) ### Fix - Revert to using protected attrs for property cache (#71) ([`f0b259c`](https://github.com/uilibs/uiprotect/commit/f0b259caaf7c990de68f1a51a0bd166f94eb3bf7)) ## v1.11.0 (2024-06-16) ### Feature - Speed up bootstrap by adding cached_property (#68) ([`c6b746d`](https://github.com/uilibs/uiprotect/commit/c6b746d8e4d961c0fc1f98d693357e9becd26baa)) ## v1.10.0 (2024-06-16) ### Feature - Make websocket dataclasses sloted (#67) ([`58e42f6`](https://github.com/uilibs/uiprotect/commit/58e42f69b7603ab77ffe170d091051febe22e48f)) ## v1.9.0 (2024-06-15) ### Feature - Improve performance of websocket message processing (#66) ([`d6a6472`](https://github.com/uilibs/uiprotect/commit/d6a6472d3516e27dcfdd2ed3b5d8ca68428e273f)) ## v1.8.0 (2024-06-15) ### Feature - Replace some attrs with cached methods (#65) ([`fc0fc57`](https://github.com/uilibs/uiprotect/commit/fc0fc5717a171eb705dce4f88dca79509bd889b4)) ### Refactor - Delete unused bootstrap constants (#64) ([`0283c45`](https://github.com/uilibs/uiprotect/commit/0283c4564c905bee1b1f82cc4c0280a02e07ec5d)) - Small cleanups to _process_add_packet (#63) ([`8fd8280`](https://github.com/uilibs/uiprotect/commit/8fd82800b63c7cb8c70da164dcc3e1853fc170a6)) ## v1.7.2 (2024-06-14) ### Fix - Pingback did not hold a strong reference to the task (#61) ([`7b11ce9`](https://github.com/uilibs/uiprotect/commit/7b11ce952a9e2f66fc5ac9ceccd1a21e74c218b9)) ## v1.7.1 (2024-06-14) ### Fix - Refactoring error in _process_add_packet (#60) ([`e21516b`](https://github.com/uilibs/uiprotect/commit/e21516b212762955a49d6da66f2f823a1b252ca2)) ## v1.7.0 (2024-06-14) ### Feature - Add debug logging when saving device changes (#59) ([`1c57d00`](https://github.com/uilibs/uiprotect/commit/1c57d005f8f97c148b70401256929c262ba5a8a1)) ### Refactor - Cleanup duplicate doorbell text code (#58) ([`5e3fac8`](https://github.com/uilibs/uiprotect/commit/5e3fac8b862dfe7df83fe7b5b565578f494b8bf1)) ## v1.6.0 (2024-06-14) ### Feature - Simplify object conversions (#55) ([`feb8236`](https://github.com/uilibs/uiprotect/commit/feb8236d7e1817a604186a493d57511fff455e47)) ## v1.5.0 (2024-06-14) ### Feature - Make audio_type a cached_property (#54) ([`50d22de`](https://github.com/uilibs/uiprotect/commit/50d22de5bbf03328c307c7710015e6ec62ab6826)) ## v1.4.1 (2024-06-14) ### Fix - Use none instead of ... for privateattr (#53) ([`fc06f42`](https://github.com/uilibs/uiprotect/commit/fc06f420b6c4531dd59bfa3db8b53a965409cac0)) ## v1.4.0 (2024-06-14) ### Feature - Only process incoming websocket packet model type once (#52) ([`57d7c10`](https://github.com/uilibs/uiprotect/commit/57d7c10d3915fbf45dd81a855298530a36b9e3c7)) ## v1.3.0 (2024-06-13) ### Feature - Cleanup duplicate object lookups in event processing (#51) ([`ec00121`](https://github.com/uilibs/uiprotect/commit/ec001218a39f7ec10bcc18005e59a1130f16f8aa)) ## v1.2.2 (2024-06-13) ### Fix - Restore some unreachable code in _process_device_update (#50) ([`c638cd3`](https://github.com/uilibs/uiprotect/commit/c638cd3b087d63279bd8f798bd8831fc2e11a916)) ## v1.2.1 (2024-06-13) ### Fix - Blocking i/o in the event loop (#49) ([`36a4355`](https://github.com/uilibs/uiprotect/commit/36a4355170566b9d7cfb1632d9c35c28b693d9ce)) ## v1.2.0 (2024-06-13) ### Feature - Avoid fetching and iterating convert keys when empty (#48) ([`7c9ae89`](https://github.com/uilibs/uiprotect/commit/7c9ae89ed667bbe3e9ca2f5561489d4b8335180e)) ### Style - Remove ide workspace files and add the directories for them to the gitignore (#47) ([`486e3f9`](https://github.com/uilibs/uiprotect/commit/486e3f92f4d12ab195f0433e599c9eac0f008aef)) ## v1.1.0 (2024-06-12) ### Feature - Remove _get_frame_data helper (#45) ([`21d6768`](https://github.com/uilibs/uiprotect/commit/21d6768132d553cc9f59e73cc7adbfde02a42915)) ### Refactor - Consolidate logic to remove keys (#44) ([`9da56d2`](https://github.com/uilibs/uiprotect/commit/9da56d2c0f094d31b0cf8cba07c4c07fd96c64ea)) - Use new _event_is_in_range helper in _process_camera_event (#43) ([`49e0a67`](https://github.com/uilibs/uiprotect/commit/49e0a67c5f2473ae1a6bfbe3db513a77786a68df)) - Reduce duplicate code to process sensor events (#41) ([`78c291b`](https://github.com/uilibs/uiprotect/commit/78c291b76a0cbce1f891f91c9c01236d71edbf81)) ## v1.0.1 (2024-06-11) ### Fix - New cookie flag preventing auth cookie from being stored (#36) ([`b6eb7fc`](https://github.com/uilibs/uiprotect/commit/b6eb7fcef23885d734ba0f9031bf15bdbba91bc5)) ## v1.0.0 (2024-06-11) ### Breaking - Remove unused is_ready property from the api client (#33) ([`c36ee42`](https://github.com/uilibs/uiprotect/commit/c36ee422ddd04f811019d2e99cbb1d6b398eae01)) ### Refactor - Use internal self._api inside the object (#34) ([`c20e7a9`](https://github.com/uilibs/uiprotect/commit/c20e7a9690a15f42ff0f17105141f21b2e6e4020)) ## v0.15.1 (2024-06-11) ### Fix - Missing url param in websocket disconnected error log message (#32) ([`60e6511`](https://github.com/uilibs/uiprotect/commit/60e651110ed935bb0c35b09aedbc2253a73c35a4)) ## v0.15.0 (2024-06-11) ### Feature - Cache bootstrap on the protectapiclient once it has been initialized (#31) ([`185e47f`](https://github.com/uilibs/uiprotect/commit/185e47fed693c5a6f8383cece10c5267dbb7e046)) ## v0.14.0 (2024-06-11) ### Feature - Cache parsing of datetimes (#29) ([`8b6747a`](https://github.com/uilibs/uiprotect/commit/8b6747ae41d483da7395f49e402e29f68112fe83)) ### Refactor - Use f-strings in more places (#28) ([`22706c8`](https://github.com/uilibs/uiprotect/commit/22706c896121eac3b6847a951ef516f350119072)) ## v0.13.0 (2024-06-11) ### Feature - Cleanup processing camera events (#27) ([`2c1a266`](https://github.com/uilibs/uiprotect/commit/2c1a266a3f7c290e4ae9724642eb427ca41cabf1)) ## v0.12.0 (2024-06-11) ### Feature - Cleanup websocket add/remove packet processing (#25) ([`fdf0f6e`](https://github.com/uilibs/uiprotect/commit/fdf0f6eef96c17c0d2afe008444c24ce8fad72ee)) - Use a single function to normalize mac addresses (#26) ([`7ce8654`](https://github.com/uilibs/uiprotect/commit/7ce86543d4ec1efa9143839b1b7be1c6dd977ca1)) ## v0.11.0 (2024-06-11) ### Feature - Cleanup processing of websocket packets (#24) ([`b59e19c`](https://github.com/uilibs/uiprotect/commit/b59e19c13ea48e5ab235090c1b02d8d73c3aac24)) ## v0.10.1 (2024-06-11) ### Fix - Remove useless time check (#23) ([`749cfef`](https://github.com/uilibs/uiprotect/commit/749cfef9b44f87397153977c673c577659450a48)) ## v0.10.0 (2024-06-11) ### Feature - Improve performance of process websocket packets (#22) ([`7b59c98`](https://github.com/uilibs/uiprotect/commit/7b59c98d02d2f874375b168979a1db253da58914)) ## v0.9.0 (2024-06-10) ### Feature - Avoid linear searches to process websocket packets (#21) ([`86d5f19`](https://github.com/uilibs/uiprotect/commit/86d5f198071b0478b480804d055ed80c88341ee1)) ## v0.8.0 (2024-06-10) ### Feature - Guard debug logging that reformats data in the arguments (#20) ([`0cfdea8`](https://github.com/uilibs/uiprotect/commit/0cfdea8d27c0a35d71cd98d65120288218f4ca4c)) ### Refactor - Remove useless .keys() calls (#19) ([`ec1fd12`](https://github.com/uilibs/uiprotect/commit/ec1fd129deb06b5d2334d49ccd0b238033c5b904)) ## v0.7.0 (2024-06-10) ### Feature - Refactor protect object subtype bucketing (#18) ([`e4123ac`](https://github.com/uilibs/uiprotect/commit/e4123ac13015c186f141c1bfec3a7c064bb2d732)) ## v0.6.0 (2024-06-10) ### Feature - Small code cleanups (#17) ([`f1668ae`](https://github.com/uilibs/uiprotect/commit/f1668ae2c9c9f49f6e703a387159d305c2cba847)) ## v0.5.0 (2024-06-10) ### Feature - Memoize enum type check to speed up data conversion (#15) ([`73b0c4a`](https://github.com/uilibs/uiprotect/commit/73b0c4a813e99d3f353a8fbf3d8a997158cedf3a)) ## v0.4.1 (2024-06-10) ### Fix - Handle unifi os 4 token change (#14) ([`a6aab8f`](https://github.com/uilibs/uiprotect/commit/a6aab8f1eefd631119288f6d29d643f3984c5b0d)) ## v0.4.0 (2024-06-10) ### Feature - Avoid parsing last_update_id (#12) ([`ac86b13`](https://github.com/uilibs/uiprotect/commit/ac86b13b3efc8fc619471536ea993f3741882264)) ## v0.3.10 (2024-06-10) ### Fix - Add missing doorbellmessagetype image (#11) ([`eaed04b`](https://github.com/uilibs/uiprotect/commit/eaed04bbc1697553895a64edc573d1acc9112a1a)) ## v0.3.9 (2024-06-09) ### Fix - Revert global flags check (#9) ([`8dc437f`](https://github.com/uilibs/uiprotect/commit/8dc437f38dc4f6f6081d9a8a80f9f295b31bf579)) ## v0.3.8 (2024-06-09) ### Fix - Improve readme and testdata docs (#8) ([`90ae6a8`](https://github.com/uilibs/uiprotect/commit/90ae6a8cec7a10c1631b301a5d64c94bffdee16d)) ## v0.3.7 (2024-06-09) ### Fix - Revert pydantic changes for ha compat (#7) ([`c7770c1`](https://github.com/uilibs/uiprotect/commit/c7770c135deaa52da078794c67d5e3f5dbe3455d)) ## v0.3.6 (2024-06-09) ### Fix - Switch readthedocs to mkdocs ([`6009f9d`](https://github.com/uilibs/uiprotect/commit/6009f9dbb5beed141a8af866eb6e1dfd081af067)) - More docs fixes ([`52261ef`](https://github.com/uilibs/uiprotect/commit/52261eff11919768d75e73f9f3a85243c7eff90a)) ## v0.3.5 (2024-06-09) ### Fix - Add missing docs deps ([`399de45`](https://github.com/uilibs/uiprotect/commit/399de45721cb72c1cd6c945ad9aa0d73d82dea8f)) ## v0.3.4 (2024-06-09) ### Fix - Small fixes for readme.md (#6) ([`7a0acf4`](https://github.com/uilibs/uiprotect/commit/7a0acf4da9cfcc1cbf6111cc9d2083be68aa9d93)) ## v0.3.3 (2024-06-09) ### Fix - Ensure uv is installed for docker image ([`d286198`](https://github.com/uilibs/uiprotect/commit/d286198ce4d26ff5151c9b937058b4c223aa95f2)) ## v0.3.2 (2024-06-09) ### Fix - Docker file ([`8474862`](https://github.com/uilibs/uiprotect/commit/84748626bbe29492997801759164a6242ebf7b72)) - Update typer ([`54f26b1`](https://github.com/uilibs/uiprotect/commit/54f26b16223d0ed83c2e249df458ec5ccc407fb6)) - Make package installable ([`169e790`](https://github.com/uilibs/uiprotect/commit/169e7903bc72ad513f475c5477c0b6f4cd5c7653)) ## v0.3.1 (2024-06-09) ### Fix - Dockerfile ([`b25d8a1`](https://github.com/uilibs/uiprotect/commit/b25d8a1218158368ec50d1a2b20280b94696ccee)) - Docker ci (#5) ([`3d8e9fe`](https://github.com/uilibs/uiprotect/commit/3d8e9fe294c7c75a7efc2d2653a51fdb052fbf29)) ## v0.3.0 (2024-06-09) ### Feature - Migrate docs (#4) ([`1e62ec2`](https://github.com/uilibs/uiprotect/commit/1e62ec204c6d1b26f95486a8c27a61bb40a8219b)) ## v0.2.2 (2024-06-09) ### Fix - Readme updates (#3) ([`8cf5d24`](https://github.com/uilibs/uiprotect/commit/8cf5d24915e9aed2ffbdce4390dd061c9c40d4a1)) ## v0.2.1 (2024-06-09) ### Fix - Adjust jinja check for changelog template ([`e5f55c1`](https://github.com/uilibs/uiprotect/commit/e5f55c1f1af84d3f9053bf9b36c3662dab706882)) - Changelog generation (#2) ([`2b770e9`](https://github.com/uilibs/uiprotect/commit/2b770e9a4a6ccfa352fd0fc2b30099ef07b59db8)) ## v0.2.0 (2024-06-09) ### Feature - Update classifiers (#1) ([`0d4eaf6`](https://github.com/uilibs/uiprotect/commit/0d4eaf6e5fe30c83c52d30d388d65ebe33ee7c3f)) ### Unknown ### Fix - Re-enable changelog ([`68620b0`](https://github.com/uilibs/uiprotect/commit/68620b09b65ee553982c2c54bfc1e0a3c6ba4380)) ## v0.1.0 (2024-06-09) ### Fix - Pre-commit auto update ([`27c1514`](https://github.com/uilibs/uiprotect/commit/27c1514064b5b44d13abd57fc5df3f81dc741c78)) - Cli test ([`b2e4e8e`](https://github.com/uilibs/uiprotect/commit/b2e4e8ef3536bbedc8d3765afb4fd3cb45b478ba)) - Only pyupgrade non-typer code ([`8a5f9b6`](https://github.com/uilibs/uiprotect/commit/8a5f9b644b80a2f739bc5d9720e316150e938ab6)) - Ensure workers ([`d7578de`](https://github.com/uilibs/uiprotect/commit/d7578dedd0443f5ce4333475dde06c28882cbfd0)) - Tests in ci ([`f008537`](https://github.com/uilibs/uiprotect/commit/f0085378ac15125e7e75d80daae7876b37fa8b6d)) - Add mypy to dev deps ([`bde29f2`](https://github.com/uilibs/uiprotect/commit/bde29f236622ec8c3756add4ddb8103644b04c8f)) - More mypy fixes ([`f889c50`](https://github.com/uilibs/uiprotect/commit/f889c5061dd3428bf47bcf1294c0117880e3f20b)) - Add more missing types ([`6d959f9`](https://github.com/uilibs/uiprotect/commit/6d959f9f48b0fd14a43468d52abd8593311bfe10)) - Disable some more rules inline ([`03c726f`](https://github.com/uilibs/uiprotect/commit/03c726f0c0594fcde0a4bb93020c1c99dce6a149)) - Add missing types ([`ef87e72`](https://github.com/uilibs/uiprotect/commit/ef87e72b73e1ec5372bc19260916f093f2b2fe45)) - Disable some rules ([`6cfd103`](https://github.com/uilibs/uiprotect/commit/6cfd103beba3d8689f2c9730831efd89bc0fd679)) ### Unknown ## v0.0.0 (2024-06-09) ### Unknown ### Fix - Actually set chime_duration ([`e7edd26`](https://github.com/uilibs/uiprotect/commit/e7edd26823505f73e97b1a46e70f397a95126a3f)) ### Feature - Make chime duration adjustable ([`b4d13c1`](https://github.com/uilibs/uiprotect/commit/b4d13c146f292eae216109f747d3bee6608b0f28)) uiprotect-6.1.0/CONTRIBUTING.md000066400000000000000000000074071467310220200157720ustar00rootroot00000000000000# Contributing Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given. You can contribute in many ways: ## Types of Contributions ### Report Bugs Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include: - Your operating system name and version. - Any details about your local setup that might be helpful in troubleshooting. - Detailed steps to reproduce the bug. ### Fix Bugs Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it. ### Implement Features Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. ### Write Documentation uiprotect could always use more documentation, whether as part of the official uiprotect docs, in docstrings, or even on the web in blog posts, articles, and such. ### Submit Feedback The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature: - Explain in detail how it would work. - Keep the scope as narrow as possible, to make it easier to implement. - Remember that this is a volunteer-driven project, and that contributions are welcome 😊 ## Get Started! Ready to contribute? Here's how to set yourself up for local development. 1. Fork the repo on GitHub. 2. Clone your fork locally: ```shell $ git clone git@github.com:your_name_here/uiprotect.git ``` 3. Install the project dependencies with [Poetry](https://python-poetry.org): ```shell $ poetry install ``` 4. Create a branch for local development: ```shell $ git checkout -b name-of-your-bugfix-or-feature ``` Now you can make your changes locally. 5. When you're done making changes, check that your changes pass our tests: ```shell $ poetry run pytest ``` 6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off: ```shell $ pre-commit run -a ``` Or better, install the hooks once and have them run automatically each time you commit: ```shell $ pre-commit install ``` 7. Commit your changes and push your branch to GitHub: ```shell $ git add . $ git commit -m "feat(something): your detailed description of your changes" $ git push origin name-of-your-bugfix-or-feature ``` Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time. 8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed): ```shell $ gh pr create --fill ``` ## Pull Request Guidelines We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow: 1. Include tests for feature or bug fixes. 2. Update the documentation for significant features. 3. Ensure tests are passing on CI. ## Tips To run a subset of tests: ```shell $ pytest tests ``` ## Making a new release The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action. [gh-issues]: https://github.com/uilibs/uiprotect/issues uiprotect-6.1.0/Dockerfile000066400000000000000000000046311467310220200155270ustar00rootroot00000000000000FROM python:3.12-slim-bookworm as base LABEL org.opencontainers.image.source https://github.com/uilibs/uiprotect ENV PYTHONUNBUFFERED 1 ENV UV_SYSTEM_PYTHON true ARG TARGETPLATFORM RUN addgroup --system --gid 1000 app \ && adduser --system --shell /bin/bash --uid 1000 --home /home/app --ingroup app app RUN --mount=type=cache,mode=0755,id=apt-$TARGETPLATFORM,target=/var/lib/apt/lists \ apt-get update -qq \ && apt-get install -yqq ffmpeg FROM base as builder RUN --mount=type=cache,mode=0755,id=apt-$TARGETPLATFORM,target=/var/lib/apt/lists \ apt-get update -qq \ && apt-get install -yqq build-essential git RUN --mount=type=cache,mode=0755,id=pip-$TARGETPLATFORM,target=/root/.cache \ pip install --root-user-action=ignore -U pip uv FROM base as prod ARG UIPROTECT_VERSION COPY --from=builder /usr/local/bin/ /usr/local/bin/ COPY --from=builder /usr/local/lib/python3.12/ /usr/local/lib/python3.12/ RUN --mount=source=.,target=/tmp/uiprotect,type=bind,readwrite \ --mount=type=cache,mode=0755,id=pip-$TARGETPLATFORM,target=/root/.cache \ SETUPTOOLS_SCM_PRETEND_VERSION=${UIPROTECT_VERSION} uv pip install -U "/tmp/uiprotect[tz]" \ && cp /tmp/uiprotect/.docker/entrypoint.sh /usr/local/bin/entrypoint \ && chmod +x /usr/local/bin/entrypoint \ && mkdir /data \ && chown app:app /data USER app VOLUME /data WORKDIR /data ENTRYPOINT ["/usr/local/bin/entrypoint"] FROM builder as builder-dev RUN --mount=type=cache,mode=0755,id=pip-$TARGETPLATFORM,target=/root/.cache \ poetry install FROM base as dev # Python will not automatically write .pyc files ENV PYTHONDONTWRITEBYTECODE 1 # Enables Python development mode, see https://docs.python.org/3/library/devmode.html ENV PYTHONDEVMODE 1 COPY --from=builder-dev /usr/local/bin/ /usr/local/bin/ COPY --from=builder-dev /usr/local/lib/python3.12/ /usr/local/lib/python3.12/ COPY ./.docker/docker-fix.sh /usr/local/bin/docker-fix COPY ./.docker/bashrc /root/.bashrc COPY ./.docker/bashrc /home/app/.bashrc RUN --mount=type=cache,mode=0755,id=apt-$TARGETPLATFORM,target=/var/lib/apt/lists \ apt-get update -qq \ && apt-get install -yqq git curl vim procps curl jq sudo \ && echo 'app ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers \ && chown app:app /home/app/.bashrc \ && chmod +x /usr/local/bin/docker-fix ENV PYTHONPATH /workspaces/uiprotect/ ENV PATH $PATH:/workspaces/uiprotect/.bin USER app WORKDIR /workspaces/uiprotect/ uiprotect-6.1.0/LICENSE000066400000000000000000000021271467310220200145400ustar00rootroot00000000000000 MIT License Copyright (c) 2024 UI Protect Maintainers Copyright (c) 2020 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. uiprotect-6.1.0/LIVE_DATA_CI.md000066400000000000000000000120531467310220200157570ustar00rootroot00000000000000# How to make a GHA Workflow to Test Against your UFP Instance ## Fork vs. PR to main Repo It is recommended you do this all on your personal fork. Test it and make it all works. Then if you would like to submit your workflow to the main repo so we can use data from it, make a PR and coorindate with @uilibs to get it merged. **NOTE** If you do choose to make the PR and submit it, we will **not** have access to the sample data that is generated by your NVR. In the event the tests fail, we may request the files from you so we can reproduce any issues. ## Create a self-hosted GHA Runner You can use any method for creating a GHA runner you want. It is just required you use the labels `self-hosted,linux,ufp,YOUR_USERNAME`. To help out, 3 possible install methods are listed below. The only other additional requirement for the GHA runner is that it _must_ be able to communicate directly to your UniFi Protect instance. ### Getting Required variables Before starting any of the 3 options, you need 4 pieces of data: - `REPO_URL`: the URL for the repo the GHA action runner will be for - Either `https://github.com/uilibs/uiprotect` or the URL of your fork - `RUNNER_NAME`: an identifiable name for your runner. Can be anything, but make sure it unique. - `LABELS`: should be `self-hosted,linux,ufp,YOUR_USERNAME` - `RUNNER_TOKEN`: See below #### Generating Runner Token --- ## **NOTE**: If you want your workflow running on the main `uiprotect` repo, you will need to get @uilibs to do this step and give you the token. To create a self-hosted GHA runner, you need owner level access to the repo and then you need to go and generate a time-based auth token to create the runner (token expires in ~1 hour). 1. On your Github repo, Go to Settings -> Actions -> Runners and click "New self-hosted runner" 2. Copy the value after the `--token` argument under the "Configure" section ### Using a Home Assistant Add-on To help make the process as easy as possible, I made a simple Home Assistant add-on that wraps the "Using Docker" method below. 1. Go to "Supervisor -> Add-on Store -> Triple dots in corner -> Repositories" 2. Add the URL `https://github.com/uilibs/ha-addons` 3. Install the new "Github Actions Runner" add-on 4. Click the "Configuration" tab and enter your values from above 5. Start up the add-on `workdir` can be left as the default. It should already be a unique location that will not cause any issues. ### Using Docker You can use the awesome [premade Docker GHA runner](https://github.com/myoung34/docker-github-actions-runner). Just start it in your preferred method. To be able to access the data from outside of the docker container to debug and such, you will also need to make a fodler on the host machine for the runner workflow. Replace `/path/to/host/folder/for/data` for the path to your folder. ```bash docker run --rm -it \ -e REPO_URL=REPO_URL \ -e RUNNER_NAME=RUNNER_NAME \ -e LABELS=LABELS \ -e RUNNER_TOKEN=RUNNER_TOKEN \ -e RUNNER_WORKDIR=/data \ -v /path/to/host/folder/for/data:/data \ myoung34/github-runner:ubuntu-bionic ``` ### Roll your Own Runner If you would prefer to make your own runner to ensure the complete security of your home network. Go for it. The docs for making your own runner can be found on [Github's docs site](https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners) ## Create a Local User in UFP for GHA Go to your UFP instance and create a Local User with admin permissions for GHA to use to access your instance. If you are already using the HA Integration for UFP, you can just reuse those credentials if you want. ## Add Secrets to Github Repo --- ## **NOTE**: If you want your workflow running on the main `uiprotect` repo, you will need to give the secrets to @uilibs and add the environment for you. Next step is to create an "environment" for your workflow to run it with the secrets you will need to connect to your UFP instance. We will also want to lock it down to prevent other users from dumping your secrets. 1. Go to "Settings -> Environments -> New Environment" and name it your Github username. 2. Change "Deployment Branches" to "Selected Branches" and add `master` 3. Under "Environment Secrets" add the following secrets: - `UFP_ADDRESS`: IP or host name to your UFP instance - `UFP_PORT`: Port for your UFP instance - `UFP_SSL_VERIFY`: True or False. Whether or not to verify SSL certs for instance - `UFP_USERNAME`: Username for your local admin user - `UFP_PASSWORD`: Password for your local admin user ## Create a workflow to Test data 1. Copy one of the existing workflows under `.github/workflows/test-live-*.yml` 2. Rename it to `test-live-YOUR_USERNAME.yml` 3. Open the file and change the username in the `name` section at the top 4. Replace the username in the `runs-on` section 5. Replace the username in the `environment` section 6. Replace `/share/gha-runner` in `UFP_SAMPLE_DIR: /share/gha-runner/ufp-data` to match the root directory of your GHA runner you configured above. if you are using the HA Add-on, you may not need to change anything here. uiprotect-6.1.0/README.md000066400000000000000000000223101467310220200150060ustar00rootroot00000000000000# Unofficial UniFi Protect Python API and CLI

CI Status Documentation Status Test coverage percentage

Poetry Ruff pre-commit

PyPI Version Supported Python versions License

--- **Documentation**: https://uiprotect.readthedocs.io **Source Code**: https://github.com/uilibs/uiprotect --- Python API for UniFi Protect (Unofficial) ## Looking for maintainers This project is looking for maintainers. ## Installation Install this via pip (or your favorite package manager): `pip install uiprotect` ## History This project was split off from `pyunifiprotect` because that project changed its license to one that would not be accepted in Home Assistant. This project is committed to keeping the MIT license. ## Credits - Bjarne Riis ([@briis](https://github.com/briis/)) for the original pyunifiprotect package - Christopher Bailey ([@AngellusMortis](https://github.com/AngellusMortis/)) for the maintaining the pyunifiprotect package ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! `uiprotect` is an unofficial API for UniFi Protect. There is no affiliation with Ubiquiti. This module communicates with UniFi Protect surveillance software installed on a UniFi OS Console such as a Ubiquiti CloudKey+ or UniFi Dream Machine Pro. The API is not documented by Ubiquiti, so there might be misses and/or frequent changes in this module, as Ubiquiti evolves the software. The module is primarily written for the purpose of being used in Home Assistant core [integration for UniFi Protect](https://www.home-assistant.io/integrations/unifiprotect) but might be used for other purposes also. ## Smart Detections now Require Remote Access to enable Smart Detections (person, vehicle, animal, face), a feature that previously could be used with local only console, [now requires you to enable remote access to enable](https://community.ui.com/questions/Cannot-enable-Smart-Detections/e3d50641-5c00-4607-9723-453cda557e35#answer/1d146426-89aa-4022-a0ae-fd5000846028). Enabling Remote Access may grant other users access to your console [due to the fact Ubiquiti can reconfigure access controls at any time](https://community.ui.com/questions/Bug-Fix-Cloud-Access-Misconfiguration/fe8d4479-e187-4471-bf95-b2799183ceb7). If you are not okay with the feature being locked behind Remote Access, [let Ubiquiti know](https://community.ui.com/questions/Cannot-enable-Smart-Detections/e3d50641-5c00-4607-9723-453cda557e35). ## Documentation [Full documentation for the project](https://uiprotect.readthedocs.io/). ## Requirements If you want to install `uiprotect` natively, the below are the requirements: - [UniFi Protect](https://ui.com/camera-security) version 1.20+ - Latest version of library is generally only tested against the two latest minor version. This is either two latest stable versions (such as 1.21.x and 2.0.x) or the latest EA version and stable version (such as 2.2.x EA and 2.1.x). - [Python](https://www.python.org/) 3.10+ - POSIX compatible system - Library is only tested on Linux, specifically the latest Debian version available for the official Python Docker images, but there is no reason the library should not work on any Linux distro or macOS. - [ffmpeg](https://ffmpeg.org/) - ffmpeg is primarily only for streaming audio to Protect cameras, this can be considered a soft requirement Alternatively you can use the [provided Docker container](#using-docker-container), in which case the only requirement is [Docker](https://docs.docker.com/desktop/) or another OCI compatible orchestrator (such as Kubernetes or podman). Windows is **not supported**. If you need to use `uiprotect` on Windows, use Docker Desktop and the provided docker container or [WSL](https://docs.microsoft.com/en-us/windows/wsl/install). ## Install ### From PyPi `uiprotect` is available on PyPi: ```bash pip install uiprotect ``` ### From GitHub ```bash pip install git+https://github.com/uilibs/uiprotect.git#egg=uiprotect ``` ### Using Docker Container A Docker container is also provided, so you do not need to install/manage Python as well. You can add the following to your `.bashrc` or similar. ```bash function uiprotect() { docker run --rm -it \ -e UFP_USERNAME=YOUR_USERNAME_HERE \ -e UFP_PASSWORD=YOUR_PASSWORD_HERE \ -e UFP_ADDRESS=YOUR_IP_ADDRESS \ -e UFP_PORT=443 \ -e UFP_SSL_VERIFY=True \ -e TZ=America/New_York \ -v $PWD:/data ghcr.io/uilibs/uiprotect:latest "$@" } ``` Some notes about the Docker version since it is running inside a container: - You can update at any time using the command `docker pull ghcr.io/uilibs/uiprotect:latest` - Your local current working directory (`$PWD`) will automatically be mounted to `/data` inside of the container. For commands that output files, this is the _only_ path you can write to and have the file persist. - The container supports `linux/amd64` and `linux/arm64` natively. This means it will also work well on macOS or Windows using Docker Desktop. - `TZ` should be the [Olson timezone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the timezone your UniFi Protect instance is in. - For more details on `TZ` and other environment variables, check the [command line docs](https://uilibs.github.io/uiprotect/latest/cli/) ## Quickstart ### CLI > [!WARNING] > Ubiquiti SSO accounts are not supported and actively discouraged from being used. There is no option to use MFA. You are expected to use local access user. `uiprotect` is not designed to allow you to use your owner account to access the console or to be used over the public internet as both pose a security risk. ```bash export UFP_USERNAME=YOUR_USERNAME_HERE export UFP_PASSWORD=YOUR_PASSWORD_HERE export UFP_ADDRESS=YOUR_IP_ADDRESS export UFP_PORT=443 # change to false if you do not have a valid HTTPS certificate for your instance export UFP_SSL_VERIFY=True uiprotect --help uiprotect nvr ``` ### Python UniFi Protect itself is 100% async, so as such this library is primarily designed to be used in an async context. The main interface for the library is the `uiprotect.ProtectApiClient`: ```python from uiprotect import ProtectApiClient protect = ProtectApiClient(host, port, username, password, verify_ssl=True) await protect.update() # this will initialize the protect .bootstrap and open a Websocket connection for updates # get names of your cameras for camera in protect.bootstrap.cameras.values(): print(camera.name) # subscribe to Websocket for updates to UFP def callback(msg: WSSubscriptionMessage): # do stuff unsub = protect.subscribe_websocket(callback) # remove subscription unsub() ``` ## TODO / Planned / Not Implemented Generally any feature missing from the library is planned to be done eventually / nice to have with the following exceptions ### UniFi OS Features Anything that is strictly a UniFi OS feature. If it is ever done, it will be in a separate library that interacts with this one. Examples include: - Managing RAID and disks - Creating and managing users ### Remote Access / Ubiquiti Cloud Features Some features that require an Ubiquiti Account or "Remote Access" to be enabled are currently not implemented. Examples include: - Stream sharing - Face detection uiprotect-6.1.0/TESTDATA.md000066400000000000000000000031711467310220200152660ustar00rootroot00000000000000# Generating Sample Data to help with Testing/Features ## Setup Python ### With Home Assistant (via the `unifiprotect` integration) 1. Make sure you have the _Community_ [SSH & Web Terminal Add-On](https://github.com/hassio-addons/addon-ssh) install 2. Open an SSH or Web terminal to the add-on 3. Run ```bash docker exec -it homeassistant bash ``` Use `/config/ufp-data` for your `-o` argument below. ### Without Home Assistant 1. Ensure Python 3.10+ is installed 2. Install uiprotect by issuing this command: `pip install uiprotect` Use `./ufp-data` for your `-o` argument below. ## Generate Data Inside the Python environment from above, run the following command. If you are using Home Assistant, use `-o /config/ufp-data` so it will output data in your config folder to make it easy to get off of your HA instance. ```bash uiprotect generate-sample-data -o /path/to/ufp-data --actual -w 300 -v -U your-unifi-protect-username -P your-unifi-protect-password -a ip-address-to-unifi-protect ``` This will generate a ton of data from your UniFi Protect instance for 5 minutes. During this time, go do stuff with your sensor to trigger events. When it is all done, you will have a bunch of json files in `/path/to/ufp-data`. Download those and zip them up and send them to us. It is recommended that you _do not_ post these files publicly as they do have some sensitive data in them related to your UniFi Network. If you would like you manually clean out the sensitive data from these files, feel free. The most critical data for you to remove are the `authUserId`, `accessKey`, and `users` keys from the `sample_bootstrap.json` file. uiprotect-6.1.0/commitlint.config.mjs000066400000000000000000000003621467310220200176700ustar00rootroot00000000000000export default { extends: ["@commitlint/config-conventional"], rules: { "header-max-length": [0, "always", Infinity], "body-max-line-length": [0, "always", Infinity], "footer-max-line-length": [0, "always", Infinity], }, }; uiprotect-6.1.0/docs/000077500000000000000000000000001467310220200144615ustar00rootroot00000000000000uiprotect-6.1.0/docs/Makefile000066400000000000000000000013721467310220200161240ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build .PHONY: help livehtml Makefile # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) # Build, watch and serve docs with live reload livehtml: sphinx-autobuild -b html -c . $(SOURCEDIR) $(BUILDDIR)/html # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) uiprotect-6.1.0/docs/_static/000077500000000000000000000000001467310220200161075ustar00rootroot00000000000000uiprotect-6.1.0/docs/_static/.gitkeep000066400000000000000000000000001467310220200175260ustar00rootroot00000000000000uiprotect-6.1.0/docs/api.md000066400000000000000000000020051467310220200155510ustar00rootroot00000000000000--- hide: - navigation toc_depth: 3 --- # API Reference ## API Client (`uiprotect.api`) ::: uiprotect.api options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 ## Data Models (`uiprotect.data`) ::: uiprotect.data options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 ## Exceptions (`uiprotect.exception`) ::: uiprotect.exceptions options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 ## Stream (`uiprotect.stream`) ::: uiprotect.stream options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 ## Utils (`uiprotect.utils`) ::: uiprotect.utils options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 ## Websocket (`uiprotect.websocket`) ::: uiprotect.websocket options: show_root_toc_entry: false show_signature_annotations: true show_source: false heading_level: 3 uiprotect-6.1.0/docs/changelog.md000066400000000000000000000000601467310220200167260ustar00rootroot00000000000000(changelog)= ```{include} ../CHANGELOG.md ``` uiprotect-6.1.0/docs/cli.md000066400000000000000000000575051467310220200155660ustar00rootroot00000000000000--- hide: - navigation --- # Command Line The `uiprotect` command is provided to give a CLI interface to interact with your UniFi Protect instance as well. All commands support JSON output so it works great with `jq` for complex scripting. ## Authentication Following traditional [twelve factor app design](https://12factor.net/), the preferred way to provided authentication credentials to provided environment variables, but CLI args are also supported. !!! warning "About Ubiquiti SSO accounts" Ubiquiti SSO accounts are not supported and actively discouraged from being used. There is no option to use MFA. You are expected to use local access user. `uiprotect` is not designed to allow you to use your owner account to access the your console or to be used over the public Internet as both pose a security risk. ### Environment Variables ```bash export UFP_USERNAME=YOUR_USERNAME_HERE export UFP_PASSWORD=YOUR_PASSWORD_HERE export UFP_ADDRESS=YOUR_IP_ADDRESS export UFP_PORT=443 # change to false if you do not have a valid HTTPS Certificate for your instance export UFP_SSL_VERIFY=True uiprotect nvr ``` ### CLI Args ```bash uiprotect -U YOUR_USERNAME_HERE -P YOUR_PASSWORD_HERE -a YOUR_IP_ADDRESS -p 443 --no-verify nvr ``` ## Timezones A number of commands allow you to enter a datetime as an argument or output files with the datetime in the filename. As a result, it is very important for `uiprotect` to know your consoles local timezone. If you on a physical machine (not docker/VM), chances are this is already set up correctly for you (`/etc/localtime`), but otherwise you may need to set the `TZ` environment variable. `TZ` can also be used to override your system timezone as well if for whatever reason you need to. It should be the [Olson timezone name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for the timezone that your UniFi Protect Instance is in. ```bash TZ=America/New_York uiprotect --help ``` ## Reference ```bash $ uiprotect --help Usage: uiprotect [OPTIONS] COMMAND [ARGS]... UniFi Protect CLI ``` ### Options | | Option | Required? | Env | Type | Default | Description | | ---- | --------------------- | ------------------ | ---------------- | -------------- | ------- | --------------------------------------------------------------------------------------------------------------- | | `-U` | `--username` | :white_check_mark: | `UFP_USERNAME` | text | | UniFi Protect username | | `-P` | `--password` | :white_check_mark: | `UFP_PASSWORD` | text | | UniFi Protect password | | `-a` | `--address` | :white_check_mark: | `UFP_ADDRESS` | text | | UniFi Protect IP address or hostname | | `-p` | `--port` | | `UFP_PORT` | integer | `443` | UniFi Protect port | | | `--no-verify` | | `UFP_SSL_VERIFY` | boolean | `True` | Verify SSL | | | `--output-format` | | | `json`,`plain` | `plain` | Preferred output format. Not all commands support both JSON and plain and may still output in one or the other. | | `-u` | `--include-unadopted` | | | | | Include devices not adopted by this NVR. | | | `--show-completion` | | | | | Show completion for the current shell, to copy it or customize the installation. | | | `--help` | | | | | Show help message and exit. | ### Subcommands For any subcommand you can use `uiprotect COMMAND --help` | Command | Description | | ---------------------- | ---------------------------------------------------------------- | | `backup` | [Backup CLI](#backup-cli). | | `cameras` | Camera device CLI. | | `chimes` | Chime device CLI. | | `decode-ws-msg` | Decodes a base64 encoded UniFi Protect Websocket binary message. | | `doorlocks` | Doorlock device CLI. | | `events` | Events CLI. | | `generate-sample-data` | Generates sample data for UniFi Protect instance. | | `lights` | Lights device CLI. | | `liveviews` | Liveviews CLI. | | `nvr` | NVR device CLI. | | `profile-ws` | Profiles Websocket messages for UniFi Protect instance. | | `sensors` | Sensors device CLI. | | `shell` | Opens iPython shell with Protect client initialized. | | `viewers` | Viewers device CLI. | #### Multiple Item CLI Commands All adoptable device CLIs, event and liveview CLI work on the idea you have multiple cameras, multiple lights, multiple events or multiple liveviews. As such, they have four variations: ```bash # list all devices (or events/liveviews) uiprotect cameras # list short list of all devices (or events/liveviews) uiprotect cameras list-ids # list a specific device (or event/liveview) uiprotect cameras DEVICE_ID # run a command against a specific device (or event/liveview) uiprotect cameras DEVICE_ID COMMAND ``` !!! note The "list all devices" and "list a specific device" commands always return raw JSON. These commands can be paired with [jq](https://stedolan.github.io/jq/) to parse and quick extra device data from them. | Command | Description | | ------------------ | ------------------------------------------------------------------------ | | `list-ids` | Requires no device ID. Prints list of "id name" for each device. | | `set-person-track` | Requires device ID. Sets person auto tracking on or off for PTZ cameras. | ##### Examples ###### List All Cameras === "Plain" ```bash $ uiprotect cameras list-ids 61b3f5c7033ea703e7000424: G4 Bullet 61f9824e004adc03e700132c: G4 PTZ 61be1d2f004bda03e700ab12: G4 Dome ``` === "JSON" ```bash $ uiprotect --output-format json cameras list-ids [ [ "61b3f5c7033ea703e7000424", "G4 Bullet" ], [ "61f9824e004adc03e700132c", "G4 PTZ" ], [ "61be1d2f004bda03e700ab12", "G4 Done" ], ... ] ``` ###### Check if a Light is Online ```bash $ uiprotect cameras 61ddb66b018e2703e7008c19 | jq .isConnected true ``` ###### Take Snapshot of Camera ```bash $ uiprotectcameras 61ddb66b018e2703e7008c19 save-snapshot output.jpg ``` #### Adoptable Devices CLI Commands Adoptable devices (Cameras, Chimes, Doorlocks, Lights, Sensors, Viewers) all have some commands in common. | Command | Description | | -------------- | ------------------------------------------------- | | `adopt` | Adopts a device. | | `bridge` | Returns bridge device if connected via Bluetooth. | | `is-bluetooth` | Returns if the device has Bluetooth or not. | | `is-wifi` | Returns if the device has WiFi or not. | | `is-wired` | Returns if the device is wired or not. | | `protect-url` | Gets UniFi Protect management URL. | | `reboot` | Reboots the device. | | `unadopt` | Unadopt/Unmanage adopted device. | | `update` | Updates the device. | #### Backup CLI ```bash $ uiprotect backup --help Usage: uiprotect backup [OPTIONS] COMMAND [ARGS]... Backup CLI. The backup CLI is still very WIP in progress and consider experimental and potentially unstable (interface may change in the future). ``` ##### Backup Options | | Option | Env | Type | Default | Description | | ---- | ----------------- | ------------------- | -------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `-s` | `--start` | `UFP_BACKUP_START` | datetime | | Cutoff for start of backup. Defaults to start of recording for NVR. | | `-e` | `--end` | `UFP_BACKUP_END` | datetime | | Cutoff for end of backup. Defaults to now. | | | `--output-folder` | `UFP_BACKUP_OUTPUT` | path | `$PWD` | Base dir for creating files. Defaults to $PWD. | | | `--thumb-format` | | text | `{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}{sep}thumb.jpg` | Filename format to save event thumbnails to. Set to empty string ("") to skip saving event thumbnails. | | | `--gif-format` | | text | `{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}{sep}animated.gif]` | Filename format to save event gifs to. Set to empty string ("") to skip saving event gif. | | | `--event-format` | | text | `{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}.mp4` | Filename format to save event gifs to. Set to empty string ("") to skip saving event videos. | | | `--title-format` | | text | `{time_sort_pretty_local} {sep} {camera_name} {sep} {event_type_pretty} {sep} {length_pretty}` | Format to use to tag title for video metadata. | | `-v` | `--verbose` | | boolean | `False` | Debug logging. | | `-d` | `--max-download` | | integer | `5` | Max number of concurrent downloads. Adds additional loads to NVR. | | | `--page-size` | | integer | `1000` | Number of events fetched at once from local database. Increases memory usage. | | | `--length-cutoff` | | integer | `3600` | Event size cutoff for detecting abnormal events (in seconds). | | | `--sep` | | boolean | `-` | Separator used for formatting. | | | `--help` | | | | Show help message and exit. | ##### File Name and Title Formatting There are [5 options](#backup-options) controlling output format for file names and metadata. This allows you to customize backups to your liking. All 5 options are a template string. Here are all of the available templating variables: | Variable | Description | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | `year` | UTC year of start of export. | | `month` | UTC month of start of export. | | `day` | UTC day of start of export. | | `hour` | UTC hour of start of export. | | `minute` | UTC minute of start of export. | | `datetime` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted UTC datetime of start of export. Uses `sep` between parts. | | `date` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted UTC date of start of export. Uses `sep` between parts. | | `time` | UTC time of start of export. Uses `sep` between parts. 24 hour time. | | `time_sort_pretty` | UTC time of start of export. Uses `:` between parts. 24 hour time. | | `time_pretty` | UTC time of start of export. Uses `:` between parts. 12 hour time with AM/PM. | | `year_local` | [Local](#timezones) year of start of export. | | `month_local` | [Local](#timezones) month of start of export. | | `day_local` | [Local](#timezones) day of start of export. | | `hour_local` | [Local](#timezones) hour of start of export. | | `minute_local` | [Local](#timezones) minute of start of export. | | `datetime_local` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted [Local](#timezone) datetime of start of export. Uses `sep` between parts. | | `date_local` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted [Local](#timezone) date of start of export. Uses `sep` between parts. | | `time_local` | [Local](#timezones) time of start of export. Uses `sep` between parts. 24 hour time. | | `time_sort_pretty_local` | [Local](#timezones) time of start of export. Uses `:` between parts. 24 hour time. | | `time_pretty_local` | [Local](#timezones) time of start of export. Uses `:` between parts. 12 hour time with AM/PM. | | `mac` | MAC address of camera. | | `camera_name` | Name of camera. | | `camera_slug` | Lowercased name of camera with spaces replaced with `sep`. | | `event_type` | Lowercased name of the event exported. | | `event_type_pretty` | More human readable name of event exported. | | `length_pretty` | Human readable version of the length of the clip exported. | | `sep` | Separator to use in many cases. | ###### Datetimes All datetimes for the Backup CLi can either be in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format or can be a human readable format that the Python library [dateparse](https://github.com/scrapinghub/dateparser) can understand. This will allow relative datetimes to be passed, such as `"1 hour ago"` which will make backing up incremental for cron jobs. ###### Formatting for Plex You are able to export your Camera events and then access them in [Plex](https://www.plex.tv/) relatively well. For setup in Plex, the following is recommended: - Enable the "Local Media Assets" Agent Source for the Movies Library Type (Settings -> Agents -> Movies). [Plex docs](https://support.plex.tv/articles/200265246-personal-media-movies/). - Create a "Other Videos" library pointing to the same folder as your [--output-folder](#backup-options) folder. - Scanner: "Plex Video Files Scanner" - Agent: "Personal Media" Recommended formats for the backup command: | Option | Format | | ---------------- | ----------------------------------------------------------------- | | `--thumb-format` | `{year_local}/{month_local}/{day_local}/{hour_local}/{title}.jpg` | | `--gif-format` | `{year_local}/{month_local}/{day_local}/{hour_local}/{title}.gif` | | `--event-format` | `{year_local}/{month_local}/{day_local}/{hour_local}/{title}.mp4` | | `--title-format` | `default` or whatever you want the title to be in Plex. | ##### Backing Up Camera Events ```bash $ uiprotect backup events --help Usage: uiprotect backup events [OPTIONS] Backup thumbnails and video clips for camera events. ``` | | Option | Type | Default | Description | | ---- | -------------- | ----------------------------------- | ----------------------------------- | ----------------------------------------------------------- | | `-t` | `--event-type` | `motion`, `ring`, `smartDetectZone` | `motion`, `ring`, `smartDetectZone` | Events to export. Can be used multiple time. | | `-m` | `--smart-type` | `person`, `vehicle`, `package` | `person`, `vehicle`, `package` | Smart Detection types to export. Can be used multiple time. | | `-p` | `--prune` | boolean | `False` | Prune events older then start. | | `-f` | `--force` | boolean | `False` | Force update all events and redownload all clips. | | `-v` | `--verify` | boolean | `False` | Verifies files on disk. | | | `--no-input` | boolean | `False` | Disables confirmation prompt if `-p` and `-f` both passed. | | | `--help` | | | Show help message and exit. | The `backup events` command essentially mirrors all of the selected events from your UniFi Protect instance into a local sqlite database (`events.db` inside of the `--output-folder`). As a result, the initial run make take a _really long time_ to run if your UniFi Protect instance has a lot of events inside of it. As an example using a UniFi Protect instance with ~200k events and ~8 months of camera footage: - Building the database is in the range of hours - Doing the initial download of event thumbnails, gifs and video clips is in the range of tens of hours (potentially 1-2 days) - Incremental or targeted backups are much faster (<1 per event) !!! note "Cron Usage" For incremental backups in crons, it is recommended you run the command with an absolute start first to build your events database and do an initial download of files. This will significantly speed up the incremental backup commands. ##### Examples ###### Backup All Events ```bash uiprotect backup events ``` ###### Backup All Smart Detections for the Past Hour ```bash uiprotect backup --start "1 hour ago" events -t smartDetectZone ``` ###### Backup All Person Smart Detections from December 31st at 10PM to January 1st at 5AM ```bash uiprotect backup --start "2021-12-31T22:00:00" --end "2022-1-1T05:00:00" events -t smartDetectZone -m person ``` #### Camera CLI Inherits [Multiple Item CLI Commands](#multiple-item-cli-commands) and [Adoptable Devices CLI Commands](#adoptable-devices-cli-commands). ##### Examples ###### Take Snapshot of Camera ```bash $ uiprotect cameras 61ddb66b018e2703e7008c19 save-snapshot output.jpg ``` ###### Export Video From Camera ```bash $ uiprotect cameras 61ddb66b018e2703e7008c19 save-video export.mp4 2022-6-1T00:00:00 2022-6-1T00:00:30 ``` !!! note "Timezones" See the section on [Timezones](#timezone) for determined what timezone your datetimes are in. ###### Play Audio File to Cameras Speaker ```bash $ uiprotect cameras 61ddb66b018e2703e7008c19 play-audio test.mp3 ``` ###### Include Unadopted Cameras in list ```bash $ uiprotect -u cameras list-ids ``` ###### Adopt an Unadopted Camera ```bash $ uiprotect -u cameras 61ddb66b018e2703e7008c19 adopt ``` ###### Enable SSH on Camera ```bash $ uiprotect cameras 61ddb66b018e2703e7008c19 set-ssh true # get current value to verify $ uiprotect cameras 61ddb66b018e2703e7008c19 | jq .isSshEnabled true ``` ###### Reboot Camera ```bash $ uiprotect lights 61b3f5c801f8a703e7000428 reboot ``` ###### Reboot All Cameras ```bash for id in $(uiprotect cameras list-ids | awk '{ print $1 }'); do uiprotect cameras $id reboot done ``` #### Chime CLI Inherits [Multiple Item CLI Commands](#multiple-item-cli-commands) and [Adoptable Devices CLI Commands](#adoptable-devices-cli-commands). ##### Examples ###### Set Paired Cameras ```bash $ uiprotect chimes 6275b22e00e3c403e702a019 cameras 61ddb66b018e2703e7008c19 61f9824e004adc03e700132c ``` uiprotect-6.1.0/docs/conf.py000066400000000000000000000012351467310220200157610ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # Project information project = "uiprotect" copyright = "2024, UI Protect Maintainers" author = "UI Protect Maintainers" release = "6.1.0" # General configuration extensions = [ "myst_parser", ] # The suffix of source filenames. source_suffix = [ ".rst", ".md", ] templates_path = [ "_templates", ] exclude_patterns = [ "_build", "Thumbs.db", ".DS_Store", ] # Options for HTML output html_theme = "furo" html_static_path = ["_static"] uiprotect-6.1.0/docs/contributing.md000066400000000000000000000000661467310220200175140ustar00rootroot00000000000000(contributing)= ```{include} ../CONTRIBUTING.md ``` uiprotect-6.1.0/docs/dev.md000066400000000000000000000104571467310220200155700ustar00rootroot00000000000000--- hide: - navigation --- # Development ## Setup ### With VS Code Development with this project is designed to be done via VS Code + Docker. It is a pretty standard Python package, so feel free to use anything else, but all documentation assumes you are using VS Code. - [VS Code](https://code.visualstudio.com/) + [Remote Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) - [Docker](https://docs.docker.com/get-docker/) - If you are using Linux, you need Docker Engine 19.0 or newer and you need to enable [Docker Buildkit](https://docs.docker.com/develop/develop-images/build_enhancements/) - If you are using Docker Desktop on MacOS or Windows, you will need Docker Desktop 3.2.0 or newer Once you have all three setup, 1. Clone repo 2. Open the main folder 3. You should be prompted to "Reopen folder to develop in a container". If you are not, you can open the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) run the "Remote-Containers: Reopen in Container" command. This should be all you need to do to get a working development environment. The docker container will automatically be build and VS Code will attach itself to it. The integrated terminal in VS Code will already be set up with the `uiprotect` command. ### Docker (without VS Code) You can still setup develop without VS Code, but it is still recommended to use the development container to ensure you have all of the required dependencies. As a result, the above requirement for Docker is still needed. Once you have Docker setup, 1. Clone repo 2. Build and open dev container ```bash docker buildx build -f Dockerfile --target=dev -t uiprotect-dev . docker run --rm -it -v /home/cbailey/dev/uiprotect:/workspaces/uiprotect uiprotect-dev bash ``` ## Authenticating with your Local Protect Instance The project allows you to create an environment file to put your local protect instance data into so you do not need to constantly enter in or accidentally commit it to the Git repo. Make a file in the root of the project named `.env` with the following and change accordingly: ``` UFP_USERNAME=YOUR_USERNAME_HERE UFP_PASSWORD=YOUR_PASSWORD_HERE UFP_ADDRESS=YOUR_IP_ADDRESS UFP_PORT=443 # change to false if you do not have a valid HTTPS Certificate for your instance UFP_SSL_VERIFY=True ``` ## Linting and Testing The following scripts exist to easily format, lint and test code in the same fashion as CI: ``` pre-commit run --all-files .bin/test-code ``` These commands are also all available as [VS Code tasks](https://code.visualstudio.com/Docs/editor/tasks) as well. Tests are also fully integration with the Testing panel in VS Code and can be easily debug from there. ## Updating Requirements To generate an updated pinned requirements file to be used for testing and CI using the `.bin/update-requirements` script. There is also a [VS Code task](https://code.visualstudio.com/Docs/editor/tasks) to run this as well. ## Generating Test Data All of the tests in the project are ran against that is generated from a real UniFi Protect instance and then anonymized so it is safe to commit to a Git repo. To generate new sample test data: ``` uiprotect generate-sample-data ``` This will gather test data for 30 seconds and write it all into the `tests/sample_data` directory. During this time, it is a good idea to generate some good events that can tested. An example would be to generate a motion event for a FloodLight, Camera and/or Doorbell and then also ring a Doorbell. - All of the data that is generated is automatically anonymized so nothing sensitive about your NVR is exposed. To skip anonymization, use the `--actual` option. - To change output directory for sample data use the `-o / --output` option. - To adjust the time adjust how long to wait for Websocket messages, use the `-w / --wait` option. - To automatically zip up the generated sample data, use the `--zip` option. ```bash export UFP_SAMPLE_DIR=/workspaces/uiprotect/test-data uiprotect generate-sample-data ``` ### Real Data in Tests `pytest` will automatically also use the `UFP_SAMPLE_DIR` environment variable to locate sample data for running tests. This allows you to run `pytest` against a real NVR instance. ```bash export UFP_SAMPLE_DIR=/workspaces/uiprotect/test-data pytest ``` uiprotect-6.1.0/docs/index.md000066400000000000000000000001041467310220200161050ustar00rootroot00000000000000--- hide: - navigation --- {% include-markdown "../README.md" %} uiprotect-6.1.0/docs/installation.md000066400000000000000000000004111467310220200175000ustar00rootroot00000000000000(installation)= # Installation The package is published on [PyPI](https://pypi.org/project/uiprotect/) and can be installed with `pip` (or any equivalent): ```bash pip install uiprotect ``` Next, see the {ref}`section about usage ` to see how to use it. uiprotect-6.1.0/docs/make.bat000066400000000000000000000013751467310220200160740ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://www.sphinx-doc.org/ exit /b 1 ) if "%1" == "" goto help %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd uiprotect-6.1.0/docs/overrides/000077500000000000000000000000001467310220200164635ustar00rootroot00000000000000uiprotect-6.1.0/docs/overrides/main.html000066400000000000000000000002731467310220200202770ustar00rootroot00000000000000{% extends "base.html" %} {% block outdated %} You're not viewing the latest version. Click here to go to latest. {% endblock %} uiprotect-6.1.0/docs/overrides/partials/000077500000000000000000000000001467310220200203025ustar00rootroot00000000000000uiprotect-6.1.0/docs/overrides/partials/toc-item.html000066400000000000000000000007021467310220200227100ustar00rootroot00000000000000{% if not page.meta.toc_depth or toc_item.level <= page.meta.toc_depth %}
  • {{ toc_item.title }} {% if toc_item.children %} {% endif %}
  • {% endif %} uiprotect-6.1.0/docs/usage.md000066400000000000000000000003341467310220200161070ustar00rootroot00000000000000(usage)= # Usage Assuming that you've followed the {ref}`installations steps `, you're now ready to use this package. Call the command line interface: ```bash uiprotect --help ``` TODO: Document usage uiprotect-6.1.0/mkdocs.yml000066400000000000000000000036751467310220200155470ustar00rootroot00000000000000site_name: Unofficial UniFi Protect Python API and CLI site_url: https://uilibs.github.io/uiprotect/latest/ site_description: Unofficial UniFi Protect Python API and CLI repo_name: uiprotect repo_url: https://github.com/uilibs/uiprotect copyright: uiprotect is an unofficial API for UniFi Protect. There is no affiliation with Ubiquiti. markdown_extensions: - abbr - admonition - toc: permalink: true toc_depth: "1-5" - pymdownx.highlight: anchor_linenums: true use_pygments: true auto_title: true linenums: true - pymdownx.superfences - pymdownx.tabbed: alternate_style: true - attr_list - pymdownx.emoji: emoji_index: !!python/name:material.extensions.emoji.twemoji emoji_generator: !!python/name:material.extensions.emoji.to_svg nav: - Home: "index.md" - Development: "dev.md" - Command Line: "cli.md" - API Reference: "api.md" - Changelog: "https://github.com/uilibs/uiprotect/releases" plugins: - search - mike: canonical_version: null version_selector: true css_dir: css javascript_dir: js - git-revision-date-localized: enable_creation_date: true - include-markdown - mkdocstrings: default_handler: python handlers: python: paths: [src] theme: name: material custom_dir: docs/overrides features: - navigation.instant - navigation.tracking - navigation.tabs - navigation.top - search.suggest - search.highlight - search.share - header.autohide palette: - media: "(prefers-color-scheme: light)" scheme: default toggle: icon: material/brightness-7 name: Switch to dark mode primary: blue accent: light blue - media: "(prefers-color-scheme: dark)" scheme: slate toggle: icon: material/brightness-4 name: Switch to light mode primary: blue accent: light blue extra: version: provider: mike uiprotect-6.1.0/poetry.lock000066400000000000000000007313551467310220200157430ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiofiles" version = "24.1.0" description = "File support for asyncio." optional = false python-versions = ">=3.8" files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, ] [[package]] name = "aiohappyeyeballs" version = "2.3.5" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ {file = "aiohappyeyeballs-2.3.5-py3-none-any.whl", hash = "sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03"}, {file = "aiohappyeyeballs-2.3.5.tar.gz", hash = "sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105"}, ] [[package]] name = "aiohttp" version = "3.10.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aioshutil" version = "1.5" description = "Asynchronous shutil module." optional = false python-versions = ">=3.8" files = [ {file = "aioshutil-1.5-py3-none-any.whl", hash = "sha256:bc2a6cdcf1a8615b62f856154fd81131031d03f2834912ebb06d8a2391253652"}, {file = "aioshutil-1.5.tar.gz", hash = "sha256:2756d6cd3bb03405dc7348ac11a0b60eb949ebd63cdd15f56e922410231c1201"}, ] [[package]] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.7" files = [ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, ] [package.dependencies] frozenlist = ">=1.1.0" [[package]] name = "aiosqlite" version = "0.20.0" description = "asyncio bridge to the standard sqlite3 module" optional = false python-versions = ">=3.8" files = [ {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, ] [package.dependencies] typing_extensions = ">=4.0" [package.extras] dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] [[package]] name = "alabaster" version = "0.7.16" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.9" files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] name = "annotated-types" version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, ] [[package]] name = "anyio" version = "4.4.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, ] [package.dependencies] idna = ">=2.8" sniffio = ">=1.1" [package.extras] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] trio = ["trio (>=0.23)"] [[package]] name = "asttokens" version = "2.4.1" description = "Annotate AST trees with source code positions" optional = false python-versions = "*" files = [ {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, ] [package.dependencies] six = ">=1.12.0" [package.extras] astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.7" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] name = "attrs" version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "babel" version = "2.15.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "beautifulsoup4" version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] cchardet = ["cchardet"] chardet = ["chardet"] charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] [[package]] name = "bracex" version = "2.4" description = "Bash style brace expander." optional = false python-versions = ">=3.8" files = [ {file = "bracex-2.4-py3-none-any.whl", hash = "sha256:efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418"}, {file = "bracex-2.4.tar.gz", hash = "sha256:a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb"}, ] [[package]] name = "certifi" version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "click" version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "convertertools" version = "0.5.0" description = "Tools for converting python data types" optional = false python-versions = ">=3.10" files = [ {file = "convertertools-0.5.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:b723a3486f6250d5d738a31591776cbb20378177030a29f0e4fda56d7b99e795"}, {file = "convertertools-0.5.0-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:fbf4a533608c525dfbbd9b931e290dd679a74a2d6630f0f2e04d1fa44b33f3b0"}, {file = "convertertools-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8069e4c6bc908b370bfdb9129af5011d40c0704bda265a8f56345001d0478e6b"}, {file = "convertertools-0.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cfea4d24a8e988db79b2213124dafdd3d12dc3209c7d1d4d7205a21631bb8888"}, {file = "convertertools-0.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e89fbb97f5f2945e11da457c5888f50ca006f9930a3d8d3a932091403cbea36b"}, {file = "convertertools-0.5.0-cp311-cp311-win32.whl", hash = "sha256:03e2816ea8bd481e0816d27b55a37ba90c01ae9df105d654a1a0e213c6fe425a"}, {file = "convertertools-0.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:1b7e7dfd3daae6c44e9376297adc9920bd75791ad8de32ebb2f70dcee263e460"}, {file = "convertertools-0.5.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:4a8630056eb0d1250177fec1176b1bf9cf6e92eec7973cb78f238b802cc2003c"}, {file = "convertertools-0.5.0-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:e259aca6b4b9b7a30df25be2d2dafd7985c98fd6ce5f8eb5a43874e5ee1b6016"}, {file = "convertertools-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24fd3d2c71beb77502ceaab9bb99c3d28b18f071e3856e7c39a62c631173fc7e"}, {file = "convertertools-0.5.0-cp312-cp312-manylinux_2_36_x86_64.whl", hash = "sha256:021ff984d888b110040ea86a12ff8d02aa29a6f1b603bf88cd42b990f16e5f4e"}, {file = "convertertools-0.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:723e34b5859c2f7bb1892fe6be71e4d98a89cedd3f9e219b4cb70eb0d94a869a"}, {file = "convertertools-0.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:714dd41ef8bdcba5a9a20c3dc55d604b83b80b603da9ed9ca379893634792eca"}, {file = "convertertools-0.5.0-cp312-cp312-win32.whl", hash = "sha256:cefe9f4be7c600f1e27c1354e8dfd192bcd59f058f99ab93ca1c49abffbae687"}, {file = "convertertools-0.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:4ac2ae852cc6c3e0e3d75fe80276fdecd1889155141364a5c75128c9ff249c29"}, {file = "convertertools-0.5.0.tar.gz", hash = "sha256:477812a307adf368805da5ee2cbc2fe984ed305d76164fe3fbc2a9f9baf4c3f0"}, ] [[package]] name = "coverage" version = "7.5.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"}, {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"}, {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"}, {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"}, {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"}, {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"}, {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"}, {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"}, {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"}, {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"}, {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"}, {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"}, {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"}, {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"}, {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"}, {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"}, {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"}, {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"}, {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"}, {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"}, {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"}, {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"}, {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"}, {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"}, {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"}, {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"}, {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"}, {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"}, {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"}, {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"}, {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"}, {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "dateparser" version = "1.2.0" description = "Date parsing library designed to parse dates from HTML pages" optional = false python-versions = ">=3.7" files = [ {file = "dateparser-1.2.0-py2.py3-none-any.whl", hash = "sha256:0b21ad96534e562920a0083e97fd45fa959882d4162acc358705144520a35830"}, {file = "dateparser-1.2.0.tar.gz", hash = "sha256:7975b43a4222283e0ae15be7b4999d08c9a70e2d378ac87385b1ccf2cffbbb30"}, ] [package.dependencies] python-dateutil = "*" pytz = "*" regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27" tzlocal = "*" [package.extras] calendars = ["convertdate", "hijri-converter"] fasttext = ["fasttext"] langdetect = ["langdetect"] [[package]] name = "docutils" version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] name = "exceptiongroup" version = "1.2.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "execnet" version = "2.1.1" description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, ] [package.extras] testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "frozenlist" version = "1.4.1" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" files = [ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] [[package]] name = "furo" version = "2024.8.6" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.8" files = [ {file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"}, {file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"}, ] [package.dependencies] beautifulsoup4 = "*" pygments = ">=2.7" sphinx = ">=6.0,<9.0" sphinx-basic-ng = ">=1.0.0.beta2" [[package]] name = "ghp-import" version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." optional = false python-versions = "*" files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] [package.dependencies] python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "gitdb" version = "4.0.11" description = "Git Object Database" optional = false python-versions = ">=3.7" files = [ {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, ] [package.dependencies] smmap = ">=3.0.1,<6" [[package]] name = "gitpython" version = "3.1.43" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] [[package]] name = "griffe" version = "1.1.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ {file = "griffe-1.1.0-py3-none-any.whl", hash = "sha256:38ccc5721571c95ae427123074cf0dc0d36bce7c9701ab2ada9fe0566ff50c10"}, {file = "griffe-1.1.0.tar.gz", hash = "sha256:c6328cbdec0d449549c1cc332f59227cd5603f903479d73e4425d828b782ffc3"}, ] [package.dependencies] colorama = ">=0.4" [[package]] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] [[package]] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "8.0.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" version = "6.4.0" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" version = "3.6" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.8" files = [ {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, ] [package.extras] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false python-versions = ">=3.8" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] code-style = ["pre-commit (>=3.0,<4.0)"] compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "mdit-py-plugins" version = "0.4.1" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, ] [package.dependencies] markdown-it-py = ">=1.0.0,<4.0.0" [package.extras] code-style = ["pre-commit"] rtd = ["myst-parser", "sphinx-book-theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" optional = false python-versions = ">=3.7" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "mergedeep" version = "1.3.4" description = "A deep merge function for ðŸ." optional = false python-versions = ">=3.6" files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] [[package]] name = "mike" version = "2.1.3" description = "Manage multiple versions of your MkDocs-powered documentation" optional = false python-versions = "*" files = [ {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, ] [package.dependencies] importlib-metadata = "*" importlib-resources = "*" jinja2 = ">=2.7" mkdocs = ">=1.0" pyparsing = ">=3.0" pyyaml = ">=5.1" pyyaml-env-tag = "*" verspec = "*" [package.extras] dev = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] test = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] [[package]] name = "mkdocs" version = "1.6.0" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, ] [package.dependencies] click = ">=7.0" colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} ghp-import = ">=1.0" jinja2 = ">=2.11.1" markdown = ">=3.3.6" markupsafe = ">=2.0.1" mergedeep = ">=1.3.4" mkdocs-get-deps = ">=0.2.0" packaging = ">=20.5" pathspec = ">=0.11.1" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" watchdog = ">=2.0" [package.extras] i18n = ["babel (>=2.9.0)"] min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] [[package]] name = "mkdocs-autorefs" version = "1.2.0" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, ] [package.dependencies] Markdown = ">=3.3" markupsafe = ">=2.0.1" mkdocs = ">=1.1" [[package]] name = "mkdocs-get-deps" version = "0.2.0" description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, ] [package.dependencies] mergedeep = ">=1.3.4" platformdirs = ">=2.2.0" pyyaml = ">=5.1" [[package]] name = "mkdocs-git-revision-date-localized-plugin" version = "1.2.8" description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_git_revision_date_localized_plugin-1.2.8-py3-none-any.whl", hash = "sha256:c7ec3b1481ca23134269e84927bd8a5dc1aa359c0e515b832dbd5d25019b5748"}, {file = "mkdocs_git_revision_date_localized_plugin-1.2.8.tar.gz", hash = "sha256:6e09c308bb27bcf36b211d17b74152ecc2837cdfc351237f70cffc723ef0fd99"}, ] [package.dependencies] babel = ">=2.7.0" GitPython = "*" mkdocs = ">=1.0" pytz = "*" [package.extras] all = ["GitPython", "babel (>=2.7.0)", "click", "codecov", "mkdocs (>=1.0)", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkdocs-material", "mkdocs-static-i18n", "pytest", "pytest-cov", "pytz"] base = ["GitPython", "babel (>=2.7.0)", "mkdocs (>=1.0)", "pytz"] dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkdocs-material", "mkdocs-static-i18n", "pytest", "pytest-cov"] [[package]] name = "mkdocs-include-markdown-plugin" version = "6.2.2" description = "Mkdocs Markdown includer plugin." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_include_markdown_plugin-6.2.2-py3-none-any.whl", hash = "sha256:d293950f6499d2944291ca7b9bc4a60e652bbfd3e3a42b564f6cceee268694e7"}, {file = "mkdocs_include_markdown_plugin-6.2.2.tar.gz", hash = "sha256:f2bd5026650492a581d2fd44be6c22f90391910d76582b96a34c264f2d17875d"}, ] [package.dependencies] mkdocs = ">=1.4" wcmatch = "*" [package.extras] cache = ["platformdirs"] [[package]] name = "mkdocs-material" version = "9.5.34" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, ] [package.dependencies] babel = ">=2.10,<3.0" colorama = ">=0.4,<1.0" jinja2 = ">=3.0,<4.0" markdown = ">=3.2,<4.0" mkdocs = ">=1.6,<2.0" mkdocs-material-extensions = ">=1.3,<2.0" paginate = ">=0.5,<1.0" pygments = ">=2.16,<3.0" pymdown-extensions = ">=10.2,<11.0" regex = ">=2022.4" requests = ">=2.26,<3.0" [package.extras] git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] [[package]] name = "mkdocs-material-extensions" version = "1.3.1" description = "Extension pack for Python Markdown and MkDocs Material." optional = false python-versions = ">=3.8" files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, ] [[package]] name = "mkdocstrings" version = "0.26.0" description = "Automatic documentation from sources, for MkDocs." optional = false python-versions = ">=3.8" files = [ {file = "mkdocstrings-0.26.0-py3-none-any.whl", hash = "sha256:1aa227fe94f88e80737d37514523aacd473fc4b50a7f6852ce41447ab23f2654"}, {file = "mkdocstrings-0.26.0.tar.gz", hash = "sha256:ff9d0de28c8fa877ed9b29a42fe407cfe6736d70a1c48177aa84fcc3dc8518cd"}, ] [package.dependencies] click = ">=7.0" Jinja2 = ">=2.11.1" Markdown = ">=3.6" MarkupSafe = ">=1.1" mkdocs = ">=1.4" mkdocs-autorefs = ">=1.2" platformdirs = ">=2.2" pymdown-extensions = ">=6.3" [package.extras] crystal = ["mkdocstrings-crystal (>=0.3.4)"] python = ["mkdocstrings-python (>=0.5.2)"] python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" version = "1.11.1" description = "A Python handler for mkdocstrings." optional = false python-versions = ">=3.8" files = [ {file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"}, {file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"}, ] [package.dependencies] griffe = ">=0.49" mkdocs-autorefs = ">=1.2" mkdocstrings = ">=0.26" [[package]] name = "multidict" version = "6.0.5" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] [[package]] name = "mypy" version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] name = "myst-parser" version = "4.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = false python-versions = ">=3.10" files = [ {file = "myst_parser-4.0.0-py3-none-any.whl", hash = "sha256:b9317997552424448c6096c2558872fdb6f81d3ecb3a40ce84a7518798f3f28d"}, {file = "myst_parser-4.0.0.tar.gz", hash = "sha256:851c9dfb44e36e56d15d05e72f02b80da21a9e0d07cba96baf5e2d476bb91531"}, ] [package.dependencies] docutils = ">=0.19,<0.22" jinja2 = "*" markdown-it-py = ">=3.0,<4.0" mdit-py-plugins = ">=0.4.1,<1.0" pyyaml = "*" sphinx = ">=7,<9" [package.extras] code-style = ["pre-commit (>=3.0,<4.0)"] linkify = ["linkify-it-py (>=2.0,<3.0)"] rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] [[package]] name = "orjson" version = "3.10.7" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, ] [[package]] name = "packaging" version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] name = "paginate" version = "0.5.6" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" files = [ {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, ] [[package]] name = "pathspec" version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "pillow" version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] typing = ["typing-extensions"] xmp = ["defusedxml"] [[package]] name = "platformdirs" version = "4.3.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, ] [package.extras] docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" optional = false python-versions = "*" files = [ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] [[package]] name = "pydantic" version = "2.9.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, ] [package.dependencies] annotated-types = ">=0.6.0" pydantic-core = "2.23.3" typing-extensions = [ {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, ] [package.extras] email = ["email-validator (>=2.0.0)"] timezone = ["tzdata"] [[package]] name = "pydantic-core" version = "2.23.3" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, ] [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pygments" version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" version = "2.9.0" description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.8" files = [ {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, ] [package.extras] crypto = ["cryptography (>=3.4.0)"] dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymdown-extensions" version = "10.9" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, ] [package.dependencies] markdown = ">=3.6" pyyaml = "*" [package.extras] extra = ["pygments (>=2.12)"] [[package]] name = "pyparsing" version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [package.extras] diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" version = "0.24.0" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, ] [package.dependencies] pytest = ">=8.2,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "pytest-benchmark" version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." optional = false python-versions = ">=3.7" files = [ {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, ] [package.dependencies] py-cpuinfo = "*" pytest = ">=3.8" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-sugar" version = "1.0.0" description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)." optional = false python-versions = "*" files = [ {file = "pytest-sugar-1.0.0.tar.gz", hash = "sha256:6422e83258f5b0c04ce7c632176c7732cab5fdb909cb39cca5c9139f81276c0a"}, {file = "pytest_sugar-1.0.0-py3-none-any.whl", hash = "sha256:70ebcd8fc5795dc457ff8b69d266a4e2e8a74ae0c3edc749381c64b5246c8dfd"}, ] [package.dependencies] packaging = ">=21.3" pytest = ">=6.2.0" termcolor = ">=2.1.0" [package.extras] dev = ["black", "flake8", "pre-commit"] [[package]] name = "pytest-timeout" version = "2.3.1" description = "pytest plugin to abort hanging tests" optional = false python-versions = ">=3.7" files = [ {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, ] [package.dependencies] pytest = ">=7.0.0" [[package]] name = "pytest-xdist" version = "3.6.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false python-versions = ">=3.8" files = [ {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, ] [package.dependencies] execnet = ">=2.1" pytest = ">=7.0.0" [package.extras] psutil = ["psutil (>=3.0)"] setproctitle = ["setproctitle"] testing = ["filelock"] [[package]] name = "python-dateutil" version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] six = ">=1.5" [[package]] name = "pytz" version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "pyyaml-env-tag" version = "0.1" description = "A custom YAML tag for referencing environment variables in YAML files. " optional = false python-versions = ">=3.6" files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] [package.dependencies] pyyaml = "*" [[package]] name = "regex" version = "2024.5.15" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f"}, {file = "regex-2024.5.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6"}, {file = "regex-2024.5.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796"}, {file = "regex-2024.5.15-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62"}, {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0"}, {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143"}, {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f"}, {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f"}, {file = "regex-2024.5.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53"}, {file = "regex-2024.5.15-cp310-cp310-win32.whl", hash = "sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3"}, {file = "regex-2024.5.15-cp310-cp310-win_amd64.whl", hash = "sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145"}, {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a"}, {file = "regex-2024.5.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656"}, {file = "regex-2024.5.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f"}, {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35"}, {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d"}, {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb"}, {file = "regex-2024.5.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f"}, {file = "regex-2024.5.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40"}, {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649"}, {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c"}, {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890"}, {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d"}, {file = "regex-2024.5.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68"}, {file = "regex-2024.5.15-cp311-cp311-win32.whl", hash = "sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa"}, {file = "regex-2024.5.15-cp311-cp311-win_amd64.whl", hash = "sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201"}, {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014"}, {file = "regex-2024.5.15-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e"}, {file = "regex-2024.5.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49"}, {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a"}, {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b"}, {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a"}, {file = "regex-2024.5.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf"}, {file = "regex-2024.5.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2"}, {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5"}, {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5"}, {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e"}, {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d"}, {file = "regex-2024.5.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80"}, {file = "regex-2024.5.15-cp312-cp312-win32.whl", hash = "sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe"}, {file = "regex-2024.5.15-cp312-cp312-win_amd64.whl", hash = "sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2"}, {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835"}, {file = "regex-2024.5.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850"}, {file = "regex-2024.5.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa"}, {file = "regex-2024.5.15-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed"}, {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced"}, {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384"}, {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f"}, {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67"}, {file = "regex-2024.5.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741"}, {file = "regex-2024.5.15-cp38-cp38-win32.whl", hash = "sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9"}, {file = "regex-2024.5.15-cp38-cp38-win_amd64.whl", hash = "sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569"}, {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133"}, {file = "regex-2024.5.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1"}, {file = "regex-2024.5.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c"}, {file = "regex-2024.5.15-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294"}, {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629"}, {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16"}, {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa"}, {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d"}, {file = "regex-2024.5.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456"}, {file = "regex-2024.5.15-cp39-cp39-win32.whl", hash = "sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694"}, {file = "regex-2024.5.15-cp39-cp39-win_amd64.whl", hash = "sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388"}, {file = "regex-2024.5.15.tar.gz", hash = "sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c"}, ] [[package]] name = "requests" version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "shellingham" version = "1.5.4" description = "Tool to Detect Surrounding Shell" optional = false python-versions = ">=3.7" files = [ {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, ] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "smmap" version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" optional = false python-versions = ">=3.7" files = [ {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, ] [[package]] name = "sniffio" version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "soupsieve" version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] [[package]] name = "sphinx" version = "8.0.2" description = "Python documentation generator" optional = false python-versions = ">=3.10" files = [ {file = "sphinx-8.0.2-py3-none-any.whl", hash = "sha256:56173572ae6c1b9a38911786e206a110c9749116745873feae4f9ce88e59391d"}, {file = "sphinx-8.0.2.tar.gz", hash = "sha256:0cce1ddcc4fd3532cf1dd283bc7d886758362c5c1de6598696579ce96d8ffa5b"}, ] [package.dependencies] alabaster = ">=0.7.14" babel = ">=2.13" colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} docutils = ">=0.20,<0.22" imagesize = ">=1.3" Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" snowballstemmer = ">=2.2" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=6.0)", "mypy (==1.11.0)", "pytest (>=6.0)", "ruff (==0.5.5)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240520)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20240724)", "types-requests (>=2.30.0)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-autobuild" version = "2024.9.3" description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser." optional = false python-versions = ">=3.9" files = [ {file = "sphinx_autobuild-2024.9.3-py3-none-any.whl", hash = "sha256:55fe9bcc05dab659650d79bed0e6beb8b6032234edbf23f028f2cac3471f0c2d"}, {file = "sphinx_autobuild-2024.9.3.tar.gz", hash = "sha256:75929a5a92b932da8d29837406d6d973a927c456f30986a27f1f20b067897892"}, ] [package.dependencies] colorama = ">=0.4.6" sphinx = "*" starlette = ">=0.35" uvicorn = ">=0.25" watchfiles = ">=0.20" websockets = ">=11" [package.extras] test = ["httpx", "pytest (>=6)"] [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." optional = false python-versions = ">=3.7" files = [ {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, ] [package.dependencies] sphinx = ">=4.0" [package.extras] docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" files = [ {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "starlette" version = "0.37.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, ] [package.dependencies] anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] [[package]] name = "termcolor" version = "2.4.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.8" files = [ {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, ] [package.extras] tests = ["pytest", "pytest-cov"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "typer" version = "0.12.5" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, ] [package.dependencies] click = ">=8.0.0" rich = ">=10.11.0" shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" [[package]] name = "types-aiofiles" version = "24.1.0.20240626" description = "Typing stubs for aiofiles" optional = false python-versions = ">=3.8" files = [ {file = "types-aiofiles-24.1.0.20240626.tar.gz", hash = "sha256:48604663e24bc2d5038eac05ccc33e75799b0779e93e13d6a8f711ddc306ac08"}, {file = "types_aiofiles-24.1.0.20240626-py3-none-any.whl", hash = "sha256:7939eca4a8b4f9c6491b6e8ef160caee9a21d32e18534a57d5ed90aee47c66b4"}, ] [[package]] name = "types-dateparser" version = "1.2.0.20240420" description = "Typing stubs for dateparser" optional = false python-versions = ">=3.8" files = [ {file = "types-dateparser-1.2.0.20240420.tar.gz", hash = "sha256:8f813ddf5ef41b32cabe6167138ae833ada10c22811e42220a1e38a0be7adbdc"}, {file = "types_dateparser-1.2.0.20240420-py3-none-any.whl", hash = "sha256:bf3695ddfbadfdfc875064895a51d926fd80b04da1a44364c6c1a9703db7b194"}, ] [[package]] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "tzdata" version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "tzlocal" version = "5.2" description = "tzinfo object for the local timezone" optional = false python-versions = ">=3.8" files = [ {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, ] [package.dependencies] tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] [[package]] name = "urllib3" version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" version = "0.30.1" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, ] [package.dependencies] click = ">=7.0" h11 = ">=0.8" [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "verspec" version = "0.1.0" description = "Flexible version handling" optional = false python-versions = "*" files = [ {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, ] [package.extras] test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] [[package]] name = "watchdog" version = "4.0.1" description = "Filesystem events monitoring" optional = false python-versions = ">=3.8" files = [ {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645"}, {file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b"}, {file = "watchdog-4.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b"}, {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5"}, {file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767"}, {file = "watchdog-4.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459"}, {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175"}, {file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7"}, {file = "watchdog-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28"}, {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35"}, {file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db"}, {file = "watchdog-4.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709"}, {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba"}, {file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235"}, {file = "watchdog-4.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682"}, {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7"}, {file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5"}, {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193"}, {file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625"}, {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd"}, {file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"}, {file = "watchdog-4.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84"}, {file = "watchdog-4.0.1-py3-none-win32.whl", hash = "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429"}, {file = "watchdog-4.0.1-py3-none-win_amd64.whl", hash = "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a"}, {file = "watchdog-4.0.1-py3-none-win_ia64.whl", hash = "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d"}, {file = "watchdog-4.0.1.tar.gz", hash = "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44"}, ] [package.extras] watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "watchfiles" version = "0.22.0" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.8" files = [ {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, ] [package.dependencies] anyio = ">=3.0.0" [[package]] name = "wcmatch" version = "8.5.2" description = "Wildcard/glob file name matcher." optional = false python-versions = ">=3.8" files = [ {file = "wcmatch-8.5.2-py3-none-any.whl", hash = "sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478"}, {file = "wcmatch-8.5.2.tar.gz", hash = "sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2"}, ] [package.dependencies] bracex = ">=2.1.1" [[package]] name = "websockets" version = "12.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" files = [ {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, ] [[package]] name = "yarl" version = "1.11.1" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" [[package]] name = "zipp" version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.10" content-hash = "4f3a5906119cb6fb374d14db3d452001d57fe579b077b418bf33c3be7226f5e3" uiprotect-6.1.0/pyproject.toml000066400000000000000000000130711467310220200164470ustar00rootroot00000000000000[tool.poetry] name = "uiprotect" version = "6.1.0" description = "Python API for Unifi Protect (Unofficial)" authors = ["UI Protect Maintainers "] readme = "README.md" repository = "https://github.com/uilibs/uiprotect" documentation = "https://uiprotect.readthedocs.io" classifiers = [ "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Build Tools", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: MIT License" ] packages = [ { include = "uiprotect", from = "src" }, ] [tool.poetry.urls] "Bug Tracker" = "https://github.com/uilibs/uiprotect/issues" "Changelog" = "https://github.com/uilibs/uiprotect/blob/main/CHANGELOG.md" [tool.poetry.scripts] uiprotect = "uiprotect.cli:app" [tool.poetry.dependencies] python = ">=3.10" rich = ">=10" async-timeout = ">=3.0.1" aiofiles = ">=24" aiohttp = ">=3.10.0" aioshutil = ">=1.3" dateparser = ">=1.1.0" orjson = ">=3.9.15" packaging = ">=23" pillow = ">=10" platformdirs = ">=4" pydantic = "!=1.9.1,>=1.10.17" pyjwt = ">=2.6" yarl = ">=1.9" typer = ">=0.12.3" convertertools = ">=0.5.0" [tool.poetry.group.dev.dependencies] pytest = ">=7,<9" pytest-cov = ">=3,<6" aiosqlite = ">=0.20.0" asttokens = "^2.4.1" pytest-asyncio = ">=0.23.7,<0.25.0" pytest-benchmark = "^4.0.0" pytest-sugar = "^1.0.0" pytest-timeout = "^2.3.1" pytest-xdist = "^3.6.1" types-aiofiles = ">=23.2.0.20240403,<25.0.0.0" types-dateparser = "^1.2.0.20240420" mypy = "^1.10.0" [tool.poetry.group.docs] optional = true [tool.poetry.group.docs.dependencies] myst-parser = { version = ">=0.16", python = ">=3.11"} sphinx = { version = ">=4.0", python = ">=3.11"} furo = { version = ">=2023.5.20", python = ">=3.11"} sphinx-autobuild = { version = ">=2024.0.0", python = ">=3.11"} mike = "^2.1.1" mkdocs-material = "^9.5.26" mkdocs-material-extensions = "^1.3.1" pymdown-extensions = "^10.8.1" mkdocs-git-revision-date-localized-plugin = "^1.2.6" mkdocs-include-markdown-plugin = "^6.1.1" mkdocstrings = ">=0.25.1,<0.27.0" mkdocstrings-python = "^1.10.3" [tool.semantic_release] version_toml = ["pyproject.toml:tool.poetry.version"] version_variables = [ "src/uiprotect/__init__.py:__version__", "docs/conf.py:release", ] build_command = "pip install poetry && poetry build" [tool.semantic_release.changelog] exclude_commit_patterns = [ "chore*", "ci*", ] [tool.semantic_release.changelog.environment] keep_trailing_newline = true [tool.semantic_release.branches.main] match = "main" [tool.semantic_release.branches.noop] match = "(?!main$)" prerelease = true [tool.pytest.ini_options] addopts = "-v -Wdefault --cov=uiprotect --cov-report=term-missing:skip-covered -n=auto" pythonpath = ["src"] [tool.coverage.run] branch = true [tool.coverage.report] exclude_lines = [ "pragma: no cover", "@overload", "if TYPE_CHECKING", "raise NotImplementedError", 'if __name__ == "__main__":', ] [tool.ruff] target-version = "py310" line-length = 88 [tool.ruff.lint] ignore = [ "S101", # use of assert "D203", # 1 blank line required before class docstring "D212", # Multi-line docstring summary should start at the first line "D100", # Missing docstring in public module "D101", # Missing docstring in public module "D102", # Missing docstring in public method "D103", # Missing docstring in public module "D104", # Missing docstring in public package "D105", # Missing docstring in magic method "D107", # Missing docstring in `__init__` "D400", # First line should end with a period "D401", # First line of docstring should be in imperative mood "D205", # 1 blank line required between summary line and description "D415", # First line should end with a period, question mark, or exclamation point "D417", # Missing argument descriptions in the docstring "E501", # Line too long "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "B008", # Do not perform function call "S110", # `try`-`except`-`pass` detected, consider logging the exception "D106", # Missing docstring in public nested class "UP007", # typer needs Optional syntax "UP038", # Use `X | Y` in `isinstance` is slower "S603", # check for execution of untrusted input ] select = [ "B", # flake8-bugbear "D", # flake8-docstrings "C4", # flake8-comprehensions "S", # flake8-bandit "F", # pyflake "E", # pycodestyle "W", # pycodestyle "UP", # pyupgrade "I", # isort "RUF", # ruff specific ] [tool.ruff.lint.per-file-ignores] "tests/**/*" = [ "D100", "D101", "D102", "D103", "D104", "S101", ] "setup.py" = ["D100"] "conftest.py" = ["D100"] "docs/conf.py" = ["D100"] [tool.ruff.isort] known-first-party = ["uiprotect", "tests"] [tool.mypy] disable_error_code = "import-untyped,unused-ignore" check_untyped_defs = true ignore_missing_imports = true disallow_any_generics = true disallow_incomplete_defs = true disallow_untyped_defs = true mypy_path = "src/" no_implicit_optional = true show_error_codes = true warn_unreachable = true warn_unused_ignores = true exclude = [ 'docs/.*', 'setup.py', ] [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [[tool.mypy.overrides]] module = "docs.*" ignore_errors = true [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" uiprotect-6.1.0/renovate.json000066400000000000000000000001011467310220200162370ustar00rootroot00000000000000{ "extends": ["github>browniebroke/renovate-configs:python"] } uiprotect-6.1.0/setup.py000066400000000000000000000003571467310220200152500ustar00rootroot00000000000000#!/usr/bin/env python # This is a shim to allow GitHub to detect the package, build is done with poetry # Taken from https://github.com/Textualize/rich import setuptools if __name__ == "__main__": setuptools.setup(name="uiprotect") uiprotect-6.1.0/src/000077500000000000000000000000001467310220200143205ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/000077500000000000000000000000001467310220200163365ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/__init__.py000066400000000000000000000011741467310220200204520ustar00rootroot00000000000000"""Unofficial UniFi Protect Python API and Command Line Interface.""" from __future__ import annotations from .api import ProtectApiClient from .exceptions import Invalid, NotAuthorized, NvrError from .utils import ( get_nested_attr, get_nested_attr_as_bool, get_top_level_attr_as_bool, make_enabled_getter, make_required_getter, make_value_getter, ) __all__ = [ "Invalid", "NotAuthorized", "NvrError", "ProtectApiClient", "get_nested_attr", "get_nested_attr_as_bool", "get_top_level_attr_as_bool", "make_value_getter", "make_enabled_getter", "make_required_getter", ] uiprotect-6.1.0/src/uiprotect/__main__.py000066400000000000000000000007161467310220200204340ustar00rootroot00000000000000from __future__ import annotations import os try: from dotenv import load_dotenv except ImportError: load_dotenv = None # type: ignore[assignment] from .cli import app def start() -> None: if load_dotenv is not None: env_file = os.path.join(os.getcwd(), ".env") if os.path.exists(env_file): load_dotenv(dotenv_path=env_file) else: load_dotenv() app() if __name__ == "__main__": start() uiprotect-6.1.0/src/uiprotect/api.py000066400000000000000000002045361467310220200174730ustar00rootroot00000000000000"""UniFi Protect Server Wrapper.""" from __future__ import annotations import asyncio import contextlib import hashlib import logging import re import sys import time from collections.abc import Callable from datetime import datetime, timedelta from functools import cached_property, partial from http import HTTPStatus from http.cookies import Morsel, SimpleCookie from ipaddress import IPv4Address, IPv6Address from pathlib import Path from typing import Any, Literal, cast from urllib.parse import SplitResult import aiofiles import aiohttp import orjson from aiofiles import os as aos from aiohttp import CookieJar, client_exceptions from platformdirs import user_cache_dir, user_config_dir from yarl import URL from .data import ( NVR, Bootstrap, Bridge, Camera, Doorlock, Event, EventCategories, EventType, Light, Liveview, ModelType, ProtectAdoptableDeviceModel, ProtectModel, PTZPosition, PTZPreset, Sensor, SmartDetectObjectType, SmartDetectTrack, Version, Viewer, WSPacket, WSSubscriptionMessage, create_from_unifi_dict, ) from .data.base import ProtectModelWithId from .data.devices import Chime from .data.types import IteratorCallback, ProgressCallback from .exceptions import BadRequest, NotAuthorized, NvrError from .utils import ( decode_token_cookie, get_response_reason, ip_from_host, set_debug, to_js_time, utc_now, ) from .websocket import Websocket, WebsocketState if sys.version_info[:2] < (3, 13): from http import cookies # See: https://github.com/python/cpython/issues/112713 cookies.Morsel._reserved["partitioned"] = "partitioned" # type: ignore[attr-defined] cookies.Morsel._flags.add("partitioned") # type: ignore[attr-defined] TOKEN_COOKIE_MAX_EXP_SECONDS = 60 # how many seconds before the bootstrap is refreshed from Protect DEVICE_UPDATE_INTERVAL = 900 # retry timeout for thumbnails/heatmaps RETRY_TIMEOUT = 10 PROTECT_APT_URLS = [ "https://apt.artifacts.ui.com/dists/stretch/release/binary-arm64/Packages", "https://apt.artifacts.ui.com/dists/bullseye/release/binary-arm64/Packages", ] TYPES_BUG_MESSAGE = """There is currently a bug in UniFi Protect that makes `start` / `end` not work if `types` is not provided. This means uiprotect has to iterate over all of the events matching the filters provided to return values. If your Protect instance has a lot of events, this request will take much longer then expected. It is recommended adding additional filters to speed the request up.""" _LOGGER = logging.getLogger(__name__) _COOKIE_RE = re.compile(r"^set-cookie: ", re.IGNORECASE) # TODO: Urls to still support # Backups # * GET /backups - list backends # * POST /backups/import - import backup # * POST /backups - create backup # * GET /backups/{id} - download backup # * POST /backups/{id}/restore - restore backup # * DELETE /backups/{id} - delete backup # # Cameras # * POST /cameras/{id}/reset - factory reset camera # * POST /cameras/{id}/reset-isp - reset ISP settings # * POST /cameras/{id}/reset-isp - reset ISP settings # * POST /cameras/{id}/wake - battery powered cameras # * POST /cameras/{id}/sleep # * POST /cameras/{id}/homekit-talkback-speaker-muted # * GET /cameras/{id}/live-heatmap - add live heatmap to WebRTC stream # * GET /cameras/{id}/enable-control - PTZ controls # * GET /cameras/{id}/disable-control # * POST /cameras/{id}/move # * POST /cameras/{id}/ptz/position # * GET|POST /cameras/{id}/ptz/preset # * GET /cameras/{id}/ptz/snapshot # * POST /cameras/{id}/ptz/goto # * GET /cameras/{id}/analytics-heatmap - analytics # * GET /cameras/{id}/analytics-detections # * GET /cameras/{id}/wifi-list - WiFi scan # * POST /cameras/{id}/wifi-setup - Change WiFi settings # * GET /cameras/{id}/playback-history # * GET|POST|DELETE /cameras/{id}/sharedStream - stream sharing, unfinished? # # Device Groups # * GET|POST|PUT|DELETE /device-groups # * GET|PATCH|DELETE /device-groups/{id} # * PATCH /device-groups/{id}/items # # Events # POST /events/{id}/animated-thumbnail # # Lights # POST /lights/{id}/locate # # NVR # GET|PATCH /nvr/device-password # # Schedules # GET|POST /recordingSchedules # PATCH|DELETE /recordingSchedules/{id} # # Sensors # POST /sensors/{id}/locate # # Timeline # GET /timeline def get_user_hash(host: str, username: str) -> str: session = hashlib.sha256() session.update(host.encode("utf8")) session.update(username.encode("utf8")) return session.hexdigest() class BaseApiClient: _host: str _port: int _username: str _password: str _verify_ssl: bool _ws_timeout: int _is_authenticated: bool = False _last_token_cookie: Morsel[str] | None = None _last_token_cookie_decode: dict[str, Any] | None = None _session: aiohttp.ClientSession | None = None _loaded_session: bool = False _cookiename = "TOKEN" headers: dict[str, str] | None = None _websocket: Websocket | None = None api_path: str = "/proxy/protect/api/" ws_path: str = "/proxy/protect/ws/updates" cache_dir: Path config_dir: Path store_sessions: bool def __init__( self, host: str, port: int, username: str, password: str, verify_ssl: bool = True, session: aiohttp.ClientSession | None = None, ws_timeout: int = 30, cache_dir: Path | None = None, config_dir: Path | None = None, store_sessions: bool = True, ws_receive_timeout: int | None = None, ) -> None: self._auth_lock = asyncio.Lock() self._host = host self._port = port self._username = username self._password = password self._verify_ssl = verify_ssl self._ws_timeout = ws_timeout self._ws_receive_timeout = ws_receive_timeout self._loaded_session = False self._update_task: asyncio.Task[Bootstrap | None] | None = None self.config_dir = config_dir or (Path(user_config_dir()) / "ufp") self.cache_dir = cache_dir or (Path(user_cache_dir()) / "ufp_cache") self.store_sessions = store_sessions if session is not None: self._session = session self._update_url() def _update_cookiename(self, cookie: SimpleCookie) -> None: if "UOS_TOKEN" in cookie: self._cookiename = "UOS_TOKEN" def _update_url(self) -> None: """Updates the url after changing _host or _port.""" if self._port != 443: self._url = URL(f"https://{self._host}:{self._port}") self._ws_url = URL(f"wss://{self._host}:{self._port}{self.ws_path}") else: self._url = URL(f"https://{self._host}") self._ws_url = URL(f"wss://{self._host}{self.ws_path}") self.base_url = str(self._url) @property def _ws_url_object(self) -> URL: """Get Websocket URL.""" if last_update_id := self._get_last_update_id(): return self._ws_url.with_query(lastUpdateId=last_update_id) return self._ws_url @property def ws_url(self) -> str: """Get Websocket URL.""" return str(self._ws_url_object) @property def config_file(self) -> Path: return self.config_dir / "unifi_protect.json" async def get_session(self) -> aiohttp.ClientSession: """Gets or creates current client session""" if self._session is None or self._session.closed: if self._session is not None and self._session.closed: _LOGGER.debug("Session was closed, creating a new one") # need unsafe to access httponly cookies self._session = aiohttp.ClientSession(cookie_jar=CookieJar(unsafe=True)) return self._session async def _auth_websocket(self, force: bool) -> dict[str, str] | None: """Authenticate for Websocket.""" if force: if self._session is not None: self._session.cookie_jar.clear() self.set_header("cookie", None) self.set_header("x-csrf-token", None) self._is_authenticated = False await self.ensure_authenticated() return self.headers def _get_websocket(self) -> Websocket: """Gets or creates current Websocket.""" if self._websocket is None: self._websocket = Websocket( self._get_websocket_url, self._auth_websocket, self._update_bootstrap_soon, self.get_session, self._process_ws_message, self._on_websocket_state_change, verify=self._verify_ssl, timeout=self._ws_timeout, receive_timeout=self._ws_receive_timeout, ) return self._websocket def _update_bootstrap_soon(self) -> None: """Update bootstrap soon.""" _LOGGER.debug("Updating bootstrap soon") # Force the next bootstrap update # since the lastUpdateId is not valid anymore if self._update_task and not self._update_task.done(): return self._update_task = asyncio.create_task(self.update()) async def close_session(self) -> None: """Closing and deletes client session""" await self._cancel_update_task() if self._session is not None: await self._session.close() self._session = None self._loaded_session = False async def _cancel_update_task(self) -> None: if self._update_task: self._update_task.cancel() with contextlib.suppress(asyncio.CancelledError): await self._update_task self._update_task = None def set_header(self, key: str, value: str | None) -> None: """Set header.""" self.headers = self.headers or {} if value is None: self.headers.pop(key, None) else: self.headers[key] = value async def request( self, method: str, url: str, require_auth: bool = False, auto_close: bool = True, **kwargs: Any, ) -> aiohttp.ClientResponse: """Make a request to UniFi Protect""" if require_auth: await self.ensure_authenticated() request_url = self._url.join( URL(SplitResult("", "", url, "", ""), encoded=True) ) headers = kwargs.get("headers") or self.headers _LOGGER.debug("Request url: %s", request_url) if not self._verify_ssl: kwargs["ssl"] = False session = await self.get_session() for attempt in range(2): try: req_context = session.request( method, request_url, headers=headers, **kwargs, ) response = await req_context.__aenter__() await self._update_last_token_cookie(response) if auto_close: try: _LOGGER.debug( "%s %s %s", response.status, response.content_type, response, ) response.release() except Exception: # make sure response is released response.release() # re-raise exception raise return response except aiohttp.ServerDisconnectedError as err: # If the server disconnected, try again # since HTTP/1.1 allows the server to disconnect # at any time if attempt == 0: continue raise NvrError( f"Error requesting data from {self._host}: {err}", ) from err except client_exceptions.ClientError as err: raise NvrError( f"Error requesting data from {self._host}: {err}", ) from err # should never happen raise NvrError(f"Error requesting data from {self._host}") async def api_request_raw( self, url: str, method: str = "get", require_auth: bool = True, raise_exception: bool = True, **kwargs: Any, ) -> bytes | None: """Make a request to UniFi Protect API""" response = await self.request( method, f"{self.api_path}{url}", require_auth=require_auth, auto_close=False, **kwargs, ) try: if response.status != 200: await self._raise_for_status(response, raise_exception) return None data: bytes | None = await response.read() response.release() return data except Exception: # make sure response is released response.release() # re-raise exception raise async def _raise_for_status( self, response: aiohttp.ClientResponse, raise_exception: bool = True ) -> None: """Raise an exception based on the response status.""" url = response.url reason = await get_response_reason(response) msg = "Request failed: %s - Status: %s - Reason: %s" status = response.status if raise_exception: if status in { HTTPStatus.UNAUTHORIZED.value, HTTPStatus.FORBIDDEN.value, }: raise NotAuthorized(msg % (url, status, reason)) elif status == HTTPStatus.TOO_MANY_REQUESTS.value: _LOGGER.debug("Too many requests - Login is rate limited: %s", response) raise NvrError(msg % (url, status, reason)) elif ( status >= HTTPStatus.BAD_REQUEST.value and status < HTTPStatus.INTERNAL_SERVER_ERROR.value ): raise BadRequest(msg % (url, status, reason)) raise NvrError(msg % (url, status, reason)) _LOGGER.debug(msg, url, status, reason) async def api_request( self, url: str, method: str = "get", require_auth: bool = True, raise_exception: bool = True, **kwargs: Any, ) -> list[Any] | dict[str, Any] | None: data = await self.api_request_raw( url=url, method=method, require_auth=require_auth, raise_exception=raise_exception, **kwargs, ) if data is not None: json_data: list[Any] | dict[str, Any] try: json_data = orjson.loads(data) return json_data except orjson.JSONDecodeError as ex: _LOGGER.error("Could not decode JSON from %s", url) raise NvrError(f"Could not decode JSON from {url}") from ex return None async def api_request_obj( self, url: str, method: str = "get", require_auth: bool = True, raise_exception: bool = True, **kwargs: Any, ) -> dict[str, Any]: data = await self.api_request( url=url, method=method, require_auth=require_auth, raise_exception=raise_exception, **kwargs, ) if not isinstance(data, dict): raise NvrError(f"Could not decode object from {url}") return data async def api_request_list( self, url: str, method: str = "get", require_auth: bool = True, raise_exception: bool = True, **kwargs: Any, ) -> list[Any]: data = await self.api_request( url=url, method=method, require_auth=require_auth, raise_exception=raise_exception, **kwargs, ) if not isinstance(data, list): raise NvrError(f"Could not decode list from {url}") return data async def ensure_authenticated(self) -> None: """Ensure we are authenticated.""" await self._load_session() if self.is_authenticated() is False: await self.authenticate() async def authenticate(self) -> None: """Authenticate and get a token.""" if self._auth_lock.locked(): # If an auth is already in progress # do not start another one async with self._auth_lock: return async with self._auth_lock: url = "/api/auth/login" if self._session is not None: self._session.cookie_jar.clear() self.set_header("cookie", None) auth = { "username": self._username, "password": self._password, "rememberMe": self.store_sessions, } response = await self.request("post", url=url, json=auth) if response.status != 200: await self._raise_for_status(response, True) self.set_header("cookie", response.headers.get("set-cookie", "")) self._is_authenticated = True _LOGGER.debug("Authenticated successfully!") async def _update_last_token_cookie(self, response: aiohttp.ClientResponse) -> None: """Update the last token cookie.""" csrf_token = response.headers.get("x-csrf-token") if ( csrf_token is not None and self.headers and csrf_token != self.headers.get("x-csrf-token") ): self.set_header("x-csrf-token", csrf_token) await self._update_last_token_cookie(response) self._update_cookiename(response.cookies) if ( token_cookie := response.cookies.get(self._cookiename) ) and token_cookie != self._last_token_cookie: self._last_token_cookie = token_cookie if self.store_sessions: await self._update_auth_config(self._last_token_cookie) self._last_token_cookie_decode = None async def _update_auth_config(self, cookie: Morsel[str]) -> None: """Updates auth cookie on disk for persistent sessions.""" if self._last_token_cookie is None: return await aos.makedirs(self.config_dir, exist_ok=True) config: dict[str, Any] = {} session_hash = get_user_hash(str(self._url), self._username) try: async with aiofiles.open(self.config_file, "rb") as f: config_data = await f.read() if config_data: try: config = orjson.loads(config_data) except Exception: _LOGGER.warning("Invalid config file, ignoring.") except FileNotFoundError: pass config["sessions"] = config.get("sessions", {}) config["sessions"][session_hash] = { "metadata": dict(cookie), "cookiename": self._cookiename, "value": cookie.value, "csrf": self.headers.get("x-csrf-token") if self.headers else None, } async with aiofiles.open(self.config_file, "wb") as f: await f.write(orjson.dumps(config, option=orjson.OPT_INDENT_2)) async def _load_session(self) -> None: if self._session is None: await self.get_session() assert self._session is not None if not self._loaded_session and self.store_sessions: session_cookie = await self._read_auth_config() self._loaded_session = True if session_cookie: _LOGGER.debug("Successfully loaded session from config") self._session.cookie_jar.update_cookies(session_cookie) async def _read_auth_config(self) -> SimpleCookie | None: """Read auth cookie from config.""" try: async with aiofiles.open(self.config_file, "rb") as f: config_data = await f.read() if config_data: try: config = orjson.loads(config_data) except Exception: _LOGGER.warning("Invalid config file, ignoring.") return None except FileNotFoundError: _LOGGER.debug("no config file, not loading session") return None session_hash = get_user_hash(str(self._url), self._username) session = config.get("sessions", {}).get(session_hash) if not session: _LOGGER.debug("No existing session for %s", session_hash) return None cookie = SimpleCookie() cookie_name = session.get("cookiename") if cookie_name is None: return None cookie[cookie_name] = session.get("value") for key, value in session.get("metadata", {}).items(): cookie[cookie_name][key] = value cookie_value = _COOKIE_RE.sub("", str(cookie[cookie_name])) self._last_token_cookie = cookie[cookie_name] self._last_token_cookie_decode = None self._is_authenticated = True self.set_header("cookie", cookie_value) if session.get("csrf"): self.set_header("x-csrf-token", session["csrf"]) return cookie def is_authenticated(self) -> bool: """Check to see if we are already authenticated.""" if self._session is None: return False if self._is_authenticated is False: return False if self._last_token_cookie is None: return False # Lazy decode the token cookie if self._last_token_cookie and self._last_token_cookie_decode is None: self._last_token_cookie_decode = decode_token_cookie( self._last_token_cookie, ) if ( self._last_token_cookie_decode is None or "exp" not in self._last_token_cookie_decode ): return False token_expires_at = cast(int, self._last_token_cookie_decode["exp"]) max_expire_time = time.time() + TOKEN_COOKIE_MAX_EXP_SECONDS return token_expires_at >= max_expire_time def _get_websocket_url(self) -> URL: """Get Websocket URL.""" return self._ws_url_object async def async_disconnect_ws(self) -> None: """Disconnect from Websocket.""" if self._websocket: websocket = self._get_websocket() websocket.stop() await websocket.wait_closed() self._websocket = None def _process_ws_message(self, msg: aiohttp.WSMessage) -> None: raise NotImplementedError def _get_last_update_id(self) -> str | None: raise NotImplementedError async def update(self) -> Bootstrap: raise NotImplementedError def _on_websocket_state_change(self, state: WebsocketState) -> None: """Websocket state changed.""" _LOGGER.debug("Websocket state changed: %s", state) class ProtectApiClient(BaseApiClient): """ Main UFP API Client UniFi Protect is a full async application. "normal" use of interacting with it is to call `.update()` which will initialize the `.bootstrap` and create a Websocket connection to UFP. This Websocket connection will emit messages that will automatically update the `.bootstrap` over time. You can use the `.get_` methods to one off pull devices from the UFP API, but should not be used for building an aplication on top of. All objects inside of `.bootstrap` have a refernce back to the API client so they can use `.save_device()` and update themselves using their own `.set_` methods on the object. Args: ---- host: UFP hostname / IP address port: UFP HTTPS port username: UFP username password: UFP password verify_ssl: Verify HTTPS certificate (default: `True`) session: Optional aiohttp session to use (default: generate one) override_connection_host: Use `host` as your `connection_host` for RTSP stream instead of using the one provided by UniFi Protect. minimum_score: minimum score for events (default: `0`) subscribed_models: Model types you want to filter events for WS. You will need to manually check the bootstrap for updates for events that not subscibred. ignore_stats: Ignore storage, system, etc. stats/metrics from NVR and cameras (default: false) debug: Use full type validation (default: false) """ _minimum_score: int _subscribed_models: set[ModelType] _ignore_stats: bool _ws_subscriptions: list[Callable[[WSSubscriptionMessage], None]] _ws_state_subscriptions: list[Callable[[WebsocketState], None]] _bootstrap: Bootstrap | None = None _last_update_dt: datetime | None = None _connection_host: IPv4Address | IPv6Address | str | None = None ignore_unadopted: bool def __init__( self, host: str, port: int, username: str, password: str, verify_ssl: bool = True, session: aiohttp.ClientSession | None = None, ws_timeout: int = 30, cache_dir: Path | None = None, config_dir: Path | None = None, store_sessions: bool = True, override_connection_host: bool = False, minimum_score: int = 0, subscribed_models: set[ModelType] | None = None, ignore_stats: bool = False, ignore_unadopted: bool = True, debug: bool = False, ws_receive_timeout: int | None = None, ) -> None: super().__init__( host=host, port=port, username=username, password=password, verify_ssl=verify_ssl, session=session, ws_timeout=ws_timeout, ws_receive_timeout=ws_receive_timeout, cache_dir=cache_dir, config_dir=config_dir, store_sessions=store_sessions, ) self._minimum_score = minimum_score self._subscribed_models = subscribed_models or set() self._ignore_stats = ignore_stats self._ws_subscriptions = [] self._ws_state_subscriptions = [] self.ignore_unadopted = ignore_unadopted self._update_lock = asyncio.Lock() if override_connection_host: self._connection_host = ip_from_host(self._host) if debug: set_debug() @cached_property def bootstrap(self) -> Bootstrap: if self._bootstrap is None: raise BadRequest("Client not initialized, run `update` first") return self._bootstrap @property def connection_host(self) -> IPv4Address | IPv6Address | str: """Connection host to use for generating RTSP URLs""" if self._connection_host is None: # fallback if cannot find user supplied host index = 0 try: # check if user supplied host is avaiable index = self.bootstrap.nvr.hosts.index(self._host) except ValueError: # check if IP of user supplied host is avaiable host = ip_from_host(self._host) with contextlib.suppress(ValueError): index = self.bootstrap.nvr.hosts.index(host) self._connection_host = self.bootstrap.nvr.hosts[index] return self._connection_host async def update(self) -> Bootstrap: """ Updates the state of devices, initializes `.bootstrap` The websocket is auto connected once there are any subscriptions to it. update must be called at least once before subscribing to the websocket. You can use the various other `get_` methods if you need one off data from UFP """ async with self._update_lock: bootstrap = await self.get_bootstrap() self.__dict__.pop("bootstrap", None) self._bootstrap = bootstrap return bootstrap async def poll_events(self) -> None: """Poll for events.""" now_dt = utc_now() max_event_dt = now_dt - timedelta(hours=1) events = await self.get_events( start=self._last_update_dt or max_event_dt, end=now_dt, ) for event in events: self.bootstrap.process_event(event) self._last_update_dt = now_dt def emit_message(self, msg: WSSubscriptionMessage) -> None: """Emit message to all subscriptions.""" if _LOGGER.isEnabledFor(logging.DEBUG): if msg.new_obj is not None: _LOGGER.debug( "emitting message: %s:%s:%s:%s", msg.action, msg.new_obj.model, msg.new_obj.id, list(msg.changed_data), ) elif msg.old_obj is not None: _LOGGER.debug( "emitting message: %s:%s:%s", msg.action, msg.old_obj.model, msg.old_obj.id, ) else: _LOGGER.debug("emitting message: %s", msg.action) for sub in self._ws_subscriptions: try: sub(msg) except Exception: _LOGGER.exception("Exception while running subscription handler") def _get_last_update_id(self) -> str | None: if self._bootstrap is None: return None return self._bootstrap.last_update_id def _process_ws_message(self, msg: aiohttp.WSMessage) -> None: packet = WSPacket(msg.data) processed_message = self.bootstrap.process_ws_packet( packet, models=self._subscribed_models, ignore_stats=self._ignore_stats, ) if processed_message is None: return self.emit_message(processed_message) async def _get_event_paginate( self, params: dict[str, Any], *, start: datetime, end: datetime | None, ) -> list[dict[str, Any]]: start_int = to_js_time(start) end_int = to_js_time(end) if end else None offset = 0 current_start = sys.maxsize events: list[dict[str, Any]] = [] request_count = 0 logged = False params["limit"] = 100 # greedy algorithm # always force desc to receive faster results in the vast majority of cases params["orderDirection"] = "DESC" _LOGGER.debug("paginate desc %s %s", start_int, end_int) while current_start > start_int: params["offset"] = offset _LOGGER.debug("page desc %s %s", offset, current_start) new_events = await self.api_request_list("events", params=params) request_count += 1 if not new_events: break if end_int is not None: _LOGGER.debug("page end %s (%s)", new_events[0]["end"], end_int) for event in new_events: if event["start"] <= end_int: events.append(event) else: break else: events += new_events offset += 100 if events: current_start = events[-1]["start"] if not logged and request_count > 5: logged = True _LOGGER.warning(TYPES_BUG_MESSAGE) to_remove = 0 for event in reversed(events): if event["start"] < start_int: to_remove += 1 else: break if to_remove: events = events[:-to_remove] return events async def get_events_raw( self, *, start: datetime | None = None, end: datetime | None = None, limit: int | None = None, offset: int | None = None, types: list[EventType] | None = None, smart_detect_types: list[SmartDetectObjectType] | None = None, sorting: Literal["asc", "desc"] = "asc", descriptions: bool = True, all_cameras: bool | None = None, category: EventCategories | None = None, # used for testing _allow_manual_paginate: bool = True, ) -> list[dict[str, Any]]: """ Get list of events from Protect Args: ---- start: start time for events end: end time for events limit: max number of events to return offset: offset to start fetching events from types: list of EventTypes to get events for smart_detect_types: Filters the Smart detection types for the events sorting: sort events by ascending or decending, defaults to ascending (chronologic order) description: included additional event metadata category: event category, will provide additional category/subcategory fields If `limit`, `start` and `end` are not provided, it will default to all events in the last 24 hours. If `start` is provided, then `end` or `limit` must be provided. If `end` is provided, then `start` or `limit` must be provided. Otherwise, you will get a 400 error from UniFi Protect """ # if no parameters are passed in, default to all events from last 24 hours if limit is None and start is None and end is None: end = utc_now() + timedelta(seconds=10) start = end - timedelta(hours=1) params: dict[str, Any] = { "orderDirection": sorting.upper(), "withoutDescriptions": str(not descriptions).lower(), } if limit is not None: params["limit"] = limit if offset is not None: params["offset"] = offset if start is not None: params["start"] = to_js_time(start) if end is not None: params["end"] = to_js_time(end) if types is not None: params["types"] = [e.value for e in types] if smart_detect_types is not None: params["smartDetectTypes"] = [e.value for e in smart_detect_types] if all_cameras is not None: params["allCameras"] = str(all_cameras).lower() if category is not None: params["categories"] = category # manual workaround for a UniFi Protect bug # if types if missing from query params if _allow_manual_paginate and "types" not in params and start is not None: if sorting == "asc": events = await self._get_event_paginate( params, start=start, end=end, ) events = list(reversed(events)) else: events = await self._get_event_paginate( params, start=start, end=end, ) if limit: offset = offset or 0 events = events[offset : limit + offset] elif offset: events = events[offset:] return events return await self.api_request_list("events", params=params) async def get_events( self, start: datetime | None = None, end: datetime | None = None, limit: int | None = None, offset: int | None = None, types: list[EventType] | None = None, smart_detect_types: list[SmartDetectObjectType] | None = None, sorting: Literal["asc", "desc"] = "asc", descriptions: bool = True, category: EventCategories | None = None, # used for testing _allow_manual_paginate: bool = True, ) -> list[Event]: """ Same as `get_events_raw`, except * returns actual `Event` objects instead of raw Python dictionaries * filers out non-device events * filters out events with too low of a score Args: ---- start: start time for events end: end time for events limit: max number of events to return offset: offset to start fetching events from types: list of EventTypes to get events for smart_detect_types: Filters the Smart detection types for the events sorting: sort events by ascending or decending, defaults to ascending (chronologic order) description: included additional event metadata category: event category, will provide additional category/subcategory fields If `limit`, `start` and `end` are not provided, it will default to all events in the last 24 hours. If `start` is provided, then `end` or `limit` must be provided. If `end` is provided, then `start` or `limit` must be provided. Otherwise, you will get a 400 error from UniFi Protect """ response = await self.get_events_raw( start=start, end=end, limit=limit, offset=offset, types=types, smart_detect_types=smart_detect_types, sorting=sorting, descriptions=descriptions, category=category, _allow_manual_paginate=_allow_manual_paginate, ) events = [] for event_dict in response: # ignore unknown events if ( "type" not in event_dict or event_dict["type"] not in EventType.values_set() ): _LOGGER.debug("Unknown event type: %s", event_dict) continue event = create_from_unifi_dict(event_dict, api=self) # should never happen if not isinstance(event, Event): continue if ( event.type.value in EventType.device_events_set() and event.score >= self._minimum_score ): events.append(event) return events def subscribe_websocket( self, ws_callback: Callable[[WSSubscriptionMessage], None], ) -> Callable[[], None]: """ Subscribe to websocket events. Returns a callback that will unsubscribe. """ _LOGGER.debug("Adding subscription: %s", ws_callback) self._ws_subscriptions.append(ws_callback) self._get_websocket().start() return partial(self._unsubscribe_websocket, ws_callback) def _unsubscribe_websocket( self, ws_callback: Callable[[WSSubscriptionMessage], None], ) -> None: """Unsubscribe to websocket events.""" _LOGGER.debug("Removing subscription: %s", ws_callback) self._ws_subscriptions.remove(ws_callback) if not self._ws_subscriptions: self._get_websocket().stop() def subscribe_websocket_state( self, ws_callback: Callable[[WebsocketState], None], ) -> Callable[[], None]: """ Subscribe to websocket state changes. Returns a callback that will unsubscribe. """ self._ws_state_subscriptions.append(ws_callback) return partial(self._unsubscribe_websocket_state, ws_callback) def _unsubscribe_websocket_state( self, ws_callback: Callable[[WebsocketState], None], ) -> None: """Unsubscribe to websocket state changes.""" self._ws_state_subscriptions.remove(ws_callback) def _on_websocket_state_change(self, state: WebsocketState) -> None: """Websocket state changed.""" super()._on_websocket_state_change(state) for sub in self._ws_state_subscriptions: try: sub(state) except Exception: _LOGGER.exception("Exception while running websocket state handler") async def get_bootstrap(self) -> Bootstrap: """ Gets bootstrap object from UFP instance This is a great alternative if you need metadata about the NVR without connecting to the Websocket """ data = await self.api_request_obj("bootstrap") return Bootstrap.from_unifi_dict(**data, api=self) async def get_devices_raw(self, model_type: ModelType) -> list[dict[str, Any]]: """Gets a raw device list given a model_type""" return await self.api_request_list(model_type.devices_key) async def get_devices( self, model_type: ModelType, expected_type: type[ProtectModel] | None = None, ) -> list[ProtectModel]: """Gets a device list given a model_type, converted into Python objects""" objs: list[ProtectModel] = [] for obj_dict in await self.get_devices_raw(model_type): obj = create_from_unifi_dict(obj_dict, api=self) if expected_type is not None and not isinstance(obj, expected_type): raise NvrError(f"Unexpected model returned: {obj.model}") if ( self.ignore_unadopted and isinstance(obj, ProtectAdoptableDeviceModel) and not obj.is_adopted ): continue objs.append(obj) return objs async def get_cameras(self) -> list[Camera]: """ Gets the list of cameras straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.cameras` """ return cast(list[Camera], await self.get_devices(ModelType.CAMERA, Camera)) async def get_lights(self) -> list[Light]: """ Gets the list of lights straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.lights` """ return cast(list[Light], await self.get_devices(ModelType.LIGHT, Light)) async def get_sensors(self) -> list[Sensor]: """ Gets the list of sensors straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.sensors` """ return cast(list[Sensor], await self.get_devices(ModelType.SENSOR, Sensor)) async def get_doorlocks(self) -> list[Doorlock]: """ Gets the list of doorlocks straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.doorlocks` """ return cast( list[Doorlock], await self.get_devices(ModelType.DOORLOCK, Doorlock), ) async def get_chimes(self) -> list[Chime]: """ Gets the list of chimes straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.chimes` """ return cast(list[Chime], await self.get_devices(ModelType.CHIME, Chime)) async def get_viewers(self) -> list[Viewer]: """ Gets the list of viewers straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.viewers` """ return cast(list[Viewer], await self.get_devices(ModelType.VIEWPORT, Viewer)) async def get_bridges(self) -> list[Bridge]: """ Gets the list of bridges straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.bridges` """ return cast(list[Bridge], await self.get_devices(ModelType.BRIDGE, Bridge)) async def get_liveviews(self) -> list[Liveview]: """ Gets the list of liveviews straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.liveviews` """ return cast( list[Liveview], await self.get_devices(ModelType.LIVEVIEW, Liveview), ) async def get_device_raw( self, model_type: ModelType, device_id: str, ) -> dict[str, Any]: """Gets a raw device give the device model_type and id""" return await self.api_request_obj(f"{model_type.value}s/{device_id}") async def get_device( self, model_type: ModelType, device_id: str, expected_type: type[ProtectModelWithId] | None = None, ) -> ProtectModelWithId: """Gets a device give the device model_type and id, converted into Python object""" obj = create_from_unifi_dict( await self.get_device_raw(model_type, device_id), api=self, ) if expected_type is not None and not isinstance(obj, expected_type): raise NvrError(f"Unexpected model returned: {obj.model}") if ( self.ignore_unadopted and isinstance(obj, ProtectAdoptableDeviceModel) and not obj.is_adopted ): raise NvrError("Device is not adopted") return cast(ProtectModelWithId, obj) async def get_nvr(self) -> NVR: """ Gets an NVR object straight from the NVR. This is a great alternative if you need metadata about the NVR without connecting to the Websocket """ data = await self.api_request_obj("nvr") return NVR.from_unifi_dict(**data, api=self) async def get_event(self, event_id: str) -> Event: """ Gets an event straight from the NVR. This is a great alternative if the event is no longer in the `self.bootstrap.events[event_id]` cache """ return cast(Event, await self.get_device(ModelType.EVENT, event_id, Event)) async def get_camera(self, device_id: str) -> Camera: """ Gets a camera straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.cameras[device_id]` """ return cast(Camera, await self.get_device(ModelType.CAMERA, device_id, Camera)) async def get_light(self, device_id: str) -> Light: """ Gets a light straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.lights[device_id]` """ return cast(Light, await self.get_device(ModelType.LIGHT, device_id, Light)) async def get_sensor(self, device_id: str) -> Sensor: """ Gets a sensor straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.sensors[device_id]` """ return cast(Sensor, await self.get_device(ModelType.SENSOR, device_id, Sensor)) async def get_doorlock(self, device_id: str) -> Doorlock: """ Gets a doorlock straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.doorlocks[device_id]` """ return cast( Doorlock, await self.get_device(ModelType.DOORLOCK, device_id, Doorlock), ) async def get_chime(self, device_id: str) -> Chime: """ Gets a chime straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.chimes[device_id]` """ return cast(Chime, await self.get_device(ModelType.CHIME, device_id, Chime)) async def get_viewer(self, device_id: str) -> Viewer: """ Gets a viewer straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.viewers[device_id]` """ return cast( Viewer, await self.get_device(ModelType.VIEWPORT, device_id, Viewer), ) async def get_bridge(self, device_id: str) -> Bridge: """ Gets a bridge straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.bridges[device_id]` """ return cast(Bridge, await self.get_device(ModelType.BRIDGE, device_id, Bridge)) async def get_liveview(self, device_id: str) -> Liveview: """ Gets a liveview straight from the NVR. The websocket is connected and running, you likely just want to use `self.bootstrap.liveviews[device_id]` """ return cast( Liveview, await self.get_device(ModelType.LIVEVIEW, device_id, Liveview), ) async def get_camera_snapshot( self, camera_id: str, width: int | None = None, height: int | None = None, dt: datetime | None = None, ) -> bytes | None: """ Gets snapshot for a camera. Datetime of screenshot is approximate. It may be +/- a few seconds. """ params: dict[str, Any] = {} if dt is not None: path = "recording-snapshot" params["ts"] = to_js_time(dt) else: path = "snapshot" params["ts"] = int(time.time() * 1000) params["force"] = "true" if width is not None: params["w"] = width if height is not None: params["h"] = height return await self.api_request_raw( f"cameras/{camera_id}/{path}", params=params, raise_exception=False, ) async def get_package_camera_snapshot( self, camera_id: str, width: int | None = None, height: int | None = None, dt: datetime | None = None, ) -> bytes | None: """ Gets snapshot from the package camera. Datetime of screenshot is approximate. It may be +/- a few seconds. """ params: dict[str, Any] = {} if dt is not None: path = "recording-snapshot" params["ts"] = to_js_time(dt) params["lens"] = 2 else: path = "package-snapshot" params["ts"] = int(time.time() * 1000) params["force"] = "true" if width is not None: params["w"] = width if height is not None: params["h"] = height return await self.api_request_raw( f"cameras/{camera_id}/{path}", params=params, raise_exception=False, ) async def _stream_response( self, response: aiohttp.ClientResponse, chunk_size: int, iterator_callback: IteratorCallback | None = None, progress_callback: ProgressCallback | None = None, ) -> None: total = response.content_length or 0 current = 0 if iterator_callback is not None: await iterator_callback(total, None) async for chunk in response.content.iter_chunked(chunk_size): step = len(chunk) current += step if iterator_callback is not None: await iterator_callback(total, chunk) if progress_callback is not None: await progress_callback(step, current, total) async def get_camera_video( self, camera_id: str, start: datetime, end: datetime, channel_index: int = 0, validate_channel_id: bool = True, output_file: Path | None = None, iterator_callback: IteratorCallback | None = None, progress_callback: ProgressCallback | None = None, chunk_size: int = 65536, fps: int | None = None, ) -> bytes | None: """ Exports MP4 video from a given camera at a specific time. Start/End of video export are approximate. It may be +/- a few seconds. It is recommended to provide a output file or progress callback for larger video clips, otherwise the full video must be downloaded to memory before being written. Providing the `fps` parameter creates a "timelapse" export wtih the given FPS value. Protect app gives the options for 60x (fps=4), 120x (fps=8), 300x (fps=20), and 600x (fps=40). """ if validate_channel_id and self._bootstrap is not None: camera = self._bootstrap.cameras[camera_id] try: camera.channels[channel_index] except IndexError as e: raise BadRequest from e params = { "camera": camera_id, "start": to_js_time(start), "end": to_js_time(end), } if fps is not None: params["fps"] = fps params["type"] = "timelapse" if channel_index == 3: params.update({"lens": 2}) else: params.update({"channel": channel_index}) path = "video/export" if ( iterator_callback is None and progress_callback is None and output_file is None ): return await self.api_request_raw( path, params=params, raise_exception=False, ) r = await self.request( "get", f"{self.api_path}{path}", auto_close=False, timeout=0, params=params, ) if output_file is not None: async with aiofiles.open(output_file, "wb") as output: async def callback(total: int, chunk: bytes | None) -> None: if iterator_callback is not None: await iterator_callback(total, chunk) if chunk is not None: await output.write(chunk) await self._stream_response(r, chunk_size, callback, progress_callback) else: await self._stream_response( r, chunk_size, iterator_callback, progress_callback, ) r.close() return None async def _get_image_with_retry( self, path: str, retry_timeout: int = RETRY_TIMEOUT, **kwargs: Any, ) -> bytes | None: """ Retries image request until it returns or timesout. Used for event images like thumbnails and heatmaps. Note: thumbnails / heatmaps do not generate _until after the event ends_. Events that last longer then your retry timeout will always return None. """ now = time.monotonic() timeout = now + retry_timeout data: bytes | None = None while data is None and now < timeout: data = await self.api_request_raw(path, raise_exception=False, **kwargs) if data is None: await asyncio.sleep(0.5) now = time.monotonic() return data async def get_event_thumbnail( self, thumbnail_id: str, width: int | None = None, height: int | None = None, retry_timeout: int = RETRY_TIMEOUT, ) -> bytes | None: """ Gets given thumbanail from a given event. Thumbnail response is a JPEG image. Note: thumbnails / heatmaps do not generate _until after the event ends_. Events that last longer then your retry timeout will always return 404. """ params: dict[str, Any] = {} if width is not None: params.update({"w": width}) if height is not None: params.update({"h": height}) # old thumbnail URL use thumbnail ID, which is just `e-{event_id}` thumbnail_id = thumbnail_id.replace("e-", "") return await self._get_image_with_retry( f"events/{thumbnail_id}/thumbnail", params=params, retry_timeout=retry_timeout, ) async def get_event_animated_thumbnail( self, thumbnail_id: str, width: int | None = None, height: int | None = None, *, speedup: int = 10, retry_timeout: int = RETRY_TIMEOUT, ) -> bytes | None: """ Gets given animated thumbanil from a given event. Animated thumbnail response is a GIF image. Note: thumbnails / do not generate _until after the event ends_. Events that last longer then your retry timeout will always return 404. """ params: dict[str, Any] = { "keyFrameOnly": "true", "speedup": speedup, } if width is not None: params.update({"w": width}) if height is not None: params.update({"h": height}) # old thumbnail URL use thumbnail ID, which is just `e-{event_id}` thumbnail_id = thumbnail_id.replace("e-", "") return await self._get_image_with_retry( f"events/{thumbnail_id}/animated-thumbnail", params=params, retry_timeout=retry_timeout, ) async def get_event_heatmap( self, heatmap_id: str, retry_timeout: int = RETRY_TIMEOUT, ) -> bytes | None: """ Gets given heatmap from a given event. Heatmap response is a PNG image. Note: thumbnails / heatmaps do not generate _until after the event ends_. Events that last longer then your retry timeout will always return None. """ # old heatmap URL use heatmap ID, which is just `e-{event_id}` heatmap_id = heatmap_id.replace("e-", "") return await self._get_image_with_retry( f"events/{heatmap_id}/heatmap", retry_timeout=retry_timeout, ) async def get_event_smart_detect_track_raw(self, event_id: str) -> dict[str, Any]: """Gets raw Smart Detect Track for a Smart Detection""" return await self.api_request_obj(f"events/{event_id}/smartDetectTrack") async def get_event_smart_detect_track(self, event_id: str) -> SmartDetectTrack: """Gets raw Smart Detect Track for a Smart Detection""" data = await self.api_request_obj(f"events/{event_id}/smartDetectTrack") return SmartDetectTrack.from_unifi_dict(api=self, **data) async def update_device( self, model_type: ModelType, device_id: str, data: dict[str, Any], ) -> None: """ Sends an update for a device back to UFP USE WITH CAUTION, all possible combinations of updating objects have not been fully tested. May have unexpected side effects. Tested updates have been added a methods on applicable devices. """ await self.api_request( f"{model_type.value}s/{device_id}", method="patch", json=data, ) async def update_nvr(self, data: dict[str, Any]) -> None: """ Sends an update for main UFP NVR device USE WITH CAUTION, all possible combinations of updating objects have not been fully tested. May have unexpected side effects. Tested updates have been added a methods on applicable devices. """ await self.api_request("nvr", method="patch", json=data) async def reboot_nvr(self) -> None: """Reboots NVR""" await self.api_request("nvr/reboot", method="post") async def reboot_device(self, model_type: ModelType, device_id: str) -> None: """Reboots an adopted device""" await self.api_request(f"{model_type.value}s/{device_id}/reboot", method="post") async def unadopt_device(self, model_type: ModelType, device_id: str) -> None: """Unadopt/Unmanage adopted device""" await self.api_request(f"{model_type.value}s/{device_id}", method="delete") async def adopt_device(self, model_type: ModelType, device_id: str) -> None: """Adopts a device""" key = model_type.devices_key data = await self.api_request_obj( "devices/adopt", method="post", json={key: {device_id: {}}}, ) if not data.get(key, {}).get(device_id, {}).get("adopted", False): raise BadRequest("Could not adopt device") async def close_lock(self, device_id: str) -> None: """Close doorlock (lock)""" await self.api_request(f"doorlocks/{device_id}/close", method="post") async def open_lock(self, device_id: str) -> None: """Open doorlock (unlock)""" await self.api_request(f"doorlocks/{device_id}/open", method="post") async def calibrate_lock(self, device_id: str) -> None: """ Calibrate the doorlock. Door must be open and lock unlocked. """ await self.api_request( f"doorlocks/{device_id}/calibrate", method="post", json={"auto": True}, ) async def play_speaker( self, device_id: str, *, volume: int | None = None, repeat_times: int | None = None, ) -> None: """Plays chime tones on a chime""" data: dict[str, Any] | None = None if volume or repeat_times: chime = self.bootstrap.chimes.get(device_id) if chime is None: raise BadRequest("Invalid chime ID %s", device_id) data = { "volume": volume or chime.volume, "repeatTimes": repeat_times or chime.repeat_times, "trackNo": chime.track_no, } await self.api_request( f"chimes/{device_id}/play-speaker", method="post", json=data, ) async def play_buzzer(self, device_id: str) -> None: """Plays chime tones on a chime""" await self.api_request(f"chimes/{device_id}/play-buzzer", method="post") async def clear_tamper_sensor(self, device_id: str) -> None: """Clears tamper status for sensor""" await self.api_request(f"sensors/{device_id}/clear-tamper-flag", method="post") async def _get_versions_from_api( self, url: str, package: str = "unifi-protect", ) -> set[Version]: session = await self.get_session() versions: set[Version] = set() try: async with session.get(url) as response: is_package = False for line in (await response.text()).split("\n"): if line.startswith("Package: "): is_package = False if line == f"Package: {package}": is_package = True if is_package and line.startswith("Version: "): versions.add(Version(line.split(": ")[-1])) except ( TimeoutError, asyncio.TimeoutError, aiohttp.ServerDisconnectedError, client_exceptions.ClientError, ) as err: raise NvrError(f"Error packages from {url}: {err}") from err return versions async def get_release_versions(self) -> set[Version]: """Get all release versions for UniFi Protect""" versions: set[Version] = set() for url in PROTECT_APT_URLS: try: versions |= await self._get_versions_from_api(url) except NvrError: _LOGGER.warning("Failed to retrieve release versions from online.") return versions async def relative_move_ptz_camera( self, device_id: str, *, pan: float, tilt: float, pan_speed: int = 10, tilt_speed: int = 10, scale: int = 0, ) -> None: """ Move PTZ Camera relatively. Pan/tilt values vary from camera to camera, but for G4 PTZ: * Pan values range from 1 (0°) to 35200 (360°/0°). * Tilt values range from 1 (-20°) to 9777 (90°). Relative positions cannot move more then 4095 units in either direction at a time. Camera objects have ptz values in feature flags and the methods on them provide better control. """ data = { "type": "relative", "payload": { "panPos": pan, "tiltPos": tilt, "panSpeed": pan_speed, "tiltSpeed": tilt_speed, "scale": scale, }, } await self.api_request(f"cameras/{device_id}/move", method="post", json=data) async def center_ptz_camera( self, device_id: str, *, x: int, y: int, z: int, ) -> None: """ Center PTZ Camera on point in viewport. x, y, z values range from 0 to 1000. x, y are relative coords for the current viewport: * (0, 0) is top left * (500, 500) is the center * (1000, 1000) is the bottom right z value is zoom, but since it is capped at 1000, probably better to use `ptz_zoom_camera`. """ data = { "type": "center", "payload": { "x": x, "y": y, "z": z, }, } await self.api_request(f"cameras/{device_id}/move", method="post", json=data) async def zoom_ptz_camera( self, device_id: str, *, zoom: float, speed: int = 10, ) -> None: """ Zoom PTZ Camera. Zoom levels vary from camera to camera, but for G4 PTZ it goes from 0 (1x) to 2010 (22x). Zoom speed does not seem to do much, if anything. Camera objects have ptz values in feature flags and the methods on them provide better control. """ data = { "type": "zoom", "payload": { "zoomPos": zoom, "zoomSpeed": speed, }, } await self.api_request(f"cameras/{device_id}/move", method="post", json=data) async def get_position_ptz_camera(self, device_id: str) -> PTZPosition: """Get current PTZ camera position.""" pos = await self.api_request_obj(f"cameras/{device_id}/ptz/position") return PTZPosition(**pos) async def goto_ptz_camera(self, device_id: str, *, slot: int = -1) -> None: """ Goto PTZ slot position. -1 is Home slot. """ await self.api_request(f"cameras/{device_id}/ptz/goto/{slot}", method="post") async def create_preset_ptz_camera(self, device_id: str, *, name: str) -> PTZPreset: """Create PTZ Preset for camera based on current camera settings.""" preset = await self.api_request_obj( f"cameras/{device_id}/ptz/preset", method="post", json={"name": name}, ) return PTZPreset(**preset) async def get_presets_ptz_camera(self, device_id: str) -> list[PTZPreset]: """Get PTZ Presets for camera.""" presets = await self.api_request(f"cameras/{device_id}/ptz/preset") if not presets: return [] presets = cast(list[dict[str, Any]], presets) return [PTZPreset(**p) for p in presets] async def delete_preset_ptz_camera(self, device_id: str, *, slot: int) -> None: """Delete PTZ preset for camera.""" await self.api_request( f"cameras/{device_id}/ptz/preset/{slot}", method="delete", ) async def get_home_ptz_camera(self, device_id: str) -> PTZPreset: """Get PTZ home preset (-1).""" preset = await self.api_request_obj(f"cameras/{device_id}/ptz/home") return PTZPreset(**preset) async def set_home_ptz_camera(self, device_id: str) -> PTZPreset: """Set PTZ home preset (-1) to current position.""" preset = await self.api_request_obj( f"cameras/{device_id}/ptz/home", method="post", ) return PTZPreset(**preset) uiprotect-6.1.0/src/uiprotect/cli/000077500000000000000000000000001467310220200171055ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/cli/__init__.py000066400000000000000000000212441467310220200212210ustar00rootroot00000000000000from __future__ import annotations import asyncio import base64 import logging import sys from pathlib import Path from typing import Optional, cast import orjson import typer from rich.progress import track from uiprotect.api import ProtectApiClient from ..data import Version, WSPacket from ..test_util import SampleDataGenerator from ..utils import RELEASE_CACHE, get_local_timezone, run_async from ..utils import profile_ws as profile_ws_job from .base import CliContext, OutputFormatEnum from .cameras import app as camera_app from .chimes import app as chime_app from .doorlocks import app as doorlock_app from .events import app as event_app from .lights import app as light_app from .liveviews import app as liveview_app from .nvr import app as nvr_app from .sensors import app as sensor_app from .viewers import app as viewer_app try: from .backup import app as backup_app except ImportError: backup_app = None # type: ignore[assignment] _LOGGER = logging.getLogger("uiprotect") try: from IPython import embed from termcolor import colored from traitlets.config import get_config except ImportError: embed = termcolor = get_config = None # type: ignore[assignment] OPTION_USERNAME = typer.Option( ..., "--username", "-U", help="UniFi Protect username", prompt=True, envvar="UFP_USERNAME", ) OPTION_PASSWORD = typer.Option( ..., "--password", "-P", help="UniFi Protect password", prompt=True, hide_input=True, envvar="UFP_PASSWORD", ) OPTION_ADDRESS = typer.Option( ..., "--address", "-a", prompt=True, help="UniFi Protect IP address or hostname", envvar="UFP_ADDRESS", ) OPTION_PORT = typer.Option( 443, "--port", "-p", help="UniFi Protect Port", envvar="UFP_PORT", ) OPTION_SECONDS = typer.Option(15, "--seconds", "-s", help="Seconds to pull events") OPTION_VERIFY = typer.Option( True, "--no-verify", help="Verify SSL", envvar="UFP_SSL_VERIFY", ) OPTION_ANON = typer.Option(True, "--actual", help="Do not anonymize test data") OPTION_ZIP = typer.Option(False, "--zip", help="Zip up data after generate") OPTION_WAIT = typer.Option( 30, "--wait", "-w", help="Time to wait for Websocket messages", ) OPTION_OUTPUT = typer.Option( None, "--output", "-o", help="Output folder, defaults to `tests` folder one level above this file", envvar="UFP_SAMPLE_DIR", ) OPTION_OUT_FORMAT = typer.Option( OutputFormatEnum.PLAIN, "--output-format", help="Preferred output format. Not all commands support both JSON and plain and may still output in one or the other.", ) OPTION_WS_FILE = typer.Option( None, "--file", "-f", help="Path or raw binary Websocket message", ) OPTION_UNADOPTED = typer.Option( False, "-u", "--include-unadopted", help="Include devices not adopted by this NVR.", ) ARG_WS_DATA = typer.Argument(None, help="base64 encoded Websocket message") SLEEP_INTERVAL = 2 app = typer.Typer(rich_markup_mode="rich") app.add_typer(nvr_app, name="nvr") app.add_typer(event_app, name="events") app.add_typer(liveview_app, name="liveviews") app.add_typer(camera_app, name="cameras") app.add_typer(chime_app, name="chimes") app.add_typer(doorlock_app, name="doorlocks") app.add_typer(light_app, name="lights") app.add_typer(sensor_app, name="sensors") app.add_typer(viewer_app, name="viewers") if backup_app is not None: app.add_typer(backup_app, name="backup") @app.callback() def main( ctx: typer.Context, username: str = OPTION_USERNAME, password: str = OPTION_PASSWORD, address: str = OPTION_ADDRESS, port: int = OPTION_PORT, verify: bool = OPTION_VERIFY, output_format: OutputFormatEnum = OPTION_OUT_FORMAT, include_unadopted: bool = OPTION_UNADOPTED, ) -> None: """UniFi Protect CLI""" # preload the timezone before any async code runs get_local_timezone() protect = ProtectApiClient( address, port, username, password, verify_ssl=verify, ignore_unadopted=not include_unadopted, ) async def update() -> None: protect._bootstrap = await protect.get_bootstrap() await protect.close_session() run_async(update()) ctx.obj = CliContext(protect=protect, output_format=output_format) def _setup_logger(level: int = logging.DEBUG, show_level: bool = False) -> None: console_handler = logging.StreamHandler() console_handler.setLevel(level) if show_level: formatter = logging.Formatter("%(levelname)s: %(message)s") console_handler.setFormatter(formatter) _LOGGER.setLevel(logging.DEBUG) _LOGGER.addHandler(console_handler) async def _progress_bar(wait_time: int, label: str) -> None: for i in track(range(wait_time // SLEEP_INTERVAL), description=label): if i > 0: await asyncio.sleep(SLEEP_INTERVAL) @app.command() def shell(ctx: typer.Context) -> None: """ Opens iPython shell with Protect client initialized. Requires the `shell` extra to also be installed. """ if embed is None or colored is None: typer.echo("ipython and termcolor required for shell subcommand") sys.exit(1) # locals passed to shell protect = cast( ProtectApiClient, ctx.obj.protect, ) _setup_logger(show_level=True) async def wait_forever() -> None: await protect.update() protect.subscribe_websocket(lambda _: None) while True: await asyncio.sleep(10) await protect.update() c = get_config() c.InteractiveShellEmbed.colors = "Linux" embed( # type: ignore[no-untyped-call] header=colored("protect = ProtectApiClient(*args)", "green"), config=c, using="asyncio", ) @app.command() def generate_sample_data( ctx: typer.Context, anonymize: bool = OPTION_ANON, wait_time: int = OPTION_WAIT, output_folder: Optional[Path] = OPTION_OUTPUT, do_zip: bool = OPTION_ZIP, ) -> None: """Generates sample data for UniFi Protect instance.""" protect = cast(ProtectApiClient, ctx.obj.protect) if output_folder is None: tests_folder = Path(__file__).parent.parent / "tests" if not tests_folder.exists(): typer.secho("Output folder required when not in dev-mode", fg="red") sys.exit(1) output_folder = (tests_folder / "sample_data").absolute() def log(msg: str) -> None: typer.echo(msg) def log_warning(msg: str) -> None: typer.secho(msg, fg="yellow") SampleDataGenerator( protect, output_folder, anonymize, wait_time, log=log, log_warning=log_warning, ws_progress=_progress_bar, do_zip=do_zip, ).generate() @app.command() def profile_ws( ctx: typer.Context, wait_time: int = OPTION_WAIT, output_path: Optional[Path] = OPTION_OUTPUT, ) -> None: """Profiles Websocket messages for UniFi Protect instance.""" protect = cast(ProtectApiClient, ctx.obj.protect) async def callback() -> None: await protect.update() unsub = protect.subscribe_websocket(lambda _: None) await profile_ws_job( protect, wait_time, output_path=output_path, ws_progress=_progress_bar, ) unsub() await protect.async_disconnect_ws() await protect.close_session() _setup_logger() run_async(callback()) @app.command() def decode_ws_msg( ws_file: typer.FileBinaryRead = OPTION_WS_FILE, ws_data: Optional[str] = ARG_WS_DATA, ) -> None: """Decodes a base64 encoded UniFi Protect Websocket binary message.""" if ws_file is None and ws_data is None: # type: ignore[unreachable] typer.secho("Websocket data required", fg="red") # type: ignore[unreachable] sys.exit(1) ws_data_raw = b"" if ws_file is not None: ws_data_raw = ws_file.read() elif ws_data is not None: # type: ignore[unreachable] ws_data_raw = base64.b64decode(ws_data.encode("utf8")) packet = WSPacket(ws_data_raw) response = {"action": packet.action_frame.data, "data": packet.data_frame.data} typer.echo(orjson.dumps(response).decode("utf-8")) @app.command() def release_versions(ctx: typer.Context) -> None: """Updates the release version cache on disk.""" protect = cast(ProtectApiClient, ctx.obj.protect) async def callback() -> set[Version]: versions = await protect.get_release_versions() await protect.close_session() return versions _setup_logger() versions = run_async(callback()) output = orjson.dumps(sorted([str(v) for v in versions])) Path(RELEASE_CACHE).write_bytes(output) typer.echo(output.decode("utf-8")) uiprotect-6.1.0/src/uiprotect/cli/backup.py000066400000000000000000001075441467310220200207370ustar00rootroot00000000000000from __future__ import annotations import asyncio import logging import math import os import sys import time from dataclasses import dataclass from datetime import datetime, timedelta, timezone from enum import Enum from pathlib import Path from typing import TYPE_CHECKING, Any, Optional, cast import aiofiles import aiofiles.os as aos import av import dateparser import typer from PIL import Image from rich.progress import ( BarColumn, MofNCompleteColumn, Progress, TaskProgressColumn, TextColumn, TimeRemainingColumn, track, ) from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, func, or_, select from sqlalchemy import event as saevent from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine from sqlalchemy.orm import Mapped, declarative_base, relationship from .. import data as d from ..api import ProtectApiClient from ..cli import base from ..utils import ( format_duration, get_local_timezone, local_datetime, utc_now, ) if TYPE_CHECKING: from click.core import Parameter app = typer.Typer(rich_markup_mode="rich") Base = declarative_base() _LOGGER = logging.getLogger(__name__) def _on_db_connect(dbapi_con, connection_record) -> None: # type: ignore[no-untyped-def] cursor = dbapi_con.cursor() cursor.execute("PRAGMA journal_mode=WAL") cursor.execute("PRAGMA synchronous=NORMAL") @dataclass class BackupContext(base.CliContext): start: datetime end: datetime | None output: Path seperator: str thumbnail_format: str gif_format: str event_format: str title_format: str max_download: int page_size: int length_cutoff: timedelta _db_engine: AsyncEngine | None = None _db_session: AsyncSession | None = None @property def download_thumbnails(self) -> bool: return self.thumbnail_format != "" @property def download_gifs(self) -> bool: return self.gif_format != "" @property def download_videos(self) -> bool: return self.event_format != "" @property def db_file(self) -> Path: return self.output / "events.db" @property def db_engine(self) -> AsyncEngine: if self._db_engine is None: self._db_engine = create_async_engine(f"sqlite+aiosqlite:///{self.db_file}") self._db_session = None saevent.listens_for(self._db_engine.sync_engine, "connect")(_on_db_connect) return self._db_engine def create_db_session(self) -> AsyncSession: return AsyncSession(bind=self.db_engine, expire_on_commit=False) async def create_db(self) -> None: async with self.db_engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) class EventTypeChoice(str, Enum): MOTION = d.EventType.MOTION.value RING = d.EventType.RING.value SMART_DETECT = d.EventType.SMART_DETECT.value SMART_DETECT_LINE = d.EventType.SMART_DETECT_LINE.value class EventSmartType(Base): # type: ignore[valid-type,misc] __tablename__ = "event_smart_type" id = Column(Integer, primary_key=True) event_id = Column(String(24), ForeignKey("event.id"), nullable=False) smart_type = Column(String(32), index=True) class Event(Base): # type: ignore[valid-type,misc] __tablename__ = "event" __allow_unmapped__ = True id = Column(String(24), primary_key=True) start_naive = Column(DateTime()) end_naive = Column(DateTime(), nullable=True) camera_mac = Column(String(12), index=True) event_type = Column(String(32), index=True) smart_detect_types: Mapped[list[EventSmartType]] = relationship( "EventSmartType", lazy="joined", uselist=True, ) _start: datetime | None = None _end: datetime | None = None _smart_types: set[str] | None = None _context: dict[str, str] | None = None _glob_context: dict[str, str] | None = None @property def start(self) -> datetime: if self._start is None: self._start = self.start_naive.replace(tzinfo=timezone.utc) # type: ignore[union-attr] return self._start @property def end(self) -> datetime | None: if self._end is None and self.end_naive is not None: self._end = self.end_naive.replace(tzinfo=timezone.utc) return self._end @property def smart_types(self) -> set[str]: if self._smart_types is None: self._smart_types = {s.smart_type for s in self.smart_detect_types} # type: ignore[misc] return self._smart_types def get_file_context(self, ctx: BackupContext) -> dict[str, str]: if self._context is None: camera = ctx.protect.bootstrap.get_device_from_mac(self.camera_mac) # type: ignore[arg-type] camera_slug = "" display_name = "" length = timedelta(seconds=0) if camera is not None: camera_slug = ( camera.display_name.lower().replace(" ", ctx.seperator) + ctx.seperator ) display_name = camera.display_name if self.end is not None: length = self.end - self.start event_type = str(self.event_type) event_type_pretty = f"{event_type.title()} Event" if event_type in { d.EventType.SMART_DETECT.value, d.EventType.SMART_DETECT_LINE.value, }: smart_types = list(self.smart_types) smart_types.sort() event_type = f"{event_type}[{','.join(smart_types)}]" smart_types_title = [s.title() for s in smart_types] event_type_pretty = f"Smart Detection ({', '.join(smart_types_title)})" start_local = local_datetime(self.start) self._context = { "year": str(self.start.year), "month": str(self.start.month), "day": str(self.start.day), "hour": str(self.start.hour), "minute": str(self.start.minute), "datetime": self.start.strftime("%Y-%m-%dT%H-%M-%S%z").replace( "-", ctx.seperator, ), "date": self.start.strftime("%Y-%m-%d").replace("-", ctx.seperator), "time": self.start.strftime("%H-%M-%S%z").replace("-", ctx.seperator), "time_sort_pretty": self.start.strftime("%H:%M:%S (%Z)"), "time_pretty": self.start.strftime("%I:%M:%S %p (%Z)"), "year_local": str(start_local.year), "month_local": str(start_local.month), "day_local": str(start_local.day), "hour_local": str(start_local.hour), "minute_local": str(start_local.minute), "datetime_local": start_local.strftime("%Y-%m-%dT%H-%M-%S%z").replace( "-", ctx.seperator, ), "date_local": start_local.strftime("%Y-%m-%d").replace( "-", ctx.seperator, ), "time_local": start_local.strftime("%H-%M-%S%z").replace( "-", ctx.seperator, ), "time_sort_pretty_local": start_local.strftime("%H:%M:%S (%Z)"), "time_pretty_local": start_local.strftime("%I:%M:%S %p (%Z)"), "mac": str(self.camera_mac), "camera_name": display_name, "camera_slug": camera_slug, "event_type": event_type, "event_type_pretty": event_type_pretty, "length_pretty": format_duration(length), "sep": ctx.seperator, } self._context["title"] = ctx.title_format.format(**self._context) return self._context def get_glob_file_context(self, ctx: BackupContext) -> dict[str, str]: if self._glob_context is None: self._glob_context = self.get_file_context(ctx).copy() self._glob_context["camera_slug"] = "*" self._glob_context["camera_name"] = "*" return self._glob_context def get_thumbnail_path(self, ctx: BackupContext) -> Path: context = self.get_file_context(ctx) file_path = ctx.thumbnail_format.format(**context) return ctx.output / file_path def get_existing_thumbnail_path(self, ctx: BackupContext) -> Path | None: context = self.get_glob_file_context(ctx) file_path = ctx.thumbnail_format.format(**context) paths = list(ctx.output.glob(file_path)) if paths: return paths[0] return None def get_gif_path(self, ctx: BackupContext) -> Path: context = self.get_file_context(ctx) file_path = ctx.gif_format.format(**context) return ctx.output / file_path def get_existing_gif_path(self, ctx: BackupContext) -> Path | None: context = self.get_glob_file_context(ctx) file_path = ctx.gif_format.format(**context) paths = list(ctx.output.glob(file_path)) if paths: return paths[0] return None def get_event_path(self, ctx: BackupContext) -> Path: context = self.get_file_context(ctx) file_path = ctx.event_format.format(**context) return ctx.output / file_path def get_existing_event_path(self, ctx: BackupContext) -> Path | None: context = self.get_glob_file_context(ctx) file_path = ctx.event_format.format(**context) paths = list(ctx.output.glob(file_path)) if paths: return paths[0] return None @dataclass class QueuedDownload: task: asyncio.Task[bool] | None args: list[Any] def relative_datetime(ctx: typer.Context, value: str, param: Parameter) -> datetime: if dt := dateparser.parse(value): return dt raise typer.BadParameter( "Must be a ISO 8601 format or human readable relative format", ctx, param, ) _DownloadEventQueue = asyncio.Queue[QueuedDownload] OPTION_OUTPUT = typer.Option( None, help="Base dir for creating files. Defaults to $PWD.", envvar="UFP_BACKUP_OUTPUT", ) OPTION_START = typer.Option( None, "-s", "--start", help="Cutoff for start of backup. Defaults to start of recording for NVR.", envvar="UFP_BACKUP_START", ) OPTION_PAGE_SIZE = typer.Option( 1000, "--page-size", help="Number of events fetched at once from local database. Increases memory usage.", ) OPTION_LENGTH_CUTOFF = typer.Option( timedelta(hours=1).total_seconds(), "--length-cutoff", help="Event size cutoff for detecting abnormal events (in seconds).", ) OPTION_END = typer.Option( None, "-e", "--end", help="Cutoff for end of backup. Defaults to now.", envvar="UFP_BACKUP_END", ) OPTION_EVENT_TYPES = typer.Option( list(EventTypeChoice), "-t", "--event-type", help="Events to export. Can be used multiple time.", ) OPTION_SMART_TYPES = typer.Option( list(d.SmartDetectObjectType), "-m", "--smart-type", help="Smart Detection types to export. Can be used multiple time.", ) OPTION_SPERATOR = typer.Option("-", "--sep", help="Separator used for formatting.") OPTION_THUMBNAIL_FORMAT = typer.Option( "{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}{sep}thumb.jpg", "--thumb-format", help='Filename format to save event thumbnails to. Set to empty string ("") to skip saving event thumbnails.', ) OPTION_GIF_FORMAT = typer.Option( "{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}{sep}animated.gif", "--gif-format", help='Filename format to save event gifs to. Set to empty string ("") to skip saving event gif.', ) OPTION_EVENT_FORMAT = typer.Option( "{year}/{month}/{day}/{hour}/{datetime}{sep}{mac}{sep}{camera_slug}{event_type}.mp4", "--event-format", help='Filename format to save event gifs to. Set to empty string ("") to skip saving event videos.', ) OPTION_TITLE_FORMAT = typer.Option( "{time_sort_pretty_local} {sep} {camera_name} {sep} {event_type_pretty} {sep} {length_pretty}", "--title-format", help="Format to use to tag title for video metadata.", ) OPTION_VERBOSE = typer.Option(False, "-v", "--verbose", help="Debug logging.") OPTION_MAX_DOWNLOAD = typer.Option( 5, "-d", "--max-download", help="Max number of concurrent downloads. Adds additional loads to NVR.", ) def _setup_logger(verbose: bool) -> None: console_handler = logging.StreamHandler() log_format = "[%(asctime)s] %(levelname)s - %(message)s" if verbose: console_handler.setLevel(logging.DEBUG) elif sys.stdout.isatty(): console_handler.setLevel(logging.WARNING) log_format = "%(message)s" else: console_handler.setLevel(logging.INFO) formatter = logging.Formatter(log_format) console_handler.setFormatter(formatter) root_logger = logging.getLogger("uiprotect") root_logger.setLevel(logging.DEBUG) root_logger.addHandler(console_handler) @app.callback() def main( ctx: typer.Context, start: Optional[str] = OPTION_START, end: Optional[str] = OPTION_END, output_folder: Optional[Path] = OPTION_OUTPUT, thumbnail_format: str = OPTION_THUMBNAIL_FORMAT, gif_format: str = OPTION_GIF_FORMAT, event_format: str = OPTION_EVENT_FORMAT, title_format: str = OPTION_TITLE_FORMAT, verbose: bool = OPTION_VERBOSE, max_download: int = OPTION_MAX_DOWNLOAD, page_size: int = OPTION_PAGE_SIZE, length_cutoff: int = OPTION_LENGTH_CUTOFF, seperator: str = OPTION_SPERATOR, ) -> None: """ Backup CLI. The backup CLI is still very WIP in progress and consider experimental and potentially unstable (interface may change in the future). """ _setup_logger(verbose) protect: ProtectApiClient = ctx.obj.protect local_tz = get_local_timezone() if start is None: start_dt = protect.bootstrap.recording_start else: start_dt = relative_datetime(ctx, start, ctx.command.params[0]) start_dt = start_dt.replace(tzinfo=local_tz) if start_dt is None: start_dt = utc_now() end_dt = None if end is not None: end_dt = relative_datetime(ctx, end, ctx.command.params[1]) end_dt = end_dt.replace(tzinfo=local_tz) if output_folder is None: output_folder = Path(os.getcwd()) context = BackupContext( protect=ctx.obj.protect, start=start_dt, end=end_dt, output_format=ctx.obj.output_format, output=output_folder, thumbnail_format=thumbnail_format, gif_format=gif_format, event_format=event_format, title_format=title_format, max_download=max_download, page_size=page_size, length_cutoff=timedelta(seconds=length_cutoff), seperator=seperator, ) ctx.obj = context def _wipe_files(ctx: BackupContext, no_input: bool) -> None: if not no_input and not typer.confirm( "Are you sure you want to delete all existing thumbnails and video clips?", ): raise typer.Exit(1) if ctx.db_file.exists(): os.remove(ctx.db_file) for path in track(ctx.output.glob("**/*.jpg"), description="Deleting Thumbnails"): os.remove(path) for path in track(ctx.output.glob("**/*.mp4"), description="Deleting Clips"): os.remove(path) async def _newest_event(ctx: BackupContext) -> Event | None: db = ctx.create_db_session() async with db: result = await db.execute(select(Event).order_by(Event.start_naive.desc())) return result.scalars().first() async def _prune_events(ctx: BackupContext) -> int: _LOGGER.debug("Pruning events before %s", ctx.start) deleted = 0 db = ctx.create_db_session() async with db: result = await db.execute( select(Event).join(EventSmartType).where(Event.start_naive < ctx.start), ) for event in track(result.unique().scalars(), description="Pruning Events"): thumb_path = event.get_thumbnail_path(ctx) if thumb_path.exists(): _LOGGER.debug("Delete file %s", thumb_path) await aos.remove(thumb_path) event_path = event.get_event_path(ctx) if event_path.exists(): _LOGGER.debug("Delete file %s", event_path) await aos.remove(event_path) if event.event_type in { d.EventType.SMART_DETECT.value, d.EventType.SMART_DETECT_LINE.value, }: for smart_type in event.smart_detect_types: await db.delete(smart_type) await db.delete(event) deleted += 1 await db.commit() return deleted async def _update_event(ctx: BackupContext, event: d.Event) -> None: if event.camera is None: return db = ctx.create_db_session() to_delete: list[EventSmartType] = [] async with db: result = await db.execute(select(Event).where(Event.id == event.id)) db_event = result.scalars().first() do_insert = False if db_event is None: db_event = Event(id=event.id) do_insert = True db_event.start_naive = event.start db_event.end_naive = event.end db_event.camera_mac = event.camera.mac db_event.event_type = event.type.value if event.type in { d.EventType.SMART_DETECT.value, d.EventType.SMART_DETECT_LINE.value, }: types = {e.value for e in event.smart_detect_types} result = await db.execute( select(EventSmartType).where(EventSmartType.event_id == event.id), ) for event_smart_type in result.unique().scalars(): event_type = cast(EventSmartType, event_smart_type) if event_type.smart_type not in types: to_delete.append(event_type) else: types.remove(event_type.smart_type) for smart_type_str in types: db.add(EventSmartType(event_id=event.id, smart_type=smart_type_str)) if do_insert: db.add(db_event) for smart_type in to_delete: await db.delete(smart_type) await db.commit() async def _update_ongoing_events(ctx: BackupContext) -> int: db = ctx.create_db_session() async with db: result = await db.execute( select(Event) .where(Event.event_type != "ring") .where(Event.end_naive is None), # type: ignore[arg-type] ) events = list(result.unique().scalars()) if len(events) == 0: return 0 for event in track(events, description="Updating Events"): event_id = cast(str, event.id) await _update_event(ctx, await ctx.protect.get_event(event_id)) return len(events) async def _update_events(ctx: BackupContext) -> int: # update any events that are still set as ongoing in the database updated_ongoing = await _update_ongoing_events(ctx) start = ctx.start end = ctx.end or utc_now() processed: set[str] = set() total = int((end - ctx.start).total_seconds()) _LOGGER.debug("total: %s: %s %s", total, start, end) prev_start = start with Progress() as pb: task_id = pb.add_task("Fetching New Events", total=total) task = pb.tasks[0] pb.refresh() while not pb.finished: progress = int((start - prev_start).total_seconds()) pb.update(task_id, advance=progress) _LOGGER.debug( "progress: +%s: %s/%s: %s %s", progress, task.completed, task.total, start, end, ) events = await ctx.protect.get_events( start, end, limit=100, types=[ d.EventType.MOTION, d.EventType.RING, d.EventType.SMART_DETECT, d.EventType.SMART_DETECT_LINE, ], ) prev_start = start count = 0 for event in events: start = event.start if event.id not in processed: count += 1 processed.add(event.id) await _update_event(ctx, event) if start == prev_start and count == 0: pb.update(task_id, completed=total) return updated_ongoing + len(processed) async def _download_watcher( count: int, tasks: _DownloadEventQueue, no_error_flag: asyncio.Event, ) -> int: processed = 0 loop = asyncio.get_running_loop() downloaded = 0 last_print = time.monotonic() while processed < count: download = await tasks.get() task = download.task if task is None: processed += 1 continue retries = 0 while True: try: await task except asyncio.CancelledError: return downloaded except Exception: pass event: Event = download.args[1] if exception := task.exception(): no_error_flag.clear() if retries < 5: wait = math.pow(2, retries) _LOGGER.warning( "Exception while downloading event (%s): %s. Retring in %s second(s)", event.id, exception, wait, ) await asyncio.sleep(wait) retries += 1 task = loop.create_task(_download_event(*download.args)) else: _LOGGER.error("Failed to download event %s", event.id) if exception is None or retries >= 5: no_error_flag.set() processed += 1 now = time.monotonic() if now - last_print > 60: _LOGGER.info( "Processed %s/%s (%.2f%%) events", processed, count, processed / count, ) last_print = now if exception is None and task.result(): downloaded += 1 break return downloaded def _verify_thumbnail(path: Path) -> bool: try: image = Image.open(path) image.verify() # no docs on what exception could be except Exception: return False return True async def _download_event_thumb( ctx: BackupContext, event: Event, verify: bool, force: bool, animated: bool = False, ) -> bool: if animated: thumb_type = "gif" thumb_path = event.get_gif_path(ctx) existing_thumb_path = event.get_existing_gif_path(ctx) else: thumb_type = "thumbnail" thumb_path = event.get_thumbnail_path(ctx) existing_thumb_path = event.get_existing_thumbnail_path(ctx) if force and existing_thumb_path: _LOGGER.debug("Delete file %s", existing_thumb_path) await aos.remove(existing_thumb_path) if existing_thumb_path and str(existing_thumb_path) != str(thumb_path): _LOGGER.debug( "Rename event %s file %s: %s %s %s: %s", thumb_type, event.id, event.start, event.end, event.event_type, thumb_path, ) await aos.makedirs(thumb_path.parent, exist_ok=True) await aos.rename(existing_thumb_path, thumb_path) if ( verify and thumb_path.exists() and not await asyncio.get_running_loop().run_in_executor( None, _verify_thumbnail, thumb_path ) ): _LOGGER.warning( "Corrupted event %s file for event (%s), redownloading", thumb_type, event.id, ) await aos.remove(thumb_path) if not thumb_path.exists(): _LOGGER.debug( "Download event %s %s: %s %s: %s", thumb_type, event.id, event.start, event.event_type, thumb_path, ) event_id = str(event.id) if animated: thumbnail = await ctx.protect.get_event_animated_thumbnail(event_id) else: thumbnail = await ctx.protect.get_event_thumbnail(event_id) if thumbnail is not None: await aos.makedirs(thumb_path.parent, exist_ok=True) async with aiofiles.open(thumb_path, mode="wb") as f: await f.write(thumbnail) return True return False def _verify_video_file( # type: ignore[return] path: Path, length: float, width: int, height: int, title: str, ) -> tuple[bool, bool]: try: with av.open(str(path)) as video: slength = float( video.streams.video[0].duration * video.streams.video[0].time_base, # type: ignore[operator] ) valid = ( (slength / length) > 0.80 # export is fuzzy and video.streams.video[0].codec_context.width == width and video.streams.video[0].codec_context.height == height ) metadata_valid = False if valid: metadata_valid = bool(video.metadata["title"] == title) return valid, metadata_valid # no docs on what exception could be except Exception: return False, False def _add_metadata(path: Path, creation: datetime, title: str) -> bool: creation = local_datetime(creation) output_path = path.parent / path.name.replace(".mp4", ".metadata.mp4") success = True try: with ( av.open(str(path)) as input_file, av.open(str(output_path), "w") as output_file, ): for key, value in input_file.metadata.items(): output_file.metadata[key] = value output_file.metadata["creation_time"] = creation.isoformat() output_file.metadata["title"] = title output_file.metadata["year"] = creation.date().isoformat() output_file.metadata["release"] = creation.date().isoformat() in_to_out: dict[str, Any] = {} for stream in input_file.streams: in_to_out[stream] = output_file.add_stream(template=stream) # type: ignore[index] in_to_out[stream].metadata["creation_time"] = creation.isoformat() # type: ignore[index] for packet in input_file.demux(list(in_to_out)): if packet.dts is None: continue packet.stream = in_to_out[packet.stream] # type: ignore[index] try: output_file.mux(packet) # type: ignore[arg-type] # some frames may be corrupted on disk from NVR except ValueError: continue # no docs on what exception could be except Exception: success = False finally: if success: os.remove(path) output_path.rename(path) elif output_path.exists(): os.remove(output_path) return success async def _download_event_video( ctx: BackupContext, camera: d.Camera, event: Event, verify: bool, force: bool, ) -> bool: event_path = event.get_event_path(ctx) existing_event_path = event.get_existing_event_path(ctx) if force and existing_event_path: _LOGGER.debug("Delete file %s", existing_event_path) await aos.remove(existing_event_path) if existing_event_path and str(existing_event_path) != str(event_path): _LOGGER.debug( "Rename event file %s: %s %s %s: %s", event.id, event.start, event.end, event.event_type, event_path, ) await aos.makedirs(event_path.parent, exist_ok=True) await aos.rename(existing_event_path, event_path) metadata_valid = True if verify and event_path.exists(): valid = False if event.end is not None: valid, metadata_valid = await asyncio.get_running_loop().run_in_executor( None, _verify_video_file, event_path, (event.end - event.start).total_seconds(), camera.channels[0].width, camera.channels[0].height, event.get_file_context(ctx)["title"], ) if not valid: _LOGGER.warning( "Corrupted video file for event (%s), redownloading", event.id, ) await aos.remove(event_path) downloaded = False if not event_path.exists() and event.end is not None: _LOGGER.debug( "Download event %s: %s %s %s: %s", event.id, event.start, event.end, event.event_type, event_path, ) await aos.makedirs(event_path.parent, exist_ok=True) await camera.get_video(event.start, event.end, output_file=event_path) downloaded = True if (downloaded or not metadata_valid) and event.end is not None: file_context = event.get_file_context(ctx) if not await asyncio.get_running_loop().run_in_executor( None, _add_metadata, event_path, event.start, file_context["title"] ): _LOGGER.warning("Failed to write metadata for event (%s)", event.id) return downloaded async def _download_event( ctx: BackupContext, event: Event, verify: bool, force: bool, pb: Progress, ) -> bool: downloaded = False camera = ctx.protect.bootstrap.get_device_from_mac(event.camera_mac) # type: ignore[arg-type] if camera is not None: camera = cast(d.Camera, camera) downloads = [] if ctx.download_thumbnails: downloads.append(_download_event_thumb(ctx, event, verify, force)) if ctx.download_gifs: downloads.append( _download_event_thumb(ctx, event, verify, force, animated=True), ) if ctx.download_thumbnails: downloads.append(_download_event_video(ctx, camera, event, verify, force)) downloaded = any(await asyncio.gather(*downloads)) pb.update(pb.tasks[0].id, advance=1) return downloaded # TODO async def _download_events( ctx: BackupContext, event_types: list[d.EventType], smart_types: list[d.SmartDetectObjectType], verify: bool, force: bool, ) -> tuple[int, int]: start = ctx.start end = ctx.end or utc_now() db = ctx.create_db_session() async with db: count_query = ( select(func.count(Event.id)) .where(Event.event_type.in_([e.value for e in event_types])) .where(Event.start_naive >= start) .where(or_(Event.end_naive <= end, Event.end_naive is None)) # type: ignore[arg-type] ) count = cast(int, (await db.execute(count_query)).scalar()) _LOGGER.info("Downloading %s events", count) columns = [ TextColumn("[progress.description]{task.description}"), BarColumn(), TaskProgressColumn(), MofNCompleteColumn(), TimeRemainingColumn(), ] with Progress(*columns) as pb: task_id = pb.add_task("Downloading Events", total=count) query = ( select(Event) .where(Event.event_type.in_([e.value for e in event_types])) .where(Event.start_naive >= start) .where(or_(Event.end_naive <= end, Event.end_naive is None)) # type: ignore[arg-type] .limit(ctx.page_size) ) smart_types_set = {s.value for s in smart_types} loop = asyncio.get_running_loop() tasks: _DownloadEventQueue = asyncio.Queue(maxsize=ctx.max_download - 1) no_error_flag = asyncio.Event() no_error_flag.set() watcher_task = loop.create_task( _download_watcher(count, tasks, no_error_flag), ) offset = 0 page = query while offset < count: result = await db.execute(page) for event in result.unique().scalars(): if event.end is None: continue length = event.end - event.start if length > ctx.length_cutoff: _LOGGER.warning( "Skipping event %s because it is too long (%s)", event.id, length, ) await tasks.put(QueuedDownload(task=None, args=[])) continue # ensure no tasks are currently in a retry state await no_error_flag.wait() if event.event_type in { d.EventType.SMART_DETECT.value, d.EventType.SMART_DETECT_LINE.value, } and not event.smart_types.intersection(smart_types_set): continue task = loop.create_task( _download_event(ctx, event, verify, force, pb), ) # waits for a free processing slot await tasks.put( QueuedDownload(task=task, args=[ctx, event, verify, force, pb]), ) offset += ctx.page_size page = query.offset(offset) try: await watcher_task downloaded = watcher_task.result() except asyncio.CancelledError: downloaded = 0 pb.update(task_id, completed=count) return count, downloaded async def _events( ctx: BackupContext, event_types: list[d.EventType], smart_types: list[d.SmartDetectObjectType], prune: bool, force: bool, verify: bool, no_input: bool, ) -> None: try: await ctx.create_db() if prune and not force: _LOGGER.warning("Pruned %s old event(s)", await _prune_events(ctx)) original_start = ctx.start if not force: event = await _newest_event(ctx) if event is not None: ctx.start = event.start _LOGGER.warning("Updated %s event(s)", await _update_events(ctx)) ctx.start = original_start count, downloaded = await _download_events( ctx, event_types, smart_types, verify, force, ) verified = count - downloaded _LOGGER.warning( "Total events: %s. Verified %s existing event(s). Downloaded %s new event(s)", count, verified, downloaded, ) finally: _LOGGER.debug("Cleaning up Protect connection/database...") await ctx.protect.close_session() await ctx.db_engine.dispose() @app.command(name="events") def events_cmd( ctx: typer.Context, event_types: list[EventTypeChoice] = OPTION_EVENT_TYPES, smart_types: list[d.SmartDetectObjectType] = OPTION_SMART_TYPES, prune: bool = typer.Option( False, "-p", "--prune", help="Prune events older then start.", ), force: bool = typer.Option( False, "-f", "--force", help="Force update all events and redownload all clips.", ), verify: bool = typer.Option( False, "-v", "--verify", help="Verifies files on disk.", ), no_input: bool = typer.Option(False, "--no-input"), ) -> None: """Backup thumbnails and video clips for camera events.""" # surpress av logging messages av.logging.set_level(av.logging.PANIC) ufp_events = [d.EventType(e.value) for e in event_types] if prune and force: _wipe_files(ctx.obj, no_input) asyncio.run( _events(ctx.obj, ufp_events, smart_types, prune, force, verify, no_input), ) uiprotect-6.1.0/src/uiprotect/cli/base.py000066400000000000000000000166471467310220200204070ustar00rootroot00000000000000from __future__ import annotations from collections.abc import Callable, Coroutine, Mapping, Sequence from dataclasses import dataclass from enum import Enum from typing import Any, Optional, TypeVar import orjson import typer from pydantic.v1 import ValidationError from ..api import ProtectApiClient from ..data import NVR, ProtectAdoptableDeviceModel, ProtectBaseObject from ..exceptions import BadRequest, NvrError, StreamError from ..utils import run_async T = TypeVar("T") OPTION_FORCE = typer.Option(False, "-f", "--force", help="Skip confirmation prompt") class OutputFormatEnum(str, Enum): JSON = "json" PLAIN = "plain" @dataclass class CliContext: protect: ProtectApiClient output_format: OutputFormatEnum def run(ctx: typer.Context, func: Coroutine[Any, Any, T]) -> T: """Helper method to call async function and clean up API client""" async def callback() -> T: return_value = await func await ctx.obj.protect.close_session() return return_value try: return run_async(callback()) except (BadRequest, ValidationError, StreamError, NvrError) as err: typer.secho(str(err), fg="red") raise typer.Exit(1) from err def json_output(obj: Any) -> None: typer.echo(orjson.dumps(obj, option=orjson.OPT_INDENT_2).decode("utf-8")) def print_unifi_obj( obj: ProtectBaseObject | None, output_format: OutputFormatEnum, ) -> None: """Helper method to print a single protect object""" if obj is not None: json_output(obj.unifi_dict()) elif output_format == OutputFormatEnum.JSON: json_output(None) def print_unifi_list(objs: Sequence[ProtectBaseObject]) -> None: """Helper method to print a list of protect objects""" data = [o.unifi_dict() for o in objs] json_output(data) def print_unifi_dict(objs: Mapping[str, ProtectBaseObject]) -> None: """Helper method to print a dictionary of protect objects""" data = {k: v.unifi_dict() for k, v in objs.items()} json_output(data) def require_device_id(ctx: typer.Context) -> None: """Requires device ID in context""" if ctx.obj.device is None: typer.secho("Requires a valid device ID to be selected") raise typer.Exit(1) def require_no_device_id(ctx: typer.Context) -> None: """Requires no device ID in context""" if ctx.obj.device is not None: typer.secho("Requires no device ID to be selected") raise typer.Exit(1) def list_ids(ctx: typer.Context) -> None: """Requires no device ID. Prints list of "id name" for each device.""" require_no_device_id(ctx) objs: dict[str, ProtectAdoptableDeviceModel] = ctx.obj.devices to_print: list[tuple[str, str | None]] = [] for obj in objs.values(): name = obj.display_name if obj.is_adopted_by_other: name = f"{name} [Managed by Another Console]" elif obj.is_adopting: name = f"{name} [Adopting]" elif obj.can_adopt: name = f"{name} [Unadopted]" elif obj.is_rebooting: name = f"{name} [Restarting]" elif obj.is_updating: name = f"{name} [Updating]" elif not obj.is_connected: name = f"{name} [Disconnected]" to_print.append((obj.id, name)) if ctx.obj.output_format == OutputFormatEnum.JSON: json_output(to_print) else: for item in to_print: typer.echo(f"{item[0]}\t{item[1]}") def protect_url(ctx: typer.Context) -> None: """Gets UniFi Protect management URL.""" require_device_id(ctx) obj: NVR | ProtectAdoptableDeviceModel = ctx.obj.device if ctx.obj.output_format == OutputFormatEnum.JSON: json_output(obj.protect_url) else: typer.echo(obj.protect_url) def is_wired(ctx: typer.Context) -> None: """Returns if the device is wired or not.""" require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device json_output(obj.is_wired) def is_wifi(ctx: typer.Context) -> None: """Returns if the device has WiFi or not.""" require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device json_output(obj.is_wifi) def is_bluetooth(ctx: typer.Context) -> None: """Returns if the device has Bluetooth or not.""" require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device json_output(obj.is_bluetooth) def bridge(ctx: typer.Context) -> None: """Returns bridge device if connected via Bluetooth.""" require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device print_unifi_obj(obj.bridge, ctx.obj.output_format) def set_ssh(ctx: typer.Context, enabled: bool) -> None: """ Sets the isSshEnabled value for device. May not have an effect on many device types. Only seems to work for Linux and BusyBox based devices (camera, light and viewport). """ require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device run(ctx, obj.set_ssh(enabled)) def set_name(ctx: typer.Context, name: Optional[str] = typer.Argument(None)) -> None: """Sets name for the device""" require_device_id(ctx) obj: NVR | ProtectAdoptableDeviceModel = ctx.obj.device run(ctx, obj.set_name(name)) def update(ctx: typer.Context, data: str) -> None: """ Updates the device. Makes a raw PATCH request to update a device. Advanced usage and usually recommended not to use. """ require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device if obj.model is not None: run(ctx, obj.api.update_device(obj.model, obj.id, orjson.loads(data))) def reboot(ctx: typer.Context, force: bool = OPTION_FORCE) -> None: """Reboots the device.""" require_device_id(ctx) obj: NVR | ProtectAdoptableDeviceModel = ctx.obj.device if force or typer.confirm(f'Confirm reboot of "{obj.name}"" (id: {obj.id})'): run(ctx, obj.reboot()) def unadopt(ctx: typer.Context, force: bool = OPTION_FORCE) -> None: """Unadopt/Unmanage adopted device.""" require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device if force or typer.confirm(f'Confirm undopt of "{obj.name}"" (id: {obj.id})'): run(ctx, obj.unadopt()) def adopt(ctx: typer.Context, name: Optional[str] = typer.Argument(None)) -> None: """ Adopts a device. By default, unadopted devices do not show up in the bootstrap. Use `uiprotect -u` to show unadopted devices. """ require_device_id(ctx) obj: ProtectAdoptableDeviceModel = ctx.obj.device run(ctx, obj.adopt(name)) def init_common_commands( app: typer.Typer, ) -> tuple[dict[str, Callable[..., Any]], dict[str, Callable[..., Any]]]: deviceless_commands: dict[str, Callable[..., Any]] = {} device_commands: dict[str, Callable[..., Any]] = {} deviceless_commands["list-ids"] = app.command()(list_ids) device_commands["protect-url"] = app.command()(protect_url) device_commands["is-wired"] = app.command()(is_wired) device_commands["is-wifi"] = app.command()(is_wifi) device_commands["is-bluetooth"] = app.command()(is_bluetooth) device_commands["bridge"] = app.command()(bridge) device_commands["set-ssh"] = app.command()(set_ssh) device_commands["set-name"] = app.command()(set_name) device_commands["update"] = app.command()(update) device_commands["reboot"] = app.command()(reboot) device_commands["unadopt"] = app.command()(unadopt) device_commands["adopt"] = app.command()(adopt) return deviceless_commands, device_commands uiprotect-6.1.0/src/uiprotect/cli/cameras.py000066400000000000000000000411151467310220200210740ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from typing import Optional, cast import typer from rich.progress import Progress from .. import data as d from ..api import ProtectApiClient from ..cli import base app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of camera to select for subcommands") @dataclass class CameraContext(base.CliContext): devices: dict[str, d.Camera] device: d.Camera | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Camera device CLI. Returns full list of Cameras without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = CameraContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.cameras, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.cameras.get(device_id)) is None: typer.secho("Invalid camera ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def timelapse_url(ctx: typer.Context) -> None: """Returns UniFi Protect timelapse URL.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(obj.timelapse_url) else: typer.echo(obj.timelapse_url) @app.command() def privacy_mode( ctx: typer.Context, enabled: Optional[bool] = typer.Argument(None), ) -> None: """ Returns/sets library managed privacy mode. Does not change the microphone sensitivity or recording mode. It must be changed seperately. """ base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if enabled is None: base.json_output(obj.is_privacy_on) return base.run(ctx, obj.set_privacy(enabled)) @app.command() def chime_type(ctx: typer.Context, value: Optional[d.ChimeType] = None) -> None: """Returns/sets the current chime type if the camera has a chime.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if not obj.feature_flags.has_chime: typer.secho("Camera does not have a chime", fg="red") raise typer.Exit(1) if value is None: if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(obj.chime_type) elif obj.chime_type is not None: typer.echo(obj.chime_type.name) return base.run(ctx, obj.set_chime_type(value)) @app.command() def stream_urls(ctx: typer.Context) -> None: """Returns all of the enabled RTSP(S) URLs.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device data: list[tuple[str, str]] = [] for channel in obj.channels: if channel.is_rtsp_enabled: rtsp_url = cast(str, channel.rtsp_url) rtsps_url = cast(str, channel.rtsps_url) data.extend( ( (f"{channel.name} RTSP", rtsp_url), (f"{channel.name} RTSPS", rtsps_url), ), ) if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(data) else: for name, url in data: typer.echo(f"{name:20}\t{url}") @app.command() def save_snapshot( ctx: typer.Context, output_path: Path = typer.Argument(..., help="JPEG format"), width: Optional[int] = typer.Option(None, "-w", "--width"), height: Optional[int] = typer.Option(None, "-h", "--height"), dt: Optional[datetime] = typer.Option(None, "-t", "--timestamp"), package: bool = typer.Option(False, "-p", "--package", help="Get package camera"), ) -> None: """ Takes snapshot of camera. If you specify a timestamp, they are approximate. It will not export with down to the second accuracy so it may be +/- a few seconds. Timestamps use your locale timezone. If it is not configured correctly, it will default to UTC. You can override your timezone with the TZ environment variable. """ base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if dt is not None: local_tz = datetime.now(timezone.utc).astimezone().tzinfo dt = dt.replace(tzinfo=local_tz) if package: if not obj.feature_flags.has_package_camera: typer.secho("Camera does not have package camera", fg="red") raise typer.Exit(1) snapshot = base.run(ctx, obj.get_package_snapshot(width, height, dt=dt)) else: snapshot = base.run(ctx, obj.get_snapshot(width, height, dt=dt)) if snapshot is None: typer.secho("Could not get snapshot", fg="red") raise typer.Exit(1) Path(output_path).write_bytes(snapshot) @app.command() def save_video( ctx: typer.Context, output_path: Path = typer.Argument(..., help="MP4 format"), start: datetime = typer.Argument(...), end: datetime = typer.Argument(...), channel: int = typer.Option( 0, "-c", "--channel", min=0, max=3, help="0 = High, 1 = Medium, 2 = Low, 3 = Package", ), fps: Optional[int] = typer.Option( None, "--fps", min=1, max=40, help="Export as timelapse. 4 = 60x, 8 = 120x, 20 = 300x, 40 = 600x", ), ) -> None: """ Exports video of camera. Exports are approximate. It will not export with down to the second accuracy so it may be +/- a few seconds. Uses your locale timezone. If it is not configured correctly, it will default to UTC. You can override your timezone with the TZ environment variable. """ base.require_device_id(ctx) obj: d.Camera = ctx.obj.device local_tz = datetime.now(timezone.utc).astimezone().tzinfo start = start.replace(tzinfo=local_tz) end = end.replace(tzinfo=local_tz) if channel == 4 and not obj.feature_flags.has_package_camera: typer.secho("Camera does not have package camera", fg="red") raise typer.Exit(1) with Progress() as pb: task_id = pb.add_task("(1/2) Exporting", total=100) async def callback(step: int, current: int, total: int) -> None: pb.update( task_id, total=total, completed=current, description="(2/2) Downloading", ) base.run( ctx, obj.get_video( start, end, channel, output_file=output_path, progress_callback=callback, fps=fps, ), ) @app.command() def play_audio( ctx: typer.Context, url: str = typer.Argument(..., help="ffmpeg playable URL"), ffmpeg_path: Optional[Path] = typer.Option( None, "--ffmpeg-path", help="Path to ffmpeg executable", envvar="FFMPEG_PATH", ), ) -> None: """Plays audio file on camera speaker.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.play_audio(url, ffmpeg_path=ffmpeg_path)) @app.command() def smart_detects( ctx: typer.Context, values: list[d.SmartDetectObjectType] = typer.Argument( None, help="Set to [] to empty list of detect types.", ), add: bool = typer.Option(False, "-a", "--add", help="Add values instead of set"), remove: bool = typer.Option( False, "-r", "--remove", help="Remove values instead of set", ), ) -> None: """Returns/set smart detect types for camera.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if add and remove: typer.secho("Add and remove are mutally exclusive", fg="red") raise typer.Exit(1) if not obj.feature_flags.has_smart_detect: typer.secho("Camera does not support smart detections", fg="red") raise typer.Exit(1) if len(values) == 0: if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(obj.smart_detect_settings.object_types) else: for value in obj.smart_detect_settings.object_types: typer.echo(value.value) return if len(values) == 1 and values[0] == "[]": values = [] for value in values: if value not in obj.feature_flags.smart_detect_types: typer.secho(f"Camera does not support {value}", fg="red") raise typer.Exit(1) if add: values = list(set(obj.smart_detect_settings.object_types) | set(values)) elif remove: values = list(set(obj.smart_detect_settings.object_types) - set(values)) data_before_changes = obj.dict_with_excludes() obj.smart_detect_settings.object_types = values base.run(ctx, obj.save_device(data_before_changes)) @app.command() def smart_audio_detects( ctx: typer.Context, values: list[d.SmartDetectAudioType] = typer.Argument( None, help="Set to [] to empty list of detect types.", ), add: bool = typer.Option(False, "-a", "--add", help="Add values instead of set"), remove: bool = typer.Option( False, "-r", "--remove", help="Remove values instead of set", ), ) -> None: """Returns/set smart detect types for camera.""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if add and remove: typer.secho("Add and remove are mutually exclusive", fg="red") raise typer.Exit(1) if not obj.feature_flags.has_smart_detect: typer.secho("Camera does not support smart detections", fg="red") raise typer.Exit(1) obj.smart_detect_settings.audio_types = obj.smart_detect_settings.audio_types or [] obj.smart_detect_settings.audio_types = obj.smart_detect_settings.audio_types or [] if len(values) == 0: if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(obj.smart_detect_settings.audio_types) else: for value in obj.smart_detect_settings.audio_types: typer.echo(value.value) return if len(values) == 1 and values[0] == "[]": values = [] for value in values: if ( obj.feature_flags.smart_detect_audio_types is None or value not in obj.feature_flags.smart_detect_audio_types ): typer.secho(f"Camera does not support {value}", fg="red") raise typer.Exit(1) if add: values = list(set(obj.smart_detect_settings.audio_types) | set(values)) elif remove: values = list(set(obj.smart_detect_settings.audio_types) - set(values)) data_before_changes = obj.dict_with_excludes() obj.smart_detect_settings.audio_types = values base.run(ctx, obj.save_device(data_before_changes)) @app.command() def set_motion_detection(ctx: typer.Context, enabled: bool) -> None: """Sets motion detection on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_motion_detection(enabled)) @app.command() def set_recording_mode(ctx: typer.Context, mode: d.RecordingMode) -> None: """Sets recording mode on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_recording_mode(mode)) @app.command() def set_ir_led_mode(ctx: typer.Context, mode: d.IRLEDMode) -> None: """Sets IR LED mode on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_ir_led_model(mode)) @app.command() def set_status_light(ctx: typer.Context, enabled: bool) -> None: """Sets status indicicator light on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_status_light(enabled)) @app.command() def set_hdr(ctx: typer.Context, enabled: bool) -> None: """Sets HDR (High Dynamic Range) on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_hdr(enabled)) @app.command() def set_color_night_vision(ctx: typer.Context, enabled: bool) -> None: """Sets Color Night Vision on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_color_night_vision(enabled=enabled)) @app.command() def set_person_track(ctx: typer.Context, enabled: bool) -> None: """Sets person tracking on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device if not obj.feature_flags.is_ptz: typer.secho("Camera does not support person tracking", fg="red") raise typer.Exit(1) base.run(ctx, (obj.set_person_track(enabled=enabled))) @app.command() def set_video_mode(ctx: typer.Context, mode: d.VideoMode) -> None: """Sets video mode on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_video_mode(mode)) @app.command() def set_camera_zoom( ctx: typer.Context, level: int = typer.Argument(..., min=0, max=100), ) -> None: """Sets zoom level for camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_camera_zoom(level)) @app.command() def set_wdr_level( ctx: typer.Context, level: int = typer.Argument(..., min=0, max=3), ) -> None: """Sets WDR (Wide Dynamic Range) on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_wdr_level(level)) @app.command() def set_mic_volume( ctx: typer.Context, level: int = typer.Argument(..., min=0, max=100), ) -> None: """Sets the mic sensitivity level on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_mic_volume(level)) @app.command() def set_speaker_volume( ctx: typer.Context, level: int = typer.Argument(..., min=0, max=100), ) -> None: """Sets the speaker sensitivity level on camera""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_speaker_volume(level)) @app.command() def set_system_sounds(ctx: typer.Context, enabled: bool) -> None: """Sets system sound playback through speakers""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_system_sounds(enabled)) @app.command() def set_osd_name(ctx: typer.Context, enabled: bool) -> None: """Sets whether camera name is in the On Screen Display""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_osd_name(enabled)) @app.command() def set_osd_date(ctx: typer.Context, enabled: bool) -> None: """Sets whether current date is in the On Screen Display""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_osd_date(enabled)) @app.command() def set_osd_logo(ctx: typer.Context, enabled: bool) -> None: """Sets whether the UniFi logo is in the On Screen Display""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_osd_logo(enabled)) @app.command() def set_osd_bitrate(ctx: typer.Context, enabled: bool) -> None: """Sets whether camera bitrate is in the On Screen Display""" base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_osd_bitrate(enabled)) @app.command() def set_lcd_text( ctx: typer.Context, text_type: Optional[d.DoorbellMessageType] = typer.Argument( None, help="No value sets it back to the global default doorbell message.", ), text: Optional[str] = typer.Argument( None, help="Only for CUSTOM_MESSAGE text type", ), reset_at: Optional[datetime] = typer.Option( None, "-r", "--reset-time", help="Does not apply to default message", ), ) -> None: """ Sets doorbell LCD text. Uses your locale timezone. If it is not configured correctly, it will default to UTC. You can override your timezone with the TZ environment variable. """ if reset_at is not None: local_tz = datetime.now(timezone.utc).astimezone().tzinfo reset_at = reset_at.replace(tzinfo=local_tz) base.require_device_id(ctx) obj: d.Camera = ctx.obj.device base.run(ctx, obj.set_lcd_text(text_type, text, reset_at)) uiprotect-6.1.0/src/uiprotect/cli/chimes.py000066400000000000000000000123161467310220200207320ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import Chime app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of chime to select for subcommands") ARG_REPEAT = typer.Argument(..., help="Repeat times count", min=1, max=6) ARG_VOLUME = typer.Argument(..., help="Volume", min=1, max=100) @dataclass class ChimeContext(base.CliContext): devices: dict[str, Chime] device: Chime | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Chime device CLI. Returns full list of Chimes without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = ChimeContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.chimes, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.chimes.get(device_id)) is None: typer.secho("Invalid chime ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def cameras( ctx: typer.Context, camera_ids: list[str] = typer.Argument( None, help="Set to [] to empty list of cameras", ), add: bool = typer.Option(False, "-a", "--add", help="Add cameras instead of set"), remove: bool = typer.Option( False, "-r", "--remove", help="Remove cameras instead of set", ), ) -> None: """Returns or sets paired doorbells for the chime.""" base.require_device_id(ctx) obj: Chime = ctx.obj.device if add and remove: typer.secho("Add and remove are mutally exclusive", fg="red") raise typer.Exit(1) if len(camera_ids) == 0: base.print_unifi_list(obj.cameras) return protect: ProtectApiClient = ctx.obj.protect if len(camera_ids) == 1 and camera_ids[0] == "[]": camera_ids = [] for camera_id in camera_ids: if (camera := protect.bootstrap.cameras.get(camera_id)) is None: typer.secho(f"Invalid camera ID: {camera_id}", fg="red") raise typer.Exit(1) if not camera.feature_flags.is_doorbell: typer.secho(f"Camera is not a doorbell: {camera_id}", fg="red") raise typer.Exit(1) if add: camera_ids = list(set(obj.camera_ids) | set(camera_ids)) elif remove: camera_ids = list(set(obj.camera_ids) - set(camera_ids)) data_before_changes = obj.dict_with_excludes() obj.camera_ids = camera_ids base.run(ctx, obj.save_device(data_before_changes)) @app.command() def set_volume( ctx: typer.Context, value: int = ARG_VOLUME, camera_id: Optional[str] = typer.Option( None, "-c", "--camera", help="Camera ID to apply volume to", ), ) -> None: """Set volume level for chime rings.""" base.require_device_id(ctx) obj: Chime = ctx.obj.device if camera_id is None: base.run(ctx, obj.set_volume(value)) else: protect: ProtectApiClient = ctx.obj.protect camera = protect.bootstrap.cameras.get(camera_id) if camera is None: typer.secho(f"Invalid camera ID: {camera_id}", fg="red") raise typer.Exit(1) base.run(ctx, obj.set_volume_for_camera(camera, value)) @app.command() def play( ctx: typer.Context, volume: Optional[int] = typer.Option(None, "-v", "--volume", min=1, max=100), repeat_times: Optional[int] = typer.Option(None, "-r", "--repeat", min=1, max=6), ) -> None: """Plays chime tone.""" base.require_device_id(ctx) obj: Chime = ctx.obj.device base.run(ctx, obj.play(volume=volume, repeat_times=repeat_times)) @app.command() def play_buzzer(ctx: typer.Context) -> None: """Plays chime buzzer.""" base.require_device_id(ctx) obj: Chime = ctx.obj.device base.run(ctx, obj.play_buzzer()) @app.command() def set_repeat_times( ctx: typer.Context, value: int = ARG_REPEAT, camera_id: Optional[str] = typer.Option( None, "-c", "--camera", help="Camera ID to apply repeat times to", ), ) -> None: """Set number of times for a chime to repeat when doorbell is rang.""" base.require_device_id(ctx) obj: Chime = ctx.obj.device if camera_id is None: base.run(ctx, obj.set_repeat_times(value)) else: protect: ProtectApiClient = ctx.obj.protect camera = protect.bootstrap.cameras.get(camera_id) if camera is None: typer.secho(f"Invalid camera ID: {camera_id}", fg="red") raise typer.Exit(1) base.run(ctx, obj.set_repeat_times_for_camera(camera, value)) uiprotect-6.1.0/src/uiprotect/cli/doorlocks.py000066400000000000000000000067031467310220200214640ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from datetime import timedelta from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import Doorlock app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of doorlock to select for subcommands") @dataclass class DoorlockContext(base.CliContext): devices: dict[str, Doorlock] device: Doorlock | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Doorlock device CLI. Returns full list of Doorlocks without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = DoorlockContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.doorlocks, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.doorlocks.get(device_id)) is None: typer.secho("Invalid doorlock ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def camera(ctx: typer.Context, camera_id: Optional[str] = typer.Argument(None)) -> None: """Returns or sets tha paired camera for a doorlock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device if camera_id is None: base.print_unifi_obj(obj.camera, ctx.obj.output_format) else: protect: ProtectApiClient = ctx.obj.protect if (camera_obj := protect.bootstrap.cameras.get(camera_id)) is None: typer.secho("Invalid camera ID") raise typer.Exit(1) base.run(ctx, obj.set_paired_camera(camera_obj)) @app.command() def set_status_light(ctx: typer.Context, enabled: bool) -> None: """Sets status light for the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.set_status_light(enabled)) @app.command() def set_auto_close_time( ctx: typer.Context, duration: int = typer.Argument(..., min=0, max=3600), ) -> None: """Sets auto-close time for the lock (in seconds). 0 = disabled.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.set_auto_close_time(timedelta(seconds=duration))) @app.command() def unlock(ctx: typer.Context) -> None: """Unlocks the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.open_lock()) @app.command() def lock(ctx: typer.Context) -> None: """Locks the lock.""" base.require_device_id(ctx) obj: Doorlock = ctx.obj.device base.run(ctx, obj.close_lock()) @app.command() def calibrate(ctx: typer.Context, force: bool = base.OPTION_FORCE) -> None: """ Calibrate the doorlock. Door must be open and lock unlocked. """ base.require_device_id(ctx) obj: Doorlock = ctx.obj.device if force or typer.confirm("Is the door open and unlocked?"): base.run(ctx, obj.calibrate()) uiprotect-6.1.0/src/uiprotect/cli/events.py000066400000000000000000000160221467310220200207640ustar00rootroot00000000000000from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass from datetime import datetime from pathlib import Path from typing import Optional import typer from rich.progress import Progress from .. import data as d from ..api import ProtectApiClient from ..cli import base from ..exceptions import NvrError from ..utils import local_datetime app = typer.Typer(rich_markup_mode="rich") ARG_EVENT_ID = typer.Argument(None, help="ID of camera to select for subcommands") OPTION_START = typer.Option(None, "-s", "--start") OPTION_END = typer.Option(None, "-e", "--end") OPTION_LIMIT = typer.Option(None, "-l", "--limit") OPTION_OFFSET = typer.Option(None, "-o", "--offet") OPTION_TYPES = typer.Option(None, "-t", "--type") OPTION_SMART_TYPES = typer.Option( None, "-d", "--smart-detect", help="If provided, will only return smartDetectZone events", ) @dataclass class EventContext(base.CliContext): events: dict[str, d.Event] | None = None event: d.Event | None = None ALL_COMMANDS: dict[str, Callable[..., None]] = {} @app.callback(invoke_without_command=True) def main( ctx: typer.Context, event_id: Optional[str] = ARG_EVENT_ID, start: Optional[datetime] = OPTION_START, end: Optional[datetime] = OPTION_END, limit: Optional[int] = OPTION_LIMIT, offset: Optional[int] = OPTION_OFFSET, types: Optional[list[d.EventType]] = OPTION_TYPES, smart_types: Optional[list[d.SmartDetectObjectType]] = OPTION_SMART_TYPES, ) -> None: """ Events CLI. Returns list of events from the last 24 hours without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = EventContext( protect=ctx.obj.protect, event=None, events=None, output_format=ctx.obj.output_format, ) ctx.obj = context if event_id is not None and event_id not in ALL_COMMANDS: try: ctx.obj.event = base.run(ctx, protect.get_event(event_id)) except NvrError as err: typer.secho("Invalid event ID", fg="red") raise typer.Exit(1) from err if not ctx.invoked_subcommand: if ctx.obj.event is not None: base.print_unifi_obj(ctx.obj.event, ctx.obj.output_format) return if types is not None and len(types) == 0: types = None if smart_types is not None and len(smart_types) == 0: smart_types = None events = base.run( ctx, protect.get_events( start=start, end=end, limit=limit, offset=offset, types=types, smart_detect_types=smart_types, ), ) ctx.obj.events = {} for event in events: ctx.obj.events[event.id] = event if event_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[event_id], ctx) return base.print_unifi_dict(ctx.obj.events) def require_event_id(ctx: typer.Context) -> None: """Requires event ID in context""" if ctx.obj.event is None: typer.secho("Requires a valid event ID to be selected") raise typer.Exit(1) def require_no_event_id(ctx: typer.Context) -> None: """Requires no device ID in context""" if ctx.obj.event is not None or ctx.obj.events is None: typer.secho("Requires no event ID to be selected") raise typer.Exit(1) @app.command() def list_ids(ctx: typer.Context) -> None: """ Prints list of "id type timestamp" for each event. Timestamps dispalyed in your locale timezone. If it is not configured correctly, it will default to UTC. You can override your timezone with the TZ environment variable. """ require_no_event_id(ctx) objs: dict[str, d.Event] = ctx.obj.events to_print: list[tuple[str, str, datetime]] = [] longest_event = 0 for obj in objs.values(): event_type = obj.type.value if event_type in { d.EventType.SMART_DETECT.value, d.EventType.SMART_DETECT_LINE.value, }: event_type = f"{event_type}[{','.join(obj.smart_detect_types)}]" longest_event = max(len(event_type), longest_event) dt = obj.timestamp or obj.start dt = local_datetime(dt) to_print.append((obj.id, event_type, dt)) if ctx.obj.output_format == base.OutputFormatEnum.JSON: base.json_output(to_print) else: for item in to_print: typer.echo(f"{item[0]}\t{item[1]:{longest_event}}\t{item[2]}") ALL_COMMANDS["list-ids"] = list_ids @app.command() def save_thumbnail( ctx: typer.Context, output_path: Path = typer.Argument(..., help="JPEG format"), ) -> None: """ Saves thumbnail for event. Only for ring, motion and smartDetectZone events. """ require_event_id(ctx) event: d.Event = ctx.obj.event thumbnail = base.run(ctx, event.get_thumbnail()) if thumbnail is None: typer.secho("Could not get thumbnail", fg="red") raise typer.Exit(1) Path(output_path).write_bytes(thumbnail) @app.command() def save_animated_thumbnail( ctx: typer.Context, output_path: Path = typer.Argument(..., help="GIF format"), ) -> None: """ Saves animated thumbnail for event. Only for ring, motion and smartDetectZone events. """ require_event_id(ctx) event: d.Event = ctx.obj.event thumbnail = base.run(ctx, event.get_animated_thumbnail()) if thumbnail is None: typer.secho("Could not get thumbnail", fg="red") raise typer.Exit(1) Path(output_path).write_bytes(thumbnail) @app.command() def save_heatmap( ctx: typer.Context, output_path: Path = typer.Argument(..., help="PNG format"), ) -> None: """ Saves heatmap for event. Only motion events have heatmaps. """ require_event_id(ctx) event: d.Event = ctx.obj.event heatmap = base.run(ctx, event.get_heatmap()) if heatmap is None: typer.secho("Could not get heatmap", fg="red") raise typer.Exit(1) Path(output_path).write_bytes(heatmap) @app.command() def save_video( ctx: typer.Context, output_path: Path = typer.Argument(..., help="MP4 format"), channel: int = typer.Option( 0, "-c", "--channel", min=0, max=3, help="0 = High, 1 = Medium, 2 = Low, 3 = Package", ), ) -> None: """ Exports video for event. Only for ring, motion and smartDetectZone events. """ require_event_id(ctx) event: d.Event = ctx.obj.event with Progress() as pb: task_id = pb.add_task("(1/2) Exporting", total=100) async def callback(step: int, current: int, total: int) -> None: pb.update( task_id, total=total, completed=current, description="(2/2) Downloading", ) base.run( ctx, event.get_video( channel, output_file=output_path, progress_callback=callback, ), ) uiprotect-6.1.0/src/uiprotect/cli/lights.py000066400000000000000000000063621467310220200207600ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from datetime import timedelta from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import Light app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of light to select for subcommands") @dataclass class LightContext(base.CliContext): devices: dict[str, Light] device: Light | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Lights device CLI. Returns full list of Viewers without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = LightContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.lights, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.lights.get(device_id)) is None: typer.secho("Invalid light ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def camera(ctx: typer.Context, camera_id: Optional[str] = typer.Argument(None)) -> None: """Returns or sets tha paired camera for a light.""" base.require_device_id(ctx) obj: Light = ctx.obj.device if camera_id is None: base.print_unifi_obj(obj.camera, ctx.obj.output_format) else: protect: ProtectApiClient = ctx.obj.protect if (camera_obj := protect.bootstrap.cameras.get(camera_id)) is None: typer.secho("Invalid camera ID") raise typer.Exit(1) base.run(ctx, obj.set_paired_camera(camera_obj)) @app.command() def set_status_light(ctx: typer.Context, enabled: bool) -> None: """Sets status light for light device.""" base.require_device_id(ctx) obj: Light = ctx.obj.device base.run(ctx, obj.set_status_light(enabled)) @app.command() def set_led_level( ctx: typer.Context, led_level: int = typer.Argument(..., min=1, max=6), ) -> None: """Sets brightness of LED on light.""" base.require_device_id(ctx) obj: Light = ctx.obj.device base.run(ctx, obj.set_led_level(led_level)) @app.command() def set_sensitivity( ctx: typer.Context, sensitivity: int = typer.Argument(..., min=0, max=100), ) -> None: """Sets motion sensitivity for the light.""" base.require_device_id(ctx) obj: Light = ctx.obj.device base.run(ctx, obj.set_sensitivity(sensitivity)) @app.command() def set_duration( ctx: typer.Context, duration: int = typer.Argument(..., min=15, max=900), ) -> None: """Sets timeout duration (in seconds) for light.""" base.require_device_id(ctx) obj: Light = ctx.obj.device base.run(ctx, obj.set_duration(timedelta(seconds=duration))) uiprotect-6.1.0/src/uiprotect/cli/liveviews.py000066400000000000000000000035071467310220200215010ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import Liveview app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of liveview to select for subcommands") ALL_COMMANDS = {"list-ids": app.command(name="list-ids")(base.list_ids)} app.command(name="protect-url")(base.protect_url) @dataclass class LiveviewContext(base.CliContext): devices: dict[str, Liveview] device: Liveview | None = None @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Liveviews CLI. Returns full list of Liveviews without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = LiveviewContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.liveviews, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.liveviews.get(device_id)) is None: typer.secho("Invalid liveview ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def owner(ctx: typer.Context) -> None: """Gets the owner for the liveview.""" base.require_device_id(ctx) obj: Liveview = ctx.obj.device base.print_unifi_obj(obj.owner, ctx.obj.output_format) uiprotect-6.1.0/src/uiprotect/cli/nvr.py000066400000000000000000000102441467310220200202650ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from datetime import timedelta import orjson import typer from ..cli import base from ..data import NVR, AnalyticsOption app = typer.Typer(rich_markup_mode="rich") ARG_TIMEOUT = typer.Argument(..., help="Timeout (in seconds)") ARG_DOORBELL_MESSAGE = typer.Argument(..., help="ASCII only. Max length 30") OPTION_ENABLE_SMART = typer.Option( False, "--enable-smart", help="Automatically enable smart detections", ) @dataclass class NVRContext(base.CliContext): device: NVR @app.callback(invoke_without_command=True) def main(ctx: typer.Context) -> None: """ NVR device CLI. Return NVR object without any arguments passed. """ context = NVRContext( protect=ctx.obj.protect, device=ctx.obj.protect.bootstrap.nvr, output_format=ctx.obj.output_format, ) ctx.obj = context if not ctx.invoked_subcommand: base.print_unifi_obj(context.device, ctx.obj.output_format) app.command(name="protect-url")(base.protect_url) app.command(name="reboot")(base.reboot) app.command(name="set-name")(base.set_name) @app.command() def set_analytics(ctx: typer.Context, value: AnalyticsOption) -> None: """Sets analytics collection for NVR.""" nvr: NVR = ctx.obj.device base.run(ctx, nvr.set_analytics(value)) @app.command() def set_default_reset_timeout(ctx: typer.Context, timeout: int = ARG_TIMEOUT) -> None: """ Sets default message reset timeout. This is how long until a custom message is reset back to the default message if no timeout is passed in when the custom message is set. """ nvr: NVR = ctx.obj.device base.run(ctx, nvr.set_default_reset_timeout(timedelta(seconds=timeout))) base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format) @app.command() def set_default_doorbell_message( ctx: typer.Context, msg: str = ARG_DOORBELL_MESSAGE, ) -> None: """ Sets default message for doorbell. This is the message that is set when a custom doorbell message times out or an empty one is set. """ nvr: NVR = ctx.obj.device base.run(ctx, nvr.set_default_doorbell_message(msg)) base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format) @app.command() def add_custom_doorbell_message( ctx: typer.Context, msg: str = ARG_DOORBELL_MESSAGE, ) -> None: """Adds a custom doorbell message.""" nvr: NVR = ctx.obj.device base.run(ctx, nvr.add_custom_doorbell_message(msg)) base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format) @app.command() def remove_custom_doorbell_message( ctx: typer.Context, msg: str = ARG_DOORBELL_MESSAGE, ) -> None: """Removes a custom doorbell message.""" nvr: NVR = ctx.obj.device base.run(ctx, nvr.remove_custom_doorbell_message(msg)) base.print_unifi_obj(nvr.doorbell_settings, ctx.obj.output_format) @app.command() def update(ctx: typer.Context, data: str) -> None: """Updates the NVR.""" nvr: NVR = ctx.obj.device base.run(ctx, nvr.api.update_nvr(orjson.loads(data))) @app.command() def set_smart_detections(ctx: typer.Context, value: bool) -> None: """Set if smart detections are globally enabled or not.""" nvr: NVR = ctx.obj.device base.run(ctx, nvr.set_smart_detections(value)) @app.command() def set_face_recognition( ctx: typer.Context, value: bool, enable_smart: bool = OPTION_ENABLE_SMART, ) -> None: """Set if face detections is enabled. Requires smart detections to be enabled.""" nvr: NVR = ctx.obj.device async def callback() -> None: if enable_smart: await nvr.set_smart_detections(True) await nvr.set_face_recognition(value) base.run(ctx, callback()) @app.command() def set_license_plate_recognition( ctx: typer.Context, value: bool, enable_smart: bool = OPTION_ENABLE_SMART, ) -> None: """Set if license plate detections is enabled. Requires smart detections to be enabled.""" nvr: NVR = ctx.obj.device async def callback() -> None: if enable_smart: await nvr.set_smart_detections(True) await nvr.set_license_plate_recognition(value) base.run(ctx, callback()) uiprotect-6.1.0/src/uiprotect/cli/sensors.py000066400000000000000000000177311467310220200211640ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import MountType, Sensor app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of sensor to select for subcommands") @dataclass class SensorContext(base.CliContext): devices: dict[str, Sensor] device: Sensor | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Sensors device CLI. Returns full list of Sensors without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = SensorContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.sensors, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.sensors.get(device_id)) is None: typer.secho("Invalid sensor ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def camera(ctx: typer.Context, camera_id: Optional[str] = typer.Argument(None)) -> None: """Returns or sets tha paired camera for a sensor.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device if camera_id is None: base.print_unifi_obj(obj.camera, ctx.obj.output_format) else: protect: ProtectApiClient = ctx.obj.protect if (camera_obj := protect.bootstrap.cameras.get(camera_id)) is None: typer.secho("Invalid camera ID") raise typer.Exit(1) base.run(ctx, obj.set_paired_camera(camera_obj)) @app.command() def is_tampering_detected(ctx: typer.Context) -> None: """Returns if tampering is detected for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_tampering_detected) @app.command() def is_alarm_detected(ctx: typer.Context) -> None: """Returns if alarm is detected for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_alarm_detected) @app.command() def is_contact_enabled(ctx: typer.Context) -> None: """Returns if contact sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_contact_sensor_enabled) @app.command() def is_motion_enabled(ctx: typer.Context) -> None: """Returns if motion sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_contact_sensor_enabled) @app.command() def is_alarm_enabled(ctx: typer.Context) -> None: """Returns if alarm sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_alarm_sensor_enabled) @app.command() def is_light_enabled(ctx: typer.Context) -> None: """Returns if light sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_light_sensor_enabled) @app.command() def is_temperature_enabled(ctx: typer.Context) -> None: """Returns if temperature sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_temperature_sensor_enabled) @app.command() def is_humidity_enabled(ctx: typer.Context) -> None: """Returns if humidity sensor is enabled for sensor""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.json_output(obj.is_humidity_sensor_enabled) @app.command() def set_status_light(ctx: typer.Context, enabled: bool) -> None: """Sets status light for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_status_light(enabled)) @app.command() def set_mount_type(ctx: typer.Context, mount_type: MountType) -> None: """Sets mount type for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_mount_type(mount_type)) @app.command() def set_motion(ctx: typer.Context, enabled: bool) -> None: """Sets motion sensor status for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_motion_status(enabled)) @app.command() def set_temperature(ctx: typer.Context, enabled: bool) -> None: """Sets temperature sensor status for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_temperature_status(enabled)) @app.command() def set_humidity(ctx: typer.Context, enabled: bool) -> None: """Sets humidity sensor status for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_humidity_status(enabled)) @app.command() def set_light(ctx: typer.Context, enabled: bool) -> None: """Sets light sensor status for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_light_status(enabled)) @app.command() def set_alarm(ctx: typer.Context, enabled: bool) -> None: """Sets alarm sensor status for sensor device.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_alarm_status(enabled)) @app.command() def set_motion_sensitivity( ctx: typer.Context, sensitivity: int = typer.Argument(..., min=0, max=100), ) -> None: """Sets motion sensitivity for the sensor.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_motion_sensitivity(sensitivity)) @app.command() def set_temperature_range( ctx: typer.Context, low: float = typer.Argument(..., min=0, max=44), high: float = typer.Argument(..., min=1, max=45), ) -> None: """Sets temperature safe range (in °C). Anything out side of range will trigger event.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_temperature_safe_range(low, high)) @app.command() def set_humidity_range( ctx: typer.Context, low: float = typer.Argument(..., min=1, max=98), high: float = typer.Argument(..., min=2, max=99), ) -> None: """Sets humidity safe range (in relative % humidity). Anything out side of range will trigger event.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_humidity_safe_range(low, high)) @app.command() def set_light_range( ctx: typer.Context, low: float = typer.Argument(..., min=1, max=999), high: float = typer.Argument(..., min=2, max=1000), ) -> None: """Sets light safe range (in lux). Anything out side of range will trigger event.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.set_light_safe_range(low, high)) @app.command() def remove_temperature_range(ctx: typer.Context) -> None: """Removes temperature safe ranges so events will no longer fire.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.remove_temperature_safe_range()) @app.command() def remove_humidity_range(ctx: typer.Context) -> None: """Removes humidity safe ranges so events will no longer fire.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.remove_humidity_safe_range()) @app.command() def remove_light_range(ctx: typer.Context) -> None: """Removes light safe ranges so events will no longer fire.""" base.require_device_id(ctx) obj: Sensor = ctx.obj.device base.run(ctx, obj.remove_light_safe_range()) uiprotect-6.1.0/src/uiprotect/cli/viewers.py000066400000000000000000000041731467310220200211500ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from typing import Optional import typer from ..api import ProtectApiClient from ..cli import base from ..data import Viewer app = typer.Typer(rich_markup_mode="rich") ARG_DEVICE_ID = typer.Argument(None, help="ID of viewer to select for subcommands") @dataclass class ViewerContext(base.CliContext): devices: dict[str, Viewer] device: Viewer | None = None ALL_COMMANDS, DEVICE_COMMANDS = base.init_common_commands(app) @app.callback(invoke_without_command=True) def main(ctx: typer.Context, device_id: Optional[str] = ARG_DEVICE_ID) -> None: """ Viewers device CLI. Returns full list of Viewers without any arguments passed. """ protect: ProtectApiClient = ctx.obj.protect context = ViewerContext( protect=ctx.obj.protect, device=None, devices=protect.bootstrap.viewers, output_format=ctx.obj.output_format, ) ctx.obj = context if device_id is not None and device_id not in ALL_COMMANDS: if (device := protect.bootstrap.viewers.get(device_id)) is None: typer.secho("Invalid viewer ID", fg="red") raise typer.Exit(1) ctx.obj.device = device if not ctx.invoked_subcommand: if device_id in ALL_COMMANDS: ctx.invoke(ALL_COMMANDS[device_id], ctx) return if ctx.obj.device is not None: base.print_unifi_obj(ctx.obj.device, ctx.obj.output_format) return base.print_unifi_dict(ctx.obj.devices) @app.command() def liveview( ctx: typer.Context, liveview_id: Optional[str] = typer.Argument(None), ) -> None: """Returns or sets the current liveview.""" base.require_device_id(ctx) obj: Viewer = ctx.obj.device if liveview_id is None: base.print_unifi_obj(obj.liveview, ctx.obj.output_format) else: protect: ProtectApiClient = ctx.obj.protect if (liveview_obj := protect.bootstrap.liveviews.get(liveview_id)) is None: typer.secho("Invalid liveview ID") raise typer.Exit(1) base.run(ctx, obj.set_liveview(liveview_obj)) uiprotect-6.1.0/src/uiprotect/data/000077500000000000000000000000001467310220200172475ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/data/__init__.py000066400000000000000000000055721467310220200213710ustar00rootroot00000000000000from __future__ import annotations from .base import ( ProtectAdoptableDeviceModel, ProtectBaseObject, ProtectDeviceModel, ProtectModel, ProtectModelWithId, ) from .bootstrap import Bootstrap from .convert import create_from_unifi_dict from .devices import ( Bridge, Camera, CameraChannel, Chime, Doorlock, LCDMessage, Light, RingSetting, Sensor, Viewer, ) from .nvr import ( NVR, DoorbellMessage, Event, Liveview, NVRLocation, SmartDetectItem, SmartDetectTrack, ) from .types import ( DEFAULT, DEFAULT_TYPE, AnalyticsOption, AudioStyle, ChimeType, Color, CoordType, DoorbellMessageType, DoorbellText, EventCategories, EventType, FixSizeOrderedDict, HDRMode, ICRCustomValue, ICRLuxValue, IRLEDMode, LensType, LightModeEnableType, LightModeType, LockStatusType, ModelType, MountType, Percent, PermissionNode, ProtectWSPayloadFormat, PTZPosition, PTZPreset, RecordingMode, SensorStatusType, SensorType, SmartDetectAudioType, SmartDetectObjectType, StateType, StorageType, Version, VideoMode, WDRLevel, ) from .user import CloudAccount, Group, Permission, User, UserLocation from .websocket import ( WS_HEADER_SIZE, WSAction, WSJSONPacketFrame, WSPacket, WSPacketFrameHeader, WSRawPacketFrame, WSSubscriptionMessage, ) __all__ = [ "DEFAULT", "DEFAULT_TYPE", "NVR", "WS_HEADER_SIZE", "AnalyticsOption", "AudioStyle", "Bootstrap", "Bridge", "Camera", "CameraChannel", "Chime", "ChimeType", "CloudAccount", "Color", "CoordType", "DoorbellMessage", "DoorbellMessageType", "DoorbellText", "Doorlock", "Event", "EventCategories", "EventType", "FixSizeOrderedDict", "Group", "HDRMode", "ICRCustomValue", "ICRLuxValue", "IRLEDMode", "LCDMessage", "LensType", "Light", "LightModeEnableType", "LightModeType", "Liveview", "LockStatusType", "ModelType", "MountType", "NVRLocation", "PTZPosition", "PTZPreset", "Percent", "Permission", "PermissionNode", "ProtectAdoptableDeviceModel", "ProtectBaseObject", "ProtectDeviceModel", "ProtectModel", "ProtectModelWithId", "ProtectWSPayloadFormat", "RecordingMode", "RingSetting", "Sensor", "SensorStatusType", "SensorType", "SmartDetectAudioType", "SmartDetectItem", "SmartDetectObjectType", "SmartDetectTrack", "StateType", "StorageType", "User", "UserLocation", "Version", "VideoMode", "Viewer", "WDRLevel", "WSAction", "WSJSONPacketFrame", "WSPacket", "WSPacketFrameHeader", "WSRawPacketFrame", "WSSubscriptionMessage", "create_from_unifi_dict", ] uiprotect-6.1.0/src/uiprotect/data/base.py000066400000000000000000001043311467310220200205350ustar00rootroot00000000000000"""UniFi Protect Data.""" from __future__ import annotations import asyncio import logging from collections.abc import Callable from datetime import datetime, timedelta from functools import cache, cached_property from ipaddress import IPv4Address from typing import TYPE_CHECKING, Any, NamedTuple, TypeVar from uuid import UUID from convertertools import pop_dict_set_if_none, pop_dict_tuple from pydantic.v1 import BaseModel from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, PrivateAttr from ..exceptions import BadRequest, ClientError, NotAuthorized from ..utils import ( asyncio_timeout, convert_to_datetime, convert_unifi_data, dict_diff, is_debug, serialize_unifi_obj, to_snake_case, ) from .types import ( ModelType, PercentFloat, PermissionNode, ProtectWSPayloadFormat, StateType, ) from .websocket import ( WSJSONPacketFrame, WSPacket, WSPacketFrameHeader, ) if TYPE_CHECKING: from asyncio.events import TimerHandle from typing_extensions import Self # requires Python 3.11+ from ..api import ProtectApiClient from ..data.devices import Bridge from ..data.nvr import Event from ..data.user import User ProtectObject = TypeVar("ProtectObject", bound="ProtectBaseObject") RECENT_EVENT_MAX = timedelta(seconds=30) EVENT_PING_INTERVAL = timedelta(seconds=3) EVENT_PING_INTERVAL_SECONDS = EVENT_PING_INTERVAL.total_seconds() _EMPTY_EVENT_PING_BACK: dict[Any, Any] = {} _LOGGER = logging.getLogger(__name__) @cache def _is_protect_base_object(cls: type) -> bool: """A cached version of `issubclass(cls, ProtectBaseObject)` to speed up the check.""" return issubclass(cls, ProtectBaseObject) class _ProtectModelObjects(NamedTuple): """ Class to track all child of UFP objects. objs are UFP objects lists are lists of UFP objects dicts are dicts of UFP objects """ objs: dict[str, type[ProtectBaseObject]] has_objs: bool lists: dict[str, type[ProtectBaseObject]] has_lists: bool dicts: dict[str, type[ProtectBaseObject]] has_dicts: bool class ProtectBaseObject(BaseModel): """ Base class for building Python objects from UniFi Protect JSON. * Provides `.unifi_dict_to_dict` to convert UFP JSON to a more Pythonic formatted dict (camel case to snake case) * Add attrs with matching Pyhonic name and they will automatically be populated from the UFP JSON if passed in to the constructer * Provides `.unifi_dict` to convert object back into UFP JSON """ _api: ProtectApiClient = PrivateAttr(None) class Config: arbitrary_types_allowed = True validate_assignment = True copy_on_model_validation = "shallow" def __init__(self, api: ProtectApiClient | None = None, **data: Any) -> None: """ Base class for creating Python objects from UFP JSON data. Use the static method `.from_unifi_dict()` to create objects from UFP JSON data from then the main class constructor. """ super().__init__(**data) if api is not None: self._api = api @classmethod def from_unifi_dict( cls, api: ProtectApiClient | None = None, **data: Any, ) -> Self: """ Main constructor for `ProtectBaseObject` Args: ---- api: Optional reference to the ProtectAPIClient that created generated the UFP JSON **data: decoded UFP JSON `api` is is expected as a `@property`. If it is `None` and accessed, a `BadRequest` will be raised. API can be used for saving updates for the Protect object or fetching references to other objects (cameras, users, etc.) """ if api is not None: data["api"] = api data = cls.unifi_dict_to_dict(data) if is_debug(): data.pop("api", None) return cls(api=api, **data) return cls.construct(**data) @classmethod def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self: api: ProtectApiClient | None = values.pop("api", None) ( unifi_objs, has_unifi_objs, unifi_lists, has_unifi_lists, unifi_dicts, has_unifi_dicts, ) = cls._get_protect_model() for key, value in values.items(): if has_unifi_objs and key in unifi_objs and isinstance(value, dict): values[key] = unifi_objs[key].construct(**value) elif has_unifi_lists and key in unifi_lists and isinstance(value, list): values[key] = [ unifi_lists[key].construct(**v) if isinstance(v, dict) else v for v in value ] elif has_unifi_dicts and key in unifi_dicts and isinstance(value, dict): values[key] = { k: unifi_dicts[key].construct(**v) if isinstance(v, dict) else v for k, v in value.items() } obj = super().construct(_fields_set=_fields_set, **values) if api is not None: obj._api = api return obj @classmethod @cache def _get_excluded_changed_fields(cls) -> set[str]: """ Helper method for override in child classes for fields that excluded from calculating "changed" state for a model (`.get_changed()`) """ return set() @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: """ Helper method for overriding in child classes for remapping UFP JSON keys to Python ones that do not fit the simple camel case to snake case formula. Return format is { "ufpJsonName": "python_name" } """ return {} @classmethod @cache def _get_to_unifi_remaps(cls) -> dict[str, str]: """ Helper method for overriding in child classes for reversing remap UFP JSON keys to Python ones that do not fit the simple camel case to snake case formula. Return format is { "python_name": "ufpJsonName" } """ return { to_key: from_key for from_key, to_key in cls._get_unifi_remaps().items() } @classmethod @cache def _get_protect_model(cls) -> _ProtectModelObjects: """Helper method to detect attrs of current class that are UFP Objects themselves""" objs: dict[str, type[ProtectBaseObject]] = {} lists: dict[str, type[ProtectBaseObject]] = {} dicts: dict[str, type[ProtectBaseObject]] = {} for name, field in cls.__fields__.items(): try: if _is_protect_base_object(field.type_): if field.shape == SHAPE_LIST: lists[name] = field.type_ elif field.shape == SHAPE_DICT: dicts[name] = field.type_ else: objs[name] = field.type_ except TypeError: pass return _ProtectModelObjects( objs, bool(objs), lists, bool(lists), dicts, bool(dicts) ) @classmethod @cache def _get_excluded_fields(cls) -> set[str]: """Helper method to get all excluded fields for the current object.""" protect_model = cls._get_protect_model() return set(protect_model.objs) | set(protect_model.lists) @classmethod def _clean_protect_obj( cls, data: Any, klass: type[ProtectBaseObject], api: ProtectApiClient | None, ) -> Any: if isinstance(data, dict): if api is not None: data["api"] = api return klass.unifi_dict_to_dict(data=data) return data @classmethod def _clean_protect_obj_list( cls, items: list[Any], klass: type[ProtectBaseObject], api: ProtectApiClient | None, ) -> list[Any]: return [cls._clean_protect_obj(item, klass, api) for item in items] @classmethod def _clean_protect_obj_dict( cls, items: dict[Any, Any], klass: type[ProtectBaseObject], api: ProtectApiClient | None, ) -> dict[Any, Any]: return {k: cls._clean_protect_obj(v, klass, api) for k, v in items.items()} @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: """ Helper method for overriding in child classes for converting UFP JSON data to Python data types. Return format is { "ufpJsonName": Callable[[Any], Any] } """ return {} @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: """ Takes a decoded UFP JSON dict and converts it into a Python dict * Remaps items from `._get_unifi_remaps()` * Converts camelCase keys to snake_case keys * Injects ProtectAPIClient into any child UFP object Dicts * Runs `.unifi_dict_to_dict` for any child UFP objects Args: ---- data: decoded UFP JSON dict """ # get the API client instance api: ProtectApiClient | None = data.get("api") or ( cls._api if isinstance(cls, ProtectBaseObject) else None ) conversions = cls.unifi_dict_conversions() for key, convert in conversions.items(): if (val := data.get(key)) is not None: data[key] = convert(val) # type: ignore[operator] remaps = cls._get_unifi_remaps() # convert to snake_case and remove extra fields _fields = cls.__fields__ for key in list(data): if key in remaps: # remap keys that will not be converted correctly by snake_case convert remapped_key = remaps[key] data[remapped_key] = data.pop(key) key = remapped_key new_key = to_snake_case(key) data[new_key] = data.pop(key) key = new_key if key == "api": continue if key not in _fields: del data[key] continue data[key] = convert_unifi_data(data[key], _fields[key]) if not data: return data # clean child UFP objs ( unifi_objs, has_unifi_objs, unifi_lists, has_unifi_lists, unifi_dicts, has_unifi_dicts, ) = cls._get_protect_model() for key, value in data.items(): if has_unifi_objs and key in unifi_objs: data[key] = cls._clean_protect_obj(value, unifi_objs[key], api) elif has_unifi_lists and key in unifi_lists and isinstance(value, list): data[key] = cls._clean_protect_obj_list(value, unifi_lists[key], api) elif has_unifi_dicts and key in unifi_dicts and isinstance(value, dict): data[key] = cls._clean_protect_obj_dict(value, unifi_dicts[key], api) return data def _unifi_dict_protect_obj( self, data: dict[str, Any], key: str, use_obj: bool, klass: type[ProtectBaseObject], ) -> Any: value: Any | None = data.get(key) if use_obj: value = getattr(self, key) if isinstance(value, ProtectBaseObject): value = value.unifi_dict() elif isinstance(value, dict): value = klass.construct({}).unifi_dict(data=value) # type: ignore[arg-type] return value def _unifi_dict_protect_obj_list( self, data: dict[str, Any], key: str, use_obj: bool, klass: type[ProtectBaseObject], ) -> Any: value: Any | None = data.get(key) if use_obj: value = getattr(self, key) if not isinstance(value, list): return value return [ item.unifi_dict() if isinstance(item, ProtectBaseObject) else klass.construct({}).unifi_dict(data=item) # type: ignore[arg-type] for item in value ] def _unifi_dict_protect_obj_dict( self, data: dict[str, Any], key: str, use_obj: bool, ) -> Any: value: Any | None = data.get(key) if use_obj: value = getattr(self, key) if not isinstance(value, dict): return value return { obj_key: obj.unifi_dict() if isinstance(obj, ProtectBaseObject) else obj for obj_key, obj in value.items() } def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: """ Can either convert current Python object into UFP JSON dict or take the output of a `.dict()` call and convert it. * Remaps items from `._get_unifi_remaps()` in reverse * Converts snake_case to camelCase * Automatically removes any ProtectApiClient instances that might still be in the data * Automatically calls `.unifi_dict()` for any UFP Python objects that are detected Args: ---- data: Optional output of `.dict()` for the Python object. If `None`, will call `.dict` first exclude: Optional set of fields to exclude from convert. Useful for subclassing and having custom processing for dumping to UFP JSON data. """ use_obj = False if data is None: excluded_fields = self._get_excluded_fields() if exclude is not None: excluded_fields = excluded_fields.copy() | exclude data = self.dict(exclude=excluded_fields) use_obj = True ( unifi_objs, has_unifi_objs, unifi_lists, has_unifi_lists, unifi_dicts, has_unifi_dicts, ) = self._get_protect_model() if has_unifi_objs: for key, klass in unifi_objs.items(): if use_obj or key in data: data[key] = self._unifi_dict_protect_obj(data, key, use_obj, klass) if has_unifi_lists: for key, klass in unifi_lists.items(): if use_obj or key in data: data[key] = self._unifi_dict_protect_obj_list( data, key, use_obj, klass ) if has_unifi_dicts: for key in unifi_dicts: if use_obj or key in data: data[key] = self._unifi_dict_protect_obj_dict(data, key, use_obj) # all child objects have been serialized correctly do not do it twice new_data: dict[str, Any] = serialize_unifi_obj(data, levels=2) remaps = self._get_to_unifi_remaps() for to_key in set(new_data).intersection(remaps): new_data[remaps[to_key]] = new_data.pop(to_key) return new_data def update_from_dict(cls: ProtectObject, data: dict[str, Any]) -> ProtectObject: """ Updates current object from a cleaned UFP JSON dict. The api client is injected into each dict for any child UFP objects that are detected. """ ( unifi_objs, has_unifi_objs, unifi_lists, has_unifi_lists, unifi_dicts, has_unifi_dicts, ) = cls._get_protect_model() api = cls._api _fields = cls.__fields__ unifi_obj: ProtectBaseObject | None value: Any for key, item in data.items(): if has_unifi_objs and key in unifi_objs and isinstance(item, dict): if (unifi_obj := getattr(cls, key)) is not None: value = unifi_obj.update_from_dict(item) else: value = unifi_objs[key](**item, api=api) elif has_unifi_lists and key in unifi_lists and isinstance(item, list): klass = unifi_lists[key] value = [ klass(**i, api=api) if isinstance(i, dict) else i for i in item if i is not None and isinstance(i, (dict, ProtectBaseObject)) ] else: value = convert_unifi_data(item, _fields[key]) setattr(cls, key, value) return cls def dict_with_excludes(self) -> dict[str, Any]: """Returns a dict of the current object without any UFP objects converted to dicts.""" excludes = self.__class__._get_excluded_changed_fields() return self.dict(exclude=excludes) def get_changed(self, data_before_changes: dict[str, Any]) -> dict[str, Any]: return dict_diff(data_before_changes, self.dict()) @property def api(self) -> ProtectApiClient: """ ProtectApiClient that the UFP object was created with. If no API Client was passed in time of creation, will raise `BadRequest` """ if self._api is None: raise BadRequest("API Client not initialized") return self._api class ProtectModel(ProtectBaseObject): """ Base class for UFP objects with a `modelKey` attr. Provides `.from_unifi_dict()` static helper method for automatically decoding a `modelKey` object into the correct UFP object and type """ model: ModelType | None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "modelKey": "model"} def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, {"modelKey"}) return data class UpdateSynchronization: """Helper class for managing updates to Protect devices.""" @cached_property def lock(self) -> asyncio.Lock: """Lock to prevent multiple updates at once.""" return asyncio.Lock() @cached_property def queue(self) -> asyncio.Queue[Callable[[], None]]: """Queue to store device updates.""" return asyncio.Queue() @cached_property def event(self) -> asyncio.Event: """Event to signal when a device update has been queued.""" return asyncio.Event() class ProtectModelWithId(ProtectModel): id: str _update_sync: UpdateSynchronization = PrivateAttr(None) def __init__(self, **data: Any) -> None: update_sync = data.pop("update_sync", None) super().__init__(**data) self._update_sync = update_sync or UpdateSynchronization() @classmethod def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self: update_sync = values.pop("update_sync", None) obj = super().construct(_fields_set=_fields_set, **values) obj._update_sync = update_sync or UpdateSynchronization() return obj @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return set() async def _api_update(self, data: dict[str, Any]) -> None: raise NotImplementedError def revert_changes(self, data_before_changes: dict[str, Any]) -> None: """Reverts current changes to device and resets it back to initial state""" changed = self.get_changed(data_before_changes) for key in changed: setattr(self, key, data_before_changes[key]) def can_create(self, user: User) -> bool: if (model := self.model) is not None: return user.can(model, PermissionNode.CREATE, self) return True def can_read(self, user: User) -> bool: if (model := self.model) is not None: return user.can(model, PermissionNode.READ, self) return True def can_write(self, user: User) -> bool: if (model := self.model) is not None: return user.can(model, PermissionNode.WRITE, self) return True def can_delete(self, user: User) -> bool: if (model := self.model) is not None: return user.can(model, PermissionNode.DELETE, self) return True async def queue_update(self, callback: Callable[[], None]) -> None: """ Queues a device update. This allows aggregating devices updates so if multiple ones come in all at once, they can be combined in a single PATCH. """ self._update_sync.queue.put_nowait(callback) self._update_sync.event.set() # release execution so other `queue_update` calls can abort await asyncio.sleep(0.001) self._update_sync.event.clear() try: async with asyncio_timeout(0.05): await self._update_sync.event.wait() self._update_sync.event.clear() return except (TimeoutError, asyncio.TimeoutError): async with self._update_sync.lock: # Important! Now that we have the lock, we yield to the event loop so any # updates from the websocket are processed before we generate the diff await asyncio.sleep(0) # Save the initial data before we generate the diff data_before_changes = self.dict_with_excludes() while not self._update_sync.queue.empty(): callback = self._update_sync.queue.get_nowait() callback() # Important, do not yield to the event loop before generating the diff # otherwise we may miss updates from the websocket await self._save_device_changes( data_before_changes, self.unifi_dict(data=self.get_changed(data_before_changes)), ) async def save_device( self, data_before_changes: dict[str, Any], force_emit: bool = False, revert_on_fail: bool = True, ) -> None: """ Generates a diff for unsaved changed on the device and sends them back to UFP USE WITH CAUTION, updates _all_ fields for the current object that have been changed. May have unexpected side effects. Tested updates have been added a methods on applicable devices. Args: ---- force_emit: Emit a fake UFP WS message. Should only be use for when UFP does not properly emit a WS message """ # do not allow multiple save_device calls at once release_lock = False if not self._update_sync.lock.locked(): await self._update_sync.lock.acquire() release_lock = True try: await self._save_device_changes( data_before_changes, self.unifi_dict(data=self.get_changed(data_before_changes)), force_emit=force_emit, revert_on_fail=revert_on_fail, ) finally: if release_lock: self._update_sync.lock.release() async def _save_device_changes( self, data_before_changes: dict[str, Any], updated: dict[str, Any], force_emit: bool = False, revert_on_fail: bool = True, ) -> None: """Saves the current device changes to UFP.""" _LOGGER.debug( "Saving device changes for %s (%s) data_before_changes=%s updated=%s", self.id, self.model, data_before_changes, updated, ) assert ( self._update_sync.lock.locked() ), "save_device_changes should only be called when the update lock is held" read_only_fields = self.__class__._get_read_only_fields() if self.model is None: raise BadRequest("Unknown model type") if not self._api.bootstrap.auth_user.can( self.model, PermissionNode.WRITE, self ): if revert_on_fail: self.revert_changes(data_before_changes) raise NotAuthorized(f"Do not have write permission for obj: {self.id}") # do not patch when there are no updates if updated == {}: return read_only_keys = read_only_fields.intersection(updated) if len(read_only_keys) > 0: self.revert_changes(data_before_changes) raise BadRequest( f"{type(self)} The following key(s) are read only: {read_only_keys}, updated: {updated}", ) try: await self._api_update(updated) except ClientError: if revert_on_fail: self.revert_changes(data_before_changes) raise if force_emit: self._emit_message(updated) async def emit_message(self, updated: dict[str, Any]) -> None: """Emits fake WS message for ProtectApiClient to process.""" self._emit_message(updated) def _emit_message(self, updated: dict[str, Any]) -> None: """Emits fake WS message for ProtectApiClient to process.""" if _is_ping_back := updated is _EMPTY_EVENT_PING_BACK: _LOGGER.debug("Event ping callback started for %s", self.id) if self.model is None: raise BadRequest("Unknown model type") header = WSPacketFrameHeader( packet_type=1, payload_format=ProtectWSPayloadFormat.JSON.value, deflated=0, unknown=1, payload_size=1, ) action_frame = WSJSONPacketFrame() action_frame.header = header action_frame.data = { "action": "update", "newUpdateId": None, "modelKey": self.model.value, "id": self.id, } data_frame = WSJSONPacketFrame() data_frame.header = header data_frame.data = updated message = self._api.bootstrap.process_ws_packet( WSPacket(action_frame.packed + data_frame.packed), is_ping_back=_is_ping_back, ) if message is not None: self._api.emit_message(message) class ProtectDeviceModel(ProtectModelWithId): name: str | None type: str mac: str host: IPv4Address | str | None up_since: datetime | None uptime: timedelta | None last_seen: datetime | None hardware_revision: str | None firmware_version: str | None is_updating: bool is_ssh_enabled: bool _callback_ping: TimerHandle | None = PrivateAttr(None) @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "mac", "host", "type", "upSince", "uptime", "lastSeen", "hardwareRevision", "isUpdating", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "upSince": convert_to_datetime, "uptime": lambda x: timedelta(milliseconds=int(x)), "lastSeen": convert_to_datetime, # hardware revisions for all devices are not simple numbers # so cast them all to str to be consistent "hardwareRevision": str, } | super().unifi_dict_conversions() def _event_callback_ping(self) -> None: _LOGGER.debug("Event ping timer started for %s", self.id) loop = asyncio.get_event_loop() self._callback_ping = loop.call_later( EVENT_PING_INTERVAL_SECONDS, self._emit_message, _EMPTY_EVENT_PING_BACK, ) async def set_name(self, name: str | None) -> None: """Sets name for the device""" def callback() -> None: self.name = name await self.queue_update(callback) class WiredConnectionState(ProtectBaseObject): phy_rate: float | None class WirelessConnectionState(ProtectBaseObject): signal_quality: int | None signal_strength: int | None class BluetoothConnectionState(WirelessConnectionState): experience_score: PercentFloat | None = None class WifiConnectionState(WirelessConnectionState): phy_rate: float | None channel: int | None frequency: int | None ssid: str | None bssid: str | None = None tx_rate: float | None = None # requires 2.7.5+ ap_name: str | None = None experience: str | None = None # requires 2.7.15+ connectivity: str | None = None class ProtectAdoptableDeviceModel(ProtectDeviceModel): state: StateType connection_host: IPv4Address | str | None connected_since: datetime | None latest_firmware_version: str | None firmware_build: str | None is_adopting: bool is_adopted: bool is_adopted_by_other: bool is_provisioned: bool is_rebooting: bool can_adopt: bool is_attempting_to_connect: bool is_connected: bool # requires 1.21+ market_name: str | None # requires 2.7.5+ fw_update_state: str | None = None # requires 2.8.14+ nvr_mac: str | None = None # requires 2.8.22+ guid: UUID | None = None # requires 2.9.20+ is_restoring: bool | None = None last_disconnect: datetime | None = None anonymous_device_id: UUID | None = None wired_connection_state: WiredConnectionState | None = None wifi_connection_state: WifiConnectionState | None = None bluetooth_connection_state: BluetoothConnectionState | None = None bridge_id: str | None is_downloading_firmware: bool | None # TODO: # bridgeCandidates @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "connectionHost", "connectedSince", "state", "latestFirmwareVersion", "firmwareBuild", "isAdopting", "isProvisioned", "isRebooting", "canAdopt", "isAttemptingToConnect", "bluetoothConnectionState", "isDownloadingFirmware", "anonymousDeviceId", } @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "bridge": "bridgeId", "isDownloadingFW": "isDownloadingFirmware", } async def _api_update(self, data: dict[str, Any]) -> None: if (model := self.model) is not None: return await self._api.update_device(model, self.id, data) return None def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none( data, {"wiredConnectionState", "wifiConnectionState", "bluetoothConnectionState"}, ) return data @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "lastDisconnect": convert_to_datetime, } | super().unifi_dict_conversions() @property def display_name(self) -> str: return self.name or self.market_name or self.type @property def is_wired(self) -> bool: return self.wired_connection_state is not None @property def is_wifi(self) -> bool: return self.wifi_connection_state is not None @property def is_bluetooth(self) -> bool: return self.bluetooth_connection_state is not None @property def bridge(self) -> Bridge | None: if (bridge_id := self.bridge_id) is not None: return self._api.bootstrap.bridges[bridge_id] return None @property def protect_url(self) -> str: """UFP Web app URL for this device""" return f"{self._api.base_url}/protect/devices/{self.id}" @property def is_adopted_by_us(self) -> bool: """Verifies device is adopted and controlled by this NVR.""" return self.is_adopted and not self.is_adopted_by_other def get_changed(self, data_before_changes: dict[str, Any]) -> dict[str, Any]: """Gets dictionary of all changed fields""" return dict_diff(data_before_changes, self.dict_with_excludes()) async def set_ssh(self, enabled: bool) -> None: """Sets ssh status for protect device""" def callback() -> None: self.is_ssh_enabled = enabled await self.queue_update(callback) async def reboot(self) -> None: """Reboots an adopted device""" if self.model is not None: if not self._api.bootstrap.auth_user.can( self.model, PermissionNode.WRITE, self, ): raise NotAuthorized("Do not have permission to reboot device") await self._api.reboot_device(self.model, self.id) async def unadopt(self) -> None: """Unadopt/Unmanage adopted device""" if not self.is_adopted_by_us: raise BadRequest("Device is not adopted") if self.model is not None: if not self._api.bootstrap.auth_user.can( self.model, PermissionNode.DELETE, self, ): raise NotAuthorized("Do not have permission to unadopt devices") await self._api.unadopt_device(self.model, self.id) async def adopt(self, name: str | None = None) -> None: """Adopts a device""" if not self.can_adopt: raise BadRequest("Device cannot be adopted") if self.model is not None: if not self._api.bootstrap.auth_user.can(self.model, PermissionNode.CREATE): raise NotAuthorized("Do not have permission to adopt devices") await self._api.adopt_device(self.model, self.id) if name is not None: await self.set_name(name) class ProtectMotionDeviceModel(ProtectAdoptableDeviceModel): last_motion: datetime | None is_dark: bool # not directly from UniFi last_motion_event_id: str | None = None @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | {"lastMotion", "isDark"} def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_tuple(data, ("lastMotionEventId",)) return data @property def last_motion_event(self) -> Event | None: if (last_motion_event_id := self.last_motion_event_id) is not None: return self._api.bootstrap.events.get(last_motion_event_id) return None uiprotect-6.1.0/src/uiprotect/data/bootstrap.py000066400000000000000000000477461467310220200216600ustar00rootroot00000000000000"""UniFi Protect Bootstrap.""" from __future__ import annotations import asyncio import logging from dataclasses import dataclass from datetime import datetime from typing import TYPE_CHECKING, Any from aiohttp.client_exceptions import ServerDisconnectedError from convertertools import pop_dict_set, pop_dict_tuple from pydantic.v1 import PrivateAttr, ValidationError from ..exceptions import ClientError from ..utils import normalize_mac, utc_now from .base import ( RECENT_EVENT_MAX, ProtectBaseObject, ProtectDeviceModel, ProtectModel, ProtectModelWithId, ) from .convert import create_from_unifi_dict from .devices import ( Bridge, Camera, Chime, Doorlock, Light, ProtectAdoptableDeviceModel, Sensor, Viewer, ) from .nvr import NVR, Event, Liveview from .types import EventType, FixSizeOrderedDict, ModelType from .user import Group, User from .websocket import ( WSAction, WSPacket, WSSubscriptionMessage, ) if TYPE_CHECKING: from ..api import ProtectApiClient _LOGGER = logging.getLogger(__name__) MAX_SUPPORTED_CAMERAS = 256 MAX_EVENT_HISTORY_IN_STATE_MACHINE = MAX_SUPPORTED_CAMERAS * 2 STATS_KEYS = { "eventStats", "storageStats", "stats", "systemInfo", "phyRate", "wifiConnectionState", "upSince", "uptime", "lastSeen", "recordingSchedules", } IGNORE_DEVICE_KEYS = {"nvrMac", "guid"} STATS_AND_IGNORE_DEVICE_KEYS = STATS_KEYS | IGNORE_DEVICE_KEYS _IGNORE_KEYS_BY_MODEL_TYPE = { # # `lastMotion` from cameras update every 100 milliseconds when a motion event is active # this overrides the behavior to only update `lastMotion` when a new event starts # ModelType.CAMERA: {"lastMotion"}, # # `cameraIds` is updated every 10s, but we don't need to process it since bootstrap # is resynced every so often anyways. # ModelType.CHIME: {"cameraIds"}, } IGNORE_DEVICE_KEYS_BY_MODEL_TYPE = { model_type: IGNORE_DEVICE_KEYS | keys for model_type, keys in _IGNORE_KEYS_BY_MODEL_TYPE.items() } STATS_AND_IGNORE_DEVICE_KEYS_BY_MODEL_TYPE = { model_type: STATS_AND_IGNORE_DEVICE_KEYS | keys for model_type, keys in _IGNORE_KEYS_BY_MODEL_TYPE.items() } CAMERA_EVENT_ATTR_MAP: dict[EventType, tuple[str, str]] = { EventType.MOTION: ("last_motion", "last_motion_event_id"), EventType.SMART_DETECT: ("last_smart_detect", "last_smart_detect_event_id"), EventType.SMART_DETECT_LINE: ("last_smart_detect", "last_smart_detect_event_id"), EventType.SMART_AUDIO_DETECT: ( "last_smart_audio_detect", "last_smart_audio_detect_event_id", ), EventType.RING: ("last_ring", "last_ring_event_id"), } def _process_light_event(event: Event, light: Light) -> None: light.last_motion_event_id = event.id def _process_sensor_event(event: Event, sensor: Sensor) -> None: if event.type is EventType.MOTION_SENSOR: sensor.last_motion_event_id = event.id elif event.type in {EventType.SENSOR_CLOSED, EventType.SENSOR_OPENED}: sensor.last_contact_event_id = event.id elif event.type is EventType.SENSOR_EXTREME_VALUE: sensor.extreme_value_detected_at = event.end sensor.last_value_event_id = event.id elif event.type is EventType.SENSOR_ALARM: sensor.last_value_event_id = event.id _CAMERA_SMART_AND_LINE_EVENTS = { EventType.SMART_DETECT, EventType.SMART_DETECT_LINE, } _CAMERA_SMART_AUDIO_EVENT = EventType.SMART_AUDIO_DETECT def _process_camera_event(event: Event, camera: Camera) -> None: event_type = event.type dt_attr, event_attr = CAMERA_EVENT_ATTR_MAP[event_type] event_id = event.id event_start = event.start setattr(camera, event_attr, event_id) setattr(camera, dt_attr, event_start) if event_type in _CAMERA_SMART_AND_LINE_EVENTS: for smart_type in event.smart_detect_types: camera.last_smart_detect_event_ids[smart_type] = event_id camera.last_smart_detects[smart_type] = event_start elif event_type is _CAMERA_SMART_AUDIO_EVENT: for smart_type in event.smart_detect_types: if (audio_type := smart_type.audio_type) is None: continue camera.last_smart_audio_detect_event_ids[audio_type] = event_id camera.last_smart_audio_detects[audio_type] = event_start @dataclass class WSStat: model: str action: str keys: list[str] keys_set: list[str] size: int filtered: bool class ProtectDeviceRef(ProtectBaseObject): model: ModelType id: str class Bootstrap(ProtectBaseObject): auth_user_id: str access_key: str cameras: dict[str, Camera] users: dict[str, User] groups: dict[str, Group] liveviews: dict[str, Liveview] nvr: NVR viewers: dict[str, Viewer] lights: dict[str, Light] bridges: dict[str, Bridge] sensors: dict[str, Sensor] doorlocks: dict[str, Doorlock] chimes: dict[str, Chime] last_update_id: str # TODO: # schedules # agreements # not directly from UniFi events: dict[str, Event] = FixSizeOrderedDict() capture_ws_stats: bool = False mac_lookup: dict[str, ProtectDeviceRef] = {} id_lookup: dict[str, ProtectDeviceRef] = {} _ws_stats: list[WSStat] = PrivateAttr([]) _has_doorbell: bool | None = PrivateAttr(None) _has_smart: bool | None = PrivateAttr(None) _has_media: bool | None = PrivateAttr(None) _recording_start: datetime | None = PrivateAttr(None) _refresh_tasks: set[asyncio.Task[None]] = PrivateAttr(set()) @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: api: ProtectApiClient | None = data.get("api") or ( cls._api if isinstance(cls, ProtectBaseObject) else None ) mac_lookup: dict[str, dict[str, str | ModelType]] = {} id_lookup: dict[str, dict[str, str | ModelType]] = {} data["idLookup"] = id_lookup data["macLookup"] = mac_lookup for model_type in ModelType.bootstrap_models_types_set: key = model_type.devices_key # type: ignore[attr-defined] items: dict[str, ProtectModel] = {} for item in data[key]: if ( api is not None and api.ignore_unadopted and not item.get("isAdopted", True) ): continue id_: str = item["id"] ref = {"model": model_type, "id": id_} items[id_] = item id_lookup[id_] = ref if "mac" in item: cleaned_mac = normalize_mac(item["mac"]) mac_lookup[cleaned_mac] = ref data[key] = items return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_tuple(data, ("events", "captureWsStats", "macLookup", "idLookup")) for model_type in ModelType.bootstrap_models_types_set: attr = model_type.devices_key # type: ignore[attr-defined] if attr in data and isinstance(data[attr], dict): data[attr] = list(data[attr].values()) return data @property def ws_stats(self) -> list[WSStat]: return self._ws_stats def clear_ws_stats(self) -> None: self._ws_stats = [] @property def auth_user(self) -> User: return self._api.bootstrap.users[self.auth_user_id] @property def has_doorbell(self) -> bool: if self._has_doorbell is None: self._has_doorbell = any( c.feature_flags.is_doorbell for c in self.cameras.values() ) return self._has_doorbell @property def recording_start(self) -> datetime | None: """Get earilest recording date.""" if self._recording_start is None: try: self._recording_start = min( c.stats.video.recording_start for c in self.cameras.values() if c.stats.video.recording_start is not None ) except ValueError: return None return self._recording_start @property def has_smart_detections(self) -> bool: """Check if any camera has smart detections.""" if self._has_smart is None: self._has_smart = any( c.feature_flags.has_smart_detect for c in self.cameras.values() ) return self._has_smart @property def has_media(self) -> bool: """Checks if user can read media for any camera.""" if self._has_media is None: if self.recording_start is None: return False self._has_media = any( c.can_read_media(self.auth_user) for c in self.cameras.values() ) return self._has_media def get_device_from_mac(self, mac: str) -> ProtectAdoptableDeviceModel | None: """Retrieve a device from MAC address.""" return self._get_device_from_ref(self.mac_lookup.get(normalize_mac(mac))) def get_device_from_id(self, device_id: str) -> ProtectAdoptableDeviceModel | None: """Retrieve a device from device ID (without knowing model type).""" return self._get_device_from_ref(self.id_lookup.get(device_id)) def _get_device_from_ref( self, ref: ProtectDeviceRef | None ) -> ProtectAdoptableDeviceModel | None: if ref is None: return None devices_key = ref.model.devices_key devices: dict[str, ProtectAdoptableDeviceModel] = getattr(self, devices_key) return devices[ref.id] def process_event(self, event: Event) -> None: event_type = event.type if event_type in CAMERA_EVENT_ATTR_MAP and (camera := event.camera): _process_camera_event(event, camera) elif event_type is EventType.MOTION_LIGHT and (light := event.light): _process_light_event(event, light) elif event_type is EventType.MOTION_SENSOR and (sensor := event.sensor): _process_sensor_event(event, sensor) self.events[event.id] = event def _process_add_packet( self, model_type: ModelType, data: dict[str, Any], ) -> WSSubscriptionMessage | None: obj = create_from_unifi_dict(data, api=self._api, model_type=model_type) if model_type is ModelType.EVENT: if TYPE_CHECKING: assert isinstance(obj, Event) self.process_event(obj) elif model_type is ModelType.NVR: if TYPE_CHECKING: assert isinstance(obj, NVR) self.nvr = obj elif model_type in ModelType.bootstrap_models_types_set: if TYPE_CHECKING: assert isinstance(obj, ProtectAdoptableDeviceModel) if not self._api.ignore_unadopted or ( obj.is_adopted and not obj.is_adopted_by_other ): id_ = obj.id getattr(self, model_type.devices_key)[id_] = obj ref = ProtectDeviceRef(model=model_type, id=id_) self.id_lookup[id_] = ref self.mac_lookup[normalize_mac(obj.mac)] = ref else: _LOGGER.debug("Unexpected bootstrap model type for add: %s", model_type) return None return WSSubscriptionMessage( action=WSAction.ADD, new_update_id=self.last_update_id, changed_data=obj.dict(), new_obj=obj, ) def _process_remove_packet( self, model_type: ModelType, action: dict[str, Any] ) -> WSSubscriptionMessage | None: devices_key = model_type.devices_key devices: dict[str, ProtectDeviceModel] | None = getattr(self, devices_key, None) if devices is None: return None device_id: str = action["id"] self.id_lookup.pop(device_id, None) if (device := devices.pop(device_id, None)) is None: return None self.mac_lookup.pop(normalize_mac(device.mac), None) return WSSubscriptionMessage( action=WSAction.REMOVE, new_update_id=self.last_update_id, changed_data={}, old_obj=device, ) def _process_nvr_update( self, action: dict[str, Any], data: dict[str, Any], ignore_stats: bool, ) -> WSSubscriptionMessage | None: if ignore_stats: pop_dict_set(data, STATS_KEYS) # nothing left to process if not data: return None # for another NVR in stack nvr_id: str | None = action.get("id") if nvr_id and nvr_id != self.nvr.id: return None # nothing left to process if not (data := self.nvr.unifi_dict_to_dict(data)): return None old_nvr = self.nvr.copy() self.nvr = self.nvr.update_from_dict(data) return WSSubscriptionMessage( action=WSAction.UPDATE, new_update_id=self.last_update_id, changed_data=data, new_obj=self.nvr, old_obj=old_nvr, ) def _process_device_update( self, model_type: ModelType, action: dict[str, Any], data: dict[str, Any], ignore_stats: bool, is_ping_back: bool, ) -> WSSubscriptionMessage | None: """ Process a device update packet. If is_ping_back is True, the packet is an empty packet that was generated internally as a result of an event that will expire and result in a state change. """ if ignore_stats: remove_keys = STATS_AND_IGNORE_DEVICE_KEYS_BY_MODEL_TYPE.get( model_type, STATS_AND_IGNORE_DEVICE_KEYS ) else: remove_keys = IGNORE_DEVICE_KEYS_BY_MODEL_TYPE.get( model_type, IGNORE_DEVICE_KEYS ) pop_dict_set(data, remove_keys) # nothing left to process if not data and not is_ping_back: return None devices: dict[str, ProtectModelWithId] = getattr(self, model_type.devices_key) action_id: str = action["id"] if action_id not in devices: # ignore updates to events that phase out if model_type is not ModelType.EVENT: _LOGGER.debug("Unexpected %s: %s", model_type, action_id) return None obj = devices[action_id] data = obj.unifi_dict_to_dict(data) if not data and not is_ping_back: # nothing left to process return None old_obj = obj.copy() obj = obj.update_from_dict(data) if model_type is ModelType.EVENT: if TYPE_CHECKING: assert isinstance(obj, Event) self.process_event(obj) elif model_type is ModelType.SENSOR: if TYPE_CHECKING: assert isinstance(obj, Sensor) if "alarm_triggered_at" in data and (trigged_at := obj.alarm_triggered_at): if is_recent := trigged_at + RECENT_EVENT_MAX >= utc_now(): obj.set_alarm_timeout() _LOGGER.debug("alarm_triggered_at for %s (%s)", obj.id, is_recent) devices[action_id] = obj return WSSubscriptionMessage( action=WSAction.UPDATE, new_update_id=self.last_update_id, changed_data=data, new_obj=obj, old_obj=old_obj, ) def process_ws_packet( self, packet: WSPacket, models: set[ModelType] | None = None, ignore_stats: bool = False, is_ping_back: bool = False, ) -> WSSubscriptionMessage | None: """Process a WS packet.""" capture_ws_stats = self.capture_ws_stats action = packet.action_frame.data data = packet.data_frame.data keys = list(data) if capture_ws_stats else None new_update_id: str | None = action["newUpdateId"] if new_update_id is not None: self.last_update_id = new_update_id message = self._make_ws_packet_message( action, data, models, ignore_stats, is_ping_back ) if capture_ws_stats: if TYPE_CHECKING: assert keys is not None self._ws_stats.append( WSStat( model=action["modelKey"], action=action["action"], keys=keys, keys_set=[] if message is None else list(message.changed_data), size=len(packet.raw), filtered=message is None, ), ) return message def _make_ws_packet_message( self, action: dict[str, Any], data: dict[str, Any], models: set[ModelType] | None, ignore_stats: bool, is_ping_back: bool, ) -> WSSubscriptionMessage | None: """Process a WS packet.""" model_key: str = action["modelKey"] if (model_type := ModelType.from_string(model_key)) is ModelType.UNKNOWN: _LOGGER.debug("Unknown model type: %s", model_key) return None if models and model_type not in models: return None action_action: str = action["action"] if action_action == "remove": return self._process_remove_packet(model_type, action) if not data and not is_ping_back: return None try: if action_action == "add": return self._process_add_packet(model_type, data) if action_action == "update": if model_type is ModelType.NVR: return self._process_nvr_update(action, data, ignore_stats) if model_type in ModelType.bootstrap_models_types_and_event_set: return self._process_device_update( model_type, action, data, ignore_stats, is_ping_back ) except (ValidationError, ValueError) as err: self._handle_ws_error(action_action, model_type, action, err) _LOGGER.debug( "Unexpected bootstrap model type deviceadoptedfor update: %s", model_key ) return None def _handle_ws_error( self, action_action: str, model_type: ModelType, action: dict[str, Any], err: Exception, ) -> None: msg = "" device_id: str = action["id"] if model_type is ModelType.EVENT: msg = f"Validation error processing event: {device_id}. Ignoring event." else: task = asyncio.create_task(self.refresh_device(model_type, device_id)) self._refresh_tasks.add(task) task.add_done_callback(self._refresh_tasks.discard) msg = ( f"{action_action} packet caused invalid state. " f"Refreshing device: {model_type} {device_id}" ) _LOGGER.debug("%s Error: %s", msg, err) async def refresh_device(self, model_type: ModelType, device_id: str) -> None: """Refresh a device in the bootstrap.""" try: if model_type is ModelType.NVR: device: ProtectModelWithId = await self._api.get_nvr() else: device = await self._api.get_device(model_type, device_id) except ( ValidationError, TimeoutError, asyncio.TimeoutError, ClientError, ServerDisconnectedError, ): _LOGGER.warning("Failed to refresh model: %s %s", model_type, device_id) return if isinstance(device, NVR): self.nvr = device else: devices_key = model_type.devices_key devices: dict[str, ProtectModelWithId] = getattr(self, devices_key) devices[device.id] = device _LOGGER.debug("Successfully refresh model: %s %s", model_type, device_id) async def get_is_prerelease(self) -> bool: """Get if current version of Protect is a prerelease version.""" return await self.nvr.get_is_prerelease() uiprotect-6.1.0/src/uiprotect/data/convert.py000066400000000000000000000042111467310220200212770ustar00rootroot00000000000000"""UniFi Protect Data Conversion.""" from __future__ import annotations from typing import TYPE_CHECKING, Any from ..exceptions import DataDecodeError from .devices import ( Bridge, Camera, Chime, Doorlock, Light, Sensor, Viewer, ) from .nvr import NVR, Event, Liveview from .types import ModelType from .user import CloudAccount, Group, User, UserLocation if TYPE_CHECKING: from ..api import ProtectApiClient from ..data.base import ProtectModel MODEL_TO_CLASS: dict[str, type[ProtectModel]] = { ModelType.EVENT: Event, ModelType.GROUP: Group, ModelType.USER_LOCATION: UserLocation, ModelType.CLOUD_IDENTITY: CloudAccount, ModelType.USER: User, ModelType.NVR: NVR, ModelType.LIGHT: Light, ModelType.CAMERA: Camera, ModelType.LIVEVIEW: Liveview, ModelType.VIEWPORT: Viewer, ModelType.BRIDGE: Bridge, ModelType.SENSOR: Sensor, ModelType.DOORLOCK: Doorlock, ModelType.CHIME: Chime, } def get_klass_from_dict(data: dict[str, Any]) -> type[ProtectModel]: """ Helper method to read the `modelKey` from a UFP JSON dict and get the correct Python class for conversion. Will raise `DataDecodeError` if the `modelKey` is for an unknown object. """ if "modelKey" not in data: raise DataDecodeError("No modelKey") model = ModelType(data["modelKey"]) klass = MODEL_TO_CLASS.get(model) if klass is None: raise DataDecodeError("Unknown modelKey") return klass def create_from_unifi_dict( data: dict[str, Any], api: ProtectApiClient | None = None, klass: type[ProtectModel] | None = None, model_type: ModelType | None = None, ) -> ProtectModel: """ Helper method to read the `modelKey` from a UFP JSON dict and convert to currect Python class. Will raise `DataDecodeError` if the `modelKey` is for an unknown object. """ if "modelKey" not in data: raise DataDecodeError("No modelKey") if model_type is not None and klass is None: klass = MODEL_TO_CLASS.get(model_type) if klass is None: klass = get_klass_from_dict(data) return klass.from_unifi_dict(**data, api=api) uiprotect-6.1.0/src/uiprotect/data/devices.py000066400000000000000000003304401467310220200212470ustar00rootroot00000000000000"""UniFi Protect Data.""" from __future__ import annotations import asyncio import logging import warnings from collections.abc import Callable from datetime import datetime, timedelta from functools import cache, lru_cache from ipaddress import IPv4Address from pathlib import Path from typing import TYPE_CHECKING, Any, Literal, cast from convertertools import pop_dict_set_if_none, pop_dict_tuple from pydantic.v1.fields import PrivateAttr from ..exceptions import BadRequest, NotAuthorized, StreamError from ..stream import TalkbackStream from ..utils import ( clamp_value, convert_smart_audio_types, convert_smart_types, convert_to_datetime, convert_video_modes, from_js_time, serialize_point, timedelta_total_seconds, to_js_time, utc_now, ) from .base import ( EVENT_PING_INTERVAL, ProtectAdoptableDeviceModel, ProtectBaseObject, ProtectMotionDeviceModel, ) from .types import ( DEFAULT, DEFAULT_TYPE, AudioCodecs, AudioStyle, AutoExposureMode, ChimeType, Color, DoorbellMessageType, FocusMode, GeofencingSetting, HDRMode, ICRCustomValue, ICRLuxValue, ICRSensitivity, IRLEDMode, IteratorCallback, LEDLevel, LensType, LightModeEnableType, LightModeType, LockStatusType, LowMedHigh, ModelType, MotionAlgorithm, MountPosition, MountType, Percent, PercentInt, PermissionNode, ProgressCallback, PTZPosition, PTZPreset, RecordingMode, RepeatTimes, SensorStatusType, SmartDetectAudioType, SmartDetectObjectType, TwoByteInt, VideoMode, WDRLevel, ) from .user import User if TYPE_CHECKING: from .nvr import Event, Liveview PRIVACY_ZONE_NAME = "pyufp_privacy_zone" LUX_MAPPING_VALUES = [ 30, 25, 20, 15, 12, 10, 7, 5, 3, 1, ] _LOGGER = logging.getLogger(__name__) class LightDeviceSettings(ProtectBaseObject): # Status LED is_indicator_enabled: bool # Brightness led_level: LEDLevel lux_sensitivity: LowMedHigh pir_duration: timedelta pir_sensitivity: PercentInt @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "pirDuration": lambda x: timedelta(milliseconds=x) } | super().unifi_dict_conversions() class LightOnSettings(ProtectBaseObject): # Manual toggle in UI is_led_force_on: bool class LightModeSettings(ProtectBaseObject): # main "Lighting" settings mode: LightModeType enable_at: LightModeEnableType class Light(ProtectMotionDeviceModel): is_pir_motion_detected: bool is_light_on: bool is_locating: bool light_device_settings: LightDeviceSettings light_on_settings: LightOnSettings light_mode_settings: LightModeSettings camera_id: str | None is_camera_paired: bool @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "camera": "cameraId"} @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "isPirMotionDetected", "isLightOn", "isLocating", } @property def camera(self) -> Camera | None: """Paired Camera will always be none if no camera is paired""" if self.camera_id is None: return None return self._api.bootstrap.cameras[self.camera_id] async def set_paired_camera(self, camera: Camera | None) -> None: """Sets the camera paired with the light""" async with self._update_sync.lock: # yield to the event loop once we have the lock to process any pending updates await asyncio.sleep(0) data_before_changes = self.dict_with_excludes() if camera is None: self.camera_id = None else: self.camera_id = camera.id await self.save_device(data_before_changes, force_emit=True) async def set_status_light(self, enabled: bool) -> None: """Sets the status indicator light for the light""" def callback() -> None: self.light_device_settings.is_indicator_enabled = enabled await self.queue_update(callback) async def set_led_level(self, led_level: int) -> None: """Sets the LED level for the light""" def callback() -> None: self.light_device_settings.led_level = LEDLevel(led_level) await self.queue_update(callback) async def set_light(self, enabled: bool, led_level: int | None = None) -> None: """Force turns on/off the light""" def callback() -> None: self.light_on_settings.is_led_force_on = enabled if led_level is not None: self.light_device_settings.led_level = LEDLevel(led_level) await self.queue_update(callback) async def set_sensitivity(self, sensitivity: int) -> None: """Sets motion sensitivity""" def callback() -> None: self.light_device_settings.pir_sensitivity = PercentInt(sensitivity) await self.queue_update(callback) async def set_duration(self, duration: timedelta) -> None: """Sets motion sensitivity""" if duration.total_seconds() < 15 or duration.total_seconds() > 900: raise BadRequest("Duration outside of 15s to 900s range") def callback() -> None: self.light_device_settings.pir_duration = duration await self.queue_update(callback) async def set_light_settings( self, mode: LightModeType, enable_at: LightModeEnableType | None = None, duration: timedelta | None = None, sensitivity: int | None = None, ) -> None: """ Updates various Light settings. Args: ---- mode: Light trigger mode enable_at: Then the light automatically turns on by itself duration: How long the light should remain on after motion, must be timedelta between 15s and 900s sensitivity: PIR Motion sensitivity """ if duration is not None and ( duration.total_seconds() < 15 or duration.total_seconds() > 900 ): raise BadRequest("Duration outside of 15s to 900s range") def callback() -> None: self.light_mode_settings.mode = mode if enable_at is not None: self.light_mode_settings.enable_at = enable_at if duration is not None: self.light_device_settings.pir_duration = duration if sensitivity is not None: self.light_device_settings.pir_sensitivity = PercentInt(sensitivity) await self.queue_update(callback) class CameraChannel(ProtectBaseObject): id: int # read only video_id: str # read only name: str # read only enabled: bool # read only is_rtsp_enabled: bool rtsp_alias: str | None # read only width: int height: int fps: int bitrate: int min_bitrate: int # read only max_bitrate: int # read only min_client_adaptive_bit_rate: int | None # read only min_motion_adaptive_bit_rate: int | None # read only fps_values: list[int] # read only idr_interval: int # 3.0.22+ auto_bitrate: bool | None = None auto_fps: bool | None = None _rtsp_url: str | None = PrivateAttr(None) _rtsps_url: str | None = PrivateAttr(None) @property def rtsp_url(self) -> str | None: if not self.is_rtsp_enabled or self.rtsp_alias is None: return None if self._rtsp_url is not None: return self._rtsp_url self._rtsp_url = f"rtsp://{self._api.connection_host}:{self._api.bootstrap.nvr.ports.rtsp}/{self.rtsp_alias}" return self._rtsp_url @property def rtsps_url(self) -> str | None: if not self.is_rtsp_enabled or self.rtsp_alias is None: return None if self._rtsps_url is not None: return self._rtsps_url self._rtsps_url = f"rtsps://{self._api.connection_host}:{self._api.bootstrap.nvr.ports.rtsps}/{self.rtsp_alias}?enableSrtp" return self._rtsps_url @property def is_package(self) -> bool: return self.fps <= 2 class ISPSettings(ProtectBaseObject): ae_mode: AutoExposureMode ir_led_mode: IRLEDMode ir_led_level: TwoByteInt wdr: WDRLevel icr_sensitivity: ICRSensitivity brightness: int contrast: int hue: int saturation: int sharpness: int denoise: int is_flipped_vertical: bool is_flipped_horizontal: bool is_auto_rotate_enabled: bool is_ldc_enabled: bool is_3dnr_enabled: bool is_external_ir_enabled: bool is_aggressive_anti_flicker_enabled: bool is_pause_motion_enabled: bool d_zoom_center_x: int d_zoom_center_y: int d_zoom_scale: int d_zoom_stream_id: int focus_mode: FocusMode | None = None focus_position: int touch_focus_x: int | None touch_focus_y: int | None zoom_position: PercentInt mount_position: MountPosition | None = None # requires 2.8.14+ is_color_night_vision_enabled: bool | None = None # 3.0.22+ hdr_mode: HDRMode | None = None icr_custom_value: ICRCustomValue | None = None icr_switch_mode: str | None = None spotlight_duration: int | None = None def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, {"focusMode"}) return data class OSDSettings(ProtectBaseObject): # Overlay Information is_name_enabled: bool is_date_enabled: bool is_logo_enabled: bool is_debug_enabled: bool class LEDSettings(ProtectBaseObject): # Status Light is_enabled: bool blink_rate: int # in milliseconds betweeen blinks, 0 = solid class SpeakerSettings(ProtectBaseObject): is_enabled: bool # Status Sounds are_system_sounds_enabled: bool volume: PercentInt class RecordingSettings(ProtectBaseObject): # Seconds to record before Motion pre_padding: timedelta # Seconds to record after Motion post_padding: timedelta # Seconds of Motion Needed min_motion_event_trigger: timedelta end_motion_event_delay: timedelta suppress_illumination_surge: bool # High Frame Rate Mode mode: RecordingMode geofencing: GeofencingSetting motion_algorithm: MotionAlgorithm enable_motion_detection: bool | None = None use_new_motion_algorithm: bool # requires 2.9.20+ in_schedule_mode: str | None = None out_schedule_mode: str | None = None # 2.11.13+ retention_duration: datetime | None = None smart_detect_post_padding: timedelta | None = None smart_detect_pre_padding: timedelta | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "retentionDurationMs": "retentionDuration", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "minMotionEventTrigger": lambda x: timedelta(seconds=x), "endMotionEventDelay": lambda x: timedelta(seconds=x), } | super().unifi_dict_conversions() @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "prePaddingSecs" in data: data["prePadding"] = timedelta(seconds=data.pop("prePaddingSecs")) if "postPaddingSecs" in data: data["postPadding"] = timedelta(seconds=data.pop("postPaddingSecs")) if "smartDetectPrePaddingSecs" in data: data["smartDetectPrePadding"] = timedelta( seconds=data.pop("smartDetectPrePaddingSecs"), ) if "smartDetectPostPaddingSecs" in data: data["smartDetectPostPadding"] = timedelta( seconds=data.pop("smartDetectPostPaddingSecs"), ) return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if "prePadding" in data: data["prePaddingSecs"] = data.pop("prePadding") // 1000 if "postPadding" in data: data["postPaddingSecs"] = data.pop("postPadding") // 1000 if ( "smartDetectPrePadding" in data and data["smartDetectPrePadding"] is not None ): data["smartDetectPrePaddingSecs"] = ( data.pop("smartDetectPrePadding") // 1000 ) if ( "smartDetectPostPadding" in data and data["smartDetectPostPadding"] is not None ): data["smartDetectPostPaddingSecs"] = ( data.pop("smartDetectPostPadding") // 1000 ) if "minMotionEventTrigger" in data: data["minMotionEventTrigger"] = data.pop("minMotionEventTrigger") // 1000 if "endMotionEventDelay" in data: data["endMotionEventDelay"] = data.pop("endMotionEventDelay") // 1000 return data class SmartDetectSettings(ProtectBaseObject): object_types: list[SmartDetectObjectType] audio_types: list[SmartDetectAudioType] | None = None # requires 2.8.22+ auto_tracking_object_types: list[SmartDetectObjectType] | None = None @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "audioTypes": convert_smart_audio_types, "objectTypes": convert_smart_types, "autoTrackingObjectTypes": convert_smart_types, } | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if audio_types := data.get("audioTypes"): # SMOKE_CMONX is not supported for audio types # and should not be sent to the camera data["audioTypes"] = [ t for t in audio_types if t != SmartDetectAudioType.SMOKE_CMONX.value ] return data class LCDMessage(ProtectBaseObject): type: DoorbellMessageType text: str reset_at: datetime | None = None @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "resetAt": convert_to_datetime, } | super().unifi_dict_conversions() @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "text" in data: # UniFi Protect bug: some times LCD messages can get into a bad state where message = DEFAULT MESSAGE, but no type if "type" not in data: data["type"] = DoorbellMessageType.CUSTOM_MESSAGE.value data["text"] = cls._fix_text(data["text"], data["type"]) return super().unifi_dict_to_dict(data) @classmethod def _fix_text(cls, text: str, text_type: str | None) -> str: if text_type is None: text_type = cls.type.value if text_type != DoorbellMessageType.CUSTOM_MESSAGE.value: text = text_type.replace("_", " ") return text def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if "text" in data: try: msg_type = self.type.value except AttributeError: msg_type = None data["text"] = self._fix_text(data["text"], data.get("type", msg_type)) if "resetAt" in data: data["resetAt"] = to_js_time(data["resetAt"]) return data class TalkbackSettings(ProtectBaseObject): type_fmt: AudioCodecs type_in: str bind_addr: IPv4Address bind_port: int filter_addr: str | None # can be used to restrict sender address filter_port: int | None # can be used to restrict sender port channels: int # 1 or 2 sampling_rate: int # 8000, 11025, 22050, 44100, 48000 bits_per_sample: int quality: PercentInt # only for vorbis class WifiStats(ProtectBaseObject): channel: int | None frequency: int | None link_speed_mbps: str | None signal_quality: PercentInt signal_strength: int class VideoStats(ProtectBaseObject): recording_start: datetime | None recording_end: datetime | None recording_start_lq: datetime | None recording_end_lq: datetime | None timelapse_start: datetime | None timelapse_end: datetime | None timelapse_start_lq: datetime | None timelapse_end_lq: datetime | None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "recordingStartLQ": "recordingStartLq", "recordingEndLQ": "recordingEndLq", "timelapseStartLQ": "timelapseStartLq", "timelapseEndLQ": "timelapseEndLq", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { key: convert_to_datetime for key in ( "recordingStart", "recordingEnd", "recordingStartLQ", "recordingEndLQ", "timelapseStart", "timelapseEnd", "timelapseStartLQ", "timelapseEndLQ", ) } | super().unifi_dict_conversions() class StorageStats(ProtectBaseObject): used: int | None # bytes rate: float | None # bytes / millisecond @property def rate_per_second(self) -> float | None: if self.rate is None: return None return self.rate * 1000 @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "rate" not in data: data["rate"] = None return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, {"rate"}) return data class CameraStats(ProtectBaseObject): rx_bytes: int tx_bytes: int wifi: WifiStats video: VideoStats storage: StorageStats | None wifi_quality: PercentInt wifi_strength: int @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "storage" in data and data["storage"] == {}: del data["storage"] return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if "storage" in data and data["storage"] is None: data["storage"] = {} return data class CameraZone(ProtectBaseObject): id: int name: str color: Color points: list[tuple[Percent, Percent]] @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "points": lambda x: [(p[0], p[1]) for p in x], } | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if "points" in data: data["points"] = [serialize_point(p) for p in data["points"]] return data @staticmethod def create_privacy_zone(zone_id: int) -> CameraZone: return CameraZone( id=zone_id, name=PRIVACY_ZONE_NAME, color=Color("#85BCEC"), points=[[0, 0], [1, 0], [1, 1], [0, 1]], # type: ignore[list-item] ) class MotionZone(CameraZone): sensitivity: PercentInt class SmartMotionZone(MotionZone): object_types: list[SmartDetectObjectType] @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "objectTypes": convert_smart_types, } | super().unifi_dict_conversions() class PrivacyMaskCapability(ProtectBaseObject): max_masks: int | None rectangle_only: bool class HotplugExtender(ProtectBaseObject): has_flash: bool | None = None has_ir: bool | None = None has_radar: bool | None = None is_attached: bool | None = None # 3.0.22+ flash_range: Any | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "hasIR": "hasIr"} class Hotplug(ProtectBaseObject): audio: bool | None = None video: bool | None = None extender: HotplugExtender | None = None # 2.8.35+ standalone_adoption: bool | None = None class PTZRangeSingle(ProtectBaseObject): max: float | None min: float | None step: float | None class PTZRange(ProtectBaseObject): steps: PTZRangeSingle degrees: PTZRangeSingle def to_native_value(self, degree_value: float, is_relative: bool = False) -> float: """Convert degree values to step values.""" if ( self.degrees.max is None or self.degrees.min is None or self.degrees.step is None or self.steps.max is None or self.steps.min is None or self.steps.step is None ): raise BadRequest("degree to step conversion not supported.") if not is_relative: degree_value -= self.degrees.min step_range = self.steps.max - self.steps.min degree_range = self.degrees.max - self.degrees.min ratio = step_range / degree_range step_value = clamp_value(degree_value * ratio, self.steps.step) if not is_relative: step_value = self.steps.min + step_value return step_value class PTZZoomRange(PTZRange): ratio: float def to_native_value(self, zoom_value: float, is_relative: bool = False) -> float: """Convert zoom values to step values.""" if self.steps.max is None or self.steps.min is None or self.steps.step is None: raise BadRequest("step conversion not supported.") step_range = self.steps.max - self.steps.min # zoom levels start at 1 ratio = step_range / (self.ratio - 1) if not is_relative: zoom_value -= 1 step_value = clamp_value(zoom_value * ratio, self.steps.step) if not is_relative: step_value = self.steps.min + step_value return step_value class CameraFeatureFlags(ProtectBaseObject): can_adjust_ir_led_level: bool can_magic_zoom: bool can_optical_zoom: bool can_touch_focus: bool has_accelerometer: bool has_aec: bool has_bluetooth: bool has_chime: bool has_external_ir: bool has_icr_sensitivity: bool has_ldc: bool has_led_ir: bool has_led_status: bool has_line_in: bool has_mic: bool has_privacy_mask: bool has_rtc: bool has_sd_card: bool has_speaker: bool has_wifi: bool has_hdr: bool has_auto_icr_only: bool video_modes: list[VideoMode] video_mode_max_fps: list[int] has_motion_zones: bool has_lcd_screen: bool smart_detect_types: list[SmartDetectObjectType] motion_algorithms: list[MotionAlgorithm] has_square_event_thumbnail: bool has_package_camera: bool privacy_mask_capability: PrivacyMaskCapability has_smart_detect: bool audio: list[str] = [] audio_codecs: list[AudioCodecs] = [] mount_positions: list[MountPosition] = [] has_infrared: bool | None = None lens_type: LensType | None = None hotplug: Hotplug | None = None smart_detect_audio_types: list[SmartDetectAudioType] | None = None # 2.7.18+ is_doorbell: bool # 2.8.22+ lens_model: str | None = None # 2.9.20+ has_color_lcd_screen: bool | None = None has_line_crossing: bool | None = None has_line_crossing_counting: bool | None = None has_liveview_tracking: bool | None = None # 2.10.10+ has_flash: bool | None = None is_ptz: bool | None = None # 2.11.13+ audio_style: list[AudioStyle] | None = None has_vertical_flip: bool | None = None # 3.0.22+ flash_range: Any | None = None focus: PTZRange pan: PTZRange tilt: PTZRange zoom: PTZZoomRange @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "smartDetectTypes": convert_smart_types, "smartDetectAudioTypes": convert_smart_audio_types, "videoModes": convert_video_modes, } | super().unifi_dict_conversions() @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: # backport support for `is_doorbell` to older versions of Protect if "hasChime" in data and "isDoorbell" not in data: data["isDoorbell"] = data["hasChime"] return super().unifi_dict_to_dict(data) @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "hasAutoICROnly": "hasAutoIcrOnly"} @property def has_highfps(self) -> bool: return VideoMode.HIGH_FPS in self.video_modes @property def has_wdr(self) -> bool: return not self.has_hdr class CameraLenses(ProtectBaseObject): id: int video: VideoStats class CameraHomekitSettings(ProtectBaseObject): microphone_muted: bool speaker_muted: bool stream_in_progress: bool talkback_settings_active: bool class CameraAudioSettings(ProtectBaseObject): style: list[AudioStyle] @lru_cache def _chime_type_from_total_seconds(total_seconds: float) -> ChimeType: if total_seconds == 0.3: return ChimeType.MECHANICAL if total_seconds > 0.3: return ChimeType.DIGITAL return ChimeType.NONE class Camera(ProtectMotionDeviceModel): is_deleting: bool # Microphone Sensitivity mic_volume: PercentInt is_mic_enabled: bool is_recording: bool is_motion_detected: bool is_smart_detected: bool phy_rate: float | None hdr_mode: bool # Recording Quality -> High Frame video_mode: VideoMode is_probing_for_wifi: bool chime_duration: timedelta last_ring: datetime | None is_live_heatmap_enabled: bool video_reconfiguration_in_progress: bool channels: list[CameraChannel] isp_settings: ISPSettings talkback_settings: TalkbackSettings osd_settings: OSDSettings led_settings: LEDSettings speaker_settings: SpeakerSettings recording_settings: RecordingSettings smart_detect_settings: SmartDetectSettings motion_zones: list[MotionZone] privacy_zones: list[CameraZone] smart_detect_zones: list[SmartMotionZone] stats: CameraStats feature_flags: CameraFeatureFlags lcd_message: LCDMessage | None lenses: list[CameraLenses] platform: str has_speaker: bool has_wifi: bool audio_bitrate: int can_manage: bool is_managed: bool voltage: float | None # requires 1.21+ is_poor_network: bool | None is_wireless_uplink_enabled: bool | None # requires 2.6.13+ homekit_settings: CameraHomekitSettings | None = None # requires 2.6.17+ ap_mgmt_ip: IPv4Address | None = None # requires 2.7.5+ is_waterproof_case_attached: bool | None = None last_disconnect: datetime | None = None # requires 2.8.14+ is_2k: bool | None = None is_4k: bool | None = None use_global: bool | None = None # requires 2.8.22+ user_configured_ap: bool | None = None # requires 2.9.20+ has_recordings: bool | None = None # requires 2.10.10+ is_ptz: bool | None = None # requires 2.11.13+ audio_settings: CameraAudioSettings | None = None # TODO: used for adopting # apMac read only # apRssi read only # elementInfo read only # TODO: # lastPrivacyZonePositionId # smartDetectLines # streamSharing read only # stopStreamLevel # uplinkDevice # recordingSchedulesV2 # not directly from UniFi last_ring_event_id: str | None = None last_smart_detect: datetime | None = None last_smart_audio_detect: datetime | None = None last_smart_detect_event_id: str | None = None last_smart_audio_detect_event_id: str | None = None last_smart_detects: dict[SmartDetectObjectType, datetime] = {} last_smart_audio_detects: dict[SmartDetectAudioType, datetime] = {} last_smart_detect_event_ids: dict[SmartDetectObjectType, str] = {} last_smart_audio_detect_event_ids: dict[SmartDetectAudioType, str] = {} talkback_stream: TalkbackStream | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "is2K": "is2k", "is4K": "is4k"} @classmethod @cache def _get_excluded_changed_fields(cls) -> set[str]: return super()._get_excluded_changed_fields() | { "last_ring_event_id", "last_smart_detect", "last_smart_audio_detect", "last_smart_detect_event_id", "last_smart_audio_detect_event_id", "last_smart_detects", "last_smart_audio_detects", "last_smart_detect_event_ids", "last_smart_audio_detect_event_ids", "talkback_stream", } @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "stats", "isDeleting", "isRecording", "isMotionDetected", "isSmartDetected", "phyRate", "isProbingForWifi", "lastRing", "isLiveHeatmapEnabled", "videoReconfigurationInProgress", "lenses", "isPoorNetwork", "featureFlags", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "chimeDuration": lambda x: timedelta(milliseconds=x), } | super().unifi_dict_conversions() @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: # LCD messages comes back as empty dict {} if "lcdMessage" in data and len(data["lcdMessage"]) == 0: del data["lcdMessage"] return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: if data is not None: if "motion_zones" in data: data["motion_zones"] = [ MotionZone(**z).unifi_dict() for z in data["motion_zones"] ] if "privacy_zones" in data: data["privacy_zones"] = [ CameraZone(**z).unifi_dict() for z in data["privacy_zones"] ] if "smart_detect_zones" in data: data["smart_detect_zones"] = [ SmartMotionZone(**z).unifi_dict() for z in data["smart_detect_zones"] ] data = super().unifi_dict(data=data, exclude=exclude) pop_dict_tuple( data, ( "lastRingEventId", "lastSmartDetect", "lastSmartAudioDetect", "lastSmartDetectEventId", "lastSmartAudioDetectEventId", "lastSmartDetects", "lastSmartAudioDetects", "lastSmartDetectEventIds", "lastSmartAudioDetectEventIds", "talkbackStream", ), ) if "lcdMessage" in data and data["lcdMessage"] is None: data["lcdMessage"] = {} return data def get_changed(self, data_before_changes: dict[str, Any]) -> dict[str, Any]: updated = super().get_changed(data_before_changes) if "lcd_message" in updated: lcd_message = updated["lcd_message"] # to "clear" LCD message, set reset_at to a time in the past if lcd_message is None: updated["lcd_message"] = {"reset_at": utc_now() - timedelta(seconds=10)} # otherwise, pass full LCD message to prevent issues elif self.lcd_message is not None: updated["lcd_message"] = self.lcd_message.dict() # if reset_at is not passed in, it will default to reset in 1 minute if lcd_message is not None and "reset_at" not in lcd_message: if self.lcd_message is None: updated["lcd_message"]["reset_at"] = None else: updated["lcd_message"]["reset_at"] = self.lcd_message.reset_at return updated def update_from_dict(self, data: dict[str, Any]) -> Camera: # a message in the past is actually a signal to wipe the message if (reset_at := data.get("lcd_message", {}).get("reset_at")) is not None: if utc_now() > from_js_time(reset_at): # Important: Make a copy of the data before modifying it # since unifi_dict_to_dict will otherwise report incorrect changes data = data.copy() data["lcd_message"] = None return super().update_from_dict(data) @property def last_ring_event(self) -> Event | None: if (last_ring_event_id := self.last_ring_event_id) is None: return None return self._api.bootstrap.events.get(last_ring_event_id) @property def last_smart_detect_event(self) -> Event | None: """Get the last smart detect event id.""" if (last_smart_detect_event_id := self.last_smart_detect_event_id) is None: return None return self._api.bootstrap.events.get(last_smart_detect_event_id) @property def hdr_mode_display(self) -> Literal["auto", "off", "always"]: """Get HDR mode similar to how Protect interface works.""" if not self.hdr_mode: return "off" if self.isp_settings.hdr_mode == HDRMode.NORMAL: return "auto" return "always" @property def icr_lux_display(self) -> int | None: """Get ICR Custom Lux value similar to how the Protect interface works.""" if self.isp_settings.icr_custom_value is None: return None return LUX_MAPPING_VALUES[10 - self.isp_settings.icr_custom_value] def get_last_smart_detect_event( self, smart_type: SmartDetectObjectType, ) -> Event | None: """Get the last smart detect event for given type.""" if event_id := self.last_smart_detect_event_ids.get(smart_type): return self._api.bootstrap.events.get(event_id) return None @property def last_smart_audio_detect_event(self) -> Event | None: """Get the last smart audio detect event id.""" if ( last_smart_audio_detect_event_id := self.last_smart_audio_detect_event_id ) is None: return None return self._api.bootstrap.events.get(last_smart_audio_detect_event_id) def get_last_smart_audio_detect_event( self, smart_type: SmartDetectAudioType, ) -> Event | None: """Get the last smart audio detect event for given type.""" if (event_id := self.last_smart_audio_detect_event_ids.get(smart_type)) is None: return None return self._api.bootstrap.events.get(event_id) @property def timelapse_url(self) -> str: return f"{self._api.base_url}/protect/timelapse/{self.id}" @property def is_privacy_on(self) -> bool: index, _ = self.get_privacy_zone() return index is not None @property def is_recording_enabled(self) -> bool: """ Is recording footage/events from the camera enabled? If recording is not enabled, cameras will not produce any footage, thumbnails, motion/smart detection events. """ if self.use_global: return self._api.bootstrap.nvr.is_global_recording_enabled return self.recording_settings.mode is not RecordingMode.NEVER @property def is_smart_detections_allowed(self) -> bool: """Is smart detections allowed for this camera?""" return ( self.is_recording_enabled and self._api.bootstrap.nvr.is_smart_detections_enabled ) @property def is_license_plate_detections_allowed(self) -> bool: """Is license plate detections allowed for this camera?""" return ( self.is_recording_enabled and self._api.bootstrap.nvr.is_license_plate_detections_enabled ) @property def is_face_detections_allowed(self) -> bool: """Is face detections allowed for this camera?""" return ( self.is_recording_enabled and self._api.bootstrap.nvr.is_face_detections_enabled ) @property def active_recording_settings(self) -> RecordingSettings: """Get active recording settings.""" if self.use_global and self._api.bootstrap.nvr.global_camera_settings: return self._api.bootstrap.nvr.global_camera_settings.recording_settings return self.recording_settings @property def active_smart_detect_settings(self) -> SmartDetectSettings: """Get active smart detection settings.""" if self.use_global and self._api.bootstrap.nvr.global_camera_settings: return self._api.bootstrap.nvr.global_camera_settings.smart_detect_settings return self.smart_detect_settings @property def active_smart_detect_types(self) -> set[SmartDetectObjectType]: """Get active smart detection types.""" if self.use_global: return set(self.smart_detect_settings.object_types).intersection( self.feature_flags.smart_detect_types ) return set(self.smart_detect_settings.object_types) @property def active_audio_detect_types(self) -> set[SmartDetectAudioType]: """Get active audio detection types.""" if not (enabled_audio_types := self.smart_detect_settings.audio_types): return set() if self.use_global: if not (feature_audio_types := self.feature_flags.smart_detect_audio_types): return set() return set(feature_audio_types).intersection(enabled_audio_types) return set(enabled_audio_types) @property def is_motion_detection_on(self) -> bool: """Is Motion Detection available and enabled (camera will produce motion events)?""" return ( self.is_recording_enabled and self.active_recording_settings.enable_motion_detection is not False ) @property def is_motion_currently_detected(self) -> bool: """Is motion currently being detected""" return ( self.is_motion_detection_on and self.is_motion_detected and self.last_motion_event is not None and self.last_motion_event.end is None ) async def set_motion_detection(self, enabled: bool) -> None: """Sets motion detection on camera""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.recording_settings.enable_motion_detection = enabled await self.queue_update(callback) async def set_use_global(self, enabled: bool) -> None: """Sets if camera should use global recording settings or not.""" def callback() -> None: self.use_global = enabled await self.queue_update(callback) # region Object Smart Detections def _is_smart_enabled(self, smart_type: SmartDetectObjectType) -> bool: return ( self.is_recording_enabled and smart_type in self.active_smart_detect_types ) def _is_smart_detected(self, smart_type: SmartDetectObjectType) -> bool: event = self.get_last_smart_detect_event(smart_type) return ( self._is_smart_enabled(smart_type) and self.is_smart_detected and event is not None and event.end is None and smart_type in event.smart_detect_types ) @property def is_smart_currently_detected(self) -> bool: """Is smart detection currently being detected""" return ( self.is_recording_enabled and bool(self.active_smart_detect_types) and self.is_smart_detected and self.last_smart_detect_event is not None and self.last_smart_detect_event.end is None ) # region Person @property def can_detect_person(self) -> bool: return SmartDetectObjectType.PERSON in self.feature_flags.smart_detect_types @property def is_person_detection_on(self) -> bool: """ Is Person Detection available and enabled (camera will produce person smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.PERSON) @property def last_person_detect_event(self) -> Event | None: """Get the last person smart detection event.""" return self.get_last_smart_detect_event(SmartDetectObjectType.PERSON) @property def last_person_detect(self) -> datetime | None: """Get the last person smart detection event.""" return self.last_smart_detects.get(SmartDetectObjectType.PERSON) @property def is_person_currently_detected(self) -> bool: """Is person currently being detected""" return self._is_smart_detected(SmartDetectObjectType.PERSON) async def set_person_detection(self, enabled: bool) -> None: """Toggles person smart detection. Requires camera to have smart detection""" return await self._set_object_detect(SmartDetectObjectType.PERSON, enabled) @property def is_person_tracking_enabled(self) -> bool: """Is person tracking enabled""" return ( self.active_smart_detect_settings.auto_tracking_object_types is not None and SmartDetectObjectType.PERSON in self.active_smart_detect_settings.auto_tracking_object_types ) # endregion # region Vehicle @property def can_detect_vehicle(self) -> bool: return SmartDetectObjectType.VEHICLE in self.feature_flags.smart_detect_types @property def is_vehicle_detection_on(self) -> bool: """ Is Vehicle Detection available and enabled (camera will produce vehicle smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.VEHICLE) @property def last_vehicle_detect_event(self) -> Event | None: """Get the last vehicle smart detection event.""" return self.get_last_smart_detect_event(SmartDetectObjectType.VEHICLE) @property def last_vehicle_detect(self) -> datetime | None: """Get the last vehicle smart detection event.""" return self.last_smart_detects.get(SmartDetectObjectType.VEHICLE) @property def is_vehicle_currently_detected(self) -> bool: """Is vehicle currently being detected""" return self._is_smart_detected(SmartDetectObjectType.VEHICLE) async def set_vehicle_detection(self, enabled: bool) -> None: """Toggles vehicle smart detection. Requires camera to have smart detection""" return await self._set_object_detect(SmartDetectObjectType.VEHICLE, enabled) # endregion # region License Plate @property def can_detect_license_plate(self) -> bool: return ( SmartDetectObjectType.LICENSE_PLATE in self.feature_flags.smart_detect_types ) @property def is_license_plate_detection_on(self) -> bool: """ Is License Plate Detection available and enabled (camera will produce face license plate detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.LICENSE_PLATE) @property def last_license_plate_detect_event(self) -> Event | None: """Get the last license plate smart detection event.""" return self.get_last_smart_detect_event(SmartDetectObjectType.LICENSE_PLATE) @property def last_license_plate_detect(self) -> datetime | None: """Get the last license plate smart detection event.""" return self.last_smart_detects.get(SmartDetectObjectType.LICENSE_PLATE) @property def is_license_plate_currently_detected(self) -> bool: """Is license plate currently being detected""" return self._is_smart_detected(SmartDetectObjectType.LICENSE_PLATE) async def set_license_plate_detection(self, enabled: bool) -> None: """Toggles license plate smart detection. Requires camera to have smart detection""" return await self._set_object_detect( SmartDetectObjectType.LICENSE_PLATE, enabled, ) # endregion # region Package @property def can_detect_package(self) -> bool: return SmartDetectObjectType.PACKAGE in self.feature_flags.smart_detect_types @property def is_package_detection_on(self) -> bool: """ Is Package Detection available and enabled (camera will produce package smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.PACKAGE) @property def last_package_detect_event(self) -> Event | None: """Get the last package smart detection event.""" return self.get_last_smart_detect_event(SmartDetectObjectType.PACKAGE) @property def last_package_detect(self) -> datetime | None: """Get the last package smart detection event.""" return self.last_smart_detects.get(SmartDetectObjectType.PACKAGE) @property def is_package_currently_detected(self) -> bool: """Is package currently being detected""" return self._is_smart_detected(SmartDetectObjectType.PACKAGE) async def set_package_detection(self, enabled: bool) -> None: """Toggles package smart detection. Requires camera to have smart detection""" return await self._set_object_detect(SmartDetectObjectType.PACKAGE, enabled) # endregion # region Animal @property def can_detect_animal(self) -> bool: return SmartDetectObjectType.ANIMAL in self.feature_flags.smart_detect_types @property def is_animal_detection_on(self) -> bool: """ Is Animal Detection available and enabled (camera will produce package smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.ANIMAL) @property def last_animal_detect_event(self) -> Event | None: """Get the last animal smart detection event.""" return self.get_last_smart_detect_event(SmartDetectObjectType.ANIMAL) @property def last_animal_detect(self) -> datetime | None: """Get the last animal smart detection event.""" return self.last_smart_detects.get(SmartDetectObjectType.ANIMAL) @property def is_animal_currently_detected(self) -> bool: """Is animal currently being detected""" return self._is_smart_detected(SmartDetectObjectType.ANIMAL) async def set_animal_detection(self, enabled: bool) -> None: """Toggles animal smart detection. Requires camera to have smart detection""" return await self._set_object_detect(SmartDetectObjectType.ANIMAL, enabled) # endregion # endregion # region Audio Smart Detections def _can_detect_audio(self, smart_type: SmartDetectObjectType) -> bool: audio_type = smart_type.audio_type return ( audio_type is not None and ( smart_detect_audio_types := self.feature_flags.smart_detect_audio_types ) is not None and audio_type in smart_detect_audio_types ) def _is_audio_enabled(self, smart_type: SmartDetectObjectType) -> bool: audio_type = smart_type.audio_type return ( audio_type is not None and self.is_recording_enabled and audio_type in self.active_audio_detect_types ) def _is_audio_detected(self, smart_type: SmartDetectObjectType) -> bool: audio_type = smart_type.audio_type if audio_type is None: return False event = self.get_last_smart_audio_detect_event(audio_type) return ( self._is_audio_enabled(smart_type) and event is not None and event.end is None and smart_type in event.smart_detect_types ) @property def is_audio_currently_detected(self) -> bool: """Is audio detection currently being detected""" return ( self.is_recording_enabled and bool(self.active_audio_detect_types) and (last_smart_audio_detect_event := self.last_smart_audio_detect_event) is not None and last_smart_audio_detect_event.end is None ) # region Smoke Alarm @property def can_detect_smoke(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.SMOKE) @property def is_smoke_detection_on(self) -> bool: """ Is Smoke Alarm Detection available and enabled (camera will produce smoke smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SMOKE) @property def last_smoke_detect_event(self) -> Event | None: """Get the last person smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.SMOKE) @property def last_smoke_detect(self) -> datetime | None: """Get the last smoke smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.SMOKE) @property def is_smoke_currently_detected(self) -> bool: """Is smoke alarm currently being detected""" return self._is_audio_detected(SmartDetectObjectType.SMOKE) async def set_smoke_detection(self, enabled: bool) -> None: """Toggles smoke smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.SMOKE, enabled) # endregion # region CO Alarm @property def can_detect_co(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.CMONX) @property def is_co_detection_on(self) -> bool: """ Is CO Alarm Detection available and enabled (camera will produce smoke smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.CMONX) @property def last_cmonx_detect_event(self) -> Event | None: """Get the last CO alarm smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.CMONX) @property def last_cmonx_detect(self) -> datetime | None: """Get the last CO alarm smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.CMONX) @property def is_cmonx_currently_detected(self) -> bool: """Is CO alarm currently being detected""" return self._is_audio_detected(SmartDetectObjectType.CMONX) async def set_cmonx_detection(self, enabled: bool) -> None: """Toggles smoke smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.CMONX, enabled) # endregion # region Siren @property def can_detect_siren(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.SIREN) @property def is_siren_detection_on(self) -> bool: """ Is Siren Detection available and enabled (camera will produce siren smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SIREN) @property def last_siren_detect_event(self) -> Event | None: """Get the last Siren smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.SIREN) @property def last_siren_detect(self) -> datetime | None: """Get the last Siren smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.SIREN) @property def is_siren_currently_detected(self) -> bool: """Is Siren currently being detected""" return self._is_audio_detected(SmartDetectObjectType.SIREN) async def set_siren_detection(self, enabled: bool) -> None: """Toggles siren smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.SIREN, enabled) # endregion # region Baby Cry @property def can_detect_baby_cry(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.BABY_CRY) @property def is_baby_cry_detection_on(self) -> bool: """ Is Baby Cry Detection available and enabled (camera will produce baby cry smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BABY_CRY) @property def last_baby_cry_detect_event(self) -> Event | None: """Get the last Baby Cry smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.BABY_CRY) @property def last_baby_cry_detect(self) -> datetime | None: """Get the last Baby Cry smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.BABY_CRY) @property def is_baby_cry_currently_detected(self) -> bool: """Is Baby Cry currently being detected""" return self._is_audio_detected(SmartDetectObjectType.BABY_CRY) async def set_baby_cry_detection(self, enabled: bool) -> None: """Toggles baby_cry smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.BABY_CRY, enabled) # endregion # region Speaking @property def can_detect_speaking(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.SPEAK) @property def is_speaking_detection_on(self) -> bool: """ Is Speaking Detection available and enabled (camera will produce speaking smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SPEAK) @property def last_speaking_detect_event(self) -> Event | None: """Get the last Speaking smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.SPEAK) @property def last_speaking_detect(self) -> datetime | None: """Get the last Speaking smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.SPEAK) @property def is_speaking_currently_detected(self) -> bool: """Is Speaking currently being detected""" return self._is_audio_detected(SmartDetectObjectType.SPEAK) async def set_speaking_detection(self, enabled: bool) -> None: """Toggles speaking smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.SPEAK, enabled) # endregion # region Bark @property def can_detect_bark(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.BARK) @property def is_bark_detection_on(self) -> bool: """ Is Bark Detection available and enabled (camera will produce barking smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BARK) @property def last_bark_detect_event(self) -> Event | None: """Get the last Bark smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.BARK) @property def last_bark_detect(self) -> datetime | None: """Get the last Bark smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.BARK) @property def is_bark_currently_detected(self) -> bool: """Is Bark currently being detected""" return self._is_audio_detected(SmartDetectObjectType.BARK) async def set_bark_detection(self, enabled: bool) -> None: """Toggles bark smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.BARK, enabled) # endregion # region Car Alarm # (burglar in code, car alarm in Protect UI) @property def can_detect_car_alarm(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.BURGLAR) @property def is_car_alarm_detection_on(self) -> bool: """ Is Car Alarm Detection available and enabled (camera will produce car alarm smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BURGLAR) @property def last_car_alarm_detect_event(self) -> Event | None: """Get the last Car Alarm smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.BURGLAR) @property def last_car_alarm_detect(self) -> datetime | None: """Get the last Car Alarm smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.BURGLAR) @property def is_car_alarm_currently_detected(self) -> bool: """Is Car Alarm currently being detected""" return self._is_audio_detected(SmartDetectObjectType.BURGLAR) async def set_car_alarm_detection(self, enabled: bool) -> None: """Toggles car_alarm smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.BURGLAR, enabled) # endregion # region Car Horn @property def can_detect_car_horn(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.CAR_HORN) @property def is_car_horn_detection_on(self) -> bool: """ Is Car Horn Detection available and enabled (camera will produce car horn smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.CAR_HORN) @property def last_car_horn_detect_event(self) -> Event | None: """Get the last Car Horn smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.CAR_HORN) @property def last_car_horn_detect(self) -> datetime | None: """Get the last Car Horn smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.CAR_HORN) @property def is_car_horn_currently_detected(self) -> bool: """Is Car Horn currently being detected""" return self._is_audio_detected(SmartDetectObjectType.CAR_HORN) async def set_car_horn_detection(self, enabled: bool) -> None: """Toggles car_horn smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.CAR_HORN, enabled) # endregion # region Glass Break @property def can_detect_glass_break(self) -> bool: return self._can_detect_audio(SmartDetectObjectType.GLASS_BREAK) @property def is_glass_break_detection_on(self) -> bool: """ Is Glass Break available and enabled (camera will produce glass break smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.GLASS_BREAK) @property def last_glass_break_detect_event(self) -> Event | None: """Get the last Glass Break smart detection event.""" return self.get_last_smart_audio_detect_event(SmartDetectAudioType.GLASS_BREAK) @property def last_glass_break_detect(self) -> datetime | None: """Get the last Glass Break smart detection event.""" return self.last_smart_audio_detects.get(SmartDetectAudioType.GLASS_BREAK) @property def is_glass_break_currently_detected(self) -> bool: """Is Glass Break currently being detected""" return self._is_audio_detected(SmartDetectObjectType.GLASS_BREAK) async def set_glass_break_detection(self, enabled: bool) -> None: """Toggles glass_break smart detection. Requires camera to have smart detection""" return await self._set_audio_detect(SmartDetectAudioType.GLASS_BREAK, enabled) # endregion # endregion @property def chime_type(self) -> ChimeType: return _chime_type_from_total_seconds( timedelta_total_seconds(self.chime_duration) ) @property def chime_duration_seconds(self) -> float: return timedelta_total_seconds(self.chime_duration) @property def is_digital_chime(self) -> bool: return self.chime_type is ChimeType.DIGITAL @property def high_camera_channel(self) -> CameraChannel | None: if len(self.channels) >= 3: return self.channels[0] return None @property def medium_camera_channel(self) -> CameraChannel | None: if len(self.channels) >= 3: return self.channels[1] return None @property def low_camera_channel(self) -> CameraChannel | None: if len(self.channels) >= 3: return self.channels[2] return None @property def default_camera_channel(self) -> CameraChannel | None: for channel in [ self.high_camera_channel, self.medium_camera_channel, self.low_camera_channel, ]: if channel is not None and channel.is_rtsp_enabled: return channel return self.high_camera_channel @property def package_camera_channel(self) -> CameraChannel | None: if self.feature_flags.has_package_camera and len(self.channels) == 4: return self.channels[3] return None @property def is_high_fps_enabled(self) -> bool: return self.video_mode is VideoMode.HIGH_FPS @property def is_video_ready(self) -> bool: return ( lens_type := self.feature_flags.lens_type ) is None or lens_type is not LensType.NONE @property def has_removable_lens(self) -> bool: return ( hotplug := self.feature_flags.hotplug ) is not None and hotplug.video is not None @property def has_removable_speaker(self) -> bool: return ( hotplug := self.feature_flags.hotplug ) is not None and hotplug.audio is not None @property def has_mic(self) -> bool: return self.feature_flags.has_mic or self.has_removable_speaker @property def has_color_night_vision(self) -> bool: if ( (hotplug := self.feature_flags.hotplug) is not None and (extender := hotplug.extender) is not None and (is_attached := extender.is_attached) is not None ): return is_attached return False def get_privacy_zone(self) -> tuple[int | None, CameraZone | None]: for index, zone in enumerate(self.privacy_zones): if zone.name == PRIVACY_ZONE_NAME: return index, zone return None, None def add_privacy_zone(self) -> None: index, _ = self.get_privacy_zone() if index is None: zone_id = 0 privacy_zones = self.privacy_zones if len(privacy_zones) > 0: zone_id = privacy_zones[-1].id + 1 privacy_zones.append(CameraZone.create_privacy_zone(zone_id)) def remove_privacy_zone(self) -> None: index, _ = self.get_privacy_zone() if index is not None: self.privacy_zones.pop(index) async def get_snapshot( self, width: int | None = None, height: int | None = None, dt: datetime | None = None, ) -> bytes | None: """ Gets snapshot for camera. Datetime of screenshot is approximate. It may be +/- a few seconds. """ if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) if height is None and width is None and self.high_camera_channel is not None: height = self.high_camera_channel.height return await self._api.get_camera_snapshot(self.id, width, height, dt=dt) async def get_package_snapshot( self, width: int | None = None, height: int | None = None, dt: datetime | None = None, ) -> bytes | None: """ Gets snapshot from the package camera. Datetime of screenshot is approximate. It may be +/- a few seconds. """ if not self.feature_flags.has_package_camera: raise BadRequest("Device does not have package camera") if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) if height is None and width is None and self.package_camera_channel is not None: height = self.package_camera_channel.height return await self._api.get_package_camera_snapshot( self.id, width, height, dt=dt ) async def get_video( self, start: datetime, end: datetime, channel_index: int = 0, output_file: Path | None = None, iterator_callback: IteratorCallback | None = None, progress_callback: ProgressCallback | None = None, chunk_size: int = 65536, fps: int | None = None, ) -> bytes | None: """ Exports MP4 video from a given camera at a specific time. Start/End of video export are approximate. It may be +/- a few seconds. It is recommended to provide a output file or progress callback for larger video clips, otherwise the full video must be downloaded to memory before being written. Providing the `fps` parameter creates a "timelapse" export wtih the given FPS value. Protect app gives the options for 60x (fps=4), 120x (fps=8), 300x (fps=20), and 600x (fps=40). """ if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) return await self._api.get_camera_video( self.id, start, end, channel_index, output_file=output_file, iterator_callback=iterator_callback, progress_callback=progress_callback, chunk_size=chunk_size, fps=fps, ) async def set_recording_mode(self, mode: RecordingMode) -> None: """Sets recording mode on camera""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.recording_settings.mode = mode await self.queue_update(callback) async def set_ir_led_model(self, mode: IRLEDMode) -> None: """Sets IR LED mode on camera""" if not self.feature_flags.has_led_ir: raise BadRequest("Camera does not have an LED IR") def callback() -> None: self.isp_settings.ir_led_mode = mode await self.queue_update(callback) async def set_icr_custom_lux(self, value: ICRLuxValue) -> None: """Set ICRCustomValue from lux value.""" if not self.feature_flags.has_led_ir: raise BadRequest("Camera does not have an LED IR") icr_value = 0 for index, threshold in enumerate(LUX_MAPPING_VALUES): if value >= threshold: icr_value = 10 - index break def callback() -> None: self.isp_settings.icr_custom_value = cast(ICRCustomValue, icr_value) await self.queue_update(callback) @property def is_ir_led_slider_enabled(self) -> bool: """Return if IR LED custom slider is enabled.""" return ( self.feature_flags.has_led_ir and self.isp_settings.ir_led_mode == IRLEDMode.CUSTOM ) async def set_status_light(self, enabled: bool) -> None: """Sets status indicicator light on camera""" if not self.feature_flags.has_led_status: raise BadRequest("Camera does not have status light") def callback() -> None: self.led_settings.is_enabled = enabled self.led_settings.blink_rate = 0 await self.queue_update(callback) async def set_hdr(self, enabled: bool) -> None: """Sets HDR (High Dynamic Range) on camera""" warnings.warn( "set_hdr is deprecated and replaced with set_hdr_mode for versions of UniFi Protect v3.0+", DeprecationWarning, stacklevel=2, ) if not self.feature_flags.has_hdr: raise BadRequest("Camera does not have HDR") def callback() -> None: self.hdr_mode = enabled await self.queue_update(callback) async def set_hdr_mode(self, mode: Literal["auto", "off", "always"]) -> None: """Sets HDR mode similar to how Protect interface works.""" if not self.feature_flags.has_hdr: raise BadRequest("Camera does not have HDR") def callback() -> None: if mode == "off": self.hdr_mode = False if self.isp_settings.hdr_mode is not None: self.isp_settings.hdr_mode = HDRMode.NORMAL else: self.hdr_mode = True if self.isp_settings.hdr_mode is not None: self.isp_settings.hdr_mode = ( HDRMode.NORMAL if mode == "auto" else HDRMode.ALWAYS_ON ) await self.queue_update(callback) async def set_color_night_vision(self, enabled: bool) -> None: """Sets Color Night Vision on camera""" if not self.has_color_night_vision: raise BadRequest("Camera does not have Color Night Vision") def callback() -> None: self.isp_settings.is_color_night_vision_enabled = enabled await self.queue_update(callback) async def set_video_mode(self, mode: VideoMode) -> None: """Sets video mode on camera""" if mode not in self.feature_flags.video_modes: raise BadRequest(f"Camera does not have {mode}") def callback() -> None: self.video_mode = mode await self.queue_update(callback) async def set_camera_zoom(self, level: int) -> None: """Sets zoom level for camera""" if not self.feature_flags.can_optical_zoom: raise BadRequest("Camera cannot optical zoom") def callback() -> None: self.isp_settings.zoom_position = PercentInt(level) await self.queue_update(callback) async def set_wdr_level(self, level: int) -> None: """Sets WDR (Wide Dynamic Range) on camera""" if self.feature_flags.has_hdr: raise BadRequest("Cannot set WDR on cameras with HDR") def callback() -> None: self.isp_settings.wdr = WDRLevel(level) await self.queue_update(callback) async def set_mic_volume(self, level: int) -> None: """Sets the mic sensitivity level on camera""" if not self.feature_flags.has_mic: raise BadRequest("Camera does not have mic") def callback() -> None: self.mic_volume = PercentInt(level) await self.queue_update(callback) async def set_speaker_volume(self, level: int) -> None: """Sets the speaker sensitivity level on camera. Requires camera to have speakers""" if not self.feature_flags.has_speaker: raise BadRequest("Camera does not have speaker") def callback() -> None: self.speaker_settings.volume = PercentInt(level) await self.queue_update(callback) async def set_chime_type(self, chime_type: ChimeType) -> None: """Sets chime type for doorbell. Requires camera to be a doorbell""" await self.set_chime_duration(timedelta(milliseconds=chime_type.value)) async def set_chime_duration(self, duration: timedelta | float) -> None: """Sets chime duration for doorbell. Requires camera to be a doorbell""" if not self.feature_flags.has_chime: raise BadRequest("Camera does not have a chime") if isinstance(duration, (float, int)): if duration < 0: raise BadRequest("Chime duration must be a positive number of seconds") duration_td = timedelta(seconds=duration) else: duration_td = duration if duration_td.total_seconds() > 10: raise BadRequest("Chime duration is too long") def callback() -> None: self.chime_duration = duration_td await self.queue_update(callback) async def set_system_sounds(self, enabled: bool) -> None: """Sets system sound playback through speakers. Requires camera to have speakers""" if not self.feature_flags.has_speaker: raise BadRequest("Camera does not have speaker") def callback() -> None: self.speaker_settings.are_system_sounds_enabled = enabled await self.queue_update(callback) async def set_osd_name(self, enabled: bool) -> None: """Sets whether camera name is in the On Screen Display""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.osd_settings.is_name_enabled = enabled await self.queue_update(callback) async def set_osd_date(self, enabled: bool) -> None: """Sets whether current date is in the On Screen Display""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.osd_settings.is_date_enabled = enabled await self.queue_update(callback) async def set_osd_logo(self, enabled: bool) -> None: """Sets whether the UniFi logo is in the On Screen Display""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.osd_settings.is_logo_enabled = enabled await self.queue_update(callback) async def set_osd_bitrate(self, enabled: bool) -> None: """Sets whether camera bitrate is in the On Screen Display""" if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: # mismatch between UI internal data structure debug = bitrate data self.osd_settings.is_debug_enabled = enabled await self.queue_update(callback) async def set_smart_detect_types(self, types: list[SmartDetectObjectType]) -> None: """Sets current enabled smart detection types. Requires camera to have smart detection""" if not self.feature_flags.has_smart_detect: raise BadRequest("Camera does not have a smart detections") if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.smart_detect_settings.object_types = types await self.queue_update(callback) async def set_smart_audio_detect_types( self, types: list[SmartDetectAudioType], ) -> None: """Sets current enabled smart audio detection types. Requires camera to have smart detection""" if not self.feature_flags.has_smart_detect: raise BadRequest("Camera does not have a smart detections") if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.smart_detect_settings.audio_types = types await self.queue_update(callback) async def _set_object_detect( self, obj_to_mod: SmartDetectObjectType, enabled: bool, ) -> None: if obj_to_mod not in self.feature_flags.smart_detect_types: raise BadRequest(f"Camera does not support the {obj_to_mod} detection type") if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: objects = self.smart_detect_settings.object_types if enabled: if obj_to_mod not in objects: objects = [*objects, obj_to_mod] objects.sort() elif obj_to_mod in objects: objects.remove(obj_to_mod) self.smart_detect_settings.object_types = objects await self.queue_update(callback) async def _set_audio_detect( self, obj_to_mod: SmartDetectAudioType, enabled: bool, ) -> None: if ( self.feature_flags.smart_detect_audio_types is None or obj_to_mod not in self.feature_flags.smart_detect_audio_types ): raise BadRequest(f"Camera does not support the {obj_to_mod} detection type") if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: objects = self.smart_detect_settings.audio_types or [] if enabled: if obj_to_mod not in objects: objects = [*objects, obj_to_mod] objects.sort() elif obj_to_mod in objects: objects.remove(obj_to_mod) self.smart_detect_settings.audio_types = objects await self.queue_update(callback) async def set_lcd_text( self, text_type: DoorbellMessageType | None, text: str | None = None, reset_at: None | datetime | DEFAULT_TYPE = None, ) -> None: """Sets doorbell LCD text. Requires camera to be doorbell""" if not self.feature_flags.has_lcd_screen: raise BadRequest("Camera does not have an LCD screen") if text_type is None: async with self._update_sync.lock: # yield to the event loop once we have the lock to process any pending updates await asyncio.sleep(0) data_before_changes = self.dict_with_excludes() self.lcd_message = None # UniFi Protect bug: clearing LCD text message does _not_ emit a WS message await self.save_device(data_before_changes, force_emit=True) return if text_type != DoorbellMessageType.CUSTOM_MESSAGE: if text is not None: raise BadRequest("Can only set text if text_type is CUSTOM_MESSAGE") text = text_type.value.replace("_", " ") if reset_at == DEFAULT: reset_at = ( utc_now() + self._api.bootstrap.nvr.doorbell_settings.default_message_reset_timeout ) def callback() -> None: self.lcd_message = LCDMessage( # type: ignore[call-arg] api=self._api, type=text_type, text=text, # type: ignore[arg-type] reset_at=reset_at, # type: ignore[arg-type] ) await self.queue_update(callback) async def set_privacy( self, enabled: bool, mic_level: int | None = None, recording_mode: RecordingMode | None = None, reenable_global: bool = False, ) -> None: """Adds/removes a privacy zone that blacks out the whole camera.""" if not self.feature_flags.has_privacy_mask: raise BadRequest("Camera does not allow privacy zones") def callback() -> None: if enabled: self.use_global = False self.add_privacy_zone() else: if reenable_global: self.use_global = True self.remove_privacy_zone() if not reenable_global: if mic_level is not None: self.mic_volume = PercentInt(mic_level) if recording_mode is not None: self.recording_settings.mode = recording_mode await self.queue_update(callback) async def set_person_track(self, enabled: bool) -> None: """Sets person tracking on camera""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support person tracking") if self.use_global: raise BadRequest("Camera is using global recording settings.") def callback() -> None: self.smart_detect_settings.auto_tracking_object_types = ( [SmartDetectObjectType.PERSON] if enabled else [] ) await self.queue_update(callback) def create_talkback_stream( self, content_url: str, ffmpeg_path: Path | None = None, ) -> TalkbackStream: """ Creates a subprocess to play audio to a camera through its speaker. Requires ffmpeg to use. Args: ---- content_url: Either a URL accessible by python or a path to a file (ffmepg's `-i` parameter) ffmpeg_path: Optional path to ffmpeg binary Use either `await stream.run_until_complete()` or `await stream.start()` to start subprocess command after getting the stream. `.play_audio()` is a helper that wraps this method and automatically runs the subprocess as well """ if self.talkback_stream is not None and self.talkback_stream.is_running: raise BadRequest("Camera is already playing audio") self.talkback_stream = TalkbackStream(self, content_url, ffmpeg_path) return self.talkback_stream async def play_audio( self, content_url: str, ffmpeg_path: Path | None = None, blocking: bool = True, ) -> None: """ Plays audio to a camera through its speaker. Requires ffmpeg to use. Args: ---- content_url: Either a URL accessible by python or a path to a file (ffmepg's `-i` parameter) ffmpeg_path: Optional path to ffmpeg binary blocking: Awaits stream completion and logs stdout/stderr """ stream = self.create_talkback_stream(content_url, ffmpeg_path) await stream.start() if blocking: await self.wait_until_audio_completes() async def wait_until_audio_completes(self) -> None: """Awaits stream completion of audio and logs stdout/stderr.""" stream = self.talkback_stream if stream is None: raise StreamError("No audio playing to wait for") await stream.run_until_complete() _LOGGER.debug("ffmpeg stdout:\n%s", "\n".join(stream.stdout)) _LOGGER.debug("ffmpeg stderr:\n%s", "\n".join(stream.stderr)) if stream.is_error: error = "\n".join(stream.stderr) raise StreamError("Error while playing audio (ffmpeg): \n" + error) async def stop_audio(self) -> None: """Stop currently playing audio.""" if (stream := self.talkback_stream) is None: raise StreamError("No audio playing to stop") await stream.stop() def can_read_media(self, user: User) -> bool: if self.model is None: return True return user.can(self.model, PermissionNode.READ_MEDIA, self) def can_delete_media(self, user: User) -> bool: if self.model is None: return True return user.can(self.model, PermissionNode.DELETE_MEDIA, self) # region PTZ async def ptz_relative_move( self, *, pan: float, tilt: float, pan_speed: int = 10, tilt_speed: int = 10, scale: int = 0, use_native: bool = False, ) -> None: """ Move PTZ relative to current position. Pan/tilt values vary from camera to camera, but for G4 PTZ: * Pan values range from 0° and go to 360°/0° * Tilt values range from -20° and go to 90° Relative positions cannot move more then 4095 steps at a time in any direction. For the G4 PTZ, 4095 steps is ~41° for pan and ~45° for tilt. `use_native` lets you use the native step values instead of degrees. """ if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") if not use_native: pan = self.feature_flags.pan.to_native_value(pan, is_relative=True) tilt = self.feature_flags.tilt.to_native_value(tilt, is_relative=True) await self._api.relative_move_ptz_camera( self.id, pan=pan, tilt=tilt, pan_speed=pan_speed, tilt_speed=tilt_speed, scale=scale, ) async def ptz_center(self, *, x: int, y: int, z: int) -> None: """ Center PTZ Camera on point in viewport. x, y, z values range from 0 to 1000. x, y are relative coords for the current viewport: * (0, 0) is top left * (500, 500) is the center * (1000, 1000) is the bottom right z value is zoom, but since it is capped at 1000, probably better to use `ptz_zoom`. """ await self._api.center_ptz_camera(self.id, x=x, y=y, z=z) async def ptz_zoom( self, *, zoom: float, speed: int = 100, use_native: bool = False, ) -> None: """ Zoom PTZ Camera. Zoom levels vary from camera to camera, but for G4 PTZ it goes from 1x to 22x. Zoom speed seems to range from 0 to 100. Any value over 100 results in a speed of 0. """ if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") if not use_native: zoom = self.feature_flags.zoom.to_native_value(zoom) await self._api.zoom_ptz_camera(self.id, zoom=zoom, speed=speed) async def get_ptz_position(self) -> PTZPosition: """Get current PTZ Position.""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") return await self._api.get_position_ptz_camera(self.id) async def goto_ptz_slot(self, *, slot: int) -> None: """ Goto PTZ slot position. -1 is Home slot. """ if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") await self._api.goto_ptz_camera(self.id, slot=slot) async def create_ptz_preset(self, *, name: str) -> PTZPreset: """Create PTZ Preset for camera based on current camera settings.""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") return await self._api.create_preset_ptz_camera(self.id, name=name) async def get_ptz_presets(self) -> list[PTZPreset]: """Get PTZ Presets for camera.""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") return await self._api.get_presets_ptz_camera(self.id) async def delete_ptz_preset(self, *, slot: int) -> None: """Delete PTZ preset for camera.""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") await self._api.delete_preset_ptz_camera(self.id, slot=slot) async def get_ptz_home(self) -> PTZPreset: """Get PTZ home preset (-1).""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") return await self._api.get_home_ptz_camera(self.id) async def set_ptz_home(self) -> PTZPreset: """Get PTZ home preset (-1) to current position.""" if not self.feature_flags.is_ptz: raise BadRequest("Camera does not support PTZ features.") return await self._api.set_home_ptz_camera(self.id) # endregion class Viewer(ProtectAdoptableDeviceModel): stream_limit: int software_version: str liveview_id: str @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "liveview": "liveviewId"} @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | {"softwareVersion"} @property def liveview(self) -> Liveview | None: # user may not have permission to see the liveview return self._api.bootstrap.liveviews.get(self.liveview_id) async def set_liveview(self, liveview: Liveview) -> None: """ Sets the liveview current set for the viewer Args: ---- liveview: The liveview you want to set """ if self._api is not None and liveview.id not in self._api.bootstrap.liveviews: raise BadRequest("Unknown liveview") async with self._update_sync.lock: # yield to the event loop once we have the lock to process any pending updates await asyncio.sleep(0) data_before_changes = self.dict_with_excludes() self.liveview_id = liveview.id # UniFi Protect bug: changing the liveview does _not_ emit a WS message await self.save_device(data_before_changes, force_emit=True) class Bridge(ProtectAdoptableDeviceModel): platform: str class SensorSettingsBase(ProtectBaseObject): is_enabled: bool class SensorThresholdSettings(SensorSettingsBase): margin: float # read only # "safe" thresholds for alerting # anything below/above will trigger alert low_threshold: float | None high_threshold: float | None class SensorSensitivitySettings(SensorSettingsBase): sensitivity: PercentInt class SensorBatteryStatus(ProtectBaseObject): percentage: PercentInt | None is_low: bool class SensorStat(ProtectBaseObject): value: float | None status: SensorStatusType class SensorStats(ProtectBaseObject): light: SensorStat humidity: SensorStat temperature: SensorStat class Sensor(ProtectAdoptableDeviceModel): alarm_settings: SensorSettingsBase alarm_triggered_at: datetime | None battery_status: SensorBatteryStatus camera_id: str | None humidity_settings: SensorThresholdSettings is_motion_detected: bool is_opened: bool leak_detected_at: datetime | None led_settings: SensorSettingsBase light_settings: SensorThresholdSettings motion_detected_at: datetime | None motion_settings: SensorSensitivitySettings open_status_changed_at: datetime | None stats: SensorStats tampering_detected_at: datetime | None temperature_settings: SensorThresholdSettings mount_type: MountType # not directly from UniFi last_motion_event_id: str | None = None last_contact_event_id: str | None = None last_value_event_id: str | None = None last_alarm_event_id: str | None = None extreme_value_detected_at: datetime | None = None _tamper_timeout: datetime | None = PrivateAttr(None) _alarm_timeout: datetime | None = PrivateAttr(None) @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "camera": "cameraId"} @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "batteryStatus", "isMotionDetected", "leakDetectedAt", "tamperingDetectedAt", "isOpened", "openStatusChangedAt", "alarmTriggeredAt", "motionDetectedAt", "stats", } def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_tuple( data, ( "lastMotionEventId", "lastContactEventId", "lastValueEventId", "lastAlarmEventId", "extremeValueDetectedAt", ), ) return data @property def camera(self) -> Camera | None: """Paired Camera will always be none if no camera is paired""" if (camera_id := self.camera_id) is None: return None return self._api.bootstrap.cameras[camera_id] @property def is_tampering_detected(self) -> bool: return self.tampering_detected_at is not None @property def is_alarm_detected(self) -> bool: if self._alarm_timeout is None: return False return utc_now() < self._alarm_timeout @property def is_contact_sensor_enabled(self) -> bool: return self.mount_type in {MountType.DOOR, MountType.WINDOW, MountType.GARAGE} @property def is_motion_sensor_enabled(self) -> bool: return self.mount_type is not MountType.LEAK and self.motion_settings.is_enabled @property def is_alarm_sensor_enabled(self) -> bool: return self.mount_type is not MountType.LEAK and self.alarm_settings.is_enabled @property def is_light_sensor_enabled(self) -> bool: return self.mount_type is not MountType.LEAK and self.light_settings.is_enabled @property def is_temperature_sensor_enabled(self) -> bool: return ( self.mount_type is not MountType.LEAK and self.temperature_settings.is_enabled ) @property def is_humidity_sensor_enabled(self) -> bool: return ( self.mount_type is not MountType.LEAK and self.humidity_settings.is_enabled ) @property def is_leak_sensor_enabled(self) -> bool: return self.mount_type is MountType.LEAK def set_alarm_timeout(self) -> None: self._alarm_timeout = utc_now() + EVENT_PING_INTERVAL self._event_callback_ping() @property def last_motion_event(self) -> Event | None: if (last_motion_event_id := self.last_motion_event_id) is None: return None return self._api.bootstrap.events.get(last_motion_event_id) @property def last_contact_event(self) -> Event | None: if (last_contact_event_id := self.last_contact_event_id) is None: return None return self._api.bootstrap.events.get(last_contact_event_id) @property def last_value_event(self) -> Event | None: if (last_value_event_id := self.last_value_event_id) is None: return None return self._api.bootstrap.events.get(last_value_event_id) @property def last_alarm_event(self) -> Event | None: if (last_alarm_event_id := self.last_alarm_event_id) is None: return None return self._api.bootstrap.events.get(last_alarm_event_id) @property def is_leak_detected(self) -> bool: return self.leak_detected_at is not None async def set_status_light(self, enabled: bool) -> None: """Sets the status indicator light for the sensor""" def callback() -> None: self.led_settings.is_enabled = enabled await self.queue_update(callback) async def set_mount_type(self, mount_type: MountType) -> None: """Sets current mount type for sensor""" def callback() -> None: self.mount_type = mount_type await self.queue_update(callback) async def set_motion_status(self, enabled: bool) -> None: """Sets the motion detection type for the sensor""" def callback() -> None: self.motion_settings.is_enabled = enabled await self.queue_update(callback) async def set_motion_sensitivity(self, sensitivity: int) -> None: """Sets the motion sensitivity for the sensor""" def callback() -> None: self.motion_settings.sensitivity = PercentInt(sensitivity) await self.queue_update(callback) async def set_temperature_status(self, enabled: bool) -> None: """Sets the temperature detection type for the sensor""" def callback() -> None: self.temperature_settings.is_enabled = enabled await self.queue_update(callback) async def set_temperature_safe_range(self, low: float, high: float) -> None: """Sets the temperature safe range for the sensor""" if low < 0.0: raise BadRequest("Minimum value is 0°C") if high > 45.0: raise BadRequest("Maximum value is 45°C") if high <= low: raise BadRequest("High value must be above low value") def callback() -> None: self.temperature_settings.low_threshold = low self.temperature_settings.high_threshold = high await self.queue_update(callback) async def remove_temperature_safe_range(self) -> None: """Removes the temperature safe range for the sensor""" def callback() -> None: self.temperature_settings.low_threshold = None self.temperature_settings.high_threshold = None await self.queue_update(callback) async def set_humidity_status(self, enabled: bool) -> None: """Sets the humidity detection type for the sensor""" def callback() -> None: self.humidity_settings.is_enabled = enabled await self.queue_update(callback) async def set_humidity_safe_range(self, low: float, high: float) -> None: """Sets the humidity safe range for the sensor""" if low < 1.0: raise BadRequest("Minimum value is 1%") if high > 99.0: raise BadRequest("Maximum value is 99%") if high <= low: raise BadRequest("High value must be above low value") def callback() -> None: self.humidity_settings.low_threshold = low self.humidity_settings.high_threshold = high await self.queue_update(callback) async def remove_humidity_safe_range(self) -> None: """Removes the humidity safe range for the sensor""" def callback() -> None: self.humidity_settings.low_threshold = None self.humidity_settings.high_threshold = None await self.queue_update(callback) async def set_light_status(self, enabled: bool) -> None: """Sets the light detection type for the sensor""" def callback() -> None: self.light_settings.is_enabled = enabled await self.queue_update(callback) async def set_light_safe_range(self, low: float, high: float) -> None: """Sets the light safe range for the sensor""" if low < 1.0: raise BadRequest("Minimum value is 1 lux") if high > 1000.0: raise BadRequest("Maximum value is 1000 lux") if high <= low: raise BadRequest("High value must be above low value") def callback() -> None: self.light_settings.low_threshold = low self.light_settings.high_threshold = high await self.queue_update(callback) async def remove_light_safe_range(self) -> None: """Removes the light safe range for the sensor""" def callback() -> None: self.light_settings.low_threshold = None self.light_settings.high_threshold = None await self.queue_update(callback) async def set_alarm_status(self, enabled: bool) -> None: """Sets the alarm detection type for the sensor""" def callback() -> None: self.alarm_settings.is_enabled = enabled await self.queue_update(callback) async def set_paired_camera(self, camera: Camera | None) -> None: """Sets the camera paired with the sensor""" def callback() -> None: if camera is None: self.camera_id = None else: self.camera_id = camera.id await self.queue_update(callback) async def clear_tamper(self) -> None: """Clears tamper status for sensor""" if not self._api.bootstrap.auth_user.can( ModelType.SENSOR, PermissionNode.WRITE, self, ): raise NotAuthorized( f"Do not have permission to clear tamper for sensor: {self.id}", ) await self._api.clear_tamper_sensor(self.id) class Doorlock(ProtectAdoptableDeviceModel): credentials: str | None lock_status: LockStatusType enable_homekit: bool auto_close_time: timedelta led_settings: SensorSettingsBase battery_status: SensorBatteryStatus camera_id: str | None has_homekit: bool private_token: str @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "camera": "cameraId", "autoCloseTimeMs": "autoCloseTime", } @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "credentials", "lockStatus", "batteryStatus", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "autoCloseTimeMs": lambda x: timedelta(milliseconds=x) } | super().unifi_dict_conversions() @property def camera(self) -> Camera | None: """Paired Camera will always be none if no camera is paired""" if self.camera_id is None: return None return self._api.bootstrap.cameras[self.camera_id] async def set_paired_camera(self, camera: Camera | None) -> None: """Sets the camera paired with the sensor""" def callback() -> None: if camera is None: self.camera_id = None else: self.camera_id = camera.id await self.queue_update(callback) async def set_status_light(self, enabled: bool) -> None: """Sets the status indicator light for the doorlock""" def callback() -> None: self.led_settings.is_enabled = enabled await self.queue_update(callback) async def set_auto_close_time(self, duration: timedelta) -> None: """Sets the auto-close time for doorlock. 0 seconds = disabled.""" if duration > timedelta(hours=1): raise BadRequest("Max duration is 1 hour") def callback() -> None: self.auto_close_time = duration await self.queue_update(callback) async def close_lock(self) -> None: """Close doorlock (lock)""" if self.lock_status != LockStatusType.OPEN: raise BadRequest("Lock is not open") await self._api.close_lock(self.id) async def open_lock(self) -> None: """Open doorlock (unlock)""" if self.lock_status != LockStatusType.CLOSED: raise BadRequest("Lock is not closed") await self._api.open_lock(self.id) async def calibrate(self) -> None: """ Calibrate the doorlock. Door must be open and lock unlocked. """ await self._api.calibrate_lock(self.id) class ChimeFeatureFlags(ProtectBaseObject): has_wifi: bool # 2.9.20+ has_https_client_ota: bool | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "hasHttpsClientOTA": "hasHttpsClientOta"} class RingSetting(ProtectBaseObject): camera_id: str repeat_times: RepeatTimes track_no: int volume: int @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "camera": "cameraId"} @property def camera(self) -> Camera | None: """Paired Camera will always be none if no camera is paired""" if self.camera_id is None: return None # type: ignore[unreachable] return self._api.bootstrap.cameras[self.camera_id] class ChimeTrack(ProtectBaseObject): md5: str name: str state: str track_no: int volume: int size: int @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "track_no": "trackNo"} class Chime(ProtectAdoptableDeviceModel): volume: PercentInt is_probing_for_wifi: bool last_ring: datetime | None is_wireless_uplink_enabled: bool camera_ids: list[str] # requires 2.6.17+ ap_mgmt_ip: IPv4Address | None = None # requires 2.7.15+ feature_flags: ChimeFeatureFlags | None = None # requires 2.8.22+ user_configured_ap: bool | None = None # requires 3.0.22+ has_https_client_ota: bool | None = None platform: str | None = None repeat_times: RepeatTimes | None = None track_no: int | None = None ring_settings: list[RingSetting] = [] speaker_track_list: list[ChimeTrack] = [] # TODO: used for adoption # apMac read only # apRssi read only # elementInfo read only @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "hasHttpsClientOTA": "hasHttpsClientOta"} @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | {"isProbingForWifi", "lastRing"} @property def cameras(self) -> list[Camera]: """Paired Cameras for chime""" if len(self.camera_ids) == 0: return [] return [self._api.bootstrap.cameras[c] for c in self.camera_ids] async def set_volume(self, level: int) -> None: """Set the volume on chime.""" old_value = self.volume new_value = PercentInt(level) def callback() -> None: self.volume = new_value for setting in self.ring_settings: if setting.volume == old_value: setting.volume = new_value await self.queue_update(callback) async def set_volume_for_camera(self, camera: Camera, level: int) -> None: """Set the volume on chime for specific camera.""" def callback() -> None: handled = False for setting in self.ring_settings: if setting.camera_id == camera.id: setting.volume = cast(PercentInt, level) handled = True break if not handled: raise BadRequest("Camera %s is not paired with chime", camera.id) await self.queue_update(callback) async def add_camera(self, camera: Camera) -> None: """Adds new paired camera to chime""" if not camera.feature_flags.is_doorbell: raise BadRequest("Camera does not have a chime") if camera.id in self.camera_ids: raise BadRequest("Camera is already paired") def callback() -> None: self.camera_ids.append(camera.id) await self.queue_update(callback) async def remove_camera(self, camera: Camera) -> None: """Removes paired camera from chime""" if camera.id not in self.camera_ids: raise BadRequest("Camera is not paired") def callback() -> None: self.camera_ids.remove(camera.id) await self.queue_update(callback) async def play( self, *, volume: int | None = None, repeat_times: int | None = None, ) -> None: """Plays chime tone""" await self._api.play_speaker(self.id, volume=volume, repeat_times=repeat_times) async def play_buzzer(self) -> None: """Plays chime buzzer""" await self._api.play_buzzer(self.id) async def set_repeat_times(self, value: int) -> None: """Set repeat times on chime.""" old_value = self.repeat_times def callback() -> None: self.repeat_times = cast(RepeatTimes, value) for setting in self.ring_settings: if setting.repeat_times == old_value: setting.repeat_times = cast(RepeatTimes, value) await self.queue_update(callback) async def set_repeat_times_for_camera( self, camera: Camera, value: int, ) -> None: """Set repeat times on chime for specific camera.""" def callback() -> None: handled = False for setting in self.ring_settings: if setting.camera_id == camera.id: setting.repeat_times = cast(RepeatTimes, value) handled = True break if not handled: raise BadRequest("Camera %s is not paired with chime", camera.id) await self.queue_update(callback) uiprotect-6.1.0/src/uiprotect/data/nvr.py000066400000000000000000001330061467310220200204310ustar00rootroot00000000000000"""UniFi Protect Data.""" from __future__ import annotations import asyncio import logging import zoneinfo from collections.abc import Callable from datetime import datetime, timedelta, tzinfo from functools import cache from ipaddress import IPv4Address, IPv6Address from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Literal from uuid import UUID import aiofiles import orjson from aiofiles import os as aos from convertertools import pop_dict_set_if_none, pop_dict_tuple from pydantic.v1.fields import PrivateAttr from ..exceptions import BadRequest, NotAuthorized from ..utils import RELEASE_CACHE, convert_to_datetime from .base import ( ProtectBaseObject, ProtectDeviceModel, ProtectModelWithId, ) from .devices import ( Camera, CameraZone, Light, OSDSettings, RecordingSettings, Sensor, SmartDetectSettings, ) from .types import ( AnalyticsOption, DoorbellMessageType, DoorbellText, EventCategories, EventType, FirmwareReleaseChannel, IteratorCallback, ModelType, MountType, PercentFloat, PercentInt, PermissionNode, ProgressCallback, RecordingMode, RecordingType, ResolutionStorageType, SensorStatusType, SensorType, SmartDetectObjectType, StorageType, Version, ) from .user import User, UserLocation if TYPE_CHECKING: from pydantic.v1.typing import SetStr _LOGGER = logging.getLogger(__name__) MAX_SUPPORTED_CAMERAS = 256 MAX_EVENT_HISTORY_IN_STATE_MACHINE = MAX_SUPPORTED_CAMERAS * 2 DELETE_KEYS_THUMB = {"color", "vehicleType"} DELETE_KEYS_EVENT = {"deletedAt", "category", "subCategory"} class NVRLocation(UserLocation): is_geofencing_enabled: bool radius: int model: ModelType | None = None class SmartDetectItem(ProtectBaseObject): id: str timestamp: datetime level: PercentInt coord: tuple[int, int, int, int] object_type: SmartDetectObjectType zone_ids: list[int] duration: timedelta @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "zones": "zoneIds", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "duration": lambda x: timedelta(milliseconds=x), } | super().unifi_dict_conversions() class SmartDetectTrack(ProtectBaseObject): id: str payload: list[SmartDetectItem] camera_id: str event_id: str @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "camera": "cameraId", "event": "eventId", } @property def camera(self) -> Camera: return self._api.bootstrap.cameras[self.camera_id] @property def event(self) -> Event | None: return self._api.bootstrap.events.get(self.event_id) class LicensePlateMetadata(ProtectBaseObject): name: str confidence_level: int class EventThumbnailAttribute(ProtectBaseObject): confidence: int val: str class EventThumbnailAttributes(ProtectBaseObject): color: EventThumbnailAttribute | None = None vehicle_type: EventThumbnailAttribute | None = None def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, DELETE_KEYS_THUMB) return data class EventDetectedThumbnail(ProtectBaseObject): clock_best_wall: datetime | None = None type: str cropped_id: str attributes: EventThumbnailAttributes | None = None name: str | None @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return {"clockBestWall": convert_to_datetime} | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, {"name"}) return data class EventMetadata(ProtectBaseObject): client_platform: str | None reason: str | None app_update: str | None light_id: str | None light_name: str | None type: str | None sensor_id: str | None sensor_name: str | None sensor_type: SensorType | None doorlock_id: str | None doorlock_name: str | None from_value: str | None to_value: str | None mount_type: MountType | None status: SensorStatusType | None alarm_type: str | None device_id: str | None mac: str | None # require 2.7.5+ license_plate: LicensePlateMetadata | None = None # requires 2.11.13+ detected_thumbnails: list[EventDetectedThumbnail] | None = None _collapse_keys: ClassVar[SetStr] = { "lightId", "lightName", "type", "sensorId", "sensorName", "sensorType", "doorlockId", "doorlockName", "mountType", "status", "alarmType", "deviceId", "mac", } @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "from": "fromValue", "to": "toValue", } @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: for key in cls._collapse_keys.intersection(data): if isinstance(data[key], dict): data[key] = data[key]["text"] return super().unifi_dict_to_dict(data) def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) # all metadata keys optionally appear for key, value in list(data.items()): if value is None: del data[key] for key in self._collapse_keys.intersection(data): # AI Theta/Hotplug exception if key != "type" or data[key] not in {"audio", "video", "extender"}: data[key] = {"text": data[key]} return data class Event(ProtectModelWithId): type: EventType start: datetime end: datetime | None score: int heatmap_id: str | None camera_id: str | None smart_detect_types: list[SmartDetectObjectType] smart_detect_event_ids: list[str] thumbnail_id: str | None user_id: str | None timestamp: datetime | None metadata: EventMetadata | None # requires 2.7.5+ deleted_at: datetime | None = None deletion_type: Literal["manual", "automatic"] | None = None # only appears if `get_events` is called with category category: EventCategories | None = None sub_category: str | None = None # TODO: # partition # description _smart_detect_events: list[Event] | None = PrivateAttr(None) _smart_detect_track: SmartDetectTrack | None = PrivateAttr(None) _smart_detect_zones: dict[int, CameraZone] | None = PrivateAttr(None) @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "camera": "cameraId", "heatmap": "heatmapId", "user": "userId", "thumbnail": "thumbnailId", "smartDetectEvents": "smartDetectEventIds", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { key: convert_to_datetime for key in ("start", "end", "timestamp", "deletedAt") } | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, DELETE_KEYS_EVENT) return data @property def camera(self) -> Camera | None: if self.camera_id is None: return None return self._api.bootstrap.cameras.get(self.camera_id) @property def light(self) -> Light | None: if self.metadata is None or self.metadata.light_id is None: return None return self._api.bootstrap.lights.get(self.metadata.light_id) @property def sensor(self) -> Sensor | None: if self.metadata is None or self.metadata.sensor_id is None: return None return self._api.bootstrap.sensors.get(self.metadata.sensor_id) @property def user(self) -> User | None: if self.user_id is None: return None return self._api.bootstrap.users.get(self.user_id) @property def smart_detect_events(self) -> list[Event]: if self._smart_detect_events is not None: return self._smart_detect_events self._smart_detect_events = [ self._api.bootstrap.events[g] for g in self.smart_detect_event_ids if g in self._api.bootstrap.events ] return self._smart_detect_events async def get_thumbnail( self, width: int | None = None, height: int | None = None, ) -> bytes | None: """Gets thumbnail for event""" if self.thumbnail_id is None: return None if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self.camera, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) return await self._api.get_event_thumbnail(self.thumbnail_id, width, height) async def get_animated_thumbnail( self, width: int | None = None, height: int | None = None, *, speedup: int = 10, ) -> bytes | None: """Gets animated thumbnail for event""" if self.thumbnail_id is None: return None if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self.camera, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) return await self._api.get_event_animated_thumbnail( self.thumbnail_id, width, height, speedup=speedup, ) async def get_heatmap(self) -> bytes | None: """Gets heatmap for event""" if self.heatmap_id is None: return None if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self.camera, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) return await self._api.get_event_heatmap(self.heatmap_id) async def get_video( self, channel_index: int = 0, output_file: Path | None = None, iterator_callback: IteratorCallback | None = None, progress_callback: ProgressCallback | None = None, chunk_size: int = 65536, ) -> bytes | None: """ Get the MP4 video clip for this given event Args: ---- channel_index: index of `CameraChannel` on the camera to use to retrieve video from Will raise an exception if event does not have a camera, end time or the channel index is wrong. """ if self.camera is None: raise BadRequest("Event does not have a camera") if self.end is None: raise BadRequest("Event is ongoing") if not self._api.bootstrap.auth_user.can( ModelType.CAMERA, PermissionNode.READ_MEDIA, self.camera, ): raise NotAuthorized( f"Do not have permission to read media for camera: {self.id}", ) return await self._api.get_camera_video( self.camera.id, self.start, self.end, channel_index, output_file=output_file, iterator_callback=iterator_callback, progress_callback=progress_callback, chunk_size=chunk_size, ) async def get_smart_detect_track(self) -> SmartDetectTrack: """ Gets smart detect track for given smart detect event. If event is not a smart detect event, it will raise a `BadRequest` """ if self.type not in {EventType.SMART_DETECT, EventType.SMART_DETECT_LINE}: raise BadRequest("Not a smart detect event") if self._smart_detect_track is None: self._smart_detect_track = await self._api.get_event_smart_detect_track( self.id, ) return self._smart_detect_track async def get_smart_detect_zones(self) -> dict[int, CameraZone]: """Gets the triggering zones for the smart detection""" if self.camera is None: raise BadRequest("No camera on event") if self._smart_detect_zones is None: smart_track = await self.get_smart_detect_track() ids: set[int] = set() for item in smart_track.payload: ids |= set(item.zone_ids) self._smart_detect_zones = { z.id: z for z in self.camera.smart_detect_zones if z.id in ids } return self._smart_detect_zones class PortConfig(ProtectBaseObject): ump: int http: int https: int rtsp: int rtsps: int rtmp: int devices_wss: int camera_https: int live_ws: int live_wss: int tcp_streams: int playback: int ems_cli: int ems_live_flv: int camera_events: int tcp_bridge: int ucore: int discovery_client: int piongw: int | None = None ems_json_cli: int | None = None stacking: int | None = None # 3.0.22+ ai_feature_console: int | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "emsCLI": "emsCli", "emsLiveFLV": "emsLiveFlv", "emsJsonCLI": "emsJsonCli", } class CPUInfo(ProtectBaseObject): average_load: float temperature: float class MemoryInfo(ProtectBaseObject): available: int | None free: int | None total: int | None class StorageDevice(ProtectBaseObject): model: str size: int healthy: bool | str class StorageInfo(ProtectBaseObject): available: int is_recycling: bool size: int type: StorageType used: int devices: list[StorageDevice] # requires 2.8.14+ capability: str | None = None @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "type" in data: storage_type = data.pop("type") try: data["type"] = StorageType(storage_type) except ValueError: _LOGGER.warning("Unknown storage type: %s", storage_type) data["type"] = StorageType.UNKNOWN return super().unifi_dict_to_dict(data) class StorageSpace(ProtectBaseObject): total: int used: int available: int class TMPFSInfo(ProtectBaseObject): available: int total: int used: int path: Path class UOSDisk(ProtectBaseObject): slot: int state: str type: Literal["SSD", "HDD"] | None = None model: str | None = None serial: str | None = None firmware: str | None = None rpm: int | None = None ata: str | None = None sata: str | None = None action: str | None = None healthy: str | None = None reason: list[Any] | None = None temperature: int | None = None power_on_hours: int | None = None life_span: PercentFloat | None = None bad_sector: int | None = None threshold: int | None = None progress: PercentFloat | None = None estimate: timedelta | None = None # 2.10.10+ size: int | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "poweronhrs": "powerOnHours", "life_span": "lifeSpan", "bad_sector": "badSector", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "estimate": lambda x: timedelta(seconds=x) } | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) # estimate is actually in seconds, not milliseconds if "estimate" in data and data["estimate"] is not None: data["estimate"] /= 1000 if "state" in data and data["state"] == "nodisk": pop_dict_tuple( data, ( "action", "ata", "bad_sector", "estimate", "firmware", "healthy", "life_span", "model", "poweronhrs", "progress", "reason", "rpm", "sata", "serial", "tempature", "temperature", "threshold", "type", ), ) return data @property def has_disk(self) -> bool: return self.state != "nodisk" @property def is_healthy(self) -> bool: return self.state in { "initializing", "expanding", "spare", "normal", } class UOSSpace(ProtectBaseObject): device: str total_bytes: int used_bytes: int action: str progress: PercentFloat | None = None estimate: timedelta | None = None # requires 2.8.14+ health: str | None = None # requires 2.8.22+ space_type: str | None = None # TODO: # reasons @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "total_bytes": "totalBytes", "used_bytes": "usedBytes", "space_type": "spaceType", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "estimate": lambda x: timedelta(seconds=x) } | super().unifi_dict_conversions() def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) # estimate is actually in seconds, not milliseconds if "estimate" in data and data["estimate"] is not None: data["estimate"] /= 1000 return data class UOSStorage(ProtectBaseObject): disks: list[UOSDisk] space: list[UOSSpace] # TODO: # sdcards class SystemInfo(ProtectBaseObject): cpu: CPUInfo memory: MemoryInfo storage: StorageInfo tmpfs: TMPFSInfo ustorage: UOSStorage | None = None def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) pop_dict_set_if_none(data, {"ustorage"}) return data class DoorbellMessage(ProtectBaseObject): type: DoorbellMessageType text: DoorbellText class DoorbellSettings(ProtectBaseObject): default_message_text: DoorbellText default_message_reset_timeout: timedelta all_messages: list[DoorbellMessage] custom_messages: list[DoorbellText] @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "defaultMessageResetTimeoutMs": "defaultMessageResetTimeout", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { # defaultMessageResetTimeoutMs is remapped to defaultMessageResetTimeout "defaultMessageResetTimeoutMs": lambda x: timedelta(milliseconds=x), } | super().unifi_dict_conversions() class RecordingTypeDistribution(ProtectBaseObject): recording_type: RecordingType size: int percentage: float class ResolutionDistribution(ProtectBaseObject): resolution: ResolutionStorageType size: int percentage: float class StorageDistribution(ProtectBaseObject): recording_type_distributions: list[RecordingTypeDistribution] resolution_distributions: list[ResolutionDistribution] _recording_type_dict: dict[RecordingType, RecordingTypeDistribution] | None = ( PrivateAttr(None) ) _resolution_dict: dict[ResolutionStorageType, ResolutionDistribution] | None = ( PrivateAttr(None) ) def _get_recording_type_dict( self, ) -> dict[RecordingType, RecordingTypeDistribution]: if self._recording_type_dict is None: self._recording_type_dict = {} for recording_type in self.recording_type_distributions: self._recording_type_dict[recording_type.recording_type] = ( recording_type ) return self._recording_type_dict def _get_resolution_dict( self, ) -> dict[ResolutionStorageType, ResolutionDistribution]: if self._resolution_dict is None: self._resolution_dict = {} for resolution in self.resolution_distributions: self._resolution_dict[resolution.resolution] = resolution return self._resolution_dict @property def timelapse_recordings(self) -> RecordingTypeDistribution | None: return self._get_recording_type_dict().get(RecordingType.TIMELAPSE) @property def continuous_recordings(self) -> RecordingTypeDistribution | None: return self._get_recording_type_dict().get(RecordingType.CONTINUOUS) @property def detections_recordings(self) -> RecordingTypeDistribution | None: return self._get_recording_type_dict().get(RecordingType.DETECTIONS) @property def uhd_usage(self) -> ResolutionDistribution | None: return self._get_resolution_dict().get(ResolutionStorageType.UHD) @property def hd_usage(self) -> ResolutionDistribution | None: return self._get_resolution_dict().get(ResolutionStorageType.HD) @property def free(self) -> ResolutionDistribution | None: return self._get_resolution_dict().get(ResolutionStorageType.FREE) def update_from_dict(self, data: dict[str, Any]) -> StorageDistribution: # reset internal look ups when data changes self._recording_type_dict = None self._resolution_dict = None return super().update_from_dict(data) class StorageStats(ProtectBaseObject): utilization: float capacity: timedelta | None remaining_capacity: timedelta | None recording_space: StorageSpace storage_distribution: StorageDistribution @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "capacity": lambda x: timedelta(milliseconds=x), "remainingCapacity": lambda x: timedelta(milliseconds=x), } | super().unifi_dict_conversions() class NVRFeatureFlags(ProtectBaseObject): beta: bool dev: bool notifications_v2: bool homekit_paired: bool | None = None ulp_role_management: bool | None = None # 2.9.20+ detection_labels: bool | None = None has_two_way_audio_media_streams: bool | None = None class NVRSmartDetection(ProtectBaseObject): enable: bool face_recognition: bool license_plate_recognition: bool class GlobalRecordingSettings(ProtectBaseObject): osd_settings: OSDSettings recording_settings: RecordingSettings smart_detect_settings: SmartDetectSettings # TODO: # recordingSchedulesV2 class NVR(ProtectDeviceModel): can_auto_update: bool is_stats_gathering_enabled: bool timezone: tzinfo version: Version ucore_version: str hardware_platform: str ports: PortConfig last_update_at: datetime | None is_station: bool enable_automatic_backups: bool enable_stats_reporting: bool release_channel: FirmwareReleaseChannel hosts: list[IPv4Address | IPv6Address | str] enable_bridge_auto_adoption: bool hardware_id: UUID host_type: int host_shortname: str is_hardware: bool is_wireless_uplink_enabled: bool | None time_format: Literal["12h", "24h"] temperature_unit: Literal["C", "F"] recording_retention_duration: timedelta | None enable_crash_reporting: bool disable_audio: bool analytics_data: AnalyticsOption anonymous_device_id: UUID | None camera_utilization: int is_recycling: bool disable_auto_link: bool skip_firmware_update: bool location_settings: NVRLocation feature_flags: NVRFeatureFlags system_info: SystemInfo doorbell_settings: DoorbellSettings storage_stats: StorageStats is_away: bool is_setup: bool network: str max_camera_capacity: dict[Literal["4K", "2K", "HD"], int] market_name: str | None = None stream_sharing_available: bool | None = None is_db_available: bool | None = None is_insights_enabled: bool | None = None is_recording_disabled: bool | None = None is_recording_motion_only: bool | None = None ui_version: str | None = None sso_channel: FirmwareReleaseChannel | None = None is_stacked: bool | None = None is_primary: bool | None = None last_drive_slow_event: datetime | None = None is_u_core_setup: bool | None = None vault_camera_ids: list[str] = [] # requires 2.8.14+ corruption_state: str | None = None country_code: str | None = None has_gateway: bool | None = None is_vault_registered: bool | None = None public_ip: IPv4Address | None = None ulp_version: str | None = None wan_ip: IPv4Address | IPv6Address | None = None # requires 2.9.20+ hard_drive_state: str | None = None is_network_installed: bool | None = None is_protect_updatable: bool | None = None is_ucore_updatable: bool | None = None # requires 2.11.13+ last_device_fw_updates_checked_at: datetime | None = None # requires 3.0.22+ smart_detection: NVRSmartDetection | None = None is_ucore_stacked: bool | None = None global_camera_settings: GlobalRecordingSettings | None = None # TODO: # errorCode read only # wifiSettings # smartDetectAgreement # dbRecoveryOptions # portStatus # cameraCapacity # deviceFirmwareSettings @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return { **super()._get_unifi_remaps(), "recordingRetentionDurationMs": "recordingRetentionDuration", "vaultCameras": "vaultCameraIds", "lastDeviceFWUpdatesCheckedAt": "lastDeviceFwUpdatesCheckedAt", "isUCoreStacked": "isUcoreStacked", } @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | { "version", "uiVersion", "hardwarePlatform", "ports", "lastUpdateAt", "isStation", "hosts", "hostShortname", "isDbAvailable", "isRecordingDisabled", "isRecordingMotionOnly", "cameraUtilization", "storageStats", "isRecycling", "avgMotions", "streamSharingAvailable", } @classmethod @cache def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]: return { "lastUpdateAt": convert_to_datetime, "lastDeviceFwUpdatesCheckedAt": convert_to_datetime, "timezone": zoneinfo.ZoneInfo, # recordingRetentionDurationMs is remapped to recordingRetentionDuration "recordingRetentionDurationMs": lambda x: timedelta(milliseconds=x), } | super().unifi_dict_conversions() async def _api_update(self, data: dict[str, Any]) -> None: return await self._api.update_nvr(data) @property def is_analytics_enabled(self) -> bool: return self.analytics_data is not AnalyticsOption.NONE @property def protect_url(self) -> str: return f"{self._api.base_url}/protect/devices/{self._api.bootstrap.nvr.id}" @property def display_name(self) -> str: return self.name or self.market_name or self.type @property def vault_cameras(self) -> list[Camera]: """Vault Cameras for NVR""" if not self.vault_camera_ids: return [] return [self._api.bootstrap.cameras[c] for c in self.vault_camera_ids] @property def is_global_recording_enabled(self) -> bool: """ Is recording footage/events from the camera enabled? If recording is not enabled, cameras will not produce any footage, thumbnails, motion/smart detection events. """ return ( (global_camera_settings := self.global_camera_settings) is not None and global_camera_settings.recording_settings.mode is not RecordingMode.NEVER ) @property def is_smart_detections_enabled(self) -> bool: """If smart detected enabled globally.""" return ( smart_detection := self.smart_detection ) is not None and smart_detection.enable @property def is_license_plate_detections_enabled(self) -> bool: """If smart detected enabled globally.""" return ( (smart_detection := self.smart_detection) is not None and smart_detection.enable and smart_detection.license_plate_recognition ) @property def is_face_detections_enabled(self) -> bool: """If smart detected enabled globally.""" return ( (smart_detection := self.smart_detection) is not None and smart_detection.enable and smart_detection.face_recognition ) def update_all_messages(self) -> None: """Updates doorbell_settings.all_messages after adding/removing custom message""" messages = self.doorbell_settings.custom_messages self.doorbell_settings.all_messages = [ DoorbellMessage( type=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR, text=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value.replace("_", " "), # type: ignore[arg-type] ), DoorbellMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), # type: ignore[arg-type] ), *( DoorbellMessage( type=DoorbellMessageType.CUSTOM_MESSAGE, text=message, ) for message in messages ), ] async def set_insights(self, enabled: bool) -> None: """Sets analytics collection for NVR""" def callback() -> None: self.is_insights_enabled = enabled await self.queue_update(callback) async def set_analytics(self, value: AnalyticsOption) -> None: """Sets analytics collection for NVR""" def callback() -> None: self.analytics_data = value await self.queue_update(callback) async def set_anonymous_analytics(self, enabled: bool) -> None: """Enables or disables anonymous analystics for NVR""" if enabled: await self.set_analytics(AnalyticsOption.ANONYMOUS) else: await self.set_analytics(AnalyticsOption.NONE) async def set_default_reset_timeout(self, timeout: timedelta) -> None: """Sets the default message reset timeout""" def callback() -> None: self.doorbell_settings.default_message_reset_timeout = timeout await self.queue_update(callback) async def set_default_doorbell_message(self, message: str) -> None: """Sets default doorbell message""" def callback() -> None: self.doorbell_settings.default_message_text = DoorbellText(message) await self.queue_update(callback) async def add_custom_doorbell_message(self, message: str) -> None: """Adds custom doorbell message""" if len(message) > 30: raise BadRequest("Message length over 30 characters") if message in self.doorbell_settings.custom_messages: raise BadRequest("Custom doorbell message already exists") await self._update_doorbell_messages( lambda: self.doorbell_settings.custom_messages.append( DoorbellText(message) ), ) async def remove_custom_doorbell_message(self, message: str) -> None: """Removes custom doorbell message""" if message not in self.doorbell_settings.custom_messages: raise BadRequest("Custom doorbell message does not exists") await self._update_doorbell_messages( lambda: self.doorbell_settings.custom_messages.remove( DoorbellText(message) ), ) async def _update_doorbell_messages( self, update_callback: Callable[[], None] ) -> None: """Updates doorbell messages and saves to Protect.""" async with self._update_sync.lock: # yield to the event loop once we have the lock to ensure websocket updates are processed await asyncio.sleep(0) data_before_changes = self.dict_with_excludes() update_callback() await self.save_device(data_before_changes) self.update_all_messages() async def reboot(self) -> None: """Reboots the NVR""" await self._api.reboot_nvr() async def _read_cache_file(self, file_path: Path) -> set[Version] | None: versions: set[Version] | None = None try: _LOGGER.debug("Reading release cache file: %s", file_path) async with aiofiles.open(file_path, "rb") as cache_file: versions = {Version(v) for v in orjson.loads(await cache_file.read())} except FileNotFoundError: # ignore missing file pass except Exception: _LOGGER.warning("Failed to parse cache file: %s", file_path) return versions async def get_is_prerelease(self) -> bool: """Get if current version of Protect is a prerelease version.""" # only EA versions have `-beta` in versions if self.version.is_prerelease: return True # 2.6.14 is an EA version that looks like a release version cache_file_path = self._api.cache_dir / "release_cache.json" versions = await self._read_cache_file( cache_file_path, ) or await self._read_cache_file(RELEASE_CACHE) if versions is None or self.version not in versions: versions = await self._api.get_release_versions() try: _LOGGER.debug("Fetching releases from APT repos...") tmp = self._api.cache_dir / "release_cache.tmp.json" await aos.makedirs(self._api.cache_dir, exist_ok=True) async with aiofiles.open(tmp, "wb") as cache_file: await cache_file.write(orjson.dumps([str(v) for v in versions])) await aos.rename(tmp, cache_file_path) except Exception: _LOGGER.warning("Failed write cache file.") return self.version not in versions async def set_smart_detections(self, value: bool) -> None: """Set if smart detections are enabled.""" def callback() -> None: if self.smart_detection is not None: self.smart_detection.enable = value await self.queue_update(callback) async def set_face_recognition(self, value: bool) -> None: """Set if face detections are enabled. Requires smart detections to be enabled.""" if self.smart_detection is None or not self.smart_detection.enable: raise BadRequest("Smart detections are not enabled.") def callback() -> None: if self.smart_detection is not None: self.smart_detection.face_recognition = value await self.queue_update(callback) async def set_license_plate_recognition(self, value: bool) -> None: """Set if license plate detections are enabled. Requires smart detections to be enabled.""" if self.smart_detection is None or not self.smart_detection.enable: raise BadRequest("Smart detections are not enabled.") def callback() -> None: if self.smart_detection is not None: self.smart_detection.license_plate_recognition = value await self.queue_update(callback) async def set_global_osd_name(self, enabled: bool) -> None: """Sets whether camera name is in the On Screen Display""" def callback() -> None: if self.global_camera_settings: self.global_camera_settings.osd_settings.is_name_enabled = enabled await self.queue_update(callback) async def set_global_osd_date(self, enabled: bool) -> None: """Sets whether current date is in the On Screen Display""" def callback() -> None: if self.global_camera_settings: self.global_camera_settings.osd_settings.is_date_enabled = enabled await self.queue_update(callback) async def set_global_osd_logo(self, enabled: bool) -> None: """Sets whether the UniFi logo is in the On Screen Display""" def callback() -> None: if self.global_camera_settings: self.global_camera_settings.osd_settings.is_logo_enabled = enabled await self.queue_update(callback) async def set_global_osd_bitrate(self, enabled: bool) -> None: """Sets whether camera bitrate is in the On Screen Display""" def callback() -> None: # mismatch between UI internal data structure debug = bitrate data if self.global_camera_settings: self.global_camera_settings.osd_settings.is_debug_enabled = enabled await self.queue_update(callback) async def set_global_motion_detection(self, enabled: bool) -> None: """Sets motion detection on camera""" def callback() -> None: if self.global_camera_settings: self.global_camera_settings.recording_settings.enable_motion_detection = enabled await self.queue_update(callback) async def set_global_recording_mode(self, mode: RecordingMode) -> None: """Sets recording mode on camera""" def callback() -> None: if self.global_camera_settings: self.global_camera_settings.recording_settings.mode = mode await self.queue_update(callback) # object smart detections def _is_smart_enabled(self, smart_type: SmartDetectObjectType) -> bool: return ( self.is_global_recording_enabled and (global_camera_settings := self.global_camera_settings) is not None and smart_type in global_camera_settings.smart_detect_settings.object_types ) @property def is_global_person_detection_on(self) -> bool: """ Is Person Detection available and enabled (camera will produce person smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.PERSON) @property def is_global_person_tracking_enabled(self) -> bool: """Is person tracking enabled""" return ( (global_camera_settings := self.global_camera_settings) is not None and ( auto_tracking_object_types := global_camera_settings.smart_detect_settings.auto_tracking_object_types ) is not None and SmartDetectObjectType.PERSON in auto_tracking_object_types ) @property def is_global_vehicle_detection_on(self) -> bool: """ Is Vehicle Detection available and enabled (camera will produce vehicle smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.VEHICLE) @property def is_global_license_plate_detection_on(self) -> bool: """ Is License Plate Detection available and enabled (camera will produce face license plate detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.LICENSE_PLATE) @property def is_global_package_detection_on(self) -> bool: """ Is Package Detection available and enabled (camera will produce package smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.PACKAGE) @property def is_global_animal_detection_on(self) -> bool: """ Is Animal Detection available and enabled (camera will produce package smart detection events)? """ return self._is_smart_enabled(SmartDetectObjectType.ANIMAL) def _is_audio_enabled(self, smart_type: SmartDetectObjectType) -> bool: return ( (audio_type := smart_type.audio_type) is not None and self.is_global_recording_enabled and (global_camera_settings := self.global_camera_settings) is not None and ( audio_types := global_camera_settings.smart_detect_settings.audio_types ) is not None and audio_type in audio_types ) @property def is_global_smoke_detection_on(self) -> bool: """ Is Smoke Alarm Detection available and enabled (camera will produce smoke smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SMOKE) @property def is_global_co_detection_on(self) -> bool: """ Is CO Alarm Detection available and enabled (camera will produce smoke smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.CMONX) @property def is_global_siren_detection_on(self) -> bool: """ Is Siren Detection available and enabled (camera will produce siren smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SIREN) @property def is_global_baby_cry_detection_on(self) -> bool: """ Is Baby Cry Detection available and enabled (camera will produce baby cry smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BABY_CRY) @property def is_global_speaking_detection_on(self) -> bool: """ Is Speaking Detection available and enabled (camera will produce speaking smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.SPEAK) @property def is_global_bark_detection_on(self) -> bool: """ Is Bark Detection available and enabled (camera will produce barking smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BARK) @property def is_global_car_alarm_detection_on(self) -> bool: """ Is Car Alarm Detection available and enabled (camera will produce car alarm smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.BURGLAR) @property def is_global_car_horn_detection_on(self) -> bool: """ Is Car Horn Detection available and enabled (camera will produce car horn smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.CAR_HORN) @property def is_global_glass_break_detection_on(self) -> bool: """ Is Glass Break available and enabled (camera will produce glass break smart detection events)? """ return self._is_audio_enabled(SmartDetectObjectType.GLASS_BREAK) class LiveviewSlot(ProtectBaseObject): camera_ids: list[str] cycle_mode: str cycle_interval: int _cameras: list[Camera] | None = PrivateAttr(None) @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "cameras": "cameraIds"} @property def cameras(self) -> list[Camera]: if self._cameras is not None: return self._cameras # user may not have permission to see the cameras in the liveview self._cameras = [ self._api.bootstrap.cameras[g] for g in self.camera_ids if g in self._api.bootstrap.cameras ] return self._cameras class Liveview(ProtectModelWithId): name: str is_default: bool is_global: bool layout: int slots: list[LiveviewSlot] owner_id: str @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "owner": "ownerId"} @classmethod @cache def _get_read_only_fields(cls) -> set[str]: return super()._get_read_only_fields() | {"isDefault", "owner"} @property def owner(self) -> User | None: """ Owner of liveview. Will be none if the user only has read only access and it was not made by their user. """ return self._api.bootstrap.users.get(self.owner_id) @property def protect_url(self) -> str: return f"{self._api.base_url}/protect/liveview/{self.id}" uiprotect-6.1.0/src/uiprotect/data/types.py000066400000000000000000000436051467310220200207750ustar00rootroot00000000000000from __future__ import annotations import enum from collections.abc import Callable, Coroutine from functools import cache, cached_property from typing import Any, Literal, Optional, TypeVar, Union from packaging.version import Version as BaseVersion from pydantic.v1 import BaseModel, ConstrainedInt from pydantic.v1.color import Color as BaseColor from pydantic.v1.types import ConstrainedFloat, ConstrainedStr KT = TypeVar("KT") VT = TypeVar("VT") DEFAULT = "DEFAULT_VALUE" DEFAULT_TYPE = Literal["DEFAULT_VALUE"] EventCategories = Literal[ "critical", "update", "admin", "ring", "motion", "smart", "iot", ] ProgressCallback = Callable[[int, int, int], Coroutine[Any, Any, None]] IteratorCallback = Callable[[int, Optional[bytes]], Coroutine[Any, Any, None]] class FixSizeOrderedDict(dict[KT, VT]): """A fixed size ordered dict.""" def __init__(self, *args: Any, max_size: int = 0, **kwargs: Any) -> None: """Create the FixSizeOrderedDict.""" self._max_size = max_size super().__init__(*args, **kwargs) def __setitem__(self, key: KT, value: VT) -> None: """Set an update up to the max size.""" dict.__setitem__(self, key, value) if self._max_size > 0 and len(self) > 0 and len(self) > self._max_size: del self[next(iter(self))] class ValuesEnumMixin: _values: list[str] | None = None _values_normalized: dict[str, str] | None = None @classmethod @cache def from_string(cls, value: str) -> Any: return cls(value) # type: ignore[call-arg] @classmethod @cache def values(cls) -> list[str]: if cls._values is None: cls._values = [e.value for e in cls] # type: ignore[attr-defined] return cls._values @classmethod @cache def values_set(cls) -> set[str]: return set(cls.values()) @classmethod def _missing_(cls, value: Any) -> Any | None: if cls._values_normalized is None: cls._values_normalized = {e.value.lower(): e for e in cls} # type: ignore[attr-defined] value_normal = value if isinstance(value, str): value_normal = value.lower() return cls._values_normalized.get(value_normal) class UnknownValuesEnumMixin(ValuesEnumMixin): @classmethod def _missing_(cls, value: Any) -> Any | None: # value always set in superclass _missing return super()._missing_(value) or cls._values_normalized.get("unknown") # type: ignore[union-attr] @enum.unique class ModelType(str, UnknownValuesEnumMixin, enum.Enum): CAMERA = "camera" CLOUD_IDENTITY = "cloudIdentity" EVENT = "event" GROUP = "group" LIGHT = "light" LIVEVIEW = "liveview" NVR = "nvr" USER = "user" USER_LOCATION = "userLocation" VIEWPORT = "viewer" BRIDGE = "bridge" SENSOR = "sensor" DOORLOCK = "doorlock" SCHEDULE = "schedule" CHIME = "chime" DEVICE_GROUP = "deviceGroup" RECORDING_SCHEDULE = "recordingSchedule" UNKNOWN = "unknown" bootstrap_model_types: tuple[ModelType, ...] bootstrap_models: tuple[str, ...] bootstrap_models_set: set[str] bootstrap_models_types_set: set[ModelType] bootstrap_models_types_and_event_set: set[ModelType] @cached_property def devices_key(self) -> str: """Return the devices key.""" return f"{self.value}s" @cached_property def name(self) -> str: """Return the name.""" return self._name_ @cached_property def value(self) -> str: """Return the value.""" return self._value_ @classmethod @cache def from_string(cls, value: str) -> ModelType: return cls(value) @classmethod def _bootstrap_model_types(cls) -> tuple[ModelType, ...]: """Return the bootstrap models as a tuple.""" # TODO: # legacyUFV # display return ( ModelType.CAMERA, ModelType.USER, ModelType.GROUP, ModelType.LIVEVIEW, ModelType.VIEWPORT, ModelType.LIGHT, ModelType.BRIDGE, ModelType.SENSOR, ModelType.DOORLOCK, ModelType.CHIME, ) @classmethod def _bootstrap_models(cls) -> tuple[str, ...]: """Return the bootstrap models strings as a tuple.""" return tuple( model_type.value for model_type in ModelType._bootstrap_model_types() ) @classmethod def _bootstrap_models_set(cls) -> set[str]: """Return the set of bootstrap models strings as a set.""" return set(ModelType._bootstrap_models()) @classmethod def _bootstrap_models_types_set(cls) -> set[ModelType]: """Return the set of bootstrap models as a set.""" return set(ModelType._bootstrap_model_types()) @classmethod def _bootstrap_models_types_and_event_set(cls) -> set[ModelType]: """Return the set of bootstrap models and the event model as a set.""" return ModelType._bootstrap_models_types_set() | {ModelType.EVENT} def _immutable(self, name: str, value: Any) -> None: raise AttributeError("Cannot modify ModelType") ModelType.bootstrap_model_types = ModelType._bootstrap_model_types() ModelType.bootstrap_models = ModelType._bootstrap_models() ModelType.bootstrap_models_set = ModelType._bootstrap_models_set() ModelType.bootstrap_models_types_set = ModelType._bootstrap_models_types_set() ModelType.bootstrap_models_types_and_event_set = ( ModelType._bootstrap_models_types_and_event_set() ) ModelType.__setattr__ = ModelType._immutable # type: ignore[method-assign, assignment] @enum.unique class EventType(str, ValuesEnumMixin, enum.Enum): DISCONNECT = "disconnect" FACTORY_RESET = "factoryReset" PROVISION = "provision" UPDATE = "update" CAMERA_POWER_CYCLE = "cameraPowerCycling" RING = "ring" DOOR_ACCESS = "doorAccess" RESOLUTION_LOWERED = "resolutionLowered" POOR_CONNECTION = "poorConnection" STREAM_RECOVERY = "streamRecovery" MOTION = "motion" RECORDING_DELETED = "recordingDeleted" SMART_AUDIO_DETECT = "smartAudioDetect" SMART_DETECT = "smartDetectZone" SMART_DETECT_LINE = "smartDetectLine" NO_SCHEDULE = "nonScheduledRecording" RECORDING_MODE_CHANGED = "recordingModeChanged" HOTPLUG = "hotplug" FACE_GROUP_DETECTED = "faceGroupDetected" CONSOLIDATED_RESOLUTION_LOWERED = "consolidatedResolutionLowered" CONSOLIDATED_POOR_CONNECTION = "consolidatedPoorConnection" CAMERA_CONNECTED = "cameraConnected" CAMERA_REBOOTED = "cameraRebooted" CAMERA_DISCONNECTED = "cameraDisconnected" # --- INSTALLED_DISK = "installed" CORRUPTED_DB_RECOVERED = "corruptedDbRecovered" OFFLINE = "offline" OFF = "off" REBOOT = "reboot" FIRMWARE_UPDATE = "fwUpdate" APP_UPDATE = "applicationUpdate" APPLICATION_UPDATABLE = "applicationUpdatable" ACCESS = "access" DRIVE_FAILED = "driveFailed" CAMERA_UTILIZATION_LIMIT_REACHED = "cameraUtilizationLimitReached" CAMERA_UTILIZATION_LIMIT_EXCEEDED = "cameraUtilizationLimitExceeded" DRIVE_SLOW = "driveSlow" GLOBAL_RECORDING_MODE_CHANGED = "globalRecordingModeChanged" NVR_SETTINGS_CHANGED = "nvrSettingsChanged" # --- UNADOPTED_DEVICE_DISCOVERED = "unadoptedDeviceDiscovered" MULTIPLE_UNADOPTED_DEVICE_DISCOVERED = "multipleUnadoptedDeviceDiscovered" DEVICE_ADOPTED = "deviceAdopted" DEVICE_UNADOPTED = "deviceUnadopted" UVF_DISCOVERED = "ufvDiscovered" DEVICE_PASSWORD_UPDATE = "devicesPasswordUpdated" # noqa: S105 DEVICE_UPDATABLE = "deviceUpdatable" MULTIPLE_DEVICE_UPDATABLE = "multipleDeviceUpdatable" DEVICE_CONNECTED = "deviceConnected" DEVICE_REBOOTED = "deviceRebooted" DEVICE_DISCONNECTED = "deviceDisconnected" NETWORK_DEVICE_OFFLINE = "networkDeviceOffline" # --- USER_LEFT = "userLeft" USER_ARRIVED = "userArrived" VIDEO_EXPORTED = "videoExported" MIC_DISABLED = "microphoneDisabled" VIDEO_DELETED = "videoDeleted" SCHEDULE_CHANGED = "recordingScheduleChanged" # --- MOTION_SENSOR = "sensorMotion" SENSOR_OPENED = "sensorOpened" SENSOR_CLOSED = "sensorClosed" SENSOR_ALARM = "sensorAlarm" SENSOR_EXTREME_VALUE = "sensorExtremeValues" SENSOR_WATER_LEAK = "sensorWaterLeak" SENSOR_BATTERY_LOW = "sensorBatteryLow" # --- MOTION_LIGHT = "lightMotion" # --- DOORLOCK_OPEN = "doorlockOpened" DOORLOCK_CLOSE = "doorlockClosed" DOORLOCK_BATTERY_LOW = "doorlockBatteryLow" # --- DISRUPTED_CONDITIONS = "ringDisruptedConditions" # --- RECORDING_OFF = "recordingOff" @staticmethod @cache def device_events() -> list[str]: return [ EventType.MOTION.value, EventType.RING.value, EventType.SMART_DETECT.value, EventType.SMART_AUDIO_DETECT.value, EventType.SMART_DETECT_LINE.value, ] @staticmethod @cache def device_events_set() -> set[str]: return set(EventType.device_events()) @staticmethod @cache def motion_events() -> list[str]: return [EventType.MOTION.value, EventType.SMART_DETECT.value] @enum.unique class StateType(str, ValuesEnumMixin, enum.Enum): CONNECTED = "CONNECTED" CONNECTING = "CONNECTING" DISCONNECTED = "DISCONNECTED" @enum.unique class ProtectWSPayloadFormat(int, enum.Enum): """Websocket Payload formats.""" JSON = 1 UTF8String = 2 NodeBuffer = 3 @enum.unique class SmartDetectObjectType(str, ValuesEnumMixin, enum.Enum): PERSON = "person" ANIMAL = "animal" VEHICLE = "vehicle" LICENSE_PLATE = "licensePlate" PACKAGE = "package" SMOKE = "alrmSmoke" CMONX = "alrmCmonx" SIREN = "alrmSiren" BABY_CRY = "alrmBabyCry" SPEAK = "alrmSpeak" BARK = "alrmBark" BURGLAR = "alrmBurglar" CAR_HORN = "alrmCarHorn" GLASS_BREAK = "alrmGlassBreak" FACE = "face" # old? CAR = "car" PET = "pet" @cached_property def audio_type(self) -> SmartDetectAudioType | None: return OBJECT_TO_AUDIO_MAP.get(self) @enum.unique class SmartDetectAudioType(str, ValuesEnumMixin, enum.Enum): SMOKE = "alrmSmoke" CMONX = "alrmCmonx" SMOKE_CMONX = "smoke_cmonx" SIREN = "alrmSiren" BABY_CRY = "alrmBabyCry" SPEAK = "alrmSpeak" BARK = "alrmBark" BURGLAR = "alrmBurglar" CAR_HORN = "alrmCarHorn" GLASS_BREAK = "alrmGlassBreak" @enum.unique class DetectionColor(str, ValuesEnumMixin, enum.Enum): BLACK = "black" BLUE = "blue" BROWN = "brown" GRAY = "gray" GREEN = "green" ORANGE = "orange" PINK = "pink" PURPLE = "purple" RED = "red" WHITE = "white" YELLOW = "yellow" OBJECT_TO_AUDIO_MAP = { SmartDetectObjectType.SMOKE: SmartDetectAudioType.SMOKE, SmartDetectObjectType.CMONX: SmartDetectAudioType.CMONX, SmartDetectObjectType.SIREN: SmartDetectAudioType.SIREN, SmartDetectObjectType.BABY_CRY: SmartDetectAudioType.BABY_CRY, SmartDetectObjectType.SPEAK: SmartDetectAudioType.SPEAK, SmartDetectObjectType.BARK: SmartDetectAudioType.BARK, SmartDetectObjectType.BURGLAR: SmartDetectAudioType.BURGLAR, SmartDetectObjectType.CAR_HORN: SmartDetectAudioType.CAR_HORN, SmartDetectObjectType.GLASS_BREAK: SmartDetectAudioType.GLASS_BREAK, } @enum.unique class DoorbellMessageType(str, ValuesEnumMixin, enum.Enum): LEAVE_PACKAGE_AT_DOOR = "LEAVE_PACKAGE_AT_DOOR" DO_NOT_DISTURB = "DO_NOT_DISTURB" CUSTOM_MESSAGE = "CUSTOM_MESSAGE" IMAGE = "IMAGE" @enum.unique class LightModeEnableType(str, ValuesEnumMixin, enum.Enum): DARK = "dark" ALWAYS = "fulltime" NIGHT = "night" @enum.unique class LightModeType(str, ValuesEnumMixin, enum.Enum): MOTION = "motion" WHEN_DARK = "always" MANUAL = "off" SCHEDULE = "schedule" @enum.unique class VideoMode(str, ValuesEnumMixin, enum.Enum): DEFAULT = "default" HIGH_FPS = "highFps" HOMEKIT = "homekit" SPORT = "sport" SLOW_SHUTTER = "slowShutter" # should only be for unadopted devices UNKNOWN = "unknown" @enum.unique class AudioStyle(str, UnknownValuesEnumMixin, enum.Enum): NATURE = "nature" NOISE_REDUCED = "noiseReduced" @enum.unique class RecordingMode(str, ValuesEnumMixin, enum.Enum): ALWAYS = "always" NEVER = "never" SCHEDULE = "schedule" DETECTIONS = "detections" @enum.unique class AnalyticsOption(str, ValuesEnumMixin, enum.Enum): NONE = "none" ANONYMOUS = "anonymous" FULL = "full" @enum.unique class RecordingType(str, ValuesEnumMixin, enum.Enum): TIMELAPSE = "timelapse" CONTINUOUS = "rotating" DETECTIONS = "detections" @enum.unique class ResolutionStorageType(str, ValuesEnumMixin, enum.Enum): UHD = "4K" HD = "HD" FREE = "free" @enum.unique class IRLEDMode(str, UnknownValuesEnumMixin, enum.Enum): AUTO = "auto" ON = "on" AUTO_NO_LED = "autoFilterOnly" OFF = "off" MANUAL = "manual" CUSTOM = "custom" UNKNOWN = "unknown" @enum.unique class MountType(str, ValuesEnumMixin, enum.Enum): NONE = "none" LEAK = "leak" DOOR = "door" WINDOW = "window" GARAGE = "garage" @enum.unique class SensorType(str, ValuesEnumMixin, enum.Enum): TEMPERATURE = "temperature" LIGHT = "light" HUMIDITY = "humidity" @enum.unique class SensorStatusType(str, UnknownValuesEnumMixin, enum.Enum): OFFLINE = "offline" UNKNOWN = "unknown" SAFE = "safe" NEUTRAL = "neutral" LOW = "low" HIGH = "high" @enum.unique class SleepStateType(str, ValuesEnumMixin, enum.Enum): DISCONNECTED = "disconnected" AWAKE = "awake" START_SLEEP = "goingToSleep" ASLEEP = "asleep" WAKING = "waking" @enum.unique class AutoExposureMode(str, ValuesEnumMixin, enum.Enum): MANUAL = "manual" AUTO = "auto" SHUTTER = "shutter" FLICK50 = "flick50" FLICK60 = "flick60" @enum.unique class FocusMode(str, ValuesEnumMixin, enum.Enum): MANUAL = "manual" AUTO = "auto" ZTRIG = "ztrig" TOUCH = "touch" @enum.unique class MountPosition(str, UnknownValuesEnumMixin, enum.Enum): CEILING = "ceiling" WALL = "wall" DESK = "desk" NONE = "none" UNKNOWN = "unknown" @enum.unique class GeofencingSetting(str, ValuesEnumMixin, enum.Enum): OFF = "off" ALL_AWAY = "allAway" @enum.unique class MotionAlgorithm(str, ValuesEnumMixin, enum.Enum): STABLE = "stable" ENHANCED = "enhanced" @enum.unique class AudioCodecs(str, ValuesEnumMixin, enum.Enum): AAC = "aac" VORBIS = "vorbis" OPUS = "opus" @enum.unique class LowMedHigh(str, ValuesEnumMixin, enum.Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" @enum.unique class StorageType(str, UnknownValuesEnumMixin, enum.Enum): DISK = "hdd" RAID = "raid" SD_CARD = "sdcard" INTERNAL_SSD = "internalSSD" UNKNOWN = "UNKNOWN" @enum.unique class FirmwareReleaseChannel(str, ValuesEnumMixin, enum.Enum): INTERNAL = "internal" ALPHA = "alpha" BETA = "beta" RELEASE_CANDIDATE = "release-candidate" RELEASE = "release" @enum.unique class ChimeType(int, enum.Enum): NONE = 0 MECHANICAL = 300 DIGITAL = 1000 @enum.unique class LockStatusType(str, ValuesEnumMixin, enum.Enum): OPEN = "OPEN" OPENING = "OPENING" CLOSED = "CLOSED" CLOSING = "CLOSING" JAMMED_WHILE_CLOSING = "JAMMED_WHILE_CLOSING" JAMMED_WHILE_OPENING = "JAMMED_WHILE_OPENING" FAILED_WHILE_CLOSING = "FAILED_WHILE_CLOSING" FAILED_WHILE_OPENING = "FAILED_WHILE_OPENING" NOT_CALIBRATED = "NOT_CALIBRATED" AUTO_CALIBRATION_IN_PROGRESS = "AUTO_CALIBRATION_IN_PROGRESS" CALIBRATION_WAITING_OPEN = "CALIBRATION_WAITING_OPEN" CALIBRATION_WAITING_CLOSE = "CALIBRATION_WAITING_CLOSE" @enum.unique class PermissionNode(str, UnknownValuesEnumMixin, enum.Enum): CREATE = "create" READ = "read" WRITE = "write" DELETE = "delete" READ_MEDIA = "readmedia" DELETE_MEDIA = "deletemedia" READ_LIVE = "readlive" UNKNOWN = "unknown" @cached_property def name(self) -> str: """Return the name.""" return self._name_ @cached_property def value(self) -> str: """Return the value.""" return self._value_ @enum.unique class HDRMode(str, UnknownValuesEnumMixin, enum.Enum): NORMAL = "normal" ALWAYS_ON = "superHdr" @enum.unique class LensType(str, enum.Enum): NONE = "none" FULL_360 = "360" WIDE = "wide" TELESCOPIC = "tele" DLSR_17 = "m43" class DoorbellText(ConstrainedStr): max_length = 30 class ICRCustomValue(ConstrainedInt): ge = 0 le = 10 class ICRLuxValue(ConstrainedInt): ge = 1 le = 30 class LEDLevel(ConstrainedInt): ge = 0 le = 6 class PercentInt(ConstrainedInt): ge = 0 le = 100 class TwoByteInt(ConstrainedInt): ge = 1 le = 255 class PercentFloat(ConstrainedFloat): ge = 0 le = 100 class WDRLevel(ConstrainedInt): ge = 0 le = 3 class ICRSensitivity(ConstrainedInt): ge = 0 le = 3 class Percent(ConstrainedFloat): ge = 0 le = 1 class RepeatTimes(ConstrainedInt): ge = 1 le = 6 class PTZPositionDegree(BaseModel): pan: float tilt: float zoom: int class PTZPositionSteps(BaseModel): focus: int pan: int tilt: int zoom: int class PTZPosition(BaseModel): degree: PTZPositionDegree steps: PTZPositionSteps class PTZPresetPosition(BaseModel): pan: int tilt: int zoom: int class PTZPreset(BaseModel): id: str name: str slot: int ptz: PTZPresetPosition CoordType = Union[Percent, int, float] # TODO: fix when upgrading to pydantic v2 class Color(BaseColor): def __eq__(self, o: object) -> bool: if isinstance(o, Color): return self.as_hex() == o.as_hex() return super().__eq__(o) class Version(BaseVersion): def __str__(self) -> str: super_str = super().__str__() if self.pre is not None and self.pre[0] == "b": super_str = super_str.replace("b", "-beta.") return super_str uiprotect-6.1.0/src/uiprotect/data/user.py000066400000000000000000000155321467310220200206050ustar00rootroot00000000000000"""UniFi Protect User models.""" from __future__ import annotations from datetime import datetime from functools import cache from typing import Any from pydantic.v1.fields import PrivateAttr from .base import ProtectBaseObject, ProtectModel, ProtectModelWithId from .types import ModelType, PermissionNode class Permission(ProtectBaseObject): raw_permission: str model: ModelType nodes: set[PermissionNode] obj_ids: set[str] | None @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: permission = data.get("rawPermission", "") parts = permission.split(":") if len(parts) < 2: raise ValueError(f"Invalid permission: {permission}") data["model"] = ModelType(parts[0]) if parts[1] == "*": data["nodes"] = list(PermissionNode) else: data["nodes"] = [PermissionNode(n) for n in parts[1].split(",")] if len(parts) == 3 and parts[2] != "*": if parts[2] == "$": data["obj_ids"] = ["self"] else: data["obj_ids"] = parts[2].split(",") return super().unifi_dict_to_dict(data) def unifi_dict( # type: ignore[override] self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> str: return self.raw_permission @property def objs(self) -> list[ProtectModelWithId] | None: if self.obj_ids == {"self"} or self.obj_ids is None: return None devices = getattr(self._api.bootstrap, self.model.devices_key) return [devices[oid] for oid in self.obj_ids] class Group(ProtectModelWithId): name: str permissions: list[Permission] type: str is_default: bool @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "permissions" in data: permissions = data.pop("permissions") data["permissions"] = [{"rawPermission": p} for p in permissions] return super().unifi_dict_to_dict(data) class UserLocation(ProtectModel): is_away: bool latitude: float | None longitude: float | None class CloudAccount(ProtectModelWithId): first_name: str last_name: str email: str user_id: str name: str location: UserLocation | None profile_img: str | None = None @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "user": "userId"} def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) # id and cloud ID are always the same if "id" in data: data["cloudId"] = data["id"] if "location" in data and data["location"] is None: del data["location"] return data @property def user(self) -> User: return self._api.bootstrap.users[self.user_id] class UserFeatureFlags(ProtectBaseObject): notifications_v2: bool class User(ProtectModelWithId): permissions: list[Permission] last_login_ip: str | None last_login_time: datetime | None is_owner: bool enable_notifications: bool has_accepted_invite: bool all_permissions: list[Permission] scopes: list[str] | None = None location: UserLocation | None name: str first_name: str last_name: str email: str | None local_username: str group_ids: list[str] cloud_account: CloudAccount | None feature_flags: UserFeatureFlags # TODO: # settings # alertRules # notificationsV2 # notifications # cloudProviders _groups: list[Group] | None = PrivateAttr(None) _perm_cache: dict[str, bool] = PrivateAttr({}) def __init__(self, **data: Any) -> None: if "permissions" in data: permissions = data.pop("permissions") data["permissions"] = [ {"raw_permission": p} if isinstance(p, str) else p for p in permissions ] if "allPermissions" in data: permissions = data.pop("allPermissions") data["allPermissions"] = [ {"raw_permission": p} if isinstance(p, str) else p for p in permissions ] super().__init__(**data) @classmethod def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]: if "permissions" in data: permissions = data.pop("permissions") data["permissions"] = [{"rawPermission": p} for p in permissions] if "allPermissions" in data: permissions = data.pop("allPermissions") data["allPermissions"] = [{"rawPermission": p} for p in permissions] return super().unifi_dict_to_dict(data) @classmethod @cache def _get_unifi_remaps(cls) -> dict[str, str]: return {**super()._get_unifi_remaps(), "groups": "groupIds"} def unifi_dict( self, data: dict[str, Any] | None = None, exclude: set[str] | None = None, ) -> dict[str, Any]: data = super().unifi_dict(data=data, exclude=exclude) if "location" in data and data["location"] is None: del data["location"] return data @property def groups(self) -> list[Group]: """ Groups the user is in Will always be empty if the user only has read only access. """ if self._groups is not None: return self._groups self._groups = [ self._api.bootstrap.groups[g] for g in self.group_ids if g in self._api.bootstrap.groups ] return self._groups def can( self, model: ModelType, node: PermissionNode, obj: ProtectModelWithId | None = None, ) -> bool: """Checks if a user can do a specific action""" check_self = False if model is self.model and obj is not None and obj.id == self.id: perm_str = f"{model.value}:{node.value}:$" check_self = True else: perm_str = ( f"{model.value}:{node.value}:{obj.id if obj is not None else '*'}" ) if perm_str in self._perm_cache: return self._perm_cache[perm_str] for perm in self.all_permissions: if model is not perm.model or node not in perm.nodes: continue if perm.obj_ids is None: self._perm_cache[perm_str] = True return True if check_self and perm.obj_ids == {"self"}: self._perm_cache[perm_str] = True return True if perm.obj_ids is not None and obj is not None and obj.id in perm.obj_ids: self._perm_cache[perm_str] = True return True self._perm_cache[perm_str] = False return False uiprotect-6.1.0/src/uiprotect/data/websocket.py000066400000000000000000000151621467310220200216140ustar00rootroot00000000000000"""Classes for decoding/encoding data from UniFi OS Websocket""" from __future__ import annotations import base64 import enum import struct import zlib from dataclasses import dataclass from functools import cache, cached_property from typing import TYPE_CHECKING, Any import orjson from ..exceptions import WSDecodeError, WSEncodeError from .types import ProtectWSPayloadFormat if TYPE_CHECKING: from .base import ProtectModelWithId WS_HEADER_SIZE = 8 @dataclass(slots=True) class WSPacketFrameHeader: packet_type: int payload_format: int deflated: int unknown: int payload_size: int @enum.unique class WSAction(str, enum.Enum): ADD = "add" UPDATE = "update" REMOVE = "remove" @dataclass(slots=True) class WSSubscriptionMessage: action: WSAction new_update_id: str changed_data: dict[str, Any] new_obj: ProtectModelWithId | None = None old_obj: ProtectModelWithId | None = None _PACKET_STRUCT = struct.Struct("!bbbbi") class BaseWSPacketFrame: unpack = _PACKET_STRUCT.unpack pack = _PACKET_STRUCT.pack data: Any position: int = 0 header: WSPacketFrameHeader | None = None payload_format: ProtectWSPayloadFormat = ProtectWSPayloadFormat.NodeBuffer is_deflated: bool = False length: int = 0 def __repr__(self) -> str: return f"<{self.__class__.__name__} header={self.header} data={self.data}>" def set_data_from_binary(self, data: bytes) -> None: self.data = data if self.header is not None and self.header.deflated: self.data = zlib.decompress(self.data) def get_binary_from_data(self) -> bytes: raise NotImplementedError @staticmethod @cache def klass_from_format(format_raw: int) -> type[BaseWSPacketFrame]: payload_format = ProtectWSPayloadFormat(format_raw) if payload_format == ProtectWSPayloadFormat.JSON: return WSJSONPacketFrame return WSRawPacketFrame @staticmethod def from_binary( data: bytes, position: int = 0, klass: type[WSRawPacketFrame] | None = None, ) -> BaseWSPacketFrame: """ Decode a unifi updates websocket frame. The format of the frame is b: packet_type b: payload_format b: deflated b: unknown i: payload_size """ header_end = position + WS_HEADER_SIZE payload_size: int try: ( packet_type, payload_format, deflated, unknown, payload_size, ) = BaseWSPacketFrame.unpack( data[position:header_end], ) except struct.error as e: raise WSDecodeError from e if klass is None: frame = WSRawPacketFrame.klass_from_format(payload_format)() else: frame = klass() frame.payload_format = ProtectWSPayloadFormat(payload_format) frame.header = WSPacketFrameHeader( packet_type=packet_type, payload_format=payload_format, deflated=deflated, unknown=unknown, payload_size=payload_size, ) frame.length = WS_HEADER_SIZE + payload_size frame.is_deflated = bool(deflated) frame_end = header_end + payload_size frame.set_data_from_binary(data[header_end:frame_end]) return frame @property def packed(self) -> bytes: if self.header is None: raise WSEncodeError("No header to encode") data = self.get_binary_from_data() header = self.pack( self.header.packet_type, self.header.payload_format, self.header.deflated, self.header.unknown, len(data), ) return header + data class WSRawPacketFrame(BaseWSPacketFrame): data: bytes = b"" def get_binary_from_data(self) -> bytes: data = self.data if self.is_deflated: data = zlib.compress(data) return data class WSJSONPacketFrame(BaseWSPacketFrame): data: dict[str, Any] = {} payload_format: ProtectWSPayloadFormat = ProtectWSPayloadFormat.NodeBuffer def set_data_from_binary(self, data: bytes) -> None: if self.header is not None and self.header.deflated: data = zlib.decompress(data) self.data = orjson.loads(data) def get_binary_from_data(self) -> bytes: data = self.json if self.is_deflated: data = zlib.compress(data) return data @property def json(self) -> bytes: return orjson.dumps(self.data) class WSPacket: """Class to handle a unifi protect websocket packet.""" _raw: bytes _raw_encoded: str | None = None _action_frame: BaseWSPacketFrame | None = None _data_frame: BaseWSPacketFrame | None = None def __init__(self, data: bytes) -> None: self._raw = data def __repr__(self) -> str: return f"<{self.__class__.__name__} action_frame={self.action_frame} data_frame={self.data_frame}>" def decode(self) -> None: data = self._raw self._action_frame = WSRawPacketFrame.from_binary(data) length = self._action_frame.length self._data_frame = WSRawPacketFrame.from_binary(data, length) @cached_property def action_frame(self) -> BaseWSPacketFrame: if self._action_frame is None: self.decode() if TYPE_CHECKING: assert self._action_frame is not None assert self._data_frame is not None self.__dict__["data_frame"] = self._data_frame return self._action_frame @cached_property def data_frame(self) -> BaseWSPacketFrame: if self._data_frame is None: self.decode() if TYPE_CHECKING: assert self._action_frame is not None assert self._data_frame is not None self.__dict__["action_frame"] = self._action_frame return self._data_frame @property def raw(self) -> bytes: return self._raw @raw.setter def raw(self, data: bytes) -> None: self._raw = data self._action_frame = None self._data_frame = None self._raw_encoded = None self.__dict__.pop("data_frame", None) self.__dict__.pop("action_frame", None) @property def raw_base64(self) -> str: if self._raw_encoded is None: self._raw_encoded = base64.b64encode(self._raw).decode("utf-8") return self._raw_encoded def pack_frames(self) -> bytes: self._raw_encoded = None self._raw = self.action_frame.packed + self.data_frame.packed return self._raw uiprotect-6.1.0/src/uiprotect/exceptions.py000066400000000000000000000017051467310220200210740ustar00rootroot00000000000000from __future__ import annotations class UnifiProtectError(Exception): """Base class for all other UniFi Protect errors""" class StreamError(UnifiProtectError): """Expcetion raised when trying to stream content""" class DataDecodeError(UnifiProtectError): """Exception raised when trying to decode a UniFi Protect object""" class WSDecodeError(UnifiProtectError): """Exception raised when decoding Websocket packet""" class WSEncodeError(UnifiProtectError): """Exception raised when encoding Websocket packet""" class ClientError(UnifiProtectError): """Base Class for all other UniFi Protect client errors""" class BadRequest(ClientError): """Invalid request from API Client""" class Invalid(ClientError): """Invalid return from Authorization Request.""" class NotAuthorized(PermissionError, BadRequest): """Wrong username, password or permission error.""" class NvrError(ClientError): """Other error.""" uiprotect-6.1.0/src/uiprotect/py.typed000066400000000000000000000000001467310220200200230ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/release_cache.json000066400000000000000000000006021467310220200217720ustar00rootroot00000000000000["1.13.4","1.13.7","1.14.11","1.15.0","1.16.9","1.17.1","1.17.2","1.17.3","1.17.4","1.18.0","1.18.1","1.19.0","1.19.1","1.19.2","1.20.0","1.20.1","1.20.2","1.20.3","1.21.0","1.21.2","1.21.3","1.21.4","1.21.5","1.21.6","2.0.0","2.0.1","2.1.1","2.1.2","2.10.10","2.10.11","2.11.21","2.2.11","2.2.2","2.2.6","2.2.9","2.6.17","2.7.18","2.7.33","2.7.34","2.8.28","2.8.35","2.9.42","3.0.22"] uiprotect-6.1.0/src/uiprotect/stream.py000066400000000000000000000114741467310220200202120ustar00rootroot00000000000000from __future__ import annotations import asyncio import logging from asyncio.streams import StreamReader from asyncio.subprocess import PIPE, Process, create_subprocess_exec from pathlib import Path from shlex import split from typing import TYPE_CHECKING from urllib.parse import urlparse from aioshutil import which from .exceptions import BadRequest, StreamError if TYPE_CHECKING: from .data import Camera _LOGGER = logging.getLogger(__name__) class FfmpegCommand: ffmpeg_path: Path | None args: list[str] process: Process | None = None stdout: list[str] = [] stderr: list[str] = [] def __init__(self, cmd: str, ffmpeg_path: Path | None = None) -> None: self.args = split(cmd) if "ffmpeg" in self.args[0] and ffmpeg_path is None: self.ffmpeg_path = Path(self.args.pop(0)) else: self.ffmpeg_path = ffmpeg_path @property def is_started(self) -> bool: return self.process is not None @property def is_running(self) -> bool: if self.process is None: return False return self.process.returncode is None @property def is_error(self) -> bool: if self.process is None: raise StreamError("ffmpeg has not started") if self.is_running: return False return self.process.returncode != 0 async def start(self) -> None: if self.is_started: raise StreamError("ffmpeg command already started") if self.ffmpeg_path is None: system_ffmpeg = await which("ffmpeg") if system_ffmpeg is None: raise StreamError("Could not find ffmpeg") self.ffmpeg_path = Path(system_ffmpeg) if not self.ffmpeg_path.exists(): raise StreamError("Could not find ffmpeg") _LOGGER.debug("ffmpeg: %s %s", self.ffmpeg_path, " ".join(self.args)) self.process = await create_subprocess_exec( self.ffmpeg_path, *self.args, stdout=PIPE, stderr=PIPE, ) async def stop(self) -> None: if self.process is None: raise StreamError("ffmpeg has not started") self.process.kill() await self.process.wait() async def _read_stream(self, stream: StreamReader | None, attr: str) -> None: if stream is None: return while True: line = await stream.readline() if line: getattr(self, attr).append(line.decode("utf8").rstrip()) else: break async def run_until_complete(self) -> None: if not self.is_started: await self.start() if self.process is None: raise StreamError("Could not start stream") await asyncio.wait( [ asyncio.create_task(self._read_stream(self.process.stdout, "stdout")), asyncio.create_task(self._read_stream(self.process.stderr, "stderr")), ], ) await self.process.wait() class TalkbackStream(FfmpegCommand): camera: Camera content_url: str def __init__( self, camera: Camera, content_url: str, ffmpeg_path: Path | None = None, ): if not camera.feature_flags.has_speaker: raise BadRequest("Camera does not have a speaker for talkback") content_url = self.clean_url(content_url) input_args = self.get_args_from_url(content_url) if len(input_args) > 0: input_args += " " bitrate = camera.talkback_settings.bits_per_sample * 1000 # 8000 seems to result in best quality without overloading the camera udp_bitrate = bitrate + 8000 # vn = no video # acodec = audio codec to encode output in (aac) # ac = number of output channels (1) # ar = output sampling rate (22050) # b:a = set bit rate of output audio cmd = ( "-loglevel info -hide_banner " f'{input_args}-i "{content_url}" -vn ' f"-acodec {camera.talkback_settings.type_fmt.value} -ac {camera.talkback_settings.channels} " f"-ar {camera.talkback_settings.sampling_rate} -b:a {bitrate} -map 0:a " f'-f adts "udp://{camera.host}:{camera.talkback_settings.bind_port}?bitrate={udp_bitrate}"' ) super().__init__(cmd, ffmpeg_path) @classmethod def clean_url(cls, content_url: str) -> str: parsed = urlparse(content_url) if parsed.scheme in {"file", ""}: path = Path(parsed.netloc + parsed.path) if not path.exists(): raise BadRequest(f"File {path} does not exist") content_url = str(path.absolute()) return content_url @classmethod def get_args_from_url(cls, content_url: str) -> str: # TODO: return "" uiprotect-6.1.0/src/uiprotect/test_util/000077500000000000000000000000001467310220200203525ustar00rootroot00000000000000uiprotect-6.1.0/src/uiprotect/test_util/__init__.py000066400000000000000000000444201467310220200224670ustar00rootroot00000000000000from __future__ import annotations import asyncio import logging import shutil import time from collections.abc import Callable, Coroutine from copy import deepcopy from datetime import datetime, timezone from pathlib import Path from shlex import split from subprocess import run from typing import Any, overload import aiohttp from PIL import Image from ..api import ProtectApiClient from ..data import EventType, WSJSONPacketFrame, WSPacket from ..exceptions import BadRequest from ..test_util.anonymize import ( anonymize_data, anonymize_prefixed_event_id, ) from ..utils import from_js_time, is_online, run_async, write_json BLANK_VIDEO_CMD = "ffmpeg -y -hide_banner -loglevel error -f lavfi -i color=size=1280x720:rate=25:color=black -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -t {length} {filename}" def placeholder_image( output_path: Path, width: int, height: int | None = None, ) -> None: if height is None: height = width image = Image.new("RGB", (width, height), (128, 128, 128)) image.save(output_path, "PNG") _LOGGER = logging.getLogger(__name__) LOG_CALLABLE = Callable[[str], None] PROGRESS_CALLABLE = Callable[[int, str], Coroutine[Any, Any, None]] class SampleDataGenerator: """Generate sample data for debugging and testing purposes""" _record_num_ws: int = 0 _record_ws_start_time: float = time.monotonic() _record_listen_for_events: bool = False _record_ws_messages: dict[str, dict[str, Any]] = {} _log: LOG_CALLABLE | None = None _log_warning: LOG_CALLABLE | None = None _ws_progress: PROGRESS_CALLABLE | None = None constants: dict[str, Any] = {} client: ProtectApiClient output_folder: Path do_zip: bool anonymize: bool wait_time: int def __init__( self, client: ProtectApiClient, output: Path, anonymize: bool, wait_time: int, log: LOG_CALLABLE | None = None, log_warning: LOG_CALLABLE | None = None, ws_progress: PROGRESS_CALLABLE | None = None, do_zip: bool = False, ) -> None: self.client = client self.output_folder = output self.do_zip = do_zip self.anonymize = anonymize self.wait_time = wait_time self._log = log self._log_warning = log_warning self._ws_progress = ws_progress if self._log_warning is None and self._log is not None: self._log_warning = self._log def log(self, msg: str) -> None: if self._log is not None: self._log(msg) else: _LOGGER.debug(msg) def log_warning(self, msg: str) -> None: if self._log_warning is not None: self._log_warning(msg) else: _LOGGER.warning(msg) def generate(self) -> None: run_async(self.async_generate()) async def async_generate(self, close_session: bool = True) -> None: self.log(f"Output folder: {self.output_folder}") self.output_folder.mkdir(parents=True, exist_ok=True) websocket = self.client._get_websocket() websocket.start() self.log("Websocket started...") websocket._subscription = self._handle_ws_message self.log("Updating devices...") await self.client.update() bootstrap: dict[str, Any] = await self.client.api_request_obj("bootstrap") bootstrap = await self.write_json_file("sample_bootstrap", bootstrap) self.constants["server_name"] = bootstrap["nvr"]["name"] self.constants["server_id"] = bootstrap["nvr"]["mac"] self.constants["server_version"] = bootstrap["nvr"]["version"] self.constants["server_ip"] = bootstrap["nvr"]["host"] self.constants["server_model"] = bootstrap["nvr"]["type"] self.constants["last_update_id"] = bootstrap["lastUpdateId"] self.constants["user_id"] = bootstrap["authUserId"] self.constants["counts"] = { "camera": len(bootstrap["cameras"]), "user": len(bootstrap["users"]), "group": len(bootstrap["groups"]), "liveview": len(bootstrap["liveviews"]), "viewer": len(bootstrap["viewers"]), "light": len(bootstrap["lights"]), "bridge": len(bootstrap["bridges"]), "sensor": len(bootstrap["sensors"]), "doorlock": len(bootstrap["doorlocks"]), "chime": len(bootstrap["chimes"]), } self.log("Generating event data...") motion_event, smart_detection = await self.generate_event_data() await self.generate_device_data(motion_event, smart_detection) self.log("Recording websocket events...") await self.record_ws_events() if close_session: await self.client.close_session() await self.write_json_file("sample_constants", self.constants, anonymize=False) if self.do_zip: self.log("Zipping files...") def zip_files() -> None: shutil.make_archive(str(self.output_folder), "zip", self.output_folder) shutil.rmtree(self.output_folder) loop = asyncio.get_running_loop() await loop.run_in_executor(None, zip_files) async def record_ws_events(self) -> None: if self.wait_time <= 0: self.log("Skipping recording Websocket messages...") return self._record_num_ws = 0 self._record_ws_start_time = time.monotonic() self._record_listen_for_events = True self._record_ws_messages = {} self.log(f"Waiting {self.wait_time} seconds for WS messages...") if self._ws_progress is not None: await self._ws_progress(self.wait_time, "Waiting for WS messages") else: await asyncio.sleep(self.wait_time) self._record_listen_for_events = False await self.client.async_disconnect_ws() await self.write_json_file( "sample_ws_messages", self._record_ws_messages, anonymize=False, ) @overload async def write_json_file( self, name: str, data: list[Any], anonymize: bool | None = None, ) -> list[Any]: ... @overload async def write_json_file( self, name: str, data: dict[str, Any], anonymize: bool | None = None, ) -> dict[str, Any]: ... async def write_json_file( self, name: str, data: list[Any] | dict[str, Any], anonymize: bool | None = None, ) -> list[Any] | dict[str, Any]: if anonymize is None: anonymize = self.anonymize if anonymize: data = anonymize_data(data) self.log(f"Writing {name}...") await write_json(self.output_folder / f"{name}.json", data) return data async def write_binary_file( self, name: str, ext: str, raw: bytes | None, ) -> None: def write() -> None: if raw is None: self.log(f"No image data, skipping {name}...") return self.log(f"Writing {name}...") Path(self.output_folder / f"{name}.{ext}").write_bytes(raw) loop = asyncio.get_running_loop() await loop.run_in_executor(None, write) async def write_image_file(self, name: str, raw: bytes | None) -> None: await self.write_binary_file(name, "png", raw) async def generate_event_data( self, ) -> tuple[dict[str, Any] | None, dict[str, Any] | None]: data = await self.client.get_events_raw() self.constants["time"] = datetime.now(tz=timezone.utc).isoformat() self.constants["event_count"] = len(data) motion_event: dict[str, Any] | None = None smart_detection: dict[str, Any] | None = None for event_dict in reversed(data): if ( motion_event is None and event_dict["type"] == EventType.MOTION.value and event_dict["camera"] is not None and event_dict["thumbnail"] is not None and event_dict["heatmap"] is not None and event_dict["end"] is not None ): motion_event = deepcopy(event_dict) self.log(f"Using motion event: {motion_event['id']}...") elif ( smart_detection is None and event_dict["type"] == EventType.SMART_DETECT.value and event_dict["camera"] is not None and event_dict["end"] is not None ): smart_detection = deepcopy(event_dict) self.log(f"Using smart detection event: {smart_detection['id']}...") if motion_event is not None and smart_detection is not None: break # anonymize data after pulling events data = await self.write_json_file("sample_raw_events", data) return motion_event, smart_detection async def generate_device_data( self, motion_event: dict[str, Any] | None, smart_detection: dict[str, Any] | None, ) -> None: await asyncio.gather( self.generate_camera_data(), self.generate_motion_data(motion_event), self.generate_smart_detection_data(smart_detection), self.generate_light_data(), self.generate_viewport_data(), self.generate_sensor_data(), self.generate_lock_data(), self.generate_chime_data(), self.generate_bridge_data(), self.generate_liveview_data(), ) async def generate_camera_data(self) -> None: objs = await self.client.api_request_list("cameras") device_id: str | None = None camera_is_online = False for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): camera_is_online = True break if device_id is None: self.log("No camera found. Skipping camera endpoints...") return # json data obj = await self.client.api_request_obj(f"cameras/{device_id}") await self.write_json_file("sample_camera", deepcopy(obj)) self.constants["camera_online"] = camera_is_online if not camera_is_online: self.log( "Camera is not online, skipping snapshot, thumbnail and heatmap generation", ) # snapshot width = obj["channels"][0]["width"] height = obj["channels"][0]["height"] filename = "sample_camera_snapshot" if self.anonymize: self.log(f"Writing {filename}...") placeholder_image(self.output_folder / f"{filename}.png", width, height) else: snapshot = await self.client.get_camera_snapshot(obj["id"], width, height) await self.write_image_file(filename, snapshot) async def generate_motion_data( self, motion_event: dict[str, Any] | None, ) -> None: if motion_event is None: self.log("No motion event, skipping thumbnail and heatmap generation...") return # event thumbnail filename = "sample_camera_thumbnail" thumbnail_id = motion_event["thumbnail"] if self.anonymize: self.log(f"Writing {filename}...") placeholder_image(self.output_folder / f"{filename}.png", 640, 360) thumbnail_id = anonymize_prefixed_event_id(thumbnail_id) else: img = await self.client.get_event_thumbnail(thumbnail_id) await self.write_image_file(filename, img) self.constants["camera_thumbnail"] = thumbnail_id # event heatmap filename = "sample_camera_heatmap" heatmap_id = motion_event["heatmap"] if self.anonymize: self.log(f"Writing {filename}...") placeholder_image(self.output_folder / f"{filename}.png", 640, 360) heatmap_id = anonymize_prefixed_event_id(heatmap_id) else: img = await self.client.get_event_heatmap(heatmap_id) await self.write_image_file(filename, img) self.constants["camera_heatmap"] = heatmap_id # event video filename = "sample_camera_video" length = int((motion_event["end"] - motion_event["start"]) / 1000) if self.anonymize: run( split( BLANK_VIDEO_CMD.format( length=length, filename=self.output_folder / f"{filename}.mp4", ), ), check=True, ) else: video = await self.client.get_camera_video( motion_event["camera"], from_js_time(motion_event["start"]), from_js_time(motion_event["end"]), 2, ) await self.write_binary_file(filename, "mp4", video) self.constants["camera_video_length"] = length async def generate_smart_detection_data( self, smart_detection: dict[str, Any] | None, ) -> None: if smart_detection is None: self.log("No smart detection event, skipping smart detection data...") return try: data = await self.client.get_event_smart_detect_track_raw( smart_detection["id"], ) except BadRequest: self.log_warning("Event smart tracking missing") else: await self.write_json_file("sample_event_smart_track", data) async def generate_light_data(self) -> None: objs = await self.client.api_request_list("lights") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No light found. Skipping light endpoints...") return obj = await self.client.api_request_obj(f"lights/{device_id}") await self.write_json_file("sample_light", obj) async def generate_viewport_data(self) -> None: objs = await self.client.api_request_list("viewers") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No viewer found. Skipping viewer endpoints...") return obj = await self.client.api_request_obj(f"viewers/{device_id}") await self.write_json_file("sample_viewport", obj) async def generate_sensor_data(self) -> None: objs = await self.client.api_request_list("sensors") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No sensor found. Skipping sensor endpoints...") return obj = await self.client.api_request_obj(f"sensors/{device_id}") await self.write_json_file("sample_sensor", obj) async def generate_lock_data(self) -> None: objs = await self.client.api_request_list("doorlocks") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No doorlock found. Skipping doorlock endpoints...") return obj = await self.client.api_request_obj(f"doorlocks/{device_id}") await self.write_json_file("sample_doorlock", obj) async def generate_chime_data(self) -> None: objs = await self.client.api_request_list("chimes") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No chime found. Skipping doorlock endpoints...") return obj = await self.client.api_request_obj(f"chimes/{device_id}") await self.write_json_file("sample_chime", obj) async def generate_bridge_data(self) -> None: objs = await self.client.api_request_list("bridges") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] if is_online(obj_dict): break if device_id is None: self.log("No bridge found. Skipping bridge endpoints...") return obj = await self.client.api_request_obj(f"bridges/{device_id}") await self.write_json_file("sample_bridge", obj) async def generate_liveview_data(self) -> None: objs = await self.client.api_request_list("liveviews") device_id: str | None = None for obj_dict in objs: device_id = obj_dict["id"] break if device_id is None: self.log("No liveview found. Skipping liveview endpoints...") return obj = await self.client.api_request_obj(f"liveviews/{device_id}") await self.write_json_file("sample_liveview", obj) def _handle_ws_message(self, msg: aiohttp.WSMessage) -> None: if not self._record_listen_for_events: return now = time.monotonic() self._record_num_ws += 1 time_offset = now - self._record_ws_start_time if msg.type == aiohttp.WSMsgType.BINARY: packet = WSPacket(msg.data) if not isinstance(packet.action_frame, WSJSONPacketFrame): self.log_warning( f"Got non-JSON action frame: {packet.action_frame.payload_format}", ) return if not isinstance(packet.data_frame, WSJSONPacketFrame): self.log_warning( f"Got non-JSON data frame: {packet.data_frame.payload_format}", ) return if self.anonymize: packet.action_frame.data = anonymize_data(packet.action_frame.data) packet.data_frame.data = anonymize_data(packet.data_frame.data) packet.pack_frames() self._record_ws_messages[str(time_offset)] = { "raw": packet.raw_base64, "action": packet.action_frame.data, "data": packet.data_frame.data, } else: self.log_warning(f"Got non-binary message: {msg.type}") uiprotect-6.1.0/src/uiprotect/test_util/anonymize.py000066400000000000000000000204361467310220200227420ustar00rootroot00000000000000from __future__ import annotations import secrets import string import uuid from typing import Any from urllib.parse import urlparse import typer from ..data import ModelType object_id_mapping: dict[str, str] = {} def anonymize_data(value: Any, name: str | None = None) -> Any: if isinstance(value, list): value = anonymize_list(value, name=name) elif isinstance(value, dict): value = anonymize_dict(value, name=name) else: value = anonymize_value(value, name=name) return value def anonymize_user(user_dict: dict[str, Any]) -> dict[str, Any]: for index, group_id in enumerate(user_dict.get("groups", [])): user_dict["groups"][index] = anonymize_object_id(group_id) user_dict["id"] = anonymize_object_id(user_dict["id"]) if "firstName" in user_dict: user_dict["firstName"] = random_word().title() user_dict["lastName"] = random_word().title() user_dict["name"] = f"{user_dict['firstName']} {user_dict['lastName']}" user_dict["localUsername"] = random_word() user_dict["email"] = f"{user_dict['localUsername']}@example.com" if "cloudAccount" in user_dict and user_dict["cloudAccount"] is not None: user_dict["cloudAccount"]["firstName"] = user_dict["firstName"] user_dict["cloudAccount"]["lastName"] = user_dict["lastName"] user_dict["cloudAccount"]["name"] = user_dict["name"] user_dict["cloudAccount"]["email"] = user_dict["email"] user_dict["cloudAccount"]["user"] = anonymize_object_id( user_dict["cloudAccount"]["user"], ) user_dict["cloudAccount"]["id"] = anonymize_uuid( user_dict["cloudAccount"]["id"], ) user_dict["cloudAccount"]["cloudId"] = anonymize_uuid( user_dict["cloudAccount"]["cloudId"], ) camera_order = (user_dict.get("settings") or {}).get("cameraOrder") if camera_order is not None: for index, camera_id in enumerate(camera_order): camera_order[index] = anonymize_object_id(camera_id) user_dict["settings"]["cameraOrder"] = camera_order if "allPermissions" in user_dict: user_dict["allPermissions"] = anonymize_list( user_dict["allPermissions"], "allPermissions", ) if "permissions" in user_dict: user_dict["permissions"] = anonymize_list( user_dict["permissions"], "permissions", ) return user_dict def anonymize_value(value: Any, name: str | None = None) -> Any: if isinstance(value, str): if name == "accessKey": value = f"{random_number(13)}:{random_hex(24)}:{random_hex(128)}" elif name == "credentials": value = f"{random_hex(64)}" elif name == "privateToken": value = f"{random_alphanum(192)}" elif name in {"host", "connectionHost", "bindAddr"}: value = anonymize_ip(value) elif name in {"anonymousDeviceId", "hardwareId"}: value = random_identifier() elif name in {"rtspAlias", "ssid"}: value = random_alphanum(16) elif name in {"mac", "server_id"}: value = anonymize_peristent_string(value, random_hex(12).upper()) elif name == "bssid": value = anonymize_peristent_string(value, random_seperated_mac()) elif name in {"latitude", "longitude"}: value = "0.0" elif name == "name" and value != "Default": value = f"{random_word()} {random_word()}".title() elif name in {"owner", "user", "camera", "liveview", "authUserId", "event"}: value = anonymize_object_id(value) elif name == "rtsp": value = anonymize_rstp_url(value) elif value.startswith("liveview:*:"): liveview_id = value.split(":")[-1] value = f"liveview:*:{anonymize_object_id(liveview_id)}" return value def anonymize_dict(obj: dict[str, Any], name: str | None = None) -> dict[str, Any]: obj_type = None if "modelKey" in obj: if obj["modelKey"] in [m.value for m in ModelType]: obj_type = ModelType(obj["modelKey"]) else: typer.secho(f"Unknown modelKey: {obj['modelKey']}", fg="yellow") if obj_type == ModelType.USER: return anonymize_user(obj) for key, value in obj.items(): handled = False if obj_type is not None or "payload" in obj: if key == "id": obj[key] = anonymize_object_id(value) handled = True elif obj_type == ModelType.EVENT: if key in {"thumbnail", "heatmap"}: obj[key] = anonymize_prefixed_event_id(value) handled = True elif key == "metadata": if "sensorId" in obj[key]: obj[key]["sensorId"]["text"] = anonymize_object_id( obj[key]["sensorId"]["text"], ) if "sensorName" in obj[key]: obj[key]["sensorName"]["text"] = ( f"{random_word()} {random_word()}".title() ) if not handled: obj[key] = anonymize_data(value, name=key) return obj def anonymize_list(items: list[Any], name: str | None = None) -> list[Any]: for index, value in enumerate(items): handled = False if isinstance(value, str) and name in { "hosts", "smartDetectEvents", "camera", "cameras", }: handled = True if name == "hosts": items[index] = anonymize_ip(items[index]) elif name in {"smartDetectEvents", "camera", "cameras"}: items[index] = anonymize_object_id(value) if not handled: items[index] = anonymize_data(value) return items def anonymize_prefixed_event_id(event_id: str) -> str: event_id = event_id[2:] return f"e-{anonymize_object_id(event_id)}" def anonymize_ip(ip: Any) -> Any: if not isinstance(ip, str): return ip if ip in {"0.0.0.0", "127.0.0.1", "255.255.255.255"}: # noqa: S104 return ip return anonymize_peristent_string(ip, random_ip(ip)) def anonymize_uuid(uuid_str: str) -> str: return anonymize_peristent_string(uuid_str, random_identifier()) def anonymize_object_id(obj_id: str) -> str: return anonymize_peristent_string(obj_id, random_hex(24)) def anonymize_peristent_string(value: str, default: str) -> str: if value not in object_id_mapping: object_id_mapping[value] = default return object_id_mapping[value] def anonymize_rstp_url(url: str) -> str: parts = urlparse(url) port = "" if parts.port is not None and parts.port != 554: port = f":{parts.port}" return f"{parts.scheme}://{anonymize_ip(url)}{port}/{random_alphanum(16)}" def random_hex(length: int) -> str: return secrets.token_hex(length // 2) def random_seperated_mac() -> str: return ":".join(random_hex(2) for _ in range(6)) def random_str(length: int, choices: str) -> str: return "".join(secrets.choice(choices) for _ in range(length)) def random_number(length: int) -> str: return random_str(length, string.digits) def random_word() -> str: return random_char(secrets.randbelow(5) + 3) def random_char(length: int) -> str: return random_str(length, string.ascii_letters) def random_alphanum(length: int) -> str: choices = string.ascii_letters + string.ascii_letters.upper() + string.digits return random_str(length, choices) def random_ip(input_ip: str) -> str: ip = "" try: octals = [int(i) for i in input_ip.split(".")] except ValueError: pass else: if octals[0] == 10: ip = f"10.{secrets.randbelow(256)}.{secrets.randbelow(256)}.{secrets.randbelow(256)}" elif octals[0] == 172 and 16 <= octals[1] <= 31: ip = f"172.{secrets.randbelow(16) + 16}.{secrets.randbelow(256)}.{secrets.randbelow(256)}" elif octals[0] == 192 and octals[1] == 168: ip = f"192.168.{secrets.randbelow(256)}.{secrets.randbelow(256)}" if not ip: ip = f"{secrets.randbelow(255) + 1}.{secrets.randbelow(256)}.{secrets.randbelow(256)}.{secrets.randbelow(256)}" return ip def random_identifier() -> str: return str(uuid.uuid4()) uiprotect-6.1.0/src/uiprotect/utils.py000066400000000000000000000475661467310220200200720ustar00rootroot00000000000000from __future__ import annotations import asyncio import contextlib import json import logging import math import os import re import socket import sys import time import zoneinfo from collections import Counter from collections.abc import Callable, Coroutine, Iterable from copy import deepcopy from datetime import datetime, timedelta, timezone, tzinfo from decimal import Decimal from enum import Enum from functools import cache, lru_cache, partial from hashlib import sha224 from http.cookies import Morsel from inspect import isclass from ipaddress import IPv4Address, IPv6Address, ip_address from operator import attrgetter from pathlib import Path from typing import TYPE_CHECKING, Any, TypeVar, Union, overload from uuid import UUID import jwt from aiohttp import ClientResponse from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, SHAPE_SET, ModelField from pydantic.v1.utils import to_camel from .data.types import ( Color, SmartDetectAudioType, SmartDetectObjectType, Version, VideoMode, ) from .exceptions import NvrError if TYPE_CHECKING: from uiprotect.api import ProtectApiClient from uiprotect.data import CoordType, Event from uiprotect.data.bootstrap import WSStat if sys.version_info[:2] < (3, 11): from async_timeout import timeout as asyncio_timeout else: from asyncio import timeout as asyncio_timeout # noqa: F401 T = TypeVar("T") DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S" DEBUG_ENV = "UFP_DEBUG" PROGRESS_CALLABLE = Callable[[int, str], Coroutine[Any, Any, None]] SNAKE_CASE_KEYS = [ "life_span", "bad_sector", "total_bytes", "used_bytes", "space_type", ] TIMEZONE_GLOBAL: tzinfo | None = None SNAKE_CASE_MATCH_1 = re.compile("(.)([A-Z0-9][a-z]+)") SNAKE_CASE_MATCH_2 = re.compile("__([A-Z0-9])") SNAKE_CASE_MATCH_3 = re.compile("([a-z0-9])([A-Z])") _LOGGER = logging.getLogger(__name__) RELEASE_CACHE = Path(__file__).parent / "release_cache.json" _CREATE_TYPES = {IPv6Address, IPv4Address, UUID, Color, Decimal, Path, Version} _BAD_UUID = "00000000-0000-00 0- 000-000000000000" IP_TYPES = { Union[IPv4Address, str, None], Union[IPv4Address, str], Union[IPv6Address, str, None], Union[IPv6Address, str], Union[IPv6Address, IPv4Address, str, None], Union[IPv6Address, IPv4Address, str], Union[IPv6Address, IPv4Address], Union[IPv6Address, IPv4Address, None], } def set_debug() -> None: """Sets ENV variable for UFP_DEBUG to on (True)""" os.environ[DEBUG_ENV] = str(True) is_debug.cache_clear() def set_no_debug() -> None: """Sets ENV variable for UFP_DEBUG to off (False)""" os.environ[DEBUG_ENV] = str(False) is_debug.cache_clear() @cache def is_debug() -> bool: """Returns if debug ENV is on (True)""" return os.environ.get(DEBUG_ENV) == str(True) async def get_response_reason(response: ClientResponse) -> str: reason = str(response.reason) try: data = await response.json() reason = data.get("error", str(data)) except Exception: with contextlib.suppress(Exception): reason = await response.text() return reason @overload def to_js_time(dt: datetime | int) -> int: ... @overload def to_js_time(dt: None) -> None: ... def to_js_time(dt: datetime | int | None) -> int | None: """Converts Python datetime to Javascript timestamp""" if dt is None: return None if isinstance(dt, int): return dt if dt.tzinfo is None: return int(time.mktime(dt.timetuple()) * 1000) return int(dt.astimezone(timezone.utc).timestamp() * 1000) def to_ms(duration: timedelta | None) -> int | None: """Converts python timedelta to Milliseconds""" if duration is None: return None return int(round(duration.total_seconds() * 1000)) def utc_now() -> datetime: return datetime.now(tz=timezone.utc) def from_js_time(num: float | str | datetime) -> datetime: """Converts Javascript timestamp to Python datetime""" if isinstance(num, datetime): return num return datetime.fromtimestamp(int(num) / 1000, tz=timezone.utc) @lru_cache(maxsize=1024) def convert_to_datetime(source_time: float | str | datetime | None) -> datetime | None: """Converts timestamp to datetime object""" return None if source_time is None else from_js_time(source_time) def format_datetime( dt: datetime | None, default: str | None = None, ) -> str | None: """Formats a datetime object in a consisent format""" return default if dt is None else dt.strftime(DATETIME_FORMAT) def is_online(data: dict[str, Any]) -> bool: return bool(data["state"] == "CONNECTED") def is_doorbell(data: dict[str, Any]) -> bool: return "doorbell" in str(data["type"]).lower() @lru_cache(maxsize=1024) def to_snake_case(name: str) -> str: """Converts string to snake_case""" name = SNAKE_CASE_MATCH_1.sub(r"\1_\2", name) name = SNAKE_CASE_MATCH_2.sub(r"_\1", name) name = SNAKE_CASE_MATCH_3.sub(r"\1_\2", name) return name.lower() def to_camel_case(name: str) -> str: """Converts string to camelCase""" # repeated runs through should not keep lowercasing if "_" in name: name = to_camel(name) return name[0].lower() + name[1:] return name _EMPTY_UUID = UUID("0" * 32) _SHAPE_TYPES = {SHAPE_DICT, SHAPE_LIST, SHAPE_SET} def convert_unifi_data(value: Any, field: ModelField) -> Any: """Converts value from UFP data into pydantic field class""" type_ = field.type_ if type_ is Any: return value if (shape := field.shape) in _SHAPE_TYPES: if shape == SHAPE_LIST and isinstance(value, list): return [convert_unifi_data(v, field) for v in value] if shape == SHAPE_SET and isinstance(value, list): return {convert_unifi_data(v, field) for v in value} if shape == SHAPE_DICT and isinstance(value, dict): return {k: convert_unifi_data(v, field) for k, v in value.items()} if value is not None: if type_ in IP_TYPES: return _cached_ip_address(value) if type_ is datetime: return from_js_time(value) if type_ in _CREATE_TYPES: # cannot do this check too soon because some types cannot be used in isinstance if isinstance(value, type_): return value # handle edge case for improperly formatted UUIDs # 00000000-0000-00 0- 000-000000000000 if type_ is UUID and value == _BAD_UUID: return _EMPTY_UUID return type_(value) if _is_enum_type(type_): if _is_from_string_enum(type_): return type_.from_string(value) return type_(value) return value @lru_cache def _cached_ip_address(value: str) -> IPv4Address | IPv6Address | str: try: return ip_address(value) except ValueError: return value @lru_cache def _is_enum_type(type_: Any) -> bool: """Checks if type is an Enum.""" return isclass(type_) and issubclass(type_, Enum) @lru_cache def _is_from_string_enum(type_: Any) -> bool: """Checks if Enum has from_string method.""" return hasattr(type_, "from_string") def serialize_unifi_obj(value: Any, levels: int = -1) -> Any: """Serializes UFP data""" if unifi_dict := getattr(value, "unifi_dict", None): value = unifi_dict() if levels != 0 and isinstance(value, dict): return serialize_dict(value, levels=levels - 1) if levels != 0 and isinstance(value, Iterable) and not isinstance(value, str): return serialize_list(value, levels=levels - 1) if isinstance(value, Enum): return value.value if isinstance(value, (IPv4Address, IPv6Address, UUID, Path, tzinfo, Version)): return str(value) if isinstance(value, datetime): return to_js_time(value) if isinstance(value, timedelta): return to_ms(value) if isinstance(value, Color): return value.as_hex().upper() return value def serialize_dict(data: dict[str, Any], levels: int = -1) -> dict[str, Any]: """Serializes UFP data dict""" for key in list(data): set_key = key if set_key not in SNAKE_CASE_KEYS: set_key = to_camel_case(set_key) data[set_key] = serialize_unifi_obj(data.pop(key), levels=levels) return data def serialize_coord(coord: CoordType) -> int | float: """Serializes UFP zone coordinate""" from uiprotect.data import Percent if not isinstance(coord, Percent): return coord if math.isclose(coord, 0) or math.isclose(coord, 1): return int(coord) return coord def serialize_point(point: tuple[CoordType, CoordType]) -> list[int | float]: """Serializes UFP zone coordinate point""" return [ serialize_coord(point[0]), serialize_coord(point[1]), ] def serialize_list(items: Iterable[Any], levels: int = -1) -> list[Any]: """Serializes UFP data list""" return [serialize_unifi_obj(i, levels=levels) for i in items] def convert_smart_types(items: Iterable[str]) -> list[SmartDetectObjectType]: """Converts list of str into SmartDetectObjectType. Any unknown values will be ignored and logged.""" types = [] for smart_type in items: try: types.append(SmartDetectObjectType(smart_type)) except ValueError: _LOGGER.warning("Unknown smart detect type: %s", smart_type) return types def convert_smart_audio_types(items: Iterable[str]) -> list[SmartDetectAudioType]: """Converts list of str into SmartDetectAudioType. Any unknown values will be ignored and logged.""" types = [] for smart_type in items: try: types.append(SmartDetectAudioType(smart_type)) except ValueError: _LOGGER.warning("Unknown smart detect audio type: %s", smart_type) return types def convert_video_modes(items: Iterable[str]) -> list[VideoMode]: """Converts list of str into VideoMode. Any unknown values will be ignored and logged.""" types = [] for video_mode in items: try: types.append(VideoMode(video_mode)) except ValueError: _LOGGER.warning("Unknown video mode: %s", video_mode) return types def ip_from_host(host: str) -> IPv4Address | IPv6Address: try: return ip_address(host) except ValueError: pass return ip_address(socket.gethostbyname(host)) def dict_diff(orig: dict[str, Any] | None, new: dict[str, Any]) -> dict[str, Any]: changed: dict[str, Any] = {} if orig is None: return new for key, value in new.items(): if key not in orig: changed[key] = deepcopy(value) continue if isinstance(value, dict): sub_changed = dict_diff(orig[key], value) if sub_changed: changed[key] = sub_changed elif value != orig[key]: changed[key] = deepcopy(value) return changed def ws_stat_summmary( stats: list[WSStat], ) -> tuple[list[WSStat], float, Counter[str], Counter[str], Counter[str]]: if len(stats) == 0: raise ValueError("No stats to summarize") unfiltered = [s for s in stats if not s.filtered] percent = (1 - len(unfiltered) / len(stats)) * 100 keys = Counter(k for s in unfiltered for k in s.keys_set) models = Counter(k.model for k in unfiltered) actions = Counter(k.action for k in unfiltered) return unfiltered, percent, keys, models, actions async def write_json(output_path: Path, data: list[Any] | dict[str, Any]) -> None: def write() -> None: with open(output_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=4) f.write("\n") loop = asyncio.get_running_loop() await loop.run_in_executor(None, write) def print_ws_stat_summary( stats: list[WSStat], output: Callable[[Any], Any] | None = None, ) -> None: # typer<0.4.1 is incompatible with click>=8.1.0 # allows only the CLI interface to break if both are installed import typer if output is None: output = typer.echo if typer is not None else print unfiltered, percent, keys, models, actions = ws_stat_summmary(stats) title = " ws stat summary " side_length = int((80 - len(title)) / 2) lines = [ "-" * side_length + title + "-" * side_length, f"packet count: {len(stats)}", f"filtered packet count: {len(unfiltered)} ({percent:.4}%)", "-" * 80, ] for key, count in models.most_common(): lines.append(f"{key}: {count}") lines.append("-" * 80) for key, count in actions.most_common(): lines.append(f"{key}: {count}") lines.append("-" * 80) for key, count in keys.most_common(10): lines.append(f"{key}: {count}") lines.append("-" * 80) output("\n".join(lines)) async def profile_ws( protect: ProtectApiClient, duration: int, output_path: Path | None = None, ws_progress: PROGRESS_CALLABLE | None = None, do_print: bool = True, print_output: Callable[[Any], Any] | None = None, ) -> None: if protect.bootstrap.capture_ws_stats: raise NvrError("Profile already in progress") _LOGGER.debug("Starting profile...") protect.bootstrap.clear_ws_stats() protect.bootstrap.capture_ws_stats = True if ws_progress is not None: await ws_progress(duration, "Waiting for WS messages") else: await asyncio.sleep(duration) protect.bootstrap.capture_ws_stats = False _LOGGER.debug("Finished profile...") if output_path: json_data = [s.__dict__ for s in protect.bootstrap.ws_stats] await write_json(output_path, json_data) if do_print: print_ws_stat_summary(protect.bootstrap.ws_stats, output=print_output) def decode_token_cookie(token_cookie: Morsel[str]) -> dict[str, Any] | None: """Decode a token cookie if it is still valid.""" try: return jwt.decode( token_cookie.value, options={"verify_signature": False, "verify_exp": True}, ) except jwt.ExpiredSignatureError: _LOGGER.debug("Authentication token has expired.") return None except Exception as broad_ex: _LOGGER.debug("Authentication token decode error: %s", broad_ex) return None def format_duration(duration: timedelta) -> str: """Formats a timedelta as a string.""" seconds = int(duration.total_seconds()) hours = seconds // 3600 seconds -= hours * 3600 minutes = seconds // 60 seconds -= minutes * 60 output = "" if hours > 0: output = f"{hours}h" if minutes > 0: output = f"{output}{minutes}m" return f"{output}{seconds}s" def _set_timezone(tz: tzinfo | str) -> tzinfo: global TIMEZONE_GLOBAL if isinstance(tz, str): tz = zoneinfo.ZoneInfo(tz) TIMEZONE_GLOBAL = tz return TIMEZONE_GLOBAL def get_local_timezone() -> tzinfo: """Gets Olson timezone name for localizing datetimes""" if TIMEZONE_GLOBAL is not None: return TIMEZONE_GLOBAL try: from homeassistant.util import dt as dt_util # type: ignore[import-not-found] return _set_timezone(dt_util.DEFAULT_TIME_ZONE) except ImportError: pass timezone_name = os.environ.get("TZ") if timezone_name: return _set_timezone(timezone_name) timezone_name = "UTC" timezone_locale = Path("/etc/localtime") if timezone_locale.exists(): tzfile_digest = sha224(Path(timezone_locale).read_bytes()).hexdigest() for root, _, filenames in os.walk(Path("/usr/share/zoneinfo/")): for filename in filenames: fullname = os.path.join(root, filename) digest = sha224(Path(fullname).read_bytes()).hexdigest() if digest == tzfile_digest: timezone_name = "/".join((fullname.split("/"))[-2:]) return _set_timezone(timezone_name) def local_datetime(dt: datetime | None = None) -> datetime: """Returns datetime in local timezone""" if dt is None: dt = datetime.now(tz=timezone.utc) local_tz = get_local_timezone() if dt.tzinfo is None: return dt.replace(tzinfo=local_tz) return dt.astimezone(local_tz) def log_event(event: Event) -> None: from uiprotect.data import EventType _LOGGER.debug("event WS msg: %s", event.dict()) if "smart" not in event.type.value: return camera = event.camera if camera is None: return if event.end is not None: _LOGGER.debug( "%s (%s): Smart detection ended for %s (%s)", camera.name, camera.mac, event.smart_detect_types, event.id, ) return _LOGGER.debug( "%s (%s): New smart detection started for %s (%s)", camera.name, camera.mac, event.smart_detect_types, event.id, ) smart_settings = camera.smart_detect_settings for smart_type in event.smart_detect_types: is_audio = event.type is EventType.SMART_AUDIO_DETECT if is_audio: if smart_type.audio_type is None: return is_enabled = ( smart_settings.audio_types is not None and smart_type.audio_type in smart_settings.audio_types ) last_event = camera.get_last_smart_audio_detect_event(smart_type.audio_type) else: is_enabled = smart_type in smart_settings.object_types last_event = camera.get_last_smart_detect_event(smart_type) _LOGGER.debug( "Event info (%s):\n" " is_smart_detected: %s\n" " is_recording_enabled: %s\n" " is_enabled: %s\n" " event: %s", smart_type, camera.is_smart_detected, camera.is_recording_enabled, is_enabled, last_event, ) def run_async(callback: Coroutine[Any, Any, T]) -> T: """Run async coroutine.""" if sys.version_info >= (3, 11): return asyncio.run(callback) loop = asyncio.get_event_loop() # type: ignore[unreachable] return loop.run_until_complete(callback) def clamp_value(value: float, step_size: float) -> float: """Clamps value to multiples of step size.""" ratio = 1 / step_size return int(value * ratio) / ratio @lru_cache(maxsize=1024) def normalize_mac(mac: str) -> str: """Normalize MAC address.""" return mac.lower().replace(":", "").replace("-", "").replace("_", "") _SENTINEL = object() def get_nested_attr(attrs: tuple[str, ...], obj: Any) -> Any: """Fetch a nested attribute.""" value = obj for key in attrs: if (value := getattr(value, key, _SENTINEL)) is _SENTINEL: return None return value def get_nested_attr_as_bool(attrs: tuple[str, ...], obj: Any) -> bool: """Fetch a nested attribute as a bool.""" value = obj for key in attrs: if (value := getattr(value, key, _SENTINEL)) is _SENTINEL: return False return bool(value) def get_top_level_attr_as_bool(attr: str, obj: Any) -> Any: """Fetch a top level attribute as a bool.""" return bool(getattr(obj, attr)) def make_value_getter(ufp_value: str) -> Callable[[T], Any]: """Return a function to get a value from a Protect device.""" if "." not in ufp_value: return attrgetter(ufp_value) return partial(get_nested_attr, tuple(ufp_value.split("."))) def make_enabled_getter(ufp_enabled: str) -> Callable[[T], bool]: """Return a function to get a value from a Protect device.""" if "." not in ufp_enabled: return attrgetter(ufp_enabled) return partial(get_nested_attr, tuple(ufp_enabled.split("."))) def make_required_getter(ufp_required_field: str) -> Callable[[T], bool]: """Return a function to get a value from a Protect device.""" if "." not in ufp_required_field: return partial(get_top_level_attr_as_bool, ufp_required_field) return partial(get_nested_attr_as_bool, tuple(ufp_required_field.split("."))) @lru_cache def timedelta_total_seconds(td: timedelta) -> float: return td.total_seconds() uiprotect-6.1.0/src/uiprotect/websocket.py000066400000000000000000000176351467310220200207120ustar00rootroot00000000000000"""UniFi Protect Websockets.""" from __future__ import annotations import asyncio import contextlib import logging from collections.abc import Awaitable, Callable, Coroutine from enum import Enum from http import HTTPStatus from typing import Any, Optional from aiohttp import ( ClientError, ClientSession, ClientWebSocketResponse, WSMessage, WSMsgType, WSServerHandshakeError, ) from yarl import URL from .exceptions import NotAuthorized, NvrError _LOGGER = logging.getLogger(__name__) AuthCallbackType = Callable[..., Coroutine[Any, Any, Optional[dict[str, str]]]] GetSessionCallbackType = Callable[[], Awaitable[ClientSession]] UpdateBootstrapCallbackType = Callable[[], None] _CLOSE_MESSAGE_TYPES = {WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED} class WebsocketState(Enum): CONNECTED = True DISCONNECTED = False class Websocket: """UniFi Protect Websocket manager.""" _running = False _headers: dict[str, str] | None = None _websocket_loop_task: asyncio.Task[None] | None = None _stop_task: asyncio.Task[None] | None = None _ws_connection: ClientWebSocketResponse | None = None def __init__( self, get_url: Callable[[], URL], auth_callback: AuthCallbackType, update_bootstrap: UpdateBootstrapCallbackType, get_session: GetSessionCallbackType, subscription: Callable[[WSMessage], None], state_callback: Callable[[WebsocketState], None], *, timeout: float = 30.0, backoff: int = 10, verify: bool = True, receive_timeout: float | None = None, ) -> None: """Init Websocket.""" self.get_url = get_url self.timeout = timeout self.receive_timeout = receive_timeout self.backoff = backoff self.verify = verify self._get_session = get_session self._auth = auth_callback self._update_bootstrap = update_bootstrap self._subscription = subscription self._seen_non_close_message = False self._websocket_state = state_callback self._current_state: WebsocketState = WebsocketState.DISCONNECTED @property def is_connected(self) -> bool: """Return if the websocket is connected and has received a valid message.""" return self._ws_connection is not None and not self._ws_connection.closed async def _websocket_loop(self) -> None: """Running loop for websocket.""" await self.wait_closed() backoff = self.backoff while True: url = self.get_url() try: await self._websocket_inner_loop(url) except ClientError as ex: level = logging.ERROR if self._seen_non_close_message else logging.DEBUG if isinstance(ex, WSServerHandshakeError): if ex.status == HTTPStatus.UNAUTHORIZED.value: _LOGGER.log( level, "Websocket authentication error: %s: %s", url, ex ) await self._attempt_auth(True) else: _LOGGER.log(level, "Websocket handshake error: %s: %s", url, ex) else: _LOGGER.log(level, "Websocket disconnect error: %s: %s", url, ex) except asyncio.TimeoutError: level = logging.ERROR if self._seen_non_close_message else logging.DEBUG _LOGGER.log(level, "Websocket timeout: %s", url) except Exception: _LOGGER.exception("Unexpected error in websocket loop") self._state_changed(WebsocketState.DISCONNECTED) if self._running is False: break _LOGGER.debug("Reconnecting websocket in %s seconds", backoff) await asyncio.sleep(self.backoff) def _state_changed(self, state: WebsocketState) -> None: """State changed.""" if self._current_state is state: return self._current_state = state self._websocket_state(state) async def _websocket_inner_loop(self, url: URL) -> None: _LOGGER.debug("Connecting WS to %s", url) await self._attempt_auth(False) msg: WSMessage | None = None self._seen_non_close_message = False session = await self._get_session() # catch any and all errors for Websocket so we can clean up correctly try: self._ws_connection = await session.ws_connect( url, ssl=self.verify, headers=self._headers, timeout=self.timeout ) while True: msg = await self._ws_connection.receive(self.receive_timeout) msg_type = msg.type if msg_type is WSMsgType.ERROR: _LOGGER.exception("Error from Websocket: %s", msg.data) break elif msg_type in _CLOSE_MESSAGE_TYPES: _LOGGER.debug("Websocket closed: %s", msg) break if not self._seen_non_close_message: self._seen_non_close_message = True self._state_changed(WebsocketState.CONNECTED) try: self._subscription(msg) except Exception: _LOGGER.exception("Error processing websocket message") finally: if ( msg is not None and msg.type is WSMsgType.CLOSE # If it closes right away or lastUpdateId is in the extra # its an indication that we should update the bootstrap # since lastUpdateId is invalid and ( not self._seen_non_close_message or (msg.extra and "lastUpdateId" in msg.extra) ) ): self._update_bootstrap() _LOGGER.debug("Websocket disconnected: last message: %s", msg) if self._ws_connection is not None and not self._ws_connection.closed: await self._ws_connection.close() self._ws_connection = None async def _attempt_auth(self, force: bool) -> None: """Attempt to authenticate.""" try: self._headers = await self._auth(force) except (NotAuthorized, NvrError) as ex: _LOGGER.debug("Error authenticating websocket: %s", ex) except Exception: _LOGGER.exception("Unknown error authenticating websocket") def start(self) -> None: """Start the websocket.""" if self._running: return self._running = True self._websocket_loop_task = asyncio.create_task(self._websocket_loop()) def stop(self) -> None: """Disconnect the websocket.""" _LOGGER.debug("Disconnecting websocket...") if not self._running: return if self._websocket_loop_task: self._websocket_loop_task.cancel() self._running = False ws_connection = self._ws_connection websocket_loop_task = self._websocket_loop_task self._ws_connection = None self._websocket_loop_task = None self._stop_task = asyncio.create_task( self._stop(ws_connection, websocket_loop_task) ) self._state_changed(WebsocketState.DISCONNECTED) async def wait_closed(self) -> None: """Wait for the websocket to close.""" if self._stop_task and not self._stop_task.done(): with contextlib.suppress(asyncio.CancelledError): await self._stop_task self._stop_task = None async def _stop( self, ws_connection: ClientWebSocketResponse | None, websocket_loop_task: asyncio.Task[None] | None, ) -> None: """Stop the websocket.""" if ws_connection: await ws_connection.close() if websocket_loop_task: with contextlib.suppress(asyncio.CancelledError): await websocket_loop_task uiprotect-6.1.0/templates/000077500000000000000000000000001467310220200155275ustar00rootroot00000000000000uiprotect-6.1.0/templates/CHANGELOG.md.j2000066400000000000000000000012351467310220200176530ustar00rootroot00000000000000# Changelog {%- for version, release in context.history.released.items() %} ## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) {%- for category, commits in release["elements"].items() %} {# Category title: Breaking, Fix, Documentation #} ### {{ category | capitalize }} {# List actual changes in the category #} {%- for commit in commits %} {% if commit is not none and commit.descriptions is defined %} - {{ commit.descriptions[0] | capitalize }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) {% endif %} {%- endfor %}{# for commit #} {%- endfor %}{# for category, commits #} {%- endfor %}{# for version, release #} uiprotect-6.1.0/tests/000077500000000000000000000000001467310220200146735ustar00rootroot00000000000000uiprotect-6.1.0/tests/__init__.py000066400000000000000000000000001467310220200167720ustar00rootroot00000000000000uiprotect-6.1.0/tests/conftest.py000066400000000000000000000740361467310220200171040ustar00rootroot00000000000000from __future__ import annotations import asyncio import base64 import json import math import os from contextlib import suppress from copy import deepcopy from datetime import datetime, timezone from pathlib import Path from shlex import split from subprocess import run from tempfile import NamedTemporaryFile from typing import Any from unittest.mock import AsyncMock, Mock import aiohttp import pytest import pytest_asyncio from tests.sample_data.constants import CONSTANTS from uiprotect import ProtectApiClient from uiprotect.data import Camera, ModelType from uiprotect.data.devices import PTZRange, PTZZoomRange from uiprotect.data.nvr import Event from uiprotect.data.types import EventType from uiprotect.utils import _BAD_UUID, set_debug, set_no_debug UFP_SAMPLE_DIR = os.environ.get("UFP_SAMPLE_DIR") if UFP_SAMPLE_DIR: SAMPLE_DATA_DIRECTORY = Path(UFP_SAMPLE_DIR) else: SAMPLE_DATA_DIRECTORY = Path(__file__).parent / "sample_data" CHECK_CMD = "ffprobe -v error -select_streams v:0 -show_entries stream=codec_type -of csv=p=0 {filename}" LENGTH_CMD = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {filename}" TEST_CAMERA_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_camera.json").exists() TEST_SNAPSHOT_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_camera_snapshot.png").exists() TEST_VIDEO_EXISTS = ( SAMPLE_DATA_DIRECTORY / "sample_camera_video.mp4" ).exists() or "camera_video_length" not in CONSTANTS TEST_THUMBNAIL_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_camera_thumbnail.png").exists() TEST_HEATMAP_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_camera_heatmap.png").exists() TEST_SMART_TRACK_EXISTS = ( SAMPLE_DATA_DIRECTORY / "sample_event_smart_track.json" ).exists() TEST_LIGHT_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_light.json").exists() TEST_SENSOR_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_sensor.json").exists() TEST_VIEWPORT_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_viewport.json").exists() TEST_BRIDGE_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_bridge.json").exists() TEST_LIVEVIEW_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_liveview.json").exists() TEST_DOORLOCK_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_doorlock.json").exists() TEST_CHIME_EXISTS = (SAMPLE_DATA_DIRECTORY / "sample_chime.json").exists() ANY_NONE = [[None], None, []] def read_binary_file(name: str, ext: str = "png"): with (SAMPLE_DATA_DIRECTORY / f"{name}.{ext}").open("rb") as f: return f.read() def read_json_file(name: str): with (SAMPLE_DATA_DIRECTORY / f"{name}.json").open(encoding="utf8") as f: return json.load(f) def read_bootstrap_json_file(): # tests expect global recording settings to be off bootstrap = read_json_file("sample_bootstrap") cameras = [] for camera in bootstrap["cameras"]: if camera.get("useGlobal"): camera["useGlobal"] = False cameras.append(camera) bootstrap["cameras"] = cameras return bootstrap def read_camera_json_file(): # tests expect global recording settings to be off camera = read_json_file("sample_camera") if camera.get("useGlobal"): camera["useGlobal"] = False return camera def get_now(): return datetime.fromisoformat(CONSTANTS["time"]).replace(microsecond=0) def get_time(): return datetime.fromisoformat(CONSTANTS["time"]).replace(microsecond=0).timestamp() def validate_video_file(filepath: Path, length: int): output = run( split(CHECK_CMD.format(filename=filepath)), check=True, capture_output=True, ) assert output.stdout.decode("utf8").strip() == "video" output = run( split(LENGTH_CMD.format(filename=filepath)), check=True, capture_output=True, ) # it looks like UFP does not always generate a video of exact length assert length - 10 < int(float(output.stdout.decode("utf8").strip())) < length + 10 async def mock_api_request_raw(url: str, *args, **kwargs): if url.startswith("thumbnails/") or url.endswith("thumbnail"): return read_binary_file("sample_camera_thumbnail") if url.startswith("cameras/"): return read_binary_file("sample_camera_snapshot") if url.startswith("heatmaps/") or url.endswith("heatmap"): return read_binary_file("sample_camera_heatmap") if url == "video/export": return read_binary_file("sample_camera_video", "mp4") return b"" async def mock_api_request(url: str, *args, **kwargs): if url == "bootstrap": return read_bootstrap_json_file() if url == "nvr": return read_bootstrap_json_file()["nvr"] if url == "events": return read_json_file("sample_raw_events") if url == "cameras": return [read_camera_json_file()] if url == "lights": return [read_json_file("sample_light")] if url == "sensors": return [read_json_file("sample_sensor")] if url == "viewers": return [read_json_file("sample_viewport")] if url == "bridges": return [read_json_file("sample_bridge")] if url == "liveviews": return [read_json_file("sample_liveview")] if url == "doorlocks": return [read_json_file("sample_doorlock")] if url == "chimes": return [read_json_file("sample_chime")] if url.endswith("ptz/preset"): return { "id": "test-id", "name": "Test", "slot": 0, "ptz": { "pan": 100, "tilt": 100, "zoom": 0, }, } if url.endswith("ptz/home"): return { "id": "test-id", "name": "Home", "slot": -1, "ptz": { "pan": 100, "tilt": 100, "zoom": 0, }, } if url.startswith("cameras/"): return read_camera_json_file() if url.startswith("lights/"): return read_json_file("sample_light") if url.startswith("sensors/"): return read_json_file("sample_sensor") if url.startswith("viewers/"): return read_json_file("sample_viewport") if url.startswith("bridges/"): return read_json_file("sample_bridge") if url.startswith("liveviews/"): return read_json_file("sample_liveview") if url.startswith("doorlocks"): return read_json_file("sample_doorlock") if url.startswith("chimes"): return read_json_file("sample_chime") if "smartDetectTrack" in url: return read_json_file("sample_event_smart_track") return {} class SimpleMockWebsocket: is_closed: bool = False now: float = 0 events: dict[str, Any] count = 0 def __init__(self): self.events = [] @property def closed(self): return self.is_closed async def close(self): self.is_closed = True def __aiter__(self): return self async def __anext__(self): if len(self.events) == 0 or self.is_closed: raise StopAsyncIteration key = next(iter(self.events.keys())) next_time = float(key) await asyncio.sleep(next_time - self.now) self.now = next_time data = self.events.pop(key) self.count += 1 return aiohttp.WSMessage( aiohttp.WSMsgType.BINARY, base64.b64decode(data["raw"]), None, ) async def receive(self, timeout): return await self.__anext__() class MockWebsocket(SimpleMockWebsocket): def __init__(self): super().__init__() self.events = read_json_file("sample_ws_messages") MockDatetime = Mock() MockDatetime.now.return_value = get_now() MockDatetime.utcnow.return_value = get_now() @pytest.fixture(autouse=True) def _ensure_debug(): set_debug() async def setup_client( client: ProtectApiClient, websocket: SimpleMockWebsocket, timeout: int = 0, ): mock_cs = AsyncMock() mock_session = AsyncMock() mock_session.ws_connect = AsyncMock(return_value=websocket) mock_cs.return_value = mock_session ws = client._get_websocket() ws.timeout = timeout ws._get_session = mock_cs # type: ignore[method-assign] client.api_request = AsyncMock(side_effect=mock_api_request) # type: ignore[method-assign] client.api_request_raw = AsyncMock(side_effect=mock_api_request_raw) # type: ignore[method-assign] client.ensure_authenticated = AsyncMock() # type: ignore[method-assign] await client.update() # make sure global recording settings are disabled for all cameras (test expect it) for camera in client.bootstrap.cameras.values(): camera.use_global = False return client async def cleanup_client(client: ProtectApiClient): await client.async_disconnect_ws() await client.close_session() @pytest_asyncio.fixture(name="protect_client") async def protect_client_fixture(): client = ProtectApiClient( "127.0.0.1", 0, "username", "password", ws_timeout=0.1, store_sessions=False, ) yield await setup_client(client, SimpleMockWebsocket()) await cleanup_client(client) @pytest_asyncio.fixture async def protect_client_no_debug(): set_no_debug() client = ProtectApiClient( "127.0.0.1", 0, "username", "password", ws_timeout=0.1, store_sessions=False, ) yield await setup_client(client, SimpleMockWebsocket()) await cleanup_client(client) @pytest_asyncio.fixture async def protect_client_ws(): set_no_debug() client = ProtectApiClient( "127.0.0.1", 0, "username", "password", ws_timeout=0.1, ws_receive_timeout=0.1, store_sessions=False, ) yield await setup_client(client, MockWebsocket(), timeout=30) await cleanup_client(client) @pytest_asyncio.fixture async def smart_dectect_obj(protect_client: ProtectApiClient, raw_events): event_dict = None for event in raw_events: if event["type"] == EventType.SMART_DETECT.value: event_dict = event break if event_dict is None: yield None else: yield Event.from_unifi_dict(api=protect_client, **event_dict) @pytest_asyncio.fixture async def nvr_obj(protect_client: ProtectApiClient): yield protect_client.bootstrap.nvr @pytest_asyncio.fixture async def camera_obj(protect_client: ProtectApiClient): if not TEST_CAMERA_EXISTS: return None return next(iter(protect_client.bootstrap.cameras.values())) @pytest_asyncio.fixture async def ptz_camera(protect_client: ProtectApiClient): if not TEST_CAMERA_EXISTS: return None camera = next(iter(protect_client.bootstrap.cameras.values())) # G4 PTZ camera.is_ptz = True camera.feature_flags.is_ptz = True camera.feature_flags.focus = PTZRange( steps={ # type: ignore[arg-type] "max": 1560, "min": 0, "step": 1, }, degrees={ # type: ignore[arg-type] "max": None, "min": None, "step": None, }, ) camera.feature_flags.pan = PTZRange( steps={ # type: ignore[arg-type] "max": 35200, "min": 0, "step": 1, }, degrees={ # type: ignore[arg-type] "max": 360, "min": 0, "step": 0.1, }, ) camera.feature_flags.tilt = PTZRange( steps={ # type: ignore[arg-type] "max": 9777, "min": 1, "step": 1, }, degrees={ # type: ignore[arg-type] "max": 90, "min": -20, "step": 0.1, }, ) camera.feature_flags.zoom = PTZZoomRange( ratio=22, steps={ # type: ignore[arg-type] "max": 2010, "min": 0, "step": 1, }, degrees={ # type: ignore[arg-type] "max": None, "min": None, "step": None, }, ) protect_client.bootstrap.cameras[camera.id] = camera return camera @pytest_asyncio.fixture async def light_obj(protect_client: ProtectApiClient): if not TEST_LIGHT_EXISTS: return None return next(iter(protect_client.bootstrap.lights.values())) @pytest_asyncio.fixture async def viewer_obj(protect_client: ProtectApiClient): if not TEST_VIEWPORT_EXISTS: return None return next(iter(protect_client.bootstrap.viewers.values())) @pytest_asyncio.fixture async def sensor_obj(protect_client: ProtectApiClient): if not TEST_SENSOR_EXISTS: return None return next(iter(protect_client.bootstrap.sensors.values())) @pytest_asyncio.fixture(name="doorlock_obj") async def doorlock_obj_fixture(protect_client: ProtectApiClient): if not TEST_DOORLOCK_EXISTS: return None return next(iter(protect_client.bootstrap.doorlocks.values())) @pytest_asyncio.fixture(name="chime_obj") async def chime_obj_fixture(protect_client: ProtectApiClient): if not TEST_CHIME_EXISTS: return None return next(iter(protect_client.bootstrap.chimes.values())) @pytest_asyncio.fixture async def liveview_obj(protect_client: ProtectApiClient): if not TEST_LIVEVIEW_EXISTS: return None return next(iter(protect_client.bootstrap.liveviews.values())) @pytest_asyncio.fixture async def user_obj(protect_client: ProtectApiClient): return protect_client.bootstrap.auth_user @pytest.fixture() def liveview(): if not TEST_LIVEVIEW_EXISTS: return None return read_json_file("sample_liveview") @pytest.fixture() def viewport(): if not TEST_VIEWPORT_EXISTS: return None return read_json_file("sample_viewport") @pytest.fixture() def light(): if not TEST_LIGHT_EXISTS: return None return read_json_file("sample_light") @pytest.fixture() def camera(): if not TEST_CAMERA_EXISTS: return None return read_camera_json_file() @pytest.fixture() def sensor(): if not TEST_SENSOR_EXISTS: return None return read_json_file("sample_sensor") @pytest.fixture() def doorlock(): if not TEST_DOORLOCK_EXISTS: return None return read_json_file("sample_doorlock") @pytest.fixture() def chime(): if not TEST_CHIME_EXISTS: return None return read_json_file("sample_chime") @pytest.fixture() def bridge(): if not TEST_BRIDGE_EXISTS: return None return read_json_file("sample_bridge") @pytest.fixture() def liveviews(): if not TEST_LIVEVIEW_EXISTS: return [] return [read_json_file("sample_liveview")] @pytest.fixture() def viewports(): if not TEST_VIEWPORT_EXISTS: return [] return [read_json_file("sample_viewport")] @pytest.fixture() def lights(): if not TEST_LIGHT_EXISTS: return [] return [read_json_file("sample_light")] @pytest.fixture() def cameras(): if not TEST_CAMERA_EXISTS: return [] return [read_camera_json_file()] @pytest.fixture() def sensors(): if not TEST_SENSOR_EXISTS: return [] return [read_json_file("sample_sensor")] @pytest.fixture() def doorlocks(): if not TEST_DOORLOCK_EXISTS: return [] return [read_json_file("sample_doorlock")] @pytest.fixture() def chimes(): if not TEST_CHIME_EXISTS: return [] return [read_json_file("sample_chime")] @pytest.fixture() def bridges(): if not TEST_BRIDGE_EXISTS: return [] return [read_json_file("sample_bridge")] @pytest.fixture() def ws_messages(): return read_json_file("sample_ws_messages") @pytest.fixture(name="raw_events") def raw_events_fixture(): return read_json_file("sample_raw_events") @pytest.fixture() def bootstrap(): return read_bootstrap_json_file() @pytest.fixture() def nvr(): return read_bootstrap_json_file()["nvr"] @pytest.fixture() def smart_track(): if not TEST_SMART_TRACK_EXISTS: return None return read_json_file("sample_event_smart_track") @pytest.fixture() def now(): return get_now().replace(tzinfo=timezone.utc) @pytest.fixture() def tmp_binary_file(): tmp_file = NamedTemporaryFile(mode="wb", delete=False) yield tmp_file with suppress(Exception): tmp_file.close() os.remove(tmp_file.name) # new values added for newer versions of UFP (for backwards compat tests) NEW_FIELDS = { # 1.20.1 "voltage", # 1.21.0-beta1 "timestamp", "isWirelessUplinkEnabled", "marketName", # 1.21.0-beta3 "isPoorNetwork", # 2.0-beta2 "scopes", "streamSharingAvailable", "isDbAvailable", "isRecordingDisabled", "isRecordingMotionOnly", # 2.1.1-beta3 "anonymousDeviceId", # added to viewport "isStacked", "isPrimary", "lastDriveSlowEvent", "isUCoreSetup", # 2.2.1-beta2 "isInsightsEnabled", # 2.2.2 "isDownloadingFW", # 2.6.13 "vaultCameras", "homekitSettings", # 2.6.17 "apMgmtIp", # 2.7.5 "fwUpdateState", "isWaterproofCaseAttached", "deletedAt", "deletionType", "lastDisconnect", # 2.7.15 "featureFlags", # added to chime # 2.8.14+ "nvrMac", "useGlobal", "is2K", "is4K", "ulpVersion", "wanIp", "publicIp", "isVaultRegistered", "hasGateway", "corruptionState", "countryCode", # 2.8.22+ "guid", "userConfiguredAp", # 2.9.20+ "isRestoring", "hasRecordings", "hardDriveState", "isNetworkInstalled", "isProtectUpdatable", "isUcoreUpdatable", # 2.10.10+ "isPtz", # 2.11.13+ "lastDeviceFWUpdatesCheckedAt", "audioSettings", # 3.0.22+ "smartDetection", "platform", "repeatTimes", "ringSettings", "speakerTrackList", "trackNo", "hasHttpsClientOTA", "isUCoreStacked", } NEW_CAMERA_FEATURE_FLAGS = { "audio", "audioCodecs", "hasInfrared", "hotplug", "smartDetectAudioTypes", "lensType", # 2.7.18+ "isDoorbell", # 2.8.22+ "lensModel", # 2.9.20+ "hasColorLcdScreen", "hasLineCrossing", "hasLineCrossingCounting", "hasLiveviewTracking", # 2.10.10+ "hasFlash", "isPtz", # 2.11.13+ "audioStyle", "hasVerticalFlip", # 3.0.22+ "flashRange", } NEW_ISP_SETTINGS = { # 3.0.22+ "hdrMode", "icrCustomValue", "icrSwitchMode", "spotlightDuration", } NEW_NVR_FEATURE_FLAGS = { # 2.8.14+ "ulpRoleManagement", } OLD_FIELDS = { # remove in 2.7.12 "avgMotions", # removed in 2.10.11 "eventStats", # removed in 3.0.22 "pirSettings", } def compare_objs(obj_type, expected, actual): expected = deepcopy(expected) actual = deepcopy(actual) # TODO: fields not supported yet if obj_type == ModelType.CAMERA.value: # fields does not always exist (G4 Instant) expected.pop("apMac", None) # field no longer exists on newer cameras expected.pop("elementInfo", None) del expected["apRssi"] del expected["lastPrivacyZonePositionId"] expected.pop("recordingSchedules", None) del expected["smartDetectLines"] expected.pop("streamSharing", None) expected.pop("stopStreamLevel", None) expected.pop("uplinkDevice", None) expected.pop("recordingSchedulesV2", None) expected["stats"].pop("battery", None) expected["recordingSettings"].pop("enablePirTimelapse", None) expected["featureFlags"].pop("hasBattery", None) # do not compare detect zones because float math sucks assert len(expected["motionZones"]) == len(actual["motionZones"]) assert len(expected["privacyZones"]) == len(actual["privacyZones"]) assert len(expected["smartDetectZones"]) == len(actual["smartDetectZones"]) expected["motionZones"] = actual["motionZones"] = [] expected["privacyZones"] = actual["privacyZones"] = [] expected["smartDetectZones"] = actual["smartDetectZones"] = [] if "isColorNightVisionEnabled" not in expected["ispSettings"]: actual["ispSettings"].pop("isColorNightVisionEnabled", None) if ( "audioTypes" in actual["smartDetectSettings"] and "audioTypes" not in expected["smartDetectSettings"] ): del actual["smartDetectSettings"]["audioTypes"] if ( "autoTrackingObjectTypes" in actual["smartDetectSettings"] and "autoTrackingObjectTypes" not in expected["smartDetectSettings"] ): del actual["smartDetectSettings"]["autoTrackingObjectTypes"] exp_settings = expected["recordingSettings"] act_settings = actual["recordingSettings"] exp_settings["enableMotionDetection"] = exp_settings.get( "enableMotionDetection", ) if act_settings and "inScheduleMode" not in exp_settings: del act_settings["inScheduleMode"] if "outScheduleMode" in act_settings and "outScheduleMode" not in exp_settings: del act_settings["outScheduleMode"] if "retentionDurationMs" not in exp_settings: act_settings.pop("retentionDurationMs", None) if "smartDetectPostPadding" not in exp_settings: act_settings.pop("smartDetectPostPadding", None) if "smartDetectPrePadding" not in exp_settings: act_settings.pop("smartDetectPrePadding", None) for flag in NEW_CAMERA_FEATURE_FLAGS: if flag not in expected["featureFlags"]: del actual["featureFlags"][flag] for setting in NEW_ISP_SETTINGS: if setting not in expected["ispSettings"]: del actual["ispSettings"][setting] # ignore changes to motion for live tests assert isinstance(actual["isMotionDetected"], bool) expected["isMotionDetected"] = actual["isMotionDetected"] for index, channel in enumerate(expected["channels"]): if "bitrate" not in channel: actual["channels"][index].pop("bitrate", None) if "autoBitrate" not in channel: actual["channels"][index].pop("autoBitrate", None) if "autoFps" not in channel: actual["channels"][index].pop("autoFps", None) elif obj_type == ModelType.USER.value: expected.pop("settings", None) expected.pop("cloudProviders", None) del expected["alertRules"] del expected["notificationsV2"] expected.pop("notifications", None) # lastLoginIp/lastLoginTime is not always present if "lastLoginIp" not in expected: actual.pop("lastLoginIp", None) if "lastLoginTime" not in expected: actual.pop("lastLoginTime", None) if "email" not in expected and "email" in actual and actual["email"] is None: actual.pop("email", None) elif obj_type == ModelType.EVENT.value: expected.pop("partition", None) expected.pop("deletionType", None) expected.pop("description", None) if "category" in expected and expected["category"] is None: expected.pop("category", None) exp_thumbnails = expected.get("metadata", {}).pop("detectedThumbnails", []) act_thumbnails = actual.get("metadata", {}).pop("detectedThumbnails", []) for index, exp_thumb in enumerate(exp_thumbnails): if "attributes" not in exp_thumb: del act_thumbnails[index]["attributes"] if "clockBestWall" not in exp_thumb: del act_thumbnails[index]["clockBestWall"] assert exp_thumbnails == act_thumbnails expected_keys = (expected.get("metadata") or {}).keys() actual_keys = (actual.get("metadata") or {}).keys() # delete all extra metadata keys, many of which are not modeled for key in set(expected_keys).difference(actual_keys): del expected["metadata"][key] elif obj_type in {ModelType.SENSOR.value, ModelType.DOORLOCK.value}: del expected["bridgeCandidates"] actual.pop("host", None) expected.pop("host", None) elif obj_type == ModelType.CHIME.value: del expected["apMac"] del expected["apRssi"] del expected["elementInfo"] elif obj_type == ModelType.NVR.value: # TODO: fields that still need implemented del expected["errorCode"] del expected["wifiSettings"] del expected["smartDetectAgreement"] expected.pop("dbRecoveryOptions", None) expected.pop("portStatus", None) expected.pop("cameraCapacity", None) expected.pop("deviceFirmwareSettings", None) # removed fields expected["ports"].pop("cameraTcp", None) expected["ports"]["piongw"] = expected["ports"].get("piongw") expected["ports"]["stacking"] = expected["ports"].get("stacking") expected["ports"]["emsJsonCLI"] = expected["ports"].get("emsJsonCLI") expected["ports"]["aiFeatureConsole"] = expected["ports"].get( "aiFeatureConsole", ) expected["globalCameraSettings"] = expected.get("globalCameraSettings") if expected["globalCameraSettings"]: settings = expected["globalCameraSettings"]["recordingSettings"] settings["retentionDurationMs"] = settings.get( "retentionDurationMs", ) # TODO: expected["globalCameraSettings"].pop("recordingSchedulesV2", None) if ( "homekitPaired" in actual["featureFlags"] and "homekitPaired" not in expected["featureFlags"] ): del actual["featureFlags"]["homekitPaired"] if ( "detectionLabels" in actual["featureFlags"] and "detectionLabels" not in expected["featureFlags"] ): del actual["featureFlags"]["detectionLabels"] if ( "hasTwoWayAudioMediaStreams" in actual["featureFlags"] and "hasTwoWayAudioMediaStreams" not in expected["featureFlags"] ): del actual["featureFlags"]["hasTwoWayAudioMediaStreams"] if "capability" not in expected["systemInfo"]["storage"]: actual["systemInfo"]["storage"].pop("capability", None) # float math... cpu_fields = ["averageLoad", "temperature"] for key in cpu_fields: if math.isclose( expected["systemInfo"]["cpu"][key], actual["systemInfo"]["cpu"][key], rel_tol=0.01, ): expected["systemInfo"]["cpu"][key] = actual["systemInfo"]["cpu"][key] if expected["systemInfo"].get("ustorage") is not None: actual_ustor = actual["systemInfo"]["ustorage"] expected_ustor = expected["systemInfo"]["ustorage"] expected_ustor.pop("sdcards", None) for index, disk in enumerate(expected_ustor["disks"]): actual_disk = actual_ustor["disks"][index] estimate = disk.get("estimate") actual_estimate = actual_disk.get("estimate") if estimate is not None and actual_estimate is not None: if math.isclose(estimate, actual_estimate, rel_tol=0.01): actual_ustor["disks"][index]["estimate"] = estimate for index, device in enumerate(expected_ustor["space"]): actual_device = actual_ustor["space"][index] estimate = device.get("estimate") actual_estimate = actual_device.get("estimate") if estimate is not None and actual_estimate is not None: if math.isclose(estimate, actual_estimate, rel_tol=0.01): actual_ustor["space"][index]["estimate"] = estimate if "space_type" not in device: del actual_device["space_type"] if "size" in device: actual_device["size"] = actual_device.pop("size", None) # TODO field if "reasons" in device: del device["reasons"] for flag in NEW_NVR_FEATURE_FLAGS: if flag not in expected["featureFlags"]: del actual["featureFlags"][flag] if "bridge" not in expected and "bridge" in actual and actual["bridge"] is None: actual.pop("bridge", None) if "bluetoothConnectionState" in expected: expected["bluetoothConnectionState"]["experienceScore"] = expected[ "bluetoothConnectionState" ].get( "experienceScore", ) if "wifiConnectionState" in expected: expected["wifiConnectionState"]["bssid"] = expected["wifiConnectionState"].get( "bssid", ) expected["wifiConnectionState"]["txRate"] = expected["wifiConnectionState"].get( "txRate", ) expected["wifiConnectionState"]["experience"] = expected[ "wifiConnectionState" ].get("experience") expected["wifiConnectionState"]["apName"] = expected["wifiConnectionState"].get( "apName", ) expected["wifiConnectionState"]["connectivity"] = expected[ "wifiConnectionState" ].get("connectivity") # sometimes uptime comes back as a str... if "uptime" in expected and expected["uptime"] is not None: expected["uptime"] = int(expected["uptime"]) # force hardware revision to str to make sure types line up if "hardwareRevision" in expected and expected["hardwareRevision"] is not None: expected["hardwareRevision"] = str(expected["hardwareRevision"]) actual["hardwareRevision"] = str(actual["hardwareRevision"]) # edge case with broken UUID from Protect if ( "guid" in expected and expected["guid"] == _BAD_UUID and actual["guid"] == "00000000-0000-0000-0000-000000000000" ): actual["guid"] = expected["guid"] for key in NEW_FIELDS.intersection(actual.keys()): if key not in expected: del actual[key] for key in OLD_FIELDS.intersection(expected.keys()): del expected[key] assert expected == actual @pytest.fixture() def _disable_camera_validation(): Camera.__config__.validate_assignment = False yield Camera.__config__.validate_assignment = True class MockTalkback: is_error: bool = False stdout: list[str] = [] stderr: list[str] = [] def __init__(self) -> None: self.start = AsyncMock() self.stop = AsyncMock() self.run_until_complete = AsyncMock() uiprotect-6.1.0/tests/data/000077500000000000000000000000001467310220200156045ustar00rootroot00000000000000uiprotect-6.1.0/tests/data/__init__.py000066400000000000000000000000001467310220200177030ustar00rootroot00000000000000uiprotect-6.1.0/tests/data/test_camera.py000066400000000000000000001154301467310220200204510ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr, arg-type" from __future__ import annotations from datetime import datetime, timedelta, timezone from unittest.mock import Mock, patch import pytest from pydantic.v1 import ValidationError from tests.conftest import TEST_CAMERA_EXISTS from uiprotect.data import ( Camera, ChimeType, DoorbellMessageType, HDRMode, IRLEDMode, LCDMessage, PTZPreset, RecordingMode, SmartDetectAudioType, VideoMode, ) from uiprotect.data.devices import CameraZone, Hotplug, HotplugExtender from uiprotect.data.types import DEFAULT, SmartDetectObjectType from uiprotect.data.websocket import WSAction, WSSubscriptionMessage from uiprotect.exceptions import BadRequest from uiprotect.utils import to_js_time @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_motion_detection(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.recording_settings.enable_motion_detection = not status await camera_obj.set_motion_detection(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"recordingSettings": {"enableMotionDetection": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("mode", [RecordingMode.ALWAYS, RecordingMode.DETECTIONS]) @pytest.mark.asyncio() async def test_camera_set_recording_mode( camera_obj: Camera | None, mode: RecordingMode, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.recording_settings.mode = RecordingMode.NEVER await camera_obj.set_recording_mode(mode) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"recordingSettings": {"mode": mode.value}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_ir_led_model_no_ir(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_led_ir = False with pytest.raises(BadRequest): await camera_obj.set_ir_led_model(IRLEDMode.AUTO) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("mode", [IRLEDMode.AUTO, IRLEDMode.ON]) @pytest.mark.asyncio() async def test_camera_set_ir_led_model(camera_obj: Camera | None, mode: IRLEDMode): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_led_ir = True camera_obj.isp_settings.ir_led_mode = IRLEDMode.OFF await camera_obj.set_ir_led_model(mode) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"ispSettings": {"irLedMode": mode.value}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_status_light_no_status(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_led_status = False with pytest.raises(BadRequest): await camera_obj.set_status_light(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_status_light(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_led_status = True camera_obj.led_settings.is_enabled = not status camera_obj.led_settings.blink_rate = 10 await camera_obj.set_status_light(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"ledSettings": {"isEnabled": status, "blinkRate": 0}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_hdr_no_hdr(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_hdr = False with pytest.raises(BadRequest): await camera_obj.set_hdr_mode("off") assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( ("status", "state"), [ ("auto", (True, HDRMode.NORMAL)), ("off", (False, HDRMode.NORMAL)), ("always", (True, HDRMode.ALWAYS_ON)), ], ) @pytest.mark.asyncio() async def test_camera_set_hdr( camera_obj: Camera | None, status: str, state: tuple[bool, HDRMode], ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_hdr = True camera_obj.hdr_mode = not state[0] camera_obj.isp_settings.hdr_mode = ( HDRMode.NORMAL if state[1] == HDRMode.ALWAYS_ON else HDRMode.ALWAYS_ON ) await camera_obj.set_hdr_mode(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"hdrMode": state[0], "ispSettings": {"hdrMode": state[1]}}, ) assert camera_obj.hdr_mode_display == status @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_color_night_vision( camera_obj: Camera | None, status: bool, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.hotplug = Hotplug() camera_obj.feature_flags.hotplug.extender = HotplugExtender() camera_obj.feature_flags.hotplug.extender.is_attached = True camera_obj.isp_settings.is_color_night_vision_enabled = not status await camera_obj.set_color_night_vision(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"ispSettings": {"isColorNightVisionEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_color_night_vision_no_color_night_vision( camera_obj: Camera | None, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() with pytest.raises(BadRequest): await camera_obj.set_color_night_vision(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_ssh(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.is_ssh_enabled = not status await camera_obj.set_ssh(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"isSshEnabled": status}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_video_mode_no_highfps(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.video_modes = [VideoMode.DEFAULT] camera_obj.video_mode = VideoMode.DEFAULT with pytest.raises(BadRequest): await camera_obj.set_video_mode(VideoMode.HIGH_FPS) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_video_mode(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.video_modes = [VideoMode.DEFAULT, VideoMode.HIGH_FPS] camera_obj.video_mode = VideoMode.DEFAULT await camera_obj.set_video_mode(VideoMode.HIGH_FPS) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"videoMode": VideoMode.HIGH_FPS.value}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_camera_zoom_no_zoom(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.can_optical_zoom = False with pytest.raises(BadRequest): await camera_obj.set_camera_zoom(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("level", [-1, 0, 100, 200]) @pytest.mark.asyncio() async def test_camera_set_camera_zoom(camera_obj: Camera | None, level: int): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.can_optical_zoom = True camera_obj.isp_settings.zoom_position = 10 if level in {-1, 200}: with pytest.raises(ValidationError): await camera_obj.set_camera_zoom(level) assert not camera_obj.api.api_request.called else: await camera_obj.set_camera_zoom(level) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"ispSettings": {"zoomPosition": level}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("level", [-1, 0, 3, 4]) @pytest.mark.asyncio() async def test_camera_set_wdr_level(camera_obj: Camera | None, level: int): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_hdr = False camera_obj.isp_settings.wdr = 2 if level in {-1, 4}: with pytest.raises(ValidationError): await camera_obj.set_wdr_level(level) assert not camera_obj.api.api_request.called else: await camera_obj.set_wdr_level(level) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"ispSettings": {"wdr": level}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_wdr_level_hdr(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_hdr = True with pytest.raises(BadRequest): await camera_obj.set_wdr_level(1) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_mic_volume_no_mic(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_mic = False with pytest.raises(BadRequest): await camera_obj.set_mic_volume(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("level", [-1, 0, 100, 200]) @pytest.mark.asyncio() async def test_camera_set_mic_volume(camera_obj: Camera | None, level: int): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_mic = True camera_obj.mic_volume = 10 if level in {-1, 200}: with pytest.raises(ValidationError): await camera_obj.set_mic_volume(level) assert not camera_obj.api.api_request.called else: await camera_obj.set_mic_volume(level) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"micVolume": level}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_speaker_volume_no_speaker(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_speaker = False with pytest.raises(BadRequest): await camera_obj.set_speaker_volume(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("level", [-1, 0, 100, 200]) @pytest.mark.asyncio() async def test_camera_set_speaker_volume(camera_obj: Camera | None, level: int): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_speaker = True camera_obj.speaker_settings.volume = 10 if level in {-1, 200}: with pytest.raises(ValidationError): await camera_obj.set_speaker_volume(level) assert not camera_obj.api.api_request.called else: await camera_obj.set_speaker_volume(level) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"speakerSettings": {"volume": level}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_chime_duration_no_chime(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_chime = False with pytest.raises(BadRequest): await camera_obj.set_chime_duration(1000) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_mechanical_chime( camera_obj: Camera | None, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.feature_flags.has_chime = True camera_obj.chime_duration = timedelta(seconds=0.3) assert camera_obj.chime_duration_seconds == 0.3 assert camera_obj.chime_type is ChimeType.MECHANICAL @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_no_chime( camera_obj: Camera | None, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.feature_flags.has_chime = True camera_obj.chime_duration = timedelta(seconds=0) assert camera_obj.chime_duration_seconds == 0 assert camera_obj.chime_type is ChimeType.NONE @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("duration", [-1, 0, 0.5, 1, 20]) @pytest.mark.asyncio() async def test_camera_set_chime_duration_duration( camera_obj: Camera | None, duration: int, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_chime = True camera_obj.chime_duration = timedelta(seconds=300) assert camera_obj.chime_duration_seconds == 300 assert camera_obj.chime_type is ChimeType.DIGITAL camera_obj.mic_volume = 10 if duration in {-1, 20}: with pytest.raises(BadRequest): await camera_obj.set_chime_duration(duration) assert not camera_obj.api.api_request.called else: await camera_obj.set_chime_duration(duration) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"chimeDuration": duration * 1000}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_system_sounds_no_speaker(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_speaker = False with pytest.raises(BadRequest): await camera_obj.set_system_sounds(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_system_sounds(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_speaker = True camera_obj.speaker_settings.are_system_sounds_enabled = not status await camera_obj.set_system_sounds(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"speakerSettings": {"areSystemSoundsEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_osd_name(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.osd_settings.is_name_enabled = not status await camera_obj.set_osd_name(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"osdSettings": {"isNameEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_osd_date(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.osd_settings.is_date_enabled = not status await camera_obj.set_osd_date(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"osdSettings": {"isDateEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_osd_logo(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.osd_settings.is_logo_enabled = not status await camera_obj.set_osd_logo(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"osdSettings": {"isLogoEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_osd_bitrate(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.osd_settings.is_debug_enabled = not status await camera_obj.set_osd_bitrate(status) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"osdSettings": {"isDebugEnabled": status}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_smart_detect_types_no_smart(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_smart_detect = False with pytest.raises(BadRequest): await camera_obj.set_smart_detect_types([]) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_smart_detect_types(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_smart_detect = True camera_obj.smart_detect_settings.object_types = [] await camera_obj.set_smart_detect_types([SmartDetectObjectType.PERSON]) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"smartDetectSettings": {"objectTypes": ["person"]}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_lcd_text_no_lcd(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = False with pytest.raises(BadRequest): await camera_obj.set_lcd_text(DoorbellMessageType.DO_NOT_DISTURB) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_lcd_text_custom(camera_obj: Camera | None): camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True camera_obj.lcd_message = LCDMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), reset_at=None, ) now = datetime.now(tz=timezone.utc) await camera_obj.set_lcd_text(DoorbellMessageType.CUSTOM_MESSAGE, "Test", now) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={ "lcdMessage": { "type": DoorbellMessageType.CUSTOM_MESSAGE.value, "text": "Test", "resetAt": to_js_time(now), }, }, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_lcd_text_custom_to_custom(camera_obj: Camera | None): camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True camera_obj.lcd_message = LCDMessage( type=DoorbellMessageType.CUSTOM_MESSAGE, text="Welcome", reset_at=None, ) now = datetime.now(tz=timezone.utc) await camera_obj.set_lcd_text(DoorbellMessageType.CUSTOM_MESSAGE, "Test", now) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={ "lcdMessage": { "type": DoorbellMessageType.CUSTOM_MESSAGE.value, "text": "Test", "resetAt": to_js_time(now), }, }, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_lcd_text_invalid_text(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True with pytest.raises(BadRequest): await camera_obj.set_lcd_text(DoorbellMessageType.DO_NOT_DISTURB, "Test") assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_lcd_text(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True camera_obj.lcd_message = LCDMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), reset_at=None, ) await camera_obj.set_lcd_text(DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={ "lcdMessage": { "type": DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value, "text": DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value.replace( "_", " ", ), "resetAt": None, }, }, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() @patch("uiprotect.data.devices.utc_now") async def test_camera_set_lcd_text_none( mock_now, camera_obj: Camera | None, now: datetime, ): mock_now.return_value = now if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.emit_message = Mock() camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True camera_obj.lcd_message = LCDMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), reset_at=None, ) await camera_obj.set_lcd_text(None) expected_dt = now - timedelta(seconds=10) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={ "lcdMessage": { "resetAt": to_js_time(expected_dt), }, }, ) # old/new is actually the same here since the client # generating the message is the one that changed it camera_obj.api.emit_message.assert_called_with( WSSubscriptionMessage( action=WSAction.UPDATE, new_update_id=camera_obj.api.bootstrap.last_update_id, changed_data={"lcd_message": {"reset_at": expected_dt}}, old_obj=camera_obj, new_obj=camera_obj, ), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() @patch("uiprotect.data.devices.utc_now") async def test_camera_set_lcd_text_default( mock_now, camera_obj: Camera | None, now: datetime, ): mock_now.return_value = now if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_lcd_screen = True camera_obj.lcd_message = LCDMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), reset_at=None, ) await camera_obj.set_lcd_text( DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR, reset_at=DEFAULT, ) expected_dt = ( now + camera_obj.api.bootstrap.nvr.doorbell_settings.default_message_reset_timeout ) camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={ "lcdMessage": { "type": DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value, "text": DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value.replace( "_", " ", ), "resetAt": to_js_time(expected_dt), }, }, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_privacy_no_privacy(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_privacy_mask = False with pytest.raises(BadRequest): await camera_obj.set_privacy(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("actual_enabled", [True, False]) @pytest.mark.parametrize("enabled", [True, False]) @pytest.mark.parametrize("level", [None, -1, 0, 100, 200]) @pytest.mark.parametrize("mode", [None, RecordingMode.ALWAYS]) @pytest.mark.asyncio() async def test_camera_set_privacy( camera_obj: Camera | None, actual_enabled: bool, enabled: bool, level: int | None, mode: RecordingMode | None, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.has_privacy_mask = True camera_obj.privacy_zones = [] if actual_enabled: camera_obj.add_privacy_zone() camera_obj.mic_volume = 10 camera_obj.recording_settings.mode = RecordingMode.NEVER if level in {-1, 200}: with pytest.raises(ValidationError): await camera_obj.set_privacy(enabled, level, mode) assert not camera_obj.api.api_request.called else: expected = {} if level is not None: expected.update({"micVolume": level}) if mode is not None: expected.update( { "recordingSettings": { "mode": mode.value, }, }, ) if actual_enabled != enabled: if enabled: expected.update( {"privacyZones": [CameraZone.create_privacy_zone(0).unifi_dict()]}, ) else: expected.update({"privacyZones": []}) await camera_obj.set_privacy(enabled, level, mode) if not expected: assert not camera_obj.api.api_request.called else: camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json=expected, ) if enabled: assert camera_obj.is_privacy_on else: assert not camera_obj.is_privacy_on @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_person_track_no_ptz(camera_obj: Camera | None): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_ptz = False with pytest.raises(BadRequest): await camera_obj.set_person_track(True) assert not camera_obj.api.api_request.called @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_set_person_track(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.feature_flags.is_ptz = True camera_obj.recording_settings.mode = RecordingMode.ALWAYS if status: camera_obj.smart_detect_settings.auto_tracking_object_types = [] else: camera_obj.smart_detect_settings.auto_tracking_object_types = [ SmartDetectObjectType.PERSON, ] camera_obj.api.api_request.reset_mock() await camera_obj.set_person_track(status) assert camera_obj.is_person_tracking_enabled is status camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json=( {"smartDetectSettings": {"autoTrackingObjectTypes": ["person"]}} if status else {"smartDetectSettings": {"autoTrackingObjectTypes": []}} ), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_camera_disable_co(camera_obj: Camera | None, status: bool): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.feature_flags.is_ptz = True camera_obj.recording_settings.mode = RecordingMode.ALWAYS if status: camera_obj.smart_detect_settings.audio_types = [] else: camera_obj.smart_detect_settings.audio_types = [ SmartDetectAudioType.SMOKE, SmartDetectAudioType.CMONX, SmartDetectAudioType.SMOKE_CMONX, ] camera_obj.api.api_request.reset_mock() await camera_obj.set_smart_audio_detect_types( [SmartDetectAudioType.SMOKE, SmartDetectAudioType.SMOKE_CMONX] ) assert camera_obj.smart_detect_settings.audio_types == [ SmartDetectAudioType.SMOKE, SmartDetectAudioType.SMOKE_CMONX, ] camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json={"smartDetectSettings": {"audioTypes": ["alrmSmoke"]}}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( ("value", "lux"), [ (1, 1), (2, 3), (3, 5), (4, 7), (5, 10), (6, 12), (7, 15), (8, 20), (9, 25), (10, 30), ], ) @pytest.mark.asyncio() async def test_camera_set_icr_custom_lux( camera_obj: Camera | None, value: int, lux: int, ): if camera_obj is None: pytest.skip("No camera_obj obj found") camera_obj.feature_flags.has_led_ir = True camera_obj.isp_settings.icr_custom_value = 0 camera_obj.api.api_request.reset_mock() await camera_obj.set_icr_custom_lux(lux) assert camera_obj.isp_settings.icr_custom_value == value assert camera_obj.icr_lux_display == lux camera_obj.api.api_request.assert_called_with( f"cameras/{camera_obj.id}", method="patch", json=({"ispSettings": {"icrCustomValue": value}}), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( ("pan", "tilt", "pan_native", "tilt_native"), [ (0, 0, 0, 0), (1, 1, 97, 88), (5, 5, 488, 444), (20, 20, 1955, 1777), (40, 40, 3911, 3554), (-1, -1, -97, -88), (-5, -5, -488, -444), (-20, -20, -1955, -1777), (-40, -40, -3911, -3554), ], ) @pytest.mark.asyncio() async def test_camera_ptz_relative_move( ptz_camera: Camera | None, pan: float, tilt: float, pan_native: float, tilt_native: float, ): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() await ptz_camera.ptz_relative_move(pan=pan, tilt=tilt) ptz_camera.api.api_request.assert_called_with( f"cameras/{ptz_camera.id}/move", method="post", json=( { "type": "relative", "payload": { "panPos": pan_native, "tiltPos": tilt_native, "panSpeed": 10, "tiltSpeed": 10, "scale": 0, }, } ), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_ptz_center(ptz_camera: Camera | None): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() await ptz_camera.ptz_center(x=500, y=500, z=0) ptz_camera.api.api_request.assert_called_with( f"cameras/{ptz_camera.id}/move", method="post", json=( { "type": "center", "payload": { "x": 500, "y": 500, "z": 0, }, } ), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( ("zoom", "zoom_native"), [ (1, 0), (5, 382), (20, 1818), (22, 2009), ], ) @pytest.mark.asyncio() async def test_camera_ptz_zoom( ptz_camera: Camera | None, zoom: float, zoom_native: float, ): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() await ptz_camera.ptz_zoom(zoom=zoom) ptz_camera.api.api_request.assert_called_with( f"cameras/{ptz_camera.id}/move", method="post", json=( { "type": "zoom", "payload": { "zoomPos": zoom_native, "zoomSpeed": 100, }, } ), ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_goto_ptz_slot(ptz_camera: Camera | None): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() await ptz_camera.goto_ptz_slot(slot=-1) ptz_camera.api.api_request.assert_called_with( f"cameras/{ptz_camera.id}/ptz/goto/-1", method="post", ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_create_ptz_preset(ptz_camera: Camera | None): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() preset = await ptz_camera.create_ptz_preset(name="Test") assert preset == PTZPreset( id="test-id", name="Test", slot=0, ptz={ "pan": 100, "tilt": 100, "zoom": 0, }, ) ptz_camera.api.api_request.assert_called_with( url=f"cameras/{ptz_camera.id}/ptz/preset", method="post", require_auth=True, raise_exception=True, json={"name": "Test"}, ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_delete_ptz_preset(ptz_camera: Camera | None): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() await ptz_camera.delete_ptz_preset(slot=0) ptz_camera.api.api_request.assert_called_with( f"cameras/{ptz_camera.id}/ptz/preset/0", method="delete", ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_camera_set_ptz_home(ptz_camera: Camera | None): if ptz_camera is None: pytest.skip("No camera_obj obj found") ptz_camera.api.api_request.reset_mock() preset = await ptz_camera.set_ptz_home() assert preset == PTZPreset( id="test-id", name="Home", slot=-1, ptz={ "pan": 100, "tilt": 100, "zoom": 0, }, ) ptz_camera.api.api_request.assert_called_with( url=f"cameras/{ptz_camera.id}/ptz/home", method="post", require_auth=True, raise_exception=True, ) uiprotect-6.1.0/tests/data/test_chime.py000066400000000000000000000326711467310220200203130ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr" from __future__ import annotations from typing import TYPE_CHECKING import pytest from pydantic.v1 import ValidationError from tests.conftest import TEST_CAMERA_EXISTS, TEST_CHIME_EXISTS from uiprotect.data import RingSetting from uiprotect.exceptions import BadRequest if TYPE_CHECKING: from uiprotect.data import Camera, Chime @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.parametrize("level", [-1, 0, 100, 200]) @pytest.mark.asyncio() async def test_chime_set_volume( chime_obj: Chime | None, camera_obj: Camera | None, level: int, ): if chime_obj is None: pytest.skip("No chime_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.volume = 20 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=1, # type: ignore[arg-type] track_no=1, volume=20, ), ] if level in {-1, 200}: with pytest.raises(ValidationError): await chime_obj.set_volume(level) assert not chime_obj.api.api_request.called else: await chime_obj.set_volume(level) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={ "volume": level, "ringSettings": [ { "camera": camera_obj.id, "repeatTimes": 1, "trackNo": 1, "volume": level, }, ], }, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_volume_with_existing_custom( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.volume = 100 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=1, # type: ignore[arg-type] track_no=1, volume=20, ), ] camera_obj.api.api_request.reset_mock() await chime_obj.set_volume(50) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={"volume": 50}, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_volume_for_camera( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.volume = 100 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=1, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() await chime_obj.set_volume_for_camera(camera_obj, 50) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={ "ringSettings": [ { "camera": camera_obj.id, "repeatTimes": 1, "trackNo": 1, "volume": 50, }, ], }, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_volume_for_camera_not_exist( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.volume = 100 chime_obj.ring_settings = [ RingSetting( camera_id="other-id", repeat_times=1, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() with pytest.raises(BadRequest): await chime_obj.set_volume_for_camera(camera_obj, 2) assert not chime_obj.api.api_request.called @pytest.mark.skipif(not TEST_CHIME_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_chime_play(chime_obj: Chime | None): if chime_obj is None: pytest.skip("No chime_obj obj found") await chime_obj.play() chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}/play-speaker", method="post", json=None, ) @pytest.mark.skipif(not TEST_CHIME_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_chime_play_with_options(chime_obj: Chime | None): if chime_obj is None: pytest.skip("No chime_obj obj found") chime_obj.volume = 100 chime_obj.repeat_times = 1 chime_obj.track_no = 1 chime_obj.api.api_request.reset_mock() await chime_obj.play(volume=50) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}/play-speaker", method="post", json={ "volume": 50, "repeatTimes": 1, "trackNo": 1, }, ) @pytest.mark.skipif(not TEST_CHIME_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_chime_play_buzzer(chime_obj: Chime | None): if chime_obj is None: pytest.skip("No chime_obj obj found") await chime_obj.play_buzzer() chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}/play-buzzer", method="post", ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_add_camera( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.camera_ids = [] camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_doorbell = True await chime_obj.add_camera(camera_obj) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={"cameraIds": [camera_obj.id]}, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_add_camera_not_doorbell( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.camera_ids = [] camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_doorbell = False with pytest.raises(BadRequest): await chime_obj.add_camera(camera_obj) assert not chime_obj.api.api_request.called @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_add_camera_exists( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.camera_ids = [camera_obj.id] camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_doorbell = True with pytest.raises(BadRequest): await chime_obj.add_camera(camera_obj) assert not chime_obj.api.api_request.called @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_remove_camera( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.camera_ids = [camera_obj.id] camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_doorbell = True await chime_obj.remove_camera(camera_obj) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={"cameraIds": []}, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_remove_camera_not_exists( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.api.api_request.reset_mock() chime_obj.camera_ids = [] camera_obj.api.api_request.reset_mock() camera_obj.feature_flags.is_doorbell = True with pytest.raises(BadRequest): await chime_obj.remove_camera(camera_obj) assert not chime_obj.api.api_request.called @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_repeat_times( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.repeat_times = 1 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=1, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() await chime_obj.set_repeat_times(2) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={ "repeatTimes": 2, "ringSettings": [ { "camera": camera_obj.id, "repeatTimes": 2, "trackNo": 1, "volume": 100, }, ], }, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_repeat_times_with_existing_custom( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.repeat_times = 1 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=3, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() await chime_obj.set_repeat_times(2) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={"repeatTimes": 2}, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_repeat_times_for_camera( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.repeat_times = 1 chime_obj.ring_settings = [ RingSetting( camera_id=camera_obj.id, repeat_times=1, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() await chime_obj.set_repeat_times_for_camera(camera_obj, 2) chime_obj.api.api_request.assert_called_with( f"chimes/{chime_obj.id}", method="patch", json={ "ringSettings": [ { "camera": camera_obj.id, "repeatTimes": 2, "trackNo": 1, "volume": 100, }, ], }, ) @pytest.mark.skipif( not TEST_CHIME_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_chime_set_repeat_times_for_camera_not_exist( chime_obj: Chime | None, camera_obj: Camera | None, ): if chime_obj is None: pytest.skip("No chime_obj obj found") if camera_obj is None: pytest.skip("No camera_obj obj found") chime_obj.camera_ids = [camera_obj.id] chime_obj.repeat_times = 1 chime_obj.ring_settings = [ RingSetting( camera_id="other-id", repeat_times=1, # type: ignore[arg-type] track_no=1, volume=100, ), ] camera_obj.api.api_request.reset_mock() with pytest.raises(BadRequest): await chime_obj.set_repeat_times_for_camera(camera_obj, 2) assert not chime_obj.api.api_request.called uiprotect-6.1.0/tests/data/test_common.py000066400000000000000000001013601467310220200205060ustar00rootroot00000000000000"""Tests for uiprotect.data""" from __future__ import annotations import asyncio import base64 from copy import deepcopy from datetime import timedelta from ipaddress import IPv4Address from typing import TYPE_CHECKING, Any, cast from unittest.mock import Mock, patch import pytest from tests.conftest import ( TEST_BRIDGE_EXISTS, TEST_CAMERA_EXISTS, TEST_DOORLOCK_EXISTS, TEST_LIGHT_EXISTS, TEST_SENSOR_EXISTS, TEST_VIEWPORT_EXISTS, MockTalkback, compare_objs, ) from tests.sample_data.constants import CONSTANTS from uiprotect.data import ( Bootstrap, Camera, DoorbellMessageType, Event, EventType, FixSizeOrderedDict, ModelType, Permission, RecordingMode, SmartDetectAudioType, SmartDetectObjectType, StorageType, User, VideoMode, WSPacket, create_from_unifi_dict, ) from uiprotect.data.devices import LCDMessage from uiprotect.data.types import RecordingType, ResolutionStorageType from uiprotect.data.user import CloudAccount from uiprotect.exceptions import BadRequest, NotAuthorized, StreamError from uiprotect.utils import set_debug, set_no_debug, utc_now if TYPE_CHECKING: from pytest_benchmark.fixture import BenchmarkFixture from uiprotect import ProtectApiClient PACKET_B64 = "AQEBAAAAAHR4nB2MQQrCMBBFr1JmbSDNpJnRG4hrDzBNZqCgqUiriHh3SZb/Pd7/guRtWSucBtgfRTaFwwBV39c+zqUJskQW1DufUVwkJsfFxDGLyRFj0dSz+1r0dtFPa+rr2dDSD8YsyceUpskQxzjjHIIQMvz+hMoj/AIBAQAAAAA1eJyrViotKMnMTVWyUjA0MjawMLQ0MDDQUVDKSSwuCU5NzQOJmxkbACUszE0sLQ1rAVU/DPU=" PACKET_ACTION = { "action": "update", "newUpdateId": "7f67f2e0-0c3a-4787-8dfa-88afa934de6e", "modelKey": "nvr", "id": "1ca6046655f3314b3b22a738", } PACKET_DATA = {"uptime": 1230819000, "lastSeen": 1630081874991} PACKET2_B64 = "AQEBAAAAAHZ4nB2MQQrDMAwEvxJ0rqGxFNnuD0rOfYBsyRBoklISSij9e3GOO8PsF6Rs07rArYP9pbIZXDpY7PM4x12bSNdMGhkdZZ8dBakusanLHHmoojwQt2xe1Z6jHa0pMttbGp3OD0JDid7YY/VYrPfWhxQEfn/qpCUVAgEBAAAAATl4nHWQzU7DMBCEXwX5jJD/1nE5grjBoeoTuMk2tTBOsB2gqvLuOA5NaRE369vZ8cweSUwmRXJ/cyTh6+GQcHqDWEkQWt3ekLRALkAJpfhKZvxpd7Ys1XvjPbr89oNzebIL+D6grw9n5Kx/3fSIzcu2j2ccbeuNWw/G2TSpgS5wkwL6Nu0zpWOmW5MShkP5scdQo0+mxbOVjY97E1rr28x2xkWcrBxiv8n1JiFpbKy7HLVO2JDJ88M22M3Fse5Ck5ezOKSMWG4JTIKirLRdBE++KWPBGVWgKg4X47L/vD450KyoKIMrhx/B7KE5V+XO9g2d6SP+l2ERXGTgigum/+yfMlRAtZJUU3rl8CuDkBqUVNNJYurCfNcjGSJO//A8ZCA4MF2qztcUtLrjq4prClBVQo7j+A3Be62W" PACKET2_ACTION = { "action": "update", "newUpdateId": "90b4d863-4b2b-47af-96ed-b6865fad6546", "modelKey": "camera", "id": "43e3a82e623f23ce12e1797a", } PACKET2_DATA = { "stats": { "rxBytes": 53945386, "txBytes": 2356366294, "wifi": { "channel": None, "frequency": None, "linkSpeedMbps": None, "signalQuality": 50, "signalStrength": 0, }, "battery": { "percentage": None, "isCharging": False, "sleepState": "disconnected", }, "video": { "recordingStart": 1629514560194, "recordingEnd": 1632106567254, "recordingStartLQ": 1629505677015, "recordingEndLQ": 1632106582266, "timelapseStart": 1629514560194, "timelapseEnd": 1632106262318, "timelapseStartLQ": 1627508640800, "timelapseEndLQ": 1632103485646, }, "storage": {"used": 285615325184, "rate": 307.297280557734}, }, } def test_packet_decode(): packet_raw = base64.b64decode(PACKET_B64) packet = WSPacket(packet_raw) assert packet.raw == packet_raw assert packet.raw_base64 == PACKET_B64 assert packet.action_frame.data == PACKET_ACTION assert packet.data_frame.data == PACKET_DATA def test_packet_raw_setter(): packet_raw = base64.b64decode(PACKET_B64) packet2_raw = base64.b64decode(PACKET2_B64) packet = WSPacket(packet_raw) packet.raw = packet2_raw assert packet.raw == packet2_raw assert packet.raw_base64 == PACKET2_B64 assert packet.action_frame.data == PACKET2_ACTION assert packet.data_frame.data == PACKET2_DATA def compare_devices(data): obj = create_from_unifi_dict(deepcopy(data)) obj_dict = obj.unifi_dict() compare_objs(obj.model.value, data, obj_dict) set_no_debug() obj_construct = create_from_unifi_dict(deepcopy(data)) assert obj == obj_construct set_debug() @pytest.mark.skipif(not TEST_VIEWPORT_EXISTS, reason="Missing testdata") def test_viewport(viewport): compare_devices(viewport) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") def test_light(light): compare_devices(light) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_camera(camera): compare_devices(camera) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") def test_sensor(sensor): compare_devices(sensor) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") def test_doorlock(doorlock): compare_devices(doorlock) @pytest.mark.skipif(not TEST_BRIDGE_EXISTS, reason="Missing testdata") def test_bridge(bridge): compare_devices(bridge) @pytest.mark.timeout(CONSTANTS["event_count"] * 0.1) def test_events(raw_events): for event in raw_events: compare_devices(event) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_camera_smart_events(camera_obj: Camera): now = utc_now() camera_obj.last_smart_detect_event_id = None camera_obj.last_smart_detect = None camera_obj.last_smart_detect_event_ids = {} camera_obj.last_smart_detects = {} events = [ Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_1", camera_id=camera_obj.id, start=now - timedelta(seconds=10), type=EventType.SMART_DETECT_LINE, score=100, smart_detect_types=[SmartDetectObjectType.PERSON], smart_detect_event_ids=[], ), Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_2", camera_id=camera_obj.id, start=now - timedelta(seconds=15), end=now - timedelta(seconds=8), type=EventType.SMART_DETECT, score=100, smart_detect_types=[SmartDetectObjectType.PACKAGE], smart_detect_event_ids=[], ), Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_1", camera_id=camera_obj.id, start=now - timedelta(seconds=10), end=now - timedelta(seconds=7), type=EventType.SMART_DETECT_LINE, score=100, smart_detect_types=[ SmartDetectObjectType.PERSON, SmartDetectObjectType.VEHICLE, ], smart_detect_event_ids=[], ), Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_3", camera_id=camera_obj.id, start=now - timedelta(seconds=5), type=EventType.SMART_DETECT, score=100, smart_detect_types=[SmartDetectObjectType.LICENSE_PLATE], smart_detect_event_ids=[], ), ] for event in events: camera_obj.api.bootstrap.process_event(event) assert camera_obj.last_smart_detect == now - timedelta(seconds=5) assert camera_obj.last_person_detect == now - timedelta(seconds=10) assert camera_obj.last_vehicle_detect == now - timedelta(seconds=10) assert camera_obj.last_package_detect == now - timedelta(seconds=15) assert camera_obj.last_license_plate_detect == now - timedelta(seconds=5) assert camera_obj.last_smart_detect_event is not None assert camera_obj.last_smart_detect_event.id == "test_event_3" assert camera_obj.last_person_detect_event is not None assert camera_obj.last_person_detect_event.id == "test_event_1" assert camera_obj.last_vehicle_detect_event is not None assert camera_obj.last_vehicle_detect_event.id == "test_event_1" assert camera_obj.last_package_detect_event is not None assert camera_obj.last_package_detect_event.id == "test_event_2" assert camera_obj.last_license_plate_detect_event is not None assert camera_obj.last_license_plate_detect_event.id == "test_event_3" @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_camera_smart_audio_events(camera_obj: Camera): now = utc_now() camera_obj.last_smart_audio_detect_event_id = None camera_obj.last_smart_audio_detect = None camera_obj.last_smart_audio_detect_event_ids = {} camera_obj.last_smart_audio_detects = {} events = [ Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_1", camera_id=camera_obj.id, start=now - timedelta(seconds=10), type=EventType.SMART_AUDIO_DETECT, score=100, smart_detect_types=[SmartDetectObjectType.SMOKE], smart_detect_event_ids=[], ), Event( # type: ignore[call-arg] api=camera_obj.api, id="test_event_2", camera_id=camera_obj.id, start=now - timedelta(seconds=5), type=EventType.SMART_AUDIO_DETECT, score=100, smart_detect_types=[SmartDetectObjectType.CMONX], smart_detect_event_ids=[], ), ] for event in events: camera_obj.api.bootstrap.process_event(event) assert camera_obj.last_smart_audio_detect == now - timedelta(seconds=5) assert camera_obj.last_smoke_detect == now - timedelta(seconds=10) assert camera_obj.last_cmonx_detect == now - timedelta(seconds=5) assert camera_obj.last_smart_audio_detect_event is not None assert camera_obj.last_smart_audio_detect_event.id == "test_event_2" assert camera_obj.last_smoke_detect_event is not None assert camera_obj.last_smoke_detect_event.id == "test_event_1" assert camera_obj.last_cmonx_detect_event is not None assert camera_obj.last_cmonx_detect_event.id == "test_event_2" def test_bootstrap(bootstrap: dict[str, Any]): obj = Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) set_no_debug() obj_construct = Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) set_debug() obj_dict = obj.unifi_dict() # TODO: fields that still need implemented if "deviceGroups" in bootstrap: # added in 2.0-beta del bootstrap["deviceGroups"] bootstrap.pop("schedules", None) bootstrap.pop("agreements", None) if "deviceGroups" in bootstrap: del bootstrap["deviceGroups"] for model_type in ModelType.bootstrap_models: key = model_type + "s" expected_data = bootstrap.pop(key) actual_data = obj_dict.pop(key) assert len(expected_data) == len(actual_data) for index, expected in enumerate(expected_data): actual = actual_data[index] compare_objs(expected["modelKey"], expected, actual) compare_objs(ModelType.NVR.value, bootstrap.pop("nvr"), obj_dict.pop("nvr")) assert bootstrap == obj_dict assert obj == obj_construct def test_unifi_dict_exclude(bootstrap: dict[str, Any]): obj = Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) obj_dict = obj.unifi_dict(exclude=set()) assert "authUserId" in obj_dict obj_dict = obj.unifi_dict() assert "authUserId" in obj_dict obj_dict = obj.unifi_dict(exclude={"auth_user_id"}) assert "authUserId" not in obj_dict obj_dict = obj.unifi_dict() assert "authUserId" in obj_dict obj_dict = obj.unifi_dict(exclude=set()) assert "authUserId" in obj_dict @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_bootstrap_device_not_adopted(bootstrap, protect_client: ProtectApiClient): bootstrap["cameras"][0]["isAdopted"] = False obj = Bootstrap.from_unifi_dict(**deepcopy(bootstrap), api=protect_client) set_no_debug() obj_construct = Bootstrap.from_unifi_dict(**deepcopy(bootstrap), api=protect_client) set_debug() expected_count = sum(1 if c["isAdopted"] else 0 for c in bootstrap["cameras"]) assert len(obj.cameras) == expected_count assert obj.cameras == obj_construct.cameras @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_bootstrap_device_not_adopted_no_api(bootstrap): bootstrap["cameras"][0]["isAdopted"] = False obj = Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) set_no_debug() obj_construct = cast(Bootstrap, Bootstrap.from_unifi_dict(**deepcopy(bootstrap))) set_debug() assert len(obj.cameras) == len(bootstrap["cameras"]) assert obj.cameras == obj_construct.cameras @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_bootstrap_device_not_adopted_enabled( bootstrap: dict[str, Any], protect_client: ProtectApiClient, ): bootstrap["cameras"][0]["isAdopted"] = False protect_client.ignore_unadopted = False obj = Bootstrap.from_unifi_dict(**deepcopy(bootstrap), api=protect_client) set_no_debug() obj_construct = Bootstrap.from_unifi_dict(**deepcopy(bootstrap), api=protect_client) set_debug() assert len(obj.cameras) == len(bootstrap["cameras"]) assert obj.cameras == obj_construct.cameras @pytest.mark.benchmark(group="construct") @pytest.mark.timeout(0) def test_bootstrap_benchmark(bootstrap: dict[str, Any], benchmark: BenchmarkFixture): def create(): Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) benchmark.pedantic(create, rounds=50, iterations=5) @pytest.mark.benchmark(group="construct") @pytest.mark.timeout(0) def test_bootstrap_benchmark_construct( bootstrap: dict[str, Any], benchmark: BenchmarkFixture, ): set_no_debug() def create(): Bootstrap.from_unifi_dict(**deepcopy(bootstrap)) benchmark.pedantic(create, rounds=50, iterations=5) set_debug() def test_fix_order_size_dict_no_max(): d = FixSizeOrderedDict() d["test"] = 1 d["test2"] = 2 d["test3"] = 3 del d["test2"] assert d == {"test": 1, "test3": 3} def test_fix_order_size_dict_max(): d = FixSizeOrderedDict(max_size=1) d["test"] = 1 d["test2"] = 2 d["test3"] = 3 with pytest.raises(KeyError): del d["test2"] assert d == {"test3": 3} def test_fix_order_size_dict_negative_max(): d = FixSizeOrderedDict(max_size=-1) d["test"] = 1 d["test2"] = 2 d["test3"] = 3 del d["test2"] assert d == {"test": 1, "test3": 3} def test_case_str_enum(): assert RecordingMode("always") == RecordingMode.ALWAYS assert ResolutionStorageType("4K") == ResolutionStorageType.UHD assert VideoMode("highFps") == VideoMode.HIGH_FPS assert RecordingType("roTating") == RecordingType.CONTINUOUS @pytest.mark.asyncio() async def test_play_audio_no_speaker(camera_obj: Camera): camera_obj.feature_flags.has_speaker = False with pytest.raises(BadRequest): await camera_obj.play_audio("test") @pytest.mark.asyncio() @pytest.mark.usefixtures("_disable_camera_validation") async def test_play_audio_already_playing(camera_obj: Camera): camera_obj.feature_flags.has_speaker = True camera_obj.talkback_stream = Mock() camera_obj.talkback_stream.is_running = True with pytest.raises(BadRequest): await camera_obj.play_audio("test") @pytest.mark.asyncio() @pytest.mark.usefixtures("_disable_camera_validation") @patch("uiprotect.data.devices.TalkbackStream") async def test_play_audio(mock_talkback, camera_obj: Camera): camera_obj.feature_flags.has_speaker = True mock_instance = MockTalkback() mock_talkback.return_value = mock_instance await camera_obj.play_audio("test") mock_talkback.assert_called_with(camera_obj, "test", None) assert mock_instance.start.called assert mock_instance.run_until_complete.called @pytest.mark.asyncio() @pytest.mark.usefixtures("_disable_camera_validation") @patch("uiprotect.data.devices.TalkbackStream") async def test_play_audio_no_blocking(mock_talkback, camera_obj: Camera): camera_obj.feature_flags.has_speaker = True mock_instance = MockTalkback() mock_talkback.return_value = mock_instance await camera_obj.play_audio("test", blocking=False) mock_talkback.assert_called_with(camera_obj, "test", None) assert mock_instance.start.called assert not mock_instance.run_until_complete.called await camera_obj.wait_until_audio_completes() assert mock_instance.run_until_complete.called @pytest.mark.asyncio() @pytest.mark.usefixtures("_disable_camera_validation") @patch("uiprotect.data.devices.TalkbackStream") async def test_play_audio_stop(mock_talkback, camera_obj: Camera): camera_obj.feature_flags.has_speaker = True mock_instance = MockTalkback() mock_talkback.return_value = mock_instance await camera_obj.play_audio("test", blocking=False) mock_talkback.assert_called_with(camera_obj, "test", None) assert mock_instance.start.called assert not mock_instance.run_until_complete.called await camera_obj.stop_audio() assert mock_instance.stop.called @pytest.mark.asyncio() @pytest.mark.usefixtures("_disable_camera_validation") @patch("uiprotect.data.devices.TalkbackStream") async def test_play_audio_error(mock_talkback, camera_obj: Camera): camera_obj.feature_flags.has_speaker = True mock_instance = MockTalkback() mock_instance.is_error = True mock_talkback.return_value = mock_instance with pytest.raises(StreamError): await camera_obj.play_audio("test") mock_talkback.assert_called_with(camera_obj, "test", None) assert mock_instance.run_until_complete.called @pytest.mark.asyncio() async def test_get_smart_detect_track_bad_type(smart_dectect_obj: Event | None): if smart_dectect_obj is None: pytest.skip("No smart detection object found") smart_dectect_obj.type = EventType.MOTION with pytest.raises(BadRequest): await smart_dectect_obj.get_smart_detect_track() @pytest.mark.asyncio() async def test_get_smart_detect_track(smart_dectect_obj: Event | None): if smart_dectect_obj is None: pytest.skip("No smart detection object found") track = await smart_dectect_obj.get_smart_detect_track() assert track.camera @pytest.mark.asyncio() async def test_get_smart_detect_zones(smart_dectect_obj: Event | None): if smart_dectect_obj is None: pytest.skip("No smart detection object found") camera = smart_dectect_obj.camera if camera is None: pytest.skip("Camera not found for smart detection") track = await smart_dectect_obj.get_smart_detect_track() zone_ids: set[int] = set() for item in track.payload: zone_ids |= set(item.zone_ids) zones = await smart_dectect_obj.get_smart_detect_zones() for zone_id, zone in zones.items(): assert zone_id in zone_ids assert zone_id == zone.id assert zone in camera.smart_detect_zones def test_doorbell_bad_state(): message = LCDMessage.from_unifi_dict(text="Test") assert message.text == "Test" assert message.type == DoorbellMessageType.CUSTOM_MESSAGE def test_camera_ip_host(camera): camera["host"] = "1.1.1.1" camera["connectionHost"] = "1.1.1.1" camera_obj = Camera.from_unifi_dict(**camera) assert camera_obj.host == IPv4Address("1.1.1.1") assert camera_obj.connection_host == IPv4Address("1.1.1.1") def test_camera_dns_host(camera): camera["host"] = "se-gw.local" camera["connectionHost"] = "se-gw.local" camera_obj = Camera.from_unifi_dict(**camera) assert camera_obj.host == "se-gw.local" assert camera_obj.connection_host == "se-gw.local" def test_bootstrap_ip_host(bootstrap): bootstrap["nvr"]["hosts"] = ["1.1.1.1"] bootstrap_obj = Bootstrap.from_unifi_dict(**bootstrap) assert bootstrap_obj.nvr.hosts == [IPv4Address("1.1.1.1")] def test_bootstrap_dns_host(bootstrap): bootstrap["nvr"]["hosts"] = ["se-gw.local"] bootstrap_obj = Bootstrap.from_unifi_dict(**bootstrap) assert bootstrap_obj.nvr.hosts == ["se-gw.local"] @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_save_device_no_changes(camera_obj: Camera): camera_obj.api.api_request.reset_mock() # type: ignore[attr-defined] data_before_changes = camera_obj.dict_with_excludes() await camera_obj.save_device(data_before_changes) assert not camera_obj.api.api_request.called # type: ignore[attr-defined] @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_device_reboot(camera_obj: Camera): camera_obj.api.api_request.reset_mock() # type: ignore[attr-defined] await camera_obj.reboot() camera_obj.api.api_request.assert_called_with( # type: ignore[attr-defined] f"cameras/{camera_obj.id}/reboot", method="post", ) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( ( "permissions", "can_create", "can_read", "can_write", "can_delete", "can_read_media", "can_delete_media", ), [ (["camera:*:*"], True, True, True, True, True, True), ( ["camera:create,read,write,delete,readmedia,deletemedia:*"], True, True, True, True, True, True, ), ( ["camera:create,read,write,readmedia:*"], True, True, True, False, True, False, ), (["camera:read,readmedia:*"], False, True, False, False, True, False), ( ["camera:read,readmedia:test_id_1,test_id_2"], False, True, False, False, True, False, ), ( ["camera:read,readmedia:test_id_2,test_id_1"], False, True, False, False, True, False, ), ( ["camera:delete:test_id_1", "camera:read,readmedia:*"], False, True, False, True, True, False, ), ( ["camera:delete:test_id_2", "camera:read,readmedia:*"], False, True, False, False, True, False, ), ( ["camera:read,readmedia:*", "camera:delete:test_id_1"], False, True, False, True, True, False, ), ( ["camera:read,readmedia:*", "camera:delete:test_id_2"], False, True, False, False, True, False, ), ], ) @pytest.mark.asyncio() async def test_permissions( user_obj: User, camera_obj: Camera, permissions: list[str], can_create: bool, can_read: bool, can_write: bool, can_delete: bool, can_read_media: bool, can_delete_media: bool, ): if camera_obj is None: pytest.skip("No camera_obj obj found") api = user_obj.api user_obj.all_permissions = [ Permission.from_unifi_dict(rawPermission=p, api=api) for p in permissions ] camera_obj.id = "test_id_1" api.bootstrap.cameras[camera_obj.id] = camera_obj assert camera_obj.can_create(user_obj) is can_create assert camera_obj.can_read(user_obj) is can_read assert camera_obj.can_write(user_obj) is can_write assert camera_obj.can_delete(user_obj) is can_delete assert camera_obj.can_read_media(user_obj) is can_read_media assert camera_obj.can_delete_media(user_obj) is can_delete_media @pytest.mark.parametrize( ("permissions", "can_create", "can_read", "can_write", "can_delete"), [ (["user:*:*"], True, True, True, True), (["user:create,read,write,delete:*"], True, True, True, True), (["user:create,read,write,delete:$"], True, True, True, True), ( ["user:read,write:$", "user:create,read,write,delete:*"], True, True, True, True, ), (["user:read,write:*"], False, True, True, False), (["user:read,write:$"], False, True, True, False), ( ["user:read,write:$", "user:create,read,write,delete:test_id_2"], False, True, True, False, ), (["user:create,delete:$", "user:read,write:*"], True, True, True, True), ], ) @pytest.mark.asyncio() async def test_permissions_user( user_obj: User, permissions: list[str], can_create: bool, can_read: bool, can_write: bool, can_delete: bool, ): api = user_obj.api user1 = user_obj.copy() user1.id = "test_id_1" user1.all_permissions = [ Permission.from_unifi_dict(rawPermission=p, api=api) for p in permissions ] api.bootstrap.auth_user_id = user1.id api.bootstrap.users = {user1.id: user1} assert user1.can_create(user1) is can_create assert user1.can_read(user1) is can_read assert user1.can_write(user1) is can_write assert user1.can_delete(user1) is can_delete @pytest.mark.parametrize( ("permissions", "can_create", "can_read", "can_write", "can_delete"), [ (["user:*:*"], True, True, True, True), (["user:create,read,write,delete:*"], True, True, True, True), (["user:create,read,write,delete:$"], False, False, False, False), ( ["user:read,write:$", "user:create,read,write,delete:*"], True, True, True, True, ), (["user:read,write:*"], False, True, True, False), (["user:read,write:$"], False, False, False, False), ( ["user:read,write:$", "user:create,read,write,delete:test_id_2"], True, True, True, True, ), (["user:create,delete:$", "user:read,write:*"], False, True, True, False), ], ) @pytest.mark.asyncio() async def test_permissions_self_with_other( user_obj: User, permissions: list[str], can_create: bool, can_read: bool, can_write: bool, can_delete: bool, ): api = user_obj.api user1 = user_obj.copy() user1.id = "test_id_1" user1.all_permissions = [ Permission.from_unifi_dict(rawPermission=p, api=api) for p in permissions ] user2 = user_obj.copy() user2.id = "test_id_2" api.bootstrap.auth_user_id = user1.id api.bootstrap.users = {user1.id: user1, user2.id: user2} assert user2.can_create(user1) is can_create assert user2.can_read(user1) is can_read assert user2.can_write(user1) is can_write assert user2.can_delete(user1) is can_delete @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_revert(user_obj: User, camera_obj: Camera): if camera_obj is None: pytest.skip("No camera_obj obj found") api = user_obj.api camera_obj.id = "test_id_1" camera_obj.add_privacy_zone() camera_obj.recording_settings.mode = RecordingMode.NEVER api.bootstrap.cameras[camera_obj.id] = camera_obj user_obj.all_permissions = [ Permission.from_unifi_dict(rawPermission="camera:read:*", api=api), ] camera_before = camera_obj.dict() camera_obj.remove_privacy_zone() camera_obj.recording_settings.mode = RecordingMode.ALWAYS with pytest.raises(NotAuthorized): await camera_obj.save_device(camera_before) assert camera_before == camera_obj.dict() @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_multiple_updates(user_obj: User, camera_obj: Camera): if camera_obj is None: pytest.skip("No camera_obj obj found") api = user_obj.api camera_obj.id = "test_id_1" camera_obj.recording_settings.enable_motion_detection = False camera_obj.smart_detect_settings.object_types = [] api.bootstrap.cameras[camera_obj.id] = camera_obj await asyncio.gather( camera_obj.set_motion_detection(True), camera_obj.set_person_detection(True), camera_obj.set_vehicle_detection(True), ) camera_obj.api.api_request.assert_called_with( # type: ignore[attr-defined] f"cameras/{camera_obj.id}", method="patch", json={ "recordingSettings": {"enableMotionDetection": True}, "smartDetectSettings": {"objectTypes": ["person", "vehicle"]}, }, ) @pytest.mark.asyncio() async def test_user_becomes_cloud_account_and_then_removed(user_obj: User): assert not user_obj.cloud_account model = user_obj._get_protect_model() assert "cloud_account" in model.objs user_obj.update_from_dict( { "id": "test_id_1", "name": "Test", "cloud_account": { "first_name": "Qpvfly", "last_name": "Ikjzilt", "email": "QhoFvCv@example.com", "profile_img": None, "user_id": "fe4c12ae2c1348edb7854e2f", "id": "9efc4511-4539-4402-9581-51cee8b65cf5", "cloud_id": "9efc4511-4539-4402-9581-51cee8b65cf5", "name": "Qpvfly Ikjzilt", "model_key": "cloudIdentity", }, } ) assert user_obj.name == "Test" assert user_obj.cloud_account assert isinstance(user_obj.cloud_account, CloudAccount) assert user_obj.cloud_account.first_name == "Qpvfly" user_obj.update_from_dict( { "id": "test_id_1", "name": "Test", "cloud_account": None, } ) assert user_obj.name == "Test" assert user_obj.cloud_account is None @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_smart_detect_settings_becomes_none(camera_obj: Camera): camera_obj.smart_detect_settings.update_from_dict( { "audio_types": None, } ) assert camera_obj.smart_detect_settings.audio_types is None camera_obj.smart_detect_settings.update_from_dict( { "audio_types": ["alrmSmoke"], } ) assert camera_obj.smart_detect_settings.audio_types == [SmartDetectAudioType.SMOKE] camera_obj.smart_detect_settings.update_from_dict( { "audio_types": None, } ) assert camera_obj.smart_detect_settings.audio_types is None @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_unknown_smart( camera: dict[str, Any] | None, bootstrap: dict[str, Any], protect_client: ProtectApiClient, ): if camera is None: pytest.skip("No camera obj found") camera["featureFlags"]["smartDetectTypes"] = ["alrmSmoke3"] camera["smartDetectZones"][0]["objectTypes"] = ["alrmSmoke3"] camera["smartDetectSettings"]["objectTypes"] = ["alrmSmoke3"] bootstrap["cameras"] = [camera] obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) camera_obj = next(iter(obj.cameras.values())) assert camera_obj.feature_flags.smart_detect_types == [] assert camera_obj.smart_detect_zones[0].object_types == [] assert camera_obj.smart_detect_settings.object_types == [] set_no_debug() obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) camera_obj = next(iter(obj.cameras.values())) assert camera_obj.feature_flags.smart_detect_types == [] assert camera_obj.smart_detect_zones[0].object_types == [] assert camera_obj.smart_detect_settings.object_types == [] set_debug() @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") def test_unknown_video( camera: dict[str, Any] | None, bootstrap: dict[str, Any], protect_client: ProtectApiClient, ): if camera is None: pytest.skip("No camera obj found") camera["featureFlags"]["videoModes"] = ["stuff"] bootstrap["cameras"] = [camera] obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) camera_obj = next(iter(obj.cameras.values())) assert camera_obj.feature_flags.video_modes == [] set_no_debug() obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) camera_obj = next(iter(obj.cameras.values())) assert camera_obj.feature_flags.video_modes == [] set_debug() def test_unknown_storage_type( bootstrap: dict[str, Any], protect_client: ProtectApiClient, ): bootstrap["nvr"]["systemInfo"]["storage"]["type"] = "test" obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) assert obj.nvr.system_info.storage.type == StorageType.UNKNOWN set_no_debug() obj: Bootstrap = Bootstrap.from_unifi_dict( **deepcopy(bootstrap), api=protect_client, ) assert obj.nvr.system_info.storage.type == StorageType.UNKNOWN set_debug() uiprotect-6.1.0/tests/data/test_doorlock.py000066400000000000000000000110501467310220200210260ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr" from __future__ import annotations from datetime import timedelta from typing import TYPE_CHECKING import pytest from tests.conftest import TEST_CAMERA_EXISTS, TEST_DOORLOCK_EXISTS from uiprotect.data.types import LockStatusType from uiprotect.exceptions import BadRequest from uiprotect.utils import to_ms if TYPE_CHECKING: from uiprotect.data import Camera, Doorlock, Light @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_doorlock_set_paired_camera_none(doorlock_obj: Doorlock): doorlock_obj.api.api_request.reset_mock() doorlock_obj.camera_id = "bad_id" await doorlock_obj.set_paired_camera(None) doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}", method="patch", json={"camera": None}, ) @pytest.mark.skipif( not TEST_DOORLOCK_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_doorlock_set_paired_camera(doorlock_obj: Light, camera_obj: Camera): doorlock_obj.api.api_request.reset_mock() doorlock_obj.camera_id = None await doorlock_obj.set_paired_camera(camera_obj) doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}", method="patch", json={"camera": camera_obj.id}, ) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_doorlock_set_status_light(doorlock_obj: Doorlock, status: bool): doorlock_obj.api.api_request.reset_mock() doorlock_obj.led_settings.is_enabled = not status await doorlock_obj.set_status_light(status) doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}", method="patch", json={"ledSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( "duration", [ timedelta(seconds=0), timedelta(seconds=15), timedelta(seconds=3600), timedelta(seconds=3601), ], ) @pytest.mark.asyncio() async def test_doorlock_set_auto_close_time( doorlock_obj: Doorlock, duration: timedelta, ): doorlock_obj.api.api_request.reset_mock() doorlock_obj.auto_close_time = timedelta(seconds=30) duration_invalid = duration is not None and int(duration.total_seconds()) == 3601 if duration_invalid: with pytest.raises(BadRequest): await doorlock_obj.set_auto_close_time(duration) assert not doorlock_obj.api.api_request.called else: await doorlock_obj.set_auto_close_time(duration) expected = {"autoCloseTimeMs": to_ms(duration)} doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}", method="patch", json=expected, ) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_doorlock_close(doorlock_obj: Doorlock): doorlock_obj.api.api_request.reset_mock() doorlock_obj.lock_status = LockStatusType.OPEN await doorlock_obj.close_lock() doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}/close", method="post", ) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_doorlock_close_invalid(doorlock_obj: Doorlock): doorlock_obj.api.api_request.reset_mock() doorlock_obj.lock_status = LockStatusType.CLOSED with pytest.raises(BadRequest): await doorlock_obj.close_lock() assert not doorlock_obj.api.api_request.called @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_doorlock_open(doorlock_obj: Doorlock): doorlock_obj.api.api_request.reset_mock() doorlock_obj.lock_status = LockStatusType.CLOSED await doorlock_obj.open_lock() doorlock_obj.api.api_request.assert_called_with( f"doorlocks/{doorlock_obj.id}/open", method="post", ) @pytest.mark.skipif(not TEST_DOORLOCK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_doorlock_open_invalid(doorlock_obj: Doorlock): doorlock_obj.api.api_request.reset_mock() doorlock_obj.lock_status = LockStatusType.OPEN with pytest.raises(BadRequest): await doorlock_obj.open_lock() assert not doorlock_obj.api.api_request.called uiprotect-6.1.0/tests/data/test_light.py000066400000000000000000000203421467310220200203250ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr" from __future__ import annotations from datetime import timedelta from typing import TYPE_CHECKING import pytest from pydantic.v1 import ValidationError from tests.conftest import TEST_CAMERA_EXISTS, TEST_LIGHT_EXISTS from uiprotect.data.types import LightModeEnableType, LightModeType from uiprotect.exceptions import BadRequest from uiprotect.utils import to_ms if TYPE_CHECKING: from uiprotect.data import Camera, Light @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_light_set_paired_camera_none(light_obj: Light): light_obj.api.api_request.reset_mock() light_obj.camera_id = "bad_id" await light_obj.set_paired_camera(None) light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json={"camera": None}, ) @pytest.mark.skipif( not TEST_LIGHT_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_light_set_paired_camera(light_obj: Light, camera_obj: Camera): light_obj.api.api_request.reset_mock() light_obj.camera_id = None await light_obj.set_paired_camera(camera_obj) light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json={"camera": camera_obj.id}, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_light_set_status_light(light_obj: Light, status: bool): light_obj.api.api_request.reset_mock() light_obj.light_device_settings.is_indicator_enabled = not status await light_obj.set_status_light(status) light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json={"lightDeviceSettings": {"isIndicatorEnabled": status}}, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("level", [-1, 1, 3, 6, 7]) @pytest.mark.asyncio() async def test_light_set_led_level(light_obj: Light, level: int): light_obj.api.api_request.reset_mock() light_obj.light_device_settings.led_level = 2 if level in {-1, 7}: with pytest.raises(ValidationError): await light_obj.set_led_level(level) assert not light_obj.api.api_request.called else: await light_obj.set_led_level(level) light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json={"lightDeviceSettings": {"ledLevel": level}}, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.parametrize("level", [None, -1, 1, 3, 6, 7]) @pytest.mark.asyncio() async def test_light_set_light(light_obj: Light, status: bool, level: int | None): light_obj.api.api_request.reset_mock() light_obj.light_on_settings.is_led_force_on = not status if level is not None: light_obj.light_device_settings.led_level = 2 if level in {-1, 7}: with pytest.raises(ValidationError): await light_obj.set_light(status, level) assert not light_obj.api.api_request.called else: await light_obj.set_light(status, level) if level is None: expected = {"lightOnSettings": {"isLedForceOn": status}} else: expected = { "lightOnSettings": {"isLedForceOn": status}, "lightDeviceSettings": {"ledLevel": level}, } light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json=expected, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("sensitivity", [1, 100, -10]) @pytest.mark.asyncio() async def test_light_set_sensitivity( light_obj: Light, sensitivity: int, ): light_obj.api.api_request.reset_mock() light_obj.light_device_settings.pir_sensitivity = 50 if sensitivity == -10: with pytest.raises(ValidationError): await light_obj.set_sensitivity(sensitivity) assert not light_obj.api.api_request.called else: await light_obj.set_sensitivity(sensitivity) expected = {"lightDeviceSettings": {"pirSensitivity": sensitivity}} light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json=expected, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize( "duration", [ timedelta(seconds=1), timedelta(seconds=15), timedelta(seconds=900), timedelta(seconds=1000), ], ) @pytest.mark.asyncio() async def test_light_set_duration( light_obj: Light, duration: timedelta, ): light_obj.api.api_request.reset_mock() light_obj.light_device_settings.pir_duration = timedelta(seconds=30) duration_invalid = duration is not None and int(duration.total_seconds()) in { 1, 1000, } if duration_invalid: with pytest.raises(BadRequest): await light_obj.set_duration(duration) assert not light_obj.api.api_request.called else: await light_obj.set_duration(duration) expected = {"lightDeviceSettings": {"pirDuration": to_ms(duration)}} light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json=expected, ) @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("mode", [LightModeType.MANUAL, LightModeType.WHEN_DARK]) @pytest.mark.parametrize("enable_at", [None, LightModeEnableType.ALWAYS]) @pytest.mark.parametrize( "duration", [ None, timedelta(seconds=1), timedelta(seconds=15), timedelta(seconds=900), timedelta(seconds=1000), ], ) @pytest.mark.parametrize("sensitivity", [None, 1, 100, -10]) @pytest.mark.asyncio() async def test_light_set_light_settings( light_obj: Light, mode: LightModeType, enable_at: LightModeEnableType | None, duration: timedelta | None, sensitivity: int | None, ): light_obj.api.api_request.reset_mock() light_obj.light_mode_settings.mode = LightModeType.MOTION light_obj.light_mode_settings.enable_at = LightModeEnableType.DARK light_obj.light_device_settings.pir_duration = timedelta(seconds=30) light_obj.light_device_settings.pir_sensitivity = 50 duration_invalid = duration is not None and int(duration.total_seconds()) in { 1, 1000, } if duration_invalid: with pytest.raises(BadRequest): await light_obj.set_light_settings( mode, enable_at=enable_at, duration=duration, sensitivity=sensitivity, ) assert not light_obj.api.api_request.called elif sensitivity == -10: with pytest.raises(ValidationError): await light_obj.set_light_settings( mode, enable_at=enable_at, duration=duration, sensitivity=sensitivity, ) assert not light_obj.api.api_request.called else: await light_obj.set_light_settings( mode, enable_at=enable_at, duration=duration, sensitivity=sensitivity, ) expected = {"lightModeSettings": {"mode": mode.value}} if enable_at is not None: expected["lightModeSettings"].update({"enableAt": enable_at.value}) if duration is not None: expected["lightDeviceSettings"] = expected.get("lightDeviceSettings", {}) expected["lightDeviceSettings"].update({"pirDuration": to_ms(duration)}) if sensitivity is not None: expected["lightDeviceSettings"] = expected.get("lightDeviceSettings", {}) expected["lightDeviceSettings"].update({"pirSensitivity": sensitivity}) light_obj.api.api_request.assert_called_with( f"lights/{light_obj.id}", method="patch", json=expected, ) uiprotect-6.1.0/tests/data/test_nvr.py000066400000000000000000000170141467310220200200250ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr, arg-type, list-item" from __future__ import annotations from datetime import timedelta from ipaddress import IPv4Address, IPv6Address import pytest from pydantic.v1 import ValidationError from uiprotect.data import ( NVR, AnalyticsOption, DoorbellMessage, DoorbellMessageType, ) from uiprotect.data.nvr import NVRSmartDetection from uiprotect.exceptions import BadRequest from uiprotect.utils import to_ms @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_nvr_set_insights(nvr_obj: NVR, status: bool): nvr_obj.api.api_request.reset_mock() nvr_obj.is_insights_enabled = not status await nvr_obj.set_insights(status) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"isInsightsEnabled": status}, ) @pytest.mark.asyncio() async def test_nvr_set_anonymous_analytics(nvr_obj: NVR): nvr_obj.api.api_request.reset_mock() nvr_obj.analytics_data = AnalyticsOption.ANONYMOUS await nvr_obj.set_anonymous_analytics(False) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"analyticsData": "none"}, ) @pytest.mark.asyncio() async def test_nvr_set_default_reset_timeout(nvr_obj: NVR): nvr_obj.api.api_request.reset_mock() duration = timedelta(seconds=10) await nvr_obj.set_default_reset_timeout(duration) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"doorbellSettings": {"defaultMessageResetTimeoutMs": to_ms(duration)}}, ) @pytest.mark.parametrize("message", ["Test", "fqthpqBgVMKXp9jXX2VeuGeXYfx2mMjB"]) @pytest.mark.asyncio() async def test_nvr_set_default_doorbell_message(nvr_obj: NVR, message: str): nvr_obj.api.api_request.reset_mock() if len(message) > 30: with pytest.raises(ValidationError): await nvr_obj.set_default_doorbell_message(message) assert not nvr_obj.api.api_request.called else: await nvr_obj.set_default_doorbell_message(message) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"doorbellSettings": {"defaultMessageText": message}}, ) @pytest.mark.parametrize( "message", ["Welcome", "Test", "fqthpqBgVMKXp9jXX2VeuGeXYfx2mMjB"], ) @pytest.mark.asyncio() async def test_nvr_add_custom_doorbell_message(nvr_obj: NVR, message: str): nvr_obj.api.api_request.reset_mock() nvr_obj.doorbell_settings.custom_messages = ["Welcome"] if message != "Test": with pytest.raises(BadRequest): await nvr_obj.add_custom_doorbell_message(message) assert not nvr_obj.api.api_request.called else: await nvr_obj.add_custom_doorbell_message(message) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"doorbellSettings": {"customMessages": ["Welcome", "Test"]}}, ) assert nvr_obj.doorbell_settings.all_messages == [ DoorbellMessage( type=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR, text=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value.replace("_", " "), ), DoorbellMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), ), DoorbellMessage( type=DoorbellMessageType.CUSTOM_MESSAGE, text="Welcome", ), DoorbellMessage( type=DoorbellMessageType.CUSTOM_MESSAGE, text="Test", ), ] @pytest.mark.parametrize("message", ["Welcome", "Test"]) @pytest.mark.asyncio() async def test_nvr_remove_custom_doorbell_message(nvr_obj: NVR, message: str): nvr_obj.api.api_request.reset_mock() nvr_obj.doorbell_settings.custom_messages = ["Welcome"] if message == "Test": with pytest.raises(BadRequest): await nvr_obj.remove_custom_doorbell_message(message) assert not nvr_obj.api.api_request.called else: await nvr_obj.remove_custom_doorbell_message(message) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"doorbellSettings": {"customMessages": []}}, ) assert nvr_obj.doorbell_settings.all_messages == [ DoorbellMessage( type=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR, text=DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR.value.replace("_", " "), ), DoorbellMessage( type=DoorbellMessageType.DO_NOT_DISTURB, text=DoorbellMessageType.DO_NOT_DISTURB.value.replace("_", " "), ), ] @pytest.mark.parametrize( ("ip", "expected"), [ ("192.168.1.1", IPv4Address("192.168.1.1")), ("fe80::1ff:fe23:4567:890a", IPv6Address("fe80::1ff:fe23:4567:890a")), ], ) @pytest.mark.asyncio() async def test_nvr_wan_ip(nvr_obj: NVR, ip: str, expected: IPv4Address | IPv6Address): nvr_dict = nvr_obj.unifi_dict() nvr_dict["wanIp"] = ip nvr = NVR.from_unifi_dict(**nvr_dict) assert nvr.wan_ip == expected assert nvr.unifi_dict()["wanIp"] == ip @pytest.mark.asyncio() async def test_nvr_set_smart_detections(nvr_obj: NVR): nvr_obj.smart_detection = NVRSmartDetection( enable=False, face_recognition=False, license_plate_recognition=False, ) nvr_obj.api.api_request.reset_mock() await nvr_obj.set_smart_detections(True) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"smartDetection": {"enable": True}}, ) @pytest.mark.asyncio() async def test_nvr_set_face_recognition(nvr_obj: NVR): nvr_obj.smart_detection = NVRSmartDetection( enable=True, face_recognition=False, license_plate_recognition=False, ) nvr_obj.api.api_request.reset_mock() await nvr_obj.set_face_recognition(True) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"smartDetection": {"faceRecognition": True}}, ) @pytest.mark.asyncio() async def test_nvr_set_face_recognition_no_smart(nvr_obj: NVR): nvr_obj.smart_detection = NVRSmartDetection( enable=False, face_recognition=False, license_plate_recognition=False, ) nvr_obj.api.api_request.reset_mock() with pytest.raises(BadRequest): await nvr_obj.set_face_recognition(True) assert not nvr_obj.api.api_request.called @pytest.mark.asyncio() async def test_nvr_set_license_plate_recognition(nvr_obj: NVR): nvr_obj.smart_detection = NVRSmartDetection( enable=True, face_recognition=False, license_plate_recognition=False, ) nvr_obj.api.api_request.reset_mock() await nvr_obj.set_license_plate_recognition(True) nvr_obj.api.api_request.assert_called_with( "nvr", method="patch", json={"smartDetection": {"licensePlateRecognition": True}}, ) @pytest.mark.asyncio() async def test_nvr_set_license_plate_recognition_no_smart(nvr_obj: NVR): nvr_obj.smart_detection = NVRSmartDetection( enable=False, face_recognition=False, license_plate_recognition=False, ) nvr_obj.api.api_request.reset_mock() with pytest.raises(BadRequest): await nvr_obj.set_license_plate_recognition(True) assert not nvr_obj.api.api_request.called uiprotect-6.1.0/tests/data/test_sensor.py000066400000000000000000000244771467310220200205440ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr" from __future__ import annotations from typing import TYPE_CHECKING import pytest from pydantic.v1 import ValidationError from tests.conftest import TEST_CAMERA_EXISTS, TEST_SENSOR_EXISTS from uiprotect.data.types import MountType from uiprotect.exceptions import BadRequest if TYPE_CHECKING: from uiprotect.data import Camera, Light, Sensor @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_status_light(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.led_settings.is_enabled = not status await sensor_obj.set_status_light(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"ledSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("mount_type", [MountType.DOOR, MountType.NONE]) @pytest.mark.asyncio() async def test_sensor_set_mount_type(sensor_obj: Sensor, mount_type: MountType): sensor_obj.api.api_request.reset_mock() sensor_obj.mount_type = MountType.LEAK await sensor_obj.set_mount_type(mount_type) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"mountType": mount_type.value}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_motion_status(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.motion_settings.is_enabled = not status await sensor_obj.set_motion_status(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"motionSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_temperature_status(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.temperature_settings.is_enabled = not status await sensor_obj.set_temperature_status(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"temperatureSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_humidity_status(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.humidity_settings.is_enabled = not status await sensor_obj.set_humidity_status(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"humiditySettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_light_status(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.light_settings.is_enabled = not status await sensor_obj.set_light_status(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"lightSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("status", [True, False]) @pytest.mark.asyncio() async def test_sensor_set_alarm_status(sensor_obj: Sensor, status: bool): sensor_obj.api.api_request.reset_mock() sensor_obj.alarm_settings.is_enabled = not status await sensor_obj.set_alarm_status(status) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"alarmSettings": {"isEnabled": status}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("sensitivity", [1, 100, -10]) @pytest.mark.asyncio() async def test_sensor_set_motion_sensitivity( sensor_obj: Sensor, sensitivity: int, ): sensor_obj.api.api_request.reset_mock() sensor_obj.motion_settings.sensitivity = 50 if sensitivity == -10: with pytest.raises(ValidationError): await sensor_obj.set_motion_sensitivity(sensitivity) assert not sensor_obj.api.api_request.called else: await sensor_obj.set_motion_sensitivity(sensitivity) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"motionSettings": {"sensitivity": sensitivity}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("low", [-1.0, 0.0, 25.0]) @pytest.mark.parametrize("high", [20.0, 45.0, 50.0]) @pytest.mark.asyncio() async def test_sensor_set_temperature_safe_range( sensor_obj: Sensor, low: float, high: float, ): sensor_obj.api.api_request.reset_mock() sensor_obj.temperature_settings.low_threshold = None sensor_obj.temperature_settings.high_threshold = None if low == -1.0 or high == 50.0 or low > high: with pytest.raises(BadRequest): await sensor_obj.set_temperature_safe_range(low, high) assert not sensor_obj.api.api_request.called else: await sensor_obj.set_temperature_safe_range(low, high) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"temperatureSettings": {"lowThreshold": low, "highThreshold": high}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("low", [0.0, 1.0, 50.0]) @pytest.mark.parametrize("high", [45.0, 99.0, 100.0]) @pytest.mark.asyncio() async def test_sensor_set_humidity_safe_range( sensor_obj: Sensor, low: float, high: float, ): sensor_obj.api.api_request.reset_mock() sensor_obj.humidity_settings.low_threshold = None sensor_obj.humidity_settings.high_threshold = None if low == 0.0 or high == 100.0 or low > high: with pytest.raises(BadRequest): await sensor_obj.set_humidity_safe_range(low, high) assert not sensor_obj.api.api_request.called else: await sensor_obj.set_humidity_safe_range(low, high) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"humiditySettings": {"lowThreshold": low, "highThreshold": high}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.parametrize("low", [0.0, 1.0, 500.0]) @pytest.mark.parametrize("high", [400.0, 1000.0, 1001.0]) @pytest.mark.asyncio() async def test_sensor_set_light_safe_range(sensor_obj: Sensor, low: float, high: float): sensor_obj.api.api_request.reset_mock() sensor_obj.light_settings.low_threshold = None sensor_obj.light_settings.high_threshold = None if low == 0.0 or high == 1001.0 or low > high: with pytest.raises(BadRequest): await sensor_obj.set_light_safe_range(low, high) assert not sensor_obj.api.api_request.called else: await sensor_obj.set_light_safe_range(low, high) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"lightSettings": {"lowThreshold": low, "highThreshold": high}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_sensor_remove_temperature_safe_range(sensor_obj: Sensor): sensor_obj.api.api_request.reset_mock() sensor_obj.temperature_settings.low_threshold = 10 sensor_obj.temperature_settings.high_threshold = 20 await sensor_obj.remove_temperature_safe_range() sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"temperatureSettings": {"lowThreshold": None, "highThreshold": None}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_sensor_remove_humidity_safe_range(sensor_obj: Sensor): sensor_obj.api.api_request.reset_mock() sensor_obj.humidity_settings.low_threshold = 10 sensor_obj.humidity_settings.high_threshold = 20 await sensor_obj.remove_humidity_safe_range() sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"humiditySettings": {"lowThreshold": None, "highThreshold": None}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_sensor_remove_light_safe_range(sensor_obj: Sensor): sensor_obj.api.api_request.reset_mock() sensor_obj.light_settings.low_threshold = 10 sensor_obj.light_settings.high_threshold = 20 await sensor_obj.remove_light_safe_range() sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"lightSettings": {"lowThreshold": None, "highThreshold": None}}, ) @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_sensor_set_paired_camera_none(sensor_obj: Sensor): sensor_obj.api.api_request.reset_mock() sensor_obj.camera_id = "bad_id" await sensor_obj.set_paired_camera(None) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"camera": None}, ) @pytest.mark.skipif( not TEST_SENSOR_EXISTS or not TEST_CAMERA_EXISTS, reason="Missing testdata", ) @pytest.mark.asyncio() async def test_sensor_set_paired_camera(sensor_obj: Light, camera_obj: Camera): sensor_obj.api.api_request.reset_mock() sensor_obj.camera_id = None await sensor_obj.set_paired_camera(camera_obj) sensor_obj.api.api_request.assert_called_with( f"sensors/{sensor_obj.id}", method="patch", json={"camera": camera_obj.id}, ) uiprotect-6.1.0/tests/data/test_types.py000066400000000000000000000004401467310220200203570ustar00rootroot00000000000000from __future__ import annotations import pytest from uiprotect.data.types import ModelType @pytest.mark.asyncio() async def test_model_type_from_string(): assert ModelType.from_string("camera") is ModelType.CAMERA assert ModelType.from_string("invalid") is ModelType.UNKNOWN uiprotect-6.1.0/tests/data/test_viewer.py000066400000000000000000000034211467310220200205160ustar00rootroot00000000000000# mypy: disable-error-code="attr-defined, dict-item, assignment, union-attr" from __future__ import annotations from typing import TYPE_CHECKING from unittest.mock import Mock import pytest from tests.conftest import TEST_VIEWPORT_EXISTS from uiprotect.data.websocket import WSAction, WSSubscriptionMessage from uiprotect.exceptions import BadRequest if TYPE_CHECKING: from uiprotect.data import Liveview, Viewer @pytest.mark.skipif(not TEST_VIEWPORT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_viewer_set_liveview_invalid(viewer_obj: Viewer, liveview_obj: Liveview): viewer_obj.api.api_request.reset_mock() liveview = liveview_obj.update_from_dict({"id": "bad_id"}) with pytest.raises(BadRequest): await viewer_obj.set_liveview(liveview) assert not viewer_obj.api.api_request.called @pytest.mark.skipif(not TEST_VIEWPORT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_viewer_set_liveview_valid(viewer_obj: Viewer, liveview_obj: Liveview): viewer_obj.api.api_request.reset_mock() viewer_obj.api.emit_message = Mock() viewer_obj.liveview_id = "bad_id" await viewer_obj.set_liveview(liveview_obj) viewer_obj.api.api_request.assert_called_with( f"viewers/{viewer_obj.id}", method="patch", json={"liveview": liveview_obj.id}, ) # old/new is actually the same here since the client # generating the message is the one that changed it viewer_obj.api.emit_message.assert_called_with( WSSubscriptionMessage( action=WSAction.UPDATE, new_update_id=viewer_obj.api.bootstrap.last_update_id, changed_data={"liveview_id": liveview_obj.id}, old_obj=viewer_obj, new_obj=viewer_obj, ), ) uiprotect-6.1.0/tests/sample_data/000077500000000000000000000000001467310220200171455ustar00rootroot00000000000000uiprotect-6.1.0/tests/sample_data/__init__.py000066400000000000000000000000001467310220200212440ustar00rootroot00000000000000uiprotect-6.1.0/tests/sample_data/constants.py000066400000000000000000000014601467310220200215340ustar00rootroot00000000000000from __future__ import annotations import json import os from pathlib import Path from typing import Any UFP_SAMPLE_DIR = os.environ.get("UFP_SAMPLE_DIR") if UFP_SAMPLE_DIR: DATA_FILE = Path(UFP_SAMPLE_DIR) / "sample_constants.json" else: DATA_FILE = Path(__file__).parent / "sample_constants.json" class ConstantData: _data: dict[str, Any] | None = None def __getitem__(self, key): return self.data().__getitem__(key) def __contains__(self, key): return self.data().__contains__(key) def get(self, key, default=None): return self.data().get(key, default) def data(self): if self._data is None: with DATA_FILE.open(encoding="utf-8") as f: self._data = json.load(f) return self._data CONSTANTS = ConstantData() uiprotect-6.1.0/tests/sample_data/sample_bootstrap.json000066400000000000000000007247731467310220200234420ustar00rootroot00000000000000{ "authUserId": "4c5f03a8c8bd48ad8e066285", "accessKey": "8528571101220:340ff666bffb58bc404b859a:8f3f41a7b180b1ff7463fe4f7f13b528ac3d28668f25d0ecaa30c8e7888559e782b38d4335b40861030b75126eb7cea8385f3f9ab59dfa9a993e50757c277053", "cameras": [ { "isDeleting": false, "mac": "7CD8B328641B", "host": "192.168.164.242", "connectionHost": "192.168.102.63", "type": "UVC G4 Dome", "name": "Tfzotee Autbv", "upSince": 1641665036921, "uptime": null, "lastSeen": 1641666397032, "connectedSince": null, "state": "DISCONNECTED", "hardwareRevision": "10", "firmwareVersion": "4.47.15", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "524d13e.211221.1032", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1641666350274, "micVolume": 100, "isMicEnabled": true, "isRecording": false, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 0, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "355958af-1a08-4fb4-8619-d81fb88b5488", "eventStats": { "motion": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ], "recentHours": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Jsqti Dnyvdi", "enabled": true, "isRtspEnabled": true, "rtspAlias": "BViVMrQWSv8mS5M8", "width": 2688, "height": 1512, "fps": 24, "bitrate": 10000000, "minBitrate": 32000, "maxBitrate": 10000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Xlddbfz Idwiu", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 3000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Vpae Hsr", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": true, "isFlippedHorizontal": true, "isAutoRotateEnabled": true, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 50 }, "recordingSettings": { "prePaddingSecs": 2, "postPaddingSecs": 2, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 33888051, "txBytes": 1445351903, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639849398628, "recordingEnd": 1641666364031, "recordingStartLQ": 1639849398580, "recordingEndLQ": 1641666369049, "timelapseStart": 1639849398590, "timelapseEnd": 1641666309048, "timelapseStartLQ": 1639849398590, "timelapseEndLQ": 1641666294067 }, "storage": {}, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": true, "hasAec": true, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 48 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "76227b20c37b2ff0abdc9d4d", "isConnected": false, "platform": "s5l", "hasSpeaker": true, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Dome", "modelKey": "camera" }, { "isDeleting": false, "mac": "CE668E1CDADB", "host": "192.168.11.58", "connectionHost": "192.168.102.63", "type": "UVC G4 Doorbell", "name": "Xfqf Fydhbh", "upSince": 1642022726827, "uptime": null, "lastSeen": 1642023134515, "connectedSince": null, "state": "DISCONNECTED", "hardwareRevision": "21", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1642023134518, "micVolume": 100, "isMicEnabled": true, "isRecording": false, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 135, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": 1642022756933, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "97323b0e-c723-4688-b382-177c83ab6d82", "eventStats": { "motion": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ], "recentHours": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Oktg Brcmgt", "enabled": true, "isRtspEnabled": true, "rtspAlias": "d42VD60PnFGSWKAI", "width": 1600, "height": 1200, "fps": 30, "bitrate": 6000000, "minBitrate": 32000, "maxBitrate": 6000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Tgveb Xvipv", "enabled": true, "isRtspEnabled": true, "rtspAlias": "zJtlHLIysUUtrDFz", "width": 960, "height": 720, "fps": 30, "bitrate": 1200000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Zqpexd Eryy", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 480, "height": 360, "fps": 30, "bitrate": 200000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 200000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 0, "icrSensitivity": 0, "brightness": 51, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": "", "filterPort": 0, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 100 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 0.639, 0 ], [ 0.689, 0.554 ], [ 0.854, 0.575 ], [ 1, 0.601 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person" ] }, { "id": 2, "name": "Ihb Ses", "color": "#FF0075", "points": [ [ 0, 0 ], [ 0.389, 0 ], [ 0.356, 0.533 ], [ 0.648, 0.554 ], [ 0.841, 0.578 ], [ 1, 0.601 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 3607428, "txBytes": 131235466, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 81, "signalStrength": -71 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183850698, "recordingEnd": 1642023074549, "recordingStartLQ": 1639183850689, "recordingEndLQ": 1642023074607, "timelapseStart": 1639183850646, "timelapseEnd": 1642022576106, "timelapseStartLQ": 1639183850646, "timelapseEndLQ": 1642022576106 }, "storage": {}, "wifiQuality": 81, "wifiStrength": -71 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": false, "hasAec": true, "hasBattery": false, "hasBluetooth": true, "hasChime": true, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": true, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 45 ], "hasMotionZones": true, "hasLcdScreen": true, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": { "type": "CUSTOM_MESSAGE", "text": "Welcome | 01:57 PM | 25\u00b0F", "resetAt": null }, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "0777b5d342302079dc6b793d", "isConnected": false, "platform": "s5l", "hasSpeaker": true, "hasWifi": true, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Doorbell", "modelKey": "camera" }, { "isDeleting": false, "mac": "A3ABB5ABE298", "host": "192.168.23.22", "connectionHost": "192.168.102.63", "type": "UVC G4 Bullet", "name": "Ddxoxwb Ned", "upSince": 1640292872771, "uptime": null, "lastSeen": 1640293400431, "connectedSince": null, "state": "DISCONNECTED", "hardwareRevision": "5", "firmwareVersion": "4.47.13", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "0a55423.211124.717", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1640290945306, "micVolume": 100, "isMicEnabled": true, "isRecording": false, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "e12a9b6d-8a3b-411a-8ab8-34931517ed9d", "eventStats": { "motion": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ], "recentHours": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Wrdvta Imi", "enabled": true, "isRtspEnabled": true, "rtspAlias": "GNYYX1qS1sEQMErd", "width": 2688, "height": 1512, "fps": 24, "bitrate": 10000000, "minBitrate": 32000, "maxBitrate": 10000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Ijje Uqsh", "enabled": true, "isRtspEnabled": true, "rtspAlias": "CGIMLNSphQQKZT4Z", "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 3000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Tqc Jlwq", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 9642720, "txBytes": 394685474, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183865263, "recordingEnd": 1640293360356, "recordingStartLQ": 1639183865313, "recordingEndLQ": 1640293375316, "timelapseStart": 1639183865236, "timelapseEnd": 1640293205314, "timelapseStartLQ": 1639183865236, "timelapseEndLQ": 1640290918216 }, "storage": {}, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": false, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": true, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": false, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 48 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "56401f80d300adad7123c864", "isConnected": false, "platform": "s5l", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Bullet", "modelKey": "camera" }, { "isDeleting": false, "mac": "331AFA68BE00", "host": "192.168.10.22", "connectionHost": "192.168.102.63", "type": "UVC G4 Doorbell Pro", "name": "Mxxoxar Gyd", "upSince": 1642431757906, "uptime": 624002, "lastSeen": 1643055759906, "connectedSince": 1642431796280, "state": "CONNECTED", "hardwareRevision": "20", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055545876, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 200, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": 1643055447318, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "0cc40649-3501-4755-a144-14cfd449efe5", "eventStats": { "motion": { "today": 43, "average": 69, "lastDays": [ 57, 59, 126, 59, 65, 64, 53 ], "recentHours": [ 4, 2, 6, 5, 1, 0, 6, 4, 1, 5, 5, 0, 3 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": 27, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Evv Acjhsdh", "enabled": true, "isRtspEnabled": true, "rtspAlias": "OfJowIFFUBaYAtah", "width": 1600, "height": 1200, "fps": 30, "bitrate": 6000000, "minBitrate": 32000, "maxBitrate": 6000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Frjg Ciu", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 960, "height": 720, "fps": 30, "bitrate": 1200000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Huvqtqm Tbpibvh", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 480, "height": 360, "fps": 30, "bitrate": 200000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 200000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 3, "videoId": "video4", "name": "Ldmm Hnqdcg", "enabled": true, "isRtspEnabled": true, "rtspAlias": "VWJGXSBiRS7WbQGL", "width": 1600, "height": 1200, "fps": 2, "bitrate": 1000000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": null, "minMotionAdaptiveBitRate": null, "fpsValues": [ 1, 2 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": "", "filterPort": 0, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Plbxuwl Xlypwre", "color": "#AB46BC", "points": [ [ 0.569, 0 ], [ 0.604, 0.537 ], [ 1, 0.565 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.276, 0 ] ], "sensitivity": 50, "objectTypes": [ "person" ] }, { "id": 2, "name": "Oazunxs Ibxw", "color": "#586CED", "points": [ [ 0, 0 ], [ 0.369, 0 ], [ 0.358, 0.543 ], [ 1, 0.531 ], [ 1, 1 ], [ 0, 0.978 ] ], "sensitivity": 50, "objectTypes": [ "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 7916684198, "txBytes": 270419791080, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -52 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1641921049599, "recordingEnd": 1643055770699, "recordingStartLQ": 1641921049574, "recordingEndLQ": 1643055785757, "timelapseStart": 1641921049594, "timelapseEnd": 1643055460715, "timelapseStartLQ": 1641921049594, "timelapseEndLQ": 1643055520725 }, "storage": { "used": 427349245952, "rate": 642.30288184191 }, "wifiQuality": 100, "wifiStrength": -52 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": false, "hasAec": true, "hasBattery": false, "hasBluetooth": true, "hasChime": true, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": true, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default" ], "videoModeMaxFps": [], "hasMotionZones": true, "hasLcdScreen": true, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": true, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": { "type": "CUSTOM_MESSAGE", "text": "Welcome | 03:23 PM | 25\u00b0F", "resetAt": null }, "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -52, "ssid": "Mortis Camera" }, "lenses": [ { "id": 2, "video": { "recordingStart": 1642357077629, "recordingEnd": 1643055747396, "recordingStartLQ": null, "recordingEndLQ": null, "timelapseStart": 1641921049317, "timelapseEnd": 1643055438229, "timelapseStartLQ": null, "timelapseEndLQ": null } } ], "id": "1c9a2db4df6efda47a3509be", "isConnected": true, "platform": "s5l", "hasSpeaker": true, "hasWifi": true, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Doorbell Pro", "modelKey": "camera" }, { "isDeleting": false, "mac": "C1E5752D2D9B", "host": "192.168.16.186", "connectionHost": "192.168.102.63", "type": "UVC AI Bullet", "name": "Dheril Fsx", "upSince": 1641929381901, "uptime": 1126378, "lastSeen": 1643055759901, "connectedSince": 1642896021779, "state": "CONNECTED", "hardwareRevision": "3", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1820", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055575971, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 0, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "dbb18c8b-a7f1-4759-905f-e5b52e9a614d", "eventStats": { "motion": { "today": 22, "average": 18, "lastDays": [ 36, 6, 0, 3, 3, 36, 44 ], "recentHours": [ 3, 1, 12, 0, 1, 0, 5, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": 1000 }, "channels": [ { "id": 0, "videoId": "video1", "name": "Wlixnbo Nykk", "enabled": true, "isRtspEnabled": true, "rtspAlias": "AZWxWsX2PSEMI6d2", "width": 2688, "height": 1512, "fps": 30, "bitrate": 10000000, "minBitrate": 32000, "maxBitrate": 10000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Jilbyey Dtdgebd", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 1280, "height": 720, "fps": 30, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 3000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Hevuxf Sre", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 30, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": true, "isFlippedHorizontal": true, "isAutoRotateEnabled": true, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": "wall" }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 21294840858, "txBytes": 1002784215600, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640288743861, "recordingEnd": 1643055770802, "recordingStartLQ": 1640288744338, "recordingEndLQ": 1643055785778, "timelapseStart": 1640288743858, "timelapseEnd": 1643055480800, "timelapseStartLQ": 1640288743858, "timelapseEndLQ": 1643055095797 }, "storage": { "used": 1987496116224, "rate": 708.231920183599 }, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": true, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 30, 50 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "586ab7c2bb6423c3fdd47e95", "isConnected": true, "platform": "cv2x", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "AI Bullet", "modelKey": "camera" }, { "isDeleting": false, "mac": "49512F7958AE", "host": "192.168.127.173", "connectionHost": "192.168.102.63", "type": "UVC G4 Doorbell", "name": "Ftjj Bjbx", "upSince": 1642431757906, "uptime": 624002, "lastSeen": 1643055759906, "connectedSince": 1642431800250, "state": "CONNECTED", "hardwareRevision": "22", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055542532, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 43, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": 1641571914529, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "3e29e07a-13c2-4514-b610-8376df41c2ad", "eventStats": { "motion": { "today": 166, "average": 363, "lastDays": [ 272, 397, 479, 350, 326, 415, 307 ], "recentHours": [ 13, 32, 14, 18, 15, 7, 27, 17, 6, 4, 6, 2, 3 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": 25.9, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Hvx Zle", "enabled": true, "isRtspEnabled": true, "rtspAlias": "CLsJTPVhWOq4OZQa", "width": 1600, "height": 1200, "fps": 30, "bitrate": 3000000, "minBitrate": 32000, "maxBitrate": 6000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Nckpktj Mvqte", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 960, "height": 720, "fps": 30, "bitrate": 1200000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Gubbdb Tvvy", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 480, "height": 360, "fps": 30, "bitrate": 200000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 200000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": "", "filterPort": 0, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0.575, 0.451 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0.53 ], [ 0.487, 0.535 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 4499323888, "txBytes": 145588953564, "wifi": { "channel": 6, "frequency": 2437, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -62 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640308840567, "recordingEnd": 1643055771020, "recordingStartLQ": 1640308840563, "recordingEndLQ": 1643055786085, "timelapseStart": 1640308840533, "timelapseEnd": 1643055521005, "timelapseStartLQ": 1640308840533, "timelapseEndLQ": 1643055146038 }, "storage": { "used": 747324309504, "rate": 342.122831820542 }, "wifiQuality": 100, "wifiStrength": -62 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": false, "hasAec": true, "hasBattery": false, "hasBluetooth": true, "hasChime": true, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": true, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 45 ], "hasMotionZones": true, "hasLcdScreen": true, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": { "type": "CUSTOM_MESSAGE", "text": "Use Other Door | 25\u00b0F", "resetAt": null }, "wifiConnectionState": { "channel": 6, "frequency": 2437, "phyRate": 43, "signalQuality": 100, "signalStrength": -62, "ssid": "Mortis Camera" }, "lenses": [], "id": "e2ff0ade6be0f2a2beb61869", "isConnected": true, "platform": "s5l", "hasSpeaker": true, "hasWifi": true, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Doorbell", "modelKey": "camera" }, { "isDeleting": false, "mac": "CF0C20B53D5E", "host": "192.168.222.251", "connectionHost": "192.168.102.63", "type": "UVC G4 Pro", "name": "Yzlrc Duo", "upSince": 1641929375904, "uptime": 1126384, "lastSeen": 1643055759904, "connectedSince": 1643053361454, "state": "CONNECTED", "hardwareRevision": "7", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643038873769, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "7b4e5226-215f-44d1-bbf4-cdfe64599cdb", "eventStats": { "motion": { "today": 1, "average": 6, "lastDays": [ 7, 5, 5, 4, 11, 2, 8 ], "recentHours": [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": 1000 }, "channels": [ { "id": 0, "videoId": "video1", "name": "Byrhmtr Oklylbv", "enabled": true, "isRtspEnabled": true, "rtspAlias": "NSRPJJY4ePQUVZfZ", "width": 3840, "height": 2160, "fps": 24, "bitrate": 16000000, "minBitrate": 32000, "maxBitrate": 16000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Abxb Nmhhzd", "enabled": true, "isRtspEnabled": true, "rtspAlias": "VEFUSSOvpL1KVRSj", "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 4000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Mkoo Jbzf", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 2, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": false, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0.138 ], [ 0.466, 0.07 ], [ 0.601, 0.148 ], [ 1, 0.456 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 18763321757, "txBytes": 700072880391, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852977, "recordingEnd": 1643055770002, "recordingStartLQ": 1639183852992, "recordingEndLQ": 1643055785014, "timelapseStart": 1639183852941, "timelapseEnd": 1643055599988, "timelapseStartLQ": 1639183852941, "timelapseEndLQ": 1643055105008 }, "storage": { "used": 1603096543232, "rate": 415.108794037914 }, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": true, "canMagicZoom": false, "canOpticalZoom": true, "canTouchFocus": true, "hasAccelerometer": false, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": false, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 50 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "f0cd15b8bed9e38899286a8c", "isConnected": true, "platform": "s5l", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Pro", "modelKey": "camera" }, { "isDeleting": false, "mac": "D3BA2C5D0032", "host": "192.168.6.142", "connectionHost": "192.168.102.63", "type": "UVC G4 Pro", "name": "Nvs Bwd", "upSince": 1641929376903, "uptime": 1126383, "lastSeen": 1643055759903, "connectedSince": 1643053372503, "state": "CONNECTED", "hardwareRevision": "7", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1642803682789, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "36ad9efc-8b8b-4f47-815e-14f65a46a6e3", "eventStats": { "motion": { "today": 0, "average": 1, "lastDays": [ 0, 0, 2, 0, 0, 0, 11 ], "recentHours": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": 1000 }, "channels": [ { "id": 0, "videoId": "video1", "name": "Ncfv Xvxtzgd", "enabled": true, "isRtspEnabled": true, "rtspAlias": "lKaCXTMDTCJQQKum", "width": 3840, "height": 2160, "fps": 24, "bitrate": 16000000, "minBitrate": 32000, "maxBitrate": 16000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Jbsr Fbqsp", "enabled": true, "isRtspEnabled": true, "rtspAlias": "OpbJXC6TNvQNIXYN", "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 4000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Ske Yea", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 2, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": false, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0.453 ], [ 0.682, 0 ], [ 0.844, 0 ], [ 0.708, 0.431 ], [ 0.468, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 17442683098, "txBytes": 653254998457, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183853280, "recordingEnd": 1643055771104, "recordingStartLQ": 1639183853258, "recordingEndLQ": 1643055786019, "timelapseStart": 1639183853279, "timelapseEnd": 1643055611101, "timelapseStartLQ": 1639183853279, "timelapseEndLQ": 1643055116058 }, "storage": { "used": 1471026298880, "rate": 335.261879469184 }, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": true, "canMagicZoom": false, "canOpticalZoom": true, "canTouchFocus": true, "hasAccelerometer": false, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": false, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 50 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "ab3e27f2d55fad817dac7bb9", "isConnected": true, "platform": "s5l", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Pro", "modelKey": "camera" }, { "isDeleting": false, "mac": "25546150ED3E", "host": "192.168.3.158", "connectionHost": "192.168.102.63", "type": "UVC G4 Pro", "name": "Zycyj Vxvp", "upSince": 1641929377903, "uptime": 1126382, "lastSeen": 1643055759903, "connectedSince": 1643053361126, "state": "CONNECTED", "hardwareRevision": "7", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643008086650, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": false, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "c370d035-8003-4b70-85e9-ba5f460cac09", "eventStats": { "motion": { "today": 1, "average": 5, "lastDays": [ 0, 6, 2, 1, 1, 6, 20 ], "recentHours": [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": 1000 }, "channels": [ { "id": 0, "videoId": "video1", "name": "Txsfih Lvjpxxn", "enabled": true, "isRtspEnabled": true, "rtspAlias": "gXCHWI1VITVZ9PwY", "width": 3840, "height": 2160, "fps": 24, "bitrate": 16000000, "minBitrate": 32000, "maxBitrate": 16000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Rcidfx Iedpmai", "enabled": true, "isRtspEnabled": true, "rtspAlias": "PWK9O5JVX0VnVGBg", "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 4000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Jtrh Mij", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 3, "icrSensitivity": 1, "brightness": 50, "contrast": 54, "hue": 52, "saturation": 51, "sharpness": 50, "denoise": 54, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "touch", "focusPosition": 0, "touchFocusX": 865, "touchFocusY": 213, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": false, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0.201, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0.414, 1 ], [ 0.293, 0.573 ], [ 0.444, 0.478 ], [ 0.432, 0.363 ], [ 0.277, 0.456 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 27521967465, "txBytes": 1497713862841, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852998, "recordingEnd": 1643055750205, "recordingStartLQ": 1639183853004, "recordingEndLQ": 1643055780152, "timelapseStart": 1639183852995, "timelapseEnd": 1643055600214, "timelapseStartLQ": 1639183852995, "timelapseEndLQ": 1643055100356 }, "storage": { "used": 3005403365376, "rate": 832.407819806798 }, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": true, "canMagicZoom": false, "canOpticalZoom": true, "canTouchFocus": true, "hasAccelerometer": false, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": false, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 50 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "4a333d993fe8e2e8472bc901", "isConnected": true, "platform": "s5l", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Pro", "modelKey": "camera" }, { "isDeleting": false, "mac": "3F9EDAD1F6C4", "host": "192.168.29.88", "connectionHost": "192.168.102.63", "type": "UVC G4 Pro", "name": "Fxdmvtc Gdo", "upSince": 1641929376904, "uptime": 1126383, "lastSeen": 1643055759904, "connectedSince": 1642537084909, "state": "CONNECTED", "hardwareRevision": "10", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055447656, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 1000, "hdrMode": false, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "242b87f0-f0e1-402e-a48a-2a0b5a16d850", "eventStats": { "motion": { "today": 88, "average": 160, "lastDays": [ 156, 105, 202, 171, 131, 173, 187 ], "recentHours": [ 3, 10, 14, 6, 9, 0, 9, 6, 9, 9, 6, 2, 2 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": 1000 }, "channels": [ { "id": 0, "videoId": "video1", "name": "Lwp Issyqxp", "enabled": true, "isRtspEnabled": true, "rtspAlias": "ZAOBJFOXCUNAPQEz", "width": 3840, "height": 2160, "fps": 24, "bitrate": 16000000, "minBitrate": 32000, "maxBitrate": 16000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Xedhihw Ryokoqa", "enabled": true, "isRtspEnabled": true, "rtspAlias": "honlCJQSGucYfRfX", "width": 1280, "height": 720, "fps": 24, "bitrate": 2000000, "minBitrate": 32000, "maxBitrate": 4000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Ldgqgxy Ouduc", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 24, "bitrate": 300000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 300000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "autoFilterOnly", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 52, "sharpness": 51, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "touch", "focusPosition": 0, "touchFocusX": 437, "touchFocusY": 0, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": null, "filterPort": null, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": false, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0.318 ], [ 0.21, 0.158 ], [ 0.435, 0.055 ], [ 0.638, 0.07 ], [ 1, 0.203 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [ "person", "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 28446700467, "txBytes": 1583875810916, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852906, "recordingEnd": 1643055755100, "recordingStartLQ": 1639183852911, "recordingEndLQ": 1643055784968, "timelapseStart": 1639183852888, "timelapseEnd": 1643055575165, "timelapseStartLQ": 1639183852888, "timelapseEndLQ": 1643055285277 }, "storage": { "used": 5012226834432, "rate": 1753.16626544334 }, "wifiQuality": 50, "wifiStrength": 0 }, "featureFlags": { "canAdjustIrLedLevel": true, "canMagicZoom": false, "canOpticalZoom": true, "canTouchFocus": true, "hasAccelerometer": false, "hasAec": false, "hasBattery": false, "hasBluetooth": false, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": false, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": false, "hasWifi": false, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default", "highFps" ], "videoModeMaxFps": [ 24, 50 ], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": null, "signalStrength": null, "ssid": null }, "lenses": [], "id": "c462c07dbd63ad805a7318c7", "isConnected": true, "platform": "s5l", "hasSpeaker": false, "hasWifi": false, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Pro", "modelKey": "camera" }, { "isDeleting": false, "mac": "2BE358A7CE14", "host": "192.168.186.25", "connectionHost": "192.168.102.63", "type": "UVC G4 Instant", "name": "Eohx Mrkciz", "upSince": 1642456952904, "uptime": 598807, "lastSeen": 1643055759904, "connectedSince": 1642456986161, "state": "CONNECTED", "hardwareRevision": "11", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1757", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055573501, "micVolume": 100, "isMicEnabled": true, "isRecording": false, "isWirelessUplinkEnabled": true, "isMotionDetected": false, "isSmartDetected": false, "phyRate": 200, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 0, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": null, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "857fa0fd-4a74-4212-8b75-f9ce40589250", "eventStats": { "motion": { "today": 43, "average": 51, "lastDays": [ 75, 39, 36, 43, 43, 90, 35 ], "recentHours": [ 9, 0, 8, 2, 8, 1, 6, 1, 0, 0, 1, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": null, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Ajkq Ygq", "enabled": true, "isRtspEnabled": true, "rtspAlias": "ybEZtAOEnPBFDHBN", "width": 2688, "height": 1512, "fps": 30, "bitrate": 10000000, "minBitrate": 32000, "maxBitrate": 10000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 2000000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video2", "name": "Etjo Bfa", "enabled": true, "isRtspEnabled": true, "rtspAlias": "66XxksASK5XGRKEM", "width": 1280, "height": 720, "fps": 30, "bitrate": 1500000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video3", "name": "Frmeln Grau", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 640, "height": 360, "fps": 30, "bitrate": 200000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 200000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": true, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": "wall" }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": "", "filterPort": 0, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": false, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 100 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "detections", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50, "objectTypes": [] } ], "smartDetectLines": [], "stats": { "rxBytes": 8155758338, "txBytes": 346703002345, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -45 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639219284079, "recordingEnd": 1643055573528, "recordingStartLQ": 1639219283987, "recordingEndLQ": 1643055574508, "timelapseStart": 1639219284030, "timelapseEnd": 1643055513560, "timelapseStartLQ": 1639219284030, "timelapseEndLQ": 1643055533528 }, "storage": { "used": 39728447488, "rate": 13.3032332410829 }, "wifiQuality": 100, "wifiStrength": -45 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": true, "hasAec": true, "hasBattery": false, "hasBluetooth": true, "hasChime": false, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": false, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": true, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default" ], "videoModeMaxFps": [], "hasMotionZones": true, "hasLcdScreen": false, "mountPositions": [], "smartDetectTypes": [], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": false, "privacyMaskCapability": { "maxMasks": 4, "rectangleOnly": true }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": false }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": {}, "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -45, "ssid": "Mortis Camera" }, "lenses": [], "id": "1ee3895eb4ef2170046f9f2c", "isConnected": true, "platform": "sav530q", "hasSpeaker": true, "hasWifi": true, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Instant", "modelKey": "camera" } ], "users": [ { "permissions": [], "lastLoginIp": null, "lastLoginTime": null, "isOwner": true, "enableNotifications": false, "settings": { "flags": {} }, "groups": [ "b061186823695fb901973177" ], "alertRules": [], "notificationsV2": { "state": "custom", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [ { "inheritFromParent": true, "motion": [], "person": [], "vehicle": [], "camera": "61b3f5c7033ea703e7000424", "trigger": { "when": "always", "location": "away", "schedules": [] } } ], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "fe4c12ae2c1348edb7854e2f", "hasAcceptedInvite": true, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "cloudAccount": { "firstName": "Qpvfly", "lastName": "Ikjzilt", "email": "QhoFvCv@example.com", "profileImg": null, "user": "fe4c12ae2c1348edb7854e2f", "id": "9efc4511-4539-4402-9581-51cee8b65cf5", "cloudId": "9efc4511-4539-4402-9581-51cee8b65cf5", "name": "Qpvfly Ikjzilt", "modelKey": "cloudIdentity" }, "name": "Qpvfly Ikjzilt", "firstName": "Qpvfly", "lastName": "Ikjzilt", "email": "QhoFvCv@example.com", "localUsername": "QhoFvCv", "modelKey": "user" }, { "permissions": [], "lastLoginIp": null, "lastLoginTime": null, "isOwner": false, "enableNotifications": false, "settings": null, "groups": [ "a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177" ], "alertRules": [], "notificationsV2": { "state": "auto", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "dcaef9cb8aed05c7db658a46", "hasAcceptedInvite": false, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "cloudAccount": null, "name": "Uxqg Wcbz", "firstName": "Uxqg", "lastName": "Wcbz", "email": "epHDEhE@example.com", "localUsername": "epHDEhE", "modelKey": "user" }, { "permissions": [ "liveview:*:d65bb41c14d6aa92bfa4a6d1", "liveview:*:49bbb5005424a0d35152671a", "liveview:*:b28c38f1220f6b43f3930dff", "liveview:*:b9861b533a87ea639fa4d438" ], "lastLoginIp": null, "lastLoginTime": null, "isOwner": false, "enableNotifications": false, "settings": { "flags": {}, "web": { "dewarp": { "61ddb66b018e2703e7008c19": { "dewarp": false, "state": { "pan": 0, "tilt": -1.5707963267948966, "zoom": 1.5707963267948966, "panning": 0, "tilting": 0 } } }, "liveview.includeGlobal": true, "elements.events_viewmode": "grid", "elements.viewmode": "list" } }, "groups": [ "b061186823695fb901973177" ], "location": { "isAway": true, "latitude": null, "longitude": null }, "alertRules": [], "notificationsV2": { "state": "custom", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [ { "inheritFromParent": true, "motion": [], "camera": "61b3f5c703d2a703e7000427", "trigger": { "when": "always", "location": "away", "schedules": [] } }, { "inheritFromParent": true, "motion": [], "person": [], "vehicle": [], "camera": "61b3f5c7033ea703e7000424", "trigger": { "when": "always", "location": "away", "schedules": [] } } ], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "4c5f03a8c8bd48ad8e066285", "hasAcceptedInvite": false, "allPermissions": [ "liveview:*:d65bb41c14d6aa92bfa4a6d1", "liveview:*:49bbb5005424a0d35152671a", "liveview:*:b28c38f1220f6b43f3930dff", "liveview:*:b9861b533a87ea639fa4d438", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "cloudAccount": null, "name": "Ptcmsdo Tfiyoep", "firstName": "Ptcmsdo", "lastName": "Tfiyoep", "email": "EQAoXL@example.com", "localUsername": "EQAoXL", "modelKey": "user" }, { "permissions": [], "lastLoginIp": null, "lastLoginTime": null, "isOwner": false, "enableNotifications": false, "settings": { "flags": {}, "web": { "dewarp": { "61c4d1db02c82a03e700429c": { "dewarp": false, "state": { "pan": 0, "tilt": 0, "zoom": 1.5707963267948966, "panning": 0, "tilting": 0 } } }, "liveview.includeGlobal": true } }, "groups": [ "a7f3b2eb71b4c4e56f1f45ac" ], "alertRules": [], "notificationsV2": { "state": "auto", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "bc3dd633553907952a6fe20d", "hasAcceptedInvite": false, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ], "cloudAccount": null, "name": "Evdxou Zgyv", "firstName": "Evdxou", "lastName": "Zgyv", "email": "FMZuD@example.com", "localUsername": "FMZuD", "modelKey": "user" }, { "permissions": [], "lastLoginIp": null, "lastLoginTime": null, "isOwner": false, "enableNotifications": false, "settings": null, "groups": [ "a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177" ], "alertRules": [], "notificationsV2": { "state": "auto", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "adec5334b69f56f6a6c47520", "hasAcceptedInvite": false, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "cloudAccount": null, "name": "Qpv Elqfgq", "firstName": "Qpv", "lastName": "Elqfgq", "email": "xdr@example.com", "localUsername": "xdr", "modelKey": "user" }, { "permissions": [], "lastLoginIp": null, "lastLoginTime": null, "isOwner": false, "enableNotifications": false, "settings": null, "groups": [ "a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177" ], "alertRules": [], "notificationsV2": { "state": "auto", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "8593657a25b7826a4288b6af", "hasAcceptedInvite": false, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "cloudAccount": null, "name": "Sgpy Ooevsme", "firstName": "Sgpy", "lastName": "Ooevsme", "email": "WQJNT@example.com", "localUsername": "WQJNT", "modelKey": "user" }, { "permissions": [], "isOwner": false, "enableNotifications": false, "groups": [ "a7f3b2eb71b4c4e56f1f45ac" ], "alertRules": [], "notificationsV2": { "state": "off", "motionNotifications": { "trigger": { "when": "inherit", "location": "away", "schedules": [] }, "cameras": [], "doorbells": [], "lights": [], "doorlocks": [], "sensors": [] }, "systemNotifications": {} }, "featureFlags": { "notificationsV2": true }, "id": "abf647aed3650a781ceba13f", "hasAcceptedInvite": false, "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ], "cloudAccount": null, "name": "Yiiyq Glx", "firstName": "Yiiyq", "lastName": "Glx", "email": "fBjmm@example.com", "localUsername": "fBjmm", "modelKey": "user" } ], "groups": [ { "name": "Kubw Xnbb", "permissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ], "type": "preset", "isDefault": true, "id": "b061186823695fb901973177", "modelKey": "group" }, { "name": "Pmbrvp Wyzqs", "permissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ], "type": "preset", "isDefault": false, "id": "a7f3b2eb71b4c4e56f1f45ac", "modelKey": "group" } ], "liveviews": [ { "name": "Default", "isDefault": true, "isGlobal": true, "layout": 12, "slots": [ { "cameras": [ "4a333d993fe8e2e8472bc901" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "1ee3895eb4ef2170046f9f2c" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "0777b5d342302079dc6b793d" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "c462c07dbd63ad805a7318c7" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "56401f80d300adad7123c864" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "76227b20c37b2ff0abdc9d4d" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "1c9a2db4df6efda47a3509be" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "586ab7c2bb6423c3fdd47e95" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "e2ff0ade6be0f2a2beb61869" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "ab3e27f2d55fad817dac7bb9" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "f0cd15b8bed9e38899286a8c" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [], "cycleMode": "time", "cycleInterval": 10 } ], "owner": "fe4c12ae2c1348edb7854e2f", "id": "bf41f6b5ba0ddd046eeb1c98", "modelKey": "liveview" }, { "name": "Yisz Ovrhoka", "isDefault": false, "isGlobal": true, "layout": 1, "slots": [ { "cameras": [ "1c9a2db4df6efda47a3509be", "e2ff0ade6be0f2a2beb61869", "c462c07dbd63ad805a7318c7", "4a333d993fe8e2e8472bc901", "ab3e27f2d55fad817dac7bb9", "f0cd15b8bed9e38899286a8c" ], "cycleMode": "motion", "cycleInterval": 10 } ], "owner": "4c5f03a8c8bd48ad8e066285", "id": "d65bb41c14d6aa92bfa4a6d1", "modelKey": "liveview" }, { "name": "Ozbz Qle", "isDefault": false, "isGlobal": true, "layout": 1, "slots": [ { "cameras": [ "1c9a2db4df6efda47a3509be" ], "cycleMode": "time", "cycleInterval": 10 } ], "owner": "4c5f03a8c8bd48ad8e066285", "id": "b9861b533a87ea639fa4d438", "modelKey": "liveview" }, { "name": "Axehzr Dhxebqg", "isDefault": false, "isGlobal": true, "layout": 4, "slots": [ { "cameras": [ "1c9a2db4df6efda47a3509be" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "e2ff0ade6be0f2a2beb61869" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "c462c07dbd63ad805a7318c7" ], "cycleMode": "time", "cycleInterval": 10 }, { "cameras": [ "586ab7c2bb6423c3fdd47e95" ], "cycleMode": "time", "cycleInterval": 10 } ], "owner": "4c5f03a8c8bd48ad8e066285", "id": "49bbb5005424a0d35152671a", "modelKey": "liveview" }, { "name": "Jbrkb Urqq", "isDefault": false, "isGlobal": true, "layout": 1, "slots": [ { "cameras": [ "1ee3895eb4ef2170046f9f2c" ], "cycleMode": "time", "cycleInterval": 10 } ], "owner": "4c5f03a8c8bd48ad8e066285", "id": "b28c38f1220f6b43f3930dff", "modelKey": "liveview" } ], "schedules": [], "nvr": { "mac": "4B8290F6D7A3", "host": "192.168.102.63", "name": "Uiiji Ryoyo", "canAutoUpdate": true, "isStatsGatheringEnabled": true, "timezone": "America/New_York", "version": "1.21.0-beta.3", "ucoreVersion": "2.3.26", "firmwareVersion": "2.3.10", "uiVersion": null, "hardwarePlatform": "al324", "ports": { "ump": 7449, "http": 7080, "https": 7443, "rtsp": 7447, "rtsps": 7441, "rtmp": 1935, "devicesWss": 7442, "cameraHttps": 7444, "cameraTcp": 7877, "liveWs": 7445, "liveWss": 7446, "tcpStreams": 7448, "playback": 7450, "emsCLI": 7440, "emsLiveFLV": 7550, "cameraEvents": 7551, "tcpBridge": 7888, "ucore": 11081, "discoveryClient": 0 }, "uptime": 681778000, "lastSeen": 1643055784651, "isUpdating": false, "lastUpdateAt": null, "isStation": false, "enableAutomaticBackups": true, "enableStatsReporting": false, "isSshEnabled": false, "errorCode": null, "releaseChannel": "beta", "ssoChannel": null, "hosts": [ "192.168.102.63" ], "enableBridgeAutoAdoption": true, "hardwareId": "4133b915-976c-4945-9da1-85a8297dc2e9", "hardwareRevision": "113-03137-22", "hostType": 59936, "hostShortname": "UNVRPRO", "isHardware": true, "isWirelessUplinkEnabled": false, "timeFormat": "24h", "temperatureUnit": "C", "recordingRetentionDurationMs": null, "enableCrashReporting": true, "disableAudio": false, "analyticsData": "anonymous", "anonymousDeviceId": "f7babe76-b29f-4786-ac5f-c4df3943287c", "cameraUtilization": 30, "isRecycling": false, "avgMotions": [ 11.14, 7.29, 6, 6.14, 5.71, 8.14, 17.29, 26.14, 19.86, 35, 33.43, 52.71, 46.71, 41.29, 28.14, 31.43, 33.71, 43.29, 58, 61.43, 39.29, 33.14, 16, 15 ], "disableAutoLink": false, "skipFirmwareUpdate": false, "wifiSettings": { "useThirdPartyWifi": false, "ssid": null, "password": null }, "locationSettings": { "isAway": true, "isGeofencingEnabled": false, "latitude": 41.4519, "longitude": -81.921, "radius": 200 }, "featureFlags": { "beta": false, "dev": false, "notificationsV2": true }, "systemInfo": { "cpu": { "averageLoad": 6, "temperature": 68 }, "memory": { "available": 6388164, "free": 102208, "total": 8163024 }, "storage": { "available": 13846957756416, "isRecycling": false, "size": 31855989432320, "type": "raid", "used": 16409797353472, "devices": [ { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true } ] }, "tmpfs": { "available": 960028, "total": 1048576, "used": 88548, "path": "/var/opt/unifi-protect/tmp" } }, "doorbellSettings": { "defaultMessageText": "Welcome", "defaultMessageResetTimeoutMs": 60000, "customMessages": [ "Come In!", "Use Other Door" ], "allMessages": [ { "type": "LEAVE_PACKAGE_AT_DOOR", "text": "LEAVE PACKAGE AT DOOR" }, { "type": "DO_NOT_DISTURB", "text": "DO NOT DISTURB" }, { "type": "CUSTOM_MESSAGE", "text": "Come In!" }, { "type": "CUSTOM_MESSAGE", "text": "Use Other Door" } ] }, "smartDetectAgreement": { "status": "agreed", "lastUpdateAt": 1606964227734 }, "storageStats": { "utilization": 51.623801166344975, "capacity": 6304614230, "remainingCapacity": 3049932715, "recordingSpace": { "total": 31787269955584, "used": 16409797038080, "available": 15377472917504 }, "storageDistribution": { "recordingTypeDistributions": [ { "recordingType": "rotating", "size": 15332774950544, "percentage": 93.45392305589237 }, { "recordingType": "timelapse", "size": 24696061952, "percentage": 0.15052356020942406 }, { "recordingType": "detections", "size": 1049304058224, "percentage": 6.395553383898204 } ], "resolutionDistributions": [ { "resolution": "4K", "size": 11108932911104, "percentage": 34.94774142801942 }, { "resolution": "HD", "size": 5297842159616, "percentage": 16.666552890571023 }, { "resolution": "free", "size": 15380494884864, "percentage": 48.385705681409554 } ] } }, "id": "2435ff5ab300d119d704bbe3", "isAway": true, "isSetup": true, "network": "Ethernet", "type": "UNVR-PRO", "upSince": 1642374007037, "isRecordingDisabled": false, "isRecordingMotionOnly": false, "maxCameraCapacity": { "4K": 20, "2K": 30, "HD": 60 }, "modelKey": "nvr" }, "lastUpdateId": "ebf25bac-d5a1-4f1d-a0ee-74c15981eb70", "viewers": [ { "mac": "85EEA7DF1601", "host": "192.168.27.69", "connectionHost": "192.168.102.63", "type": "UP Viewport", "name": "Aiyhwrk Fyoqval", "upSince": 1642661973905, "uptime": 393786, "lastSeen": 1643055759905, "connectedSince": 1642672730441, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.2.54", "latestFirmwareVersion": "1.2.54", "firmwareBuild": "dcfb16f3.210907.625", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "streamLimit": 16, "softwareVersion": "1.2.54", "wiredConnectionState": { "phyRate": 1000 }, "liveview": "bf41f6b5ba0ddd046eeb1c98", "id": "081c58d13ad7e198d3dddffa", "isConnected": true, "marketName": "UP ViewPort", "modelKey": "viewer" } ], "lights": [ { "mac": "534D8B001B14", "host": "192.168.234.163", "connectionHost": "192.168.102.63", "type": "UP FloodLight", "name": "Icxei Irdq", "upSince": 1638128967900, "uptime": 4926792, "lastSeen": 1643055759900, "connectedSince": 1642902624923, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.9.3", "latestFirmwareVersion": "1.9.3", "firmwareBuild": "g990c553.211105.251", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "isPirMotionDetected": false, "lastMotion": 1643055491157, "isDark": false, "isLightOn": false, "isLocating": false, "wiredConnectionState": { "phyRate": 100 }, "lightDeviceSettings": { "isIndicatorEnabled": false, "ledLevel": 6, "luxSensitivity": "medium", "pirDuration": 120000, "pirSensitivity": 46 }, "lightOnSettings": { "isLedForceOn": false }, "lightModeSettings": { "mode": "off", "enableAt": "fulltime" }, "camera": "1c9a2db4df6efda47a3509be", "id": "3ada785d6626c88d7d52446a", "isConnected": true, "isCameraPaired": true, "marketName": "UP FloodLight", "modelKey": "light" } ], "bridges": [ { "mac": "A28D0DB15AE1", "host": "192.168.231.68", "connectionHost": "192.168.102.63", "type": "UFP-UAP-B", "name": "Sffde Gxcaqe", "upSince": 1639807977891, "uptime": 3247782, "lastSeen": 1643055759891, "connectedSince": 1642374159304, "state": "CONNECTED", "hardwareRevision": 19, "firmwareVersion": "0.3.1", "latestFirmwareVersion": null, "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "wiredConnectionState": { "phyRate": null }, "id": "1f5a055254fb9169d7536fb9", "isConnected": true, "platform": "mt7621", "modelKey": "bridge" }, { "mac": "C65C557CCA95", "host": "192.168.87.68", "connectionHost": "192.168.102.63", "type": "UFP-UAP-B", "name": "Axiwj Bbd", "upSince": 1641257260772, "uptime": null, "lastSeen": 1643052750862, "connectedSince": 1643052754695, "state": "CONNECTED", "hardwareRevision": 19, "firmwareVersion": "0.3.1", "latestFirmwareVersion": null, "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "wiredConnectionState": { "phyRate": null }, "id": "e6901e3665a4c0eab0d9c1a5", "isConnected": true, "platform": "mt7621", "modelKey": "bridge" } ], "sensors": [ { "mac": "4191A8E35F39", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-SENSE", "name": "Yeahe Anfa", "upSince": 1642991171327, "uptime": null, "lastSeen": 1643054753862, "connectedSince": 1643054778327, "state": "CONNECTED", "hardwareRevision": 6, "firmwareVersion": "1.0.2", "latestFirmwareVersion": "1.0.2", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "isMotionDetected": false, "mountType": "door", "leakDetectedAt": null, "tamperingDetectedAt": null, "isOpened": false, "openStatusChangedAt": 1643055549536, "alarmTriggeredAt": null, "motionDetectedAt": 1643055565002, "wiredConnectionState": { "phyRate": null }, "stats": { "light": { "value": 20, "status": "neutral" }, "humidity": { "value": 29, "status": "neutral" }, "temperature": { "value": 16.95, "status": "neutral" } }, "bluetoothConnectionState": { "signalQuality": 37, "signalStrength": -75 }, "batteryStatus": { "percentage": 80, "isLow": false }, "alarmSettings": { "isEnabled": false }, "lightSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 10 }, "motionSettings": { "isEnabled": true, "sensitivity": 100 }, "temperatureSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 0.1 }, "humiditySettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 1 }, "ledSettings": { "isEnabled": true }, "bridge": "61b3f5c90054a703e700042b", "camera": null, "bridgeCandidates": [], "id": "02ee9b99f17d69346e0c8c00", "isConnected": true, "marketName": "UP Sense", "modelKey": "sensor" }, { "mac": "3CDB35674BB5", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-SENSE", "name": "Xvub Rzo", "upSince": 1641931605865, "uptime": null, "lastSeen": 1643052750836, "connectedSince": 1643052765865, "state": "CONNECTED", "hardwareRevision": 6, "firmwareVersion": "1.0.2", "latestFirmwareVersion": "1.0.2", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "isMotionDetected": false, "mountType": "none", "leakDetectedAt": null, "tamperingDetectedAt": null, "isOpened": false, "openStatusChangedAt": null, "alarmTriggeredAt": 1641931691625, "motionDetectedAt": 1642978103125, "wiredConnectionState": { "phyRate": null }, "stats": { "light": { "value": null, "status": "unknown" }, "humidity": { "value": 14, "status": "neutral" }, "temperature": { "value": 28.58, "status": "neutral" } }, "bluetoothConnectionState": { "signalQuality": 100, "signalStrength": -42 }, "batteryStatus": { "percentage": 73, "isLow": false }, "alarmSettings": { "isEnabled": false }, "lightSettings": { "isEnabled": false, "lowThreshold": null, "highThreshold": null, "margin": 10 }, "motionSettings": { "isEnabled": true, "sensitivity": 100 }, "temperatureSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 0.1 }, "humiditySettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 1 }, "ledSettings": { "isEnabled": true }, "bridge": "61b3f5c90050a703e700042a", "camera": null, "bridgeCandidates": [], "id": "d3cdb13d7b66b7d67763cd2e", "isConnected": true, "marketName": "UP Sense", "modelKey": "sensor" }, { "mac": "CB9494FA353D", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-SENSE", "name": "Vmhrp Zdebrsu", "upSince": 1642635613872, "uptime": null, "lastSeen": 1643052750853, "connectedSince": 1643052765872, "state": "CONNECTED", "hardwareRevision": 6, "firmwareVersion": "1.0.2", "latestFirmwareVersion": "1.0.2", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "isMotionDetected": false, "mountType": "window", "leakDetectedAt": null, "tamperingDetectedAt": null, "isOpened": false, "openStatusChangedAt": 1643055451596, "alarmTriggeredAt": null, "motionDetectedAt": null, "wiredConnectionState": { "phyRate": null }, "stats": { "light": { "value": null, "status": "unknown" }, "humidity": { "value": null, "status": "unknown" }, "temperature": { "value": 15.3, "status": "neutral" } }, "bluetoothConnectionState": { "signalQuality": 47, "signalStrength": -71 }, "batteryStatus": { "percentage": 86, "isLow": false }, "alarmSettings": { "isEnabled": false }, "lightSettings": { "isEnabled": false, "lowThreshold": null, "highThreshold": null, "margin": 10 }, "motionSettings": { "isEnabled": false, "sensitivity": 100 }, "temperatureSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 0.1 }, "humiditySettings": { "isEnabled": false, "lowThreshold": null, "highThreshold": null, "margin": 1 }, "ledSettings": { "isEnabled": true }, "bridge": "61b3f5c90050a703e700042a", "camera": null, "bridgeCandidates": [], "id": "20b1e28b9a5fbdf1060bf7d8", "isConnected": true, "marketName": "UP Sense", "modelKey": "sensor" }, { "mac": "0063ADA22061", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-SENSE", "name": "Mhfu Txvn", "upSince": 1642704127861, "uptime": null, "lastSeen": 1643052750856, "connectedSince": 1643052765861, "state": "CONNECTED", "hardwareRevision": 6, "firmwareVersion": "1.0.2", "latestFirmwareVersion": "1.0.2", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "isMotionDetected": false, "mountType": "garage", "leakDetectedAt": null, "tamperingDetectedAt": null, "isOpened": false, "openStatusChangedAt": 1643055553059, "alarmTriggeredAt": null, "motionDetectedAt": 1643055561549, "wiredConnectionState": { "phyRate": null }, "stats": { "light": { "value": 13, "status": "neutral" }, "humidity": { "value": 60, "status": "neutral" }, "temperature": { "value": -0.04, "status": "neutral" } }, "bluetoothConnectionState": { "signalQuality": 70, "signalStrength": -62 }, "batteryStatus": { "percentage": 74, "isLow": false }, "alarmSettings": { "isEnabled": false }, "lightSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 10 }, "motionSettings": { "isEnabled": true, "sensitivity": 100 }, "temperatureSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 0.1 }, "humiditySettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 1 }, "ledSettings": { "isEnabled": true }, "bridge": "61b3f5c90050a703e700042a", "camera": "586ab7c2bb6423c3fdd47e95", "bridgeCandidates": [], "id": "5c1b116c29e4e55a70f39736", "isConnected": true, "marketName": "UP Sense", "modelKey": "sensor" } ], "doorlocks": [ { "mac": "F10599AB6955", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-LOCK-R", "name": "Itvvaze Panc", "upSince": 1643050461849, "uptime": null, "lastSeen": 1643052750858, "connectedSince": 1643052765849, "state": "CONNECTED", "hardwareRevision": 7, "firmwareVersion": "1.2.0", "latestFirmwareVersion": "1.2.0", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "credentials": "0e84b2e9b732478bc2e0ba1d0ac7b88f90fb2ea06f31dcd779ee573487fa888c", "lockStatus": "CLOSED", "enableHomekit": false, "autoCloseTimeMs": 15000, "wiredConnectionState": { "phyRate": null }, "ledSettings": { "isEnabled": true }, "bluetoothConnectionState": { "signalQuality": 72, "signalStrength": -61 }, "batteryStatus": { "percentage": 100, "isLow": false }, "bridge": "61b3f5c90050a703e700042a", "camera": "e2ff0ade6be0f2a2beb61869", "bridgeCandidates": [], "id": "1c812e80fd693ab51535be38", "isConnected": true, "hasHomekit": false, "marketName": "UP DoorLock", "modelKey": "doorlock", "privateToken": "XVZZQh71SN4WSAffLJUQCRJHOflBV0HEYCEOmDakPgNWUUECIRXzMQG6HWLDoGlAMQKQPyvKJ37oJVnKGjhgMPqOf3IQCKSywNTYYLGXPZZT8PqKbyqDoQhRfJsWyUP4XazFXLQQsPHONUeEZP4HBKDKQUHMQTqhS7WJDzbVV1CJMPsXIDPHmJNVRBMQWGVH" } ], "chimes": [ { "mac": "BEEEE2FBE413", "host": "192.168.144.146", "connectionHost": "192.168.234.27", "type": "UP Chime", "name": "Xaorvu Tvsv", "upSince": 1651882870009, "uptime": 567870, "lastSeen": 1652450740009, "connectedSince": 1652448904587, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.3.4", "latestFirmwareVersion": "1.3.4", "firmwareBuild": "58bd350.220401.1859", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "volume": 100, "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "lastRing": 1652116059940, "isWirelessUplinkEnabled": true, "wiredConnectionState": { "phyRate": null }, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": 100, "signalStrength": -44, "ssid": null }, "cameraIds": [], "id": "cf1a330397c08f919d02bd7c", "isConnected": true, "marketName": "UP Chime", "modelKey": "chime" } ] } uiprotect-6.1.0/tests/sample_data/sample_bridge.json000066400000000000000000000015051467310220200226360ustar00rootroot00000000000000{ "mac": "A28D0DB15AE1", "host": "192.168.231.68", "connectionHost": "192.168.102.63", "type": "UFP-UAP-B", "name": "Vdr Fzr", "upSince": 1639807977891, "uptime": 3247782, "lastSeen": 1643055759891, "connectedSince": 1642374159304, "state": "CONNECTED", "hardwareRevision": 19, "firmwareVersion": "0.3.1", "latestFirmwareVersion": null, "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "wiredConnectionState": { "phyRate": null }, "id": "1f5a055254fb9169d7536fb9", "isConnected": true, "platform": "mt7621", "modelKey": "bridge" } uiprotect-6.1.0/tests/sample_data/sample_camera.json000066400000000000000000000343001467310220200226310ustar00rootroot00000000000000{ "isDeleting": false, "mac": "331AFA68BE00", "host": "192.168.10.22", "connectionHost": "192.168.102.63", "type": "UVC G4 Doorbell Pro", "name": "Oxnjk Ajfq", "upSince": 1642431757906, "uptime": 624002, "lastSeen": 1643055759906, "connectedSince": 1642431796280, "state": "CONNECTED", "hardwareRevision": "20", "firmwareVersion": "4.48.16", "latestFirmwareVersion": "4.48.16", "firmwareBuild": "dd71d61.211230.1810", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": true, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "lastMotion": 1643055816121, "micVolume": 100, "isMicEnabled": true, "isRecording": true, "isWirelessUplinkEnabled": true, "isMotionDetected": true, "isSmartDetected": true, "phyRate": 200, "hdrMode": true, "videoMode": "default", "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "chimeDuration": 300, "isDark": false, "lastPrivacyZonePositionId": null, "lastRing": 1643055803577, "isLiveHeatmapEnabled": false, "useGlobal": false, "anonymousDeviceId": "783670a8-fd5d-45f7-9e4a-5af42ba89c25", "lastDisconnect": 1669917368811, "eventStats": { "motion": { "today": 44, "average": 69, "lastDays": [ 57, 59, 126, 59, 65, 64, 53 ], "recentHours": [ 5, 2, 6, 5, 1, 0, 6, 4, 1, 5, 5, 0, 3 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "videoReconfigurationInProgress": false, "voltage": 27.3, "isPoorNetwork": false, "wiredConnectionState": { "phyRate": null }, "channels": [ { "id": 0, "videoId": "video1", "name": "Ppkqf Wamffz", "enabled": true, "isRtspEnabled": true, "rtspAlias": "bVFsZBWTKrCRgZKO", "width": 1600, "height": 1200, "fps": 30, "bitrate": 6000000, "minBitrate": 32000, "maxBitrate": 6000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 1, "videoId": "video3", "name": "Woi Uoyb", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 960, "height": 720, "fps": 30, "bitrate": 1200000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": 150000, "minMotionAdaptiveBitRate": 750000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 2, "videoId": "video2", "name": "Rtogc Kztj", "enabled": true, "isRtspEnabled": false, "rtspAlias": null, "width": 480, "height": 360, "fps": 30, "bitrate": 200000, "minBitrate": 32000, "maxBitrate": 1000000, "minClientAdaptiveBitRate": 0, "minMotionAdaptiveBitRate": 200000, "fpsValues": [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 30 ], "idrInterval": 5 }, { "id": 3, "videoId": "video4", "name": "Ohxs Zvtmc", "enabled": true, "isRtspEnabled": true, "rtspAlias": "d8KCmJNaI1NTIpGJ", "width": 1600, "height": 1200, "fps": 2, "bitrate": 1000000, "minBitrate": 32000, "maxBitrate": 2000000, "minClientAdaptiveBitRate": null, "minMotionAdaptiveBitRate": null, "fpsValues": [ 1, 2 ], "idrInterval": 5 } ], "ispSettings": { "aeMode": "auto", "irLedMode": "auto", "irLedLevel": 255, "wdr": 1, "icrSensitivity": 0, "brightness": 50, "contrast": 50, "hue": 50, "saturation": 50, "sharpness": 50, "denoise": 50, "isFlippedVertical": false, "isFlippedHorizontal": false, "isAutoRotateEnabled": false, "isLdcEnabled": true, "is3dnrEnabled": true, "isExternalIrEnabled": false, "isAggressiveAntiFlickerEnabled": false, "isPauseMotionEnabled": false, "dZoomCenterX": 50, "dZoomCenterY": 50, "dZoomScale": 0, "dZoomStreamId": 4, "focusMode": "ztrig", "focusPosition": 0, "touchFocusX": 1001, "touchFocusY": 1001, "zoomPosition": 0, "mountPosition": null }, "talkbackSettings": { "typeFmt": "aac", "typeIn": "serverudp", "bindAddr": "0.0.0.0", "bindPort": 7004, "filterAddr": "", "filterPort": 0, "channels": 1, "samplingRate": 22050, "bitsPerSample": 16, "quality": 100 }, "osdSettings": { "isNameEnabled": true, "isDateEnabled": true, "isLogoEnabled": false, "isDebugEnabled": false }, "ledSettings": { "isEnabled": true, "blinkRate": 0 }, "speakerSettings": { "isEnabled": true, "areSystemSoundsEnabled": false, "volume": 80 }, "recordingSettings": { "prePaddingSecs": 10, "postPaddingSecs": 10, "minMotionEventTrigger": 1000, "endMotionEventDelay": 3000, "suppressIlluminationSurge": false, "mode": "always", "geofencing": "off", "motionAlgorithm": "enhanced", "enablePirTimelapse": false, "useNewMotionAlgorithm": true }, "smartDetectSettings": { "objectTypes": [ "person", "vehicle" ] }, "recordingSchedules": [], "motionZones": [ { "id": 1, "name": "Default", "color": "#AB46BC", "points": [ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ] ], "sensitivity": 50 } ], "privacyZones": [], "smartDetectZones": [ { "id": 1, "name": "Txeb Hlswjhf", "color": "#AB46BC", "points": [ [ 0.569, 0 ], [ 0.604, 0.537 ], [ 1, 0.565 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.276, 0 ] ], "sensitivity": 50, "objectTypes": [ "person" ] }, { "id": 2, "name": "Krc Zhcvlco", "color": "#586CED", "points": [ [ 0, 0 ], [ 0.369, 0 ], [ 0.358, 0.543 ], [ 1, 0.531 ], [ 1, 1 ], [ 0, 0.978 ] ], "sensitivity": 50, "objectTypes": [ "vehicle" ] } ], "smartDetectLines": [], "stats": { "rxBytes": 7917059346, "txBytes": 270432644401, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -52 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1641921049599, "recordingEnd": 1643055815739, "recordingStartLQ": 1641921049574, "recordingEndLQ": 1643055815722, "timelapseStart": 1641921049594, "timelapseEnd": 1643055730717, "timelapseStartLQ": 1641921049594, "timelapseEndLQ": 1643055520725 }, "storage": { "used": 427349245952, "rate": 642.30288184191 }, "wifiQuality": 100, "wifiStrength": -52 }, "featureFlags": { "canAdjustIrLedLevel": false, "canMagicZoom": false, "canOpticalZoom": false, "canTouchFocus": false, "hasAccelerometer": false, "hasAec": true, "hasBattery": false, "hasBluetooth": true, "hasChime": true, "hasExternalIr": false, "hasIcrSensitivity": true, "hasLdc": true, "hasLedIr": true, "hasLedStatus": true, "hasLineIn": false, "hasMic": true, "hasPrivacyMask": true, "hasRtc": false, "hasSdCard": false, "hasSpeaker": true, "hasWifi": true, "hasHdr": true, "hasAutoICROnly": true, "videoModes": [ "default" ], "videoModeMaxFps": [], "hasMotionZones": true, "hasLcdScreen": true, "mountPositions": [], "smartDetectTypes": [ "person", "vehicle" ], "motionAlgorithms": [ "enhanced" ], "hasSquareEventThumbnail": true, "hasPackageCamera": true, "privacyMaskCapability": { "maxMasks": 16, "rectangleOnly": false }, "focus": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "pan": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "tilt": { "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "zoom": { "ratio": 3, "steps": { "max": null, "min": null, "step": null }, "degrees": { "max": null, "min": null, "step": null } }, "hasSmartDetect": true }, "pirSettings": { "pirSensitivity": 100, "pirMotionClipLength": 15, "timelapseFrameInterval": 15, "timelapseTransferInterval": 600 }, "lcdMessage": { "type": "CUSTOM_MESSAGE", "text": "Welcome | 03:23 PM | 25\u00b0F", "resetAt": null }, "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -52, "ssid": "Mortis Camera" }, "lenses": [ { "id": 2, "video": { "recordingStart": 1642357077629, "recordingEnd": 1643055806877, "recordingStartLQ": null, "recordingEndLQ": null, "timelapseStart": 1641921049317, "timelapseEnd": 1643055667971, "timelapseStartLQ": null, "timelapseEndLQ": null } } ], "id": "1c9a2db4df6efda47a3509be", "isConnected": true, "platform": "s5l", "hasSpeaker": true, "hasWifi": true, "audioBitrate": 64000, "canManage": false, "isManaged": true, "marketName": "G4 Doorbell Pro", "modelKey": "camera", "guid": "00000000-0000-00 0- 000-000000000000" } uiprotect-6.1.0/tests/sample_data/sample_camera_heatmap.png000066400000000000000000000027111467310220200241440ustar00rootroot00000000000000‰PNG  IHDR€hHPÝIDATxœíÕ1 À0@ù¤ã‚$ úuÏÌÞ:uüÈ€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  MaPk¸yIEND®B`‚uiprotect-6.1.0/tests/sample_data/sample_camera_snapshot.png000066400000000000000000000204461467310220200243710ustar00rootroot00000000000000‰PNG  IHDR@°,cÀ íIDATxœíØ1 À0@ù¤ã€—‰‚ÞÝ3³ êü€ €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €4 €´ Œ¹ àt·¨IEND®B`‚uiprotect-6.1.0/tests/sample_data/sample_camera_thumbnail.png000066400000000000000000000027111467310220200245100ustar00rootroot00000000000000‰PNG  IHDR€hHPÝIDATxœíÕ1 À0@ù¤ã‚$ úuÏÌÞ:uüÈ€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  €€@À€ `À0`0  MaPk¸yIEND®B`‚uiprotect-6.1.0/tests/sample_data/sample_camera_video.mp4000066400000000000000000001431101467310220200235460ustar00rootroot00000000000000 ftypisomisomiso2avc1mp41freeu‹mdat¯ÿÿ«ÜE齿ÙH·–,Ø Ù#îïx264 - core 160 r3011 cde9a93 - H.264/MPEG-4 AVC codec - Copyleft 2003-2020 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=22 lookahead_threads=3 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00€Óeˆ„;ÿþ÷N¿›TÂ*’W öʤ&Y¹Rkò¦ [j(…iôɽ4uU&Ô @2@âø †€ì ëg$Aš$lC¿þ©–0ÞLavc58.91.100B Á8!AžBx…ÿq!`Œ!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Ah™Lwÿþ©–1!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&à!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!`Œ!Ÿ/jB¿&à!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&á!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!ŸûjB¿&á!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿp!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!`Œ!Ÿ/jB¿&à!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!ŸûjB¿&á!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!`Œ!ŸûjB¿&á!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&à!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!Ÿ-tB¿&á!`Œ!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!`Œ!ŸûjB¿&á!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&à!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&á!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!Ÿ-tB¿&á!`Œ!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ#AŸ–E,/ÿpÞLavc58.91.100B Á8!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!`Œ!ŸûjB¿&á!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&á!`Œ!ž§jB¿&à!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!ŸqtB¿&à!`Œ!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&à!`Œ!`Œ!ŸûjB¿&á!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!žëjB¿&à!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!ŸµtB¿&á!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿq!`Œ!ŸùtB¿&à!`Œ!`Œ!ŸûjB¿&á!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ#AžE,/ÿp!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&à!`Œ!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–1!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&á!`Œ!`Œ!ž§jB¿&à!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&à!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&á!`Œ!Ÿ/jB¿&à!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&à!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&á!`Œ!Ÿ·jB¿&á!`Œ!`Œ)A›¹I¨Al™LWÿþ8@Ú!`Œ!`ŒÓeˆ‚þ÷ˆ2Ëoœj×rç­.­PîFq^¤½þ U¶nÃ::(  ¨ -à}’À™4`ÅX1!`Œ$Aš$lC¿þ©–0!`Œ!`Œ!AžBx…ÿq!`Œ!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&à!`Œ*AšhI¨Ah™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿp!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&à!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!Ÿ/jB¿&á!`ŒÞLavc58.91.100B Á8*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!`Œ!ŸùtB¿&á!`Œ!ŸûjB¿&à!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!`Œ!ŸùtB¿&á!`Œ!ŸûjB¿&à!`Œ!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&á!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ#AŸE,/ÿp!`Œ!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸÚE,/ÿp!`Œ!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!ž?jB¿&á!`Œ!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&á!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0ÞLavc58.91.100B Á8!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!žétB¿&á!`Œ!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ#AŸRE,/ÿp!`Œ!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–0!`Œ#AŸÚE,/ÿp!`Œ!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ*A›àI¨Al™Lwÿþ©–1!`Œ!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&á!`Œ*Aš$I¨Al™Lwÿþ©–0!`Œ!`Œ#AžBE,/ÿq!`Œ!`Œ!žatB¿&á!`Œ!žcjB¿&à!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–0!`Œ!`Œ#AžÊE,/ÿq!`Œ!`Œ!žétB¿&á!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–1!`Œ!`Œ#AŸE,/ÿp!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–0!`Œ!`Œ#AŸRE,/ÿp!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&á!`Œ!`Œ*A›xI¨Al™Lwÿþ©–1!`Œ#AŸ–E,/ÿp!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&á!`Œ!`Œ)A›¹I¨Al™LWÿþ8@Ú!`ŒÓeˆ„ÿþ÷ˆ2Ëoœj×rç­.­PîFq^¤½þ U¶nÃ::(  ¨ -à}’À™4`ÅX0!`Œ!`Œ$Aš$lC¿þ©–1!`Œ!`Œ!AžBx…ÿp!`Œ!`Œ!žatB¿&á!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Ah™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!ž¥tB¿&à!`Œ!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–1!`Œ!`Œ#AžÊE,/ÿp!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–0!`Œ#AŸE,/ÿq!`Œ!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–1!`Œ#AŸRE,/ÿq!`Œ!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–0!`Œ#AŸ–E,/ÿq!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&à!`Œ*A›¼I¨Al™Lwÿþ©–1!`Œ!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ*A›àI¨Al™Lwÿþ©–0!`Œ!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&à!`Œ*Aš$I¨Al™Lwÿþ©–1!`Œ!`Œ#AžBE,/ÿp!`Œ!`Œ!žatB¿&á!`Œ!žcjB¿&á!`Œ!`Œ*AšhI¨Al™Lwÿþ©–0!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!ž§jB¿&á!`Œ!`Œ*Aš¬I¨Al™Lwÿþ©–1!`Œ!`Œ#AžÊE,/ÿp!`Œ!žétB¿&à!`Œ!`Œ!žëjB¿&á!`Œ!`Œ*AšðI¨Al™Lwÿþ©–0!`Œ!`Œ#AŸE,/ÿq!`Œ!Ÿ-tB¿&à!`Œ!`Œ!Ÿ/jB¿&á!`Œ!`Œ*A›4I¨Al™Lwÿþ©–1!`Œ!`Œ#AŸRE,/ÿq!`Œ!ŸqtB¿&á!`Œ!`Œ!ŸsjB¿&à!`Œ!`Œ*A›xI¨Al™Lwÿþ©–0!`Œ#AŸ–E,/ÿq!`Œ!`Œ!ŸµtB¿&à!`Œ!`Œ!Ÿ·jB¿&à!`Œ!`Œ*A›¼I¨Al™Lwÿþ©–1!`Œ#AŸÚE,/ÿq!`Œ!`Œ!ŸùtB¿&á!`Œ!`Œ!ŸûjB¿&à!`Œ*A›àI¨Al™Loÿþ§„0 !`Œ!`Œ#AžE,/ÿq!`Œ!`Œ!ž=tB¿&à!`Œ!`Œ!ž?jB¿&à!`Œ*Aš$I¨Al™Loÿþ§„0!!`Œ!`Œ#AžBE,/ÿp!`Œ!`Œ!žatB¿&á!`Œ!`Œ!žcjB¿&á!`Œ*AšhI¨Al™Lgÿþž»€!`Œ!`Œ#Až†E,/ÿq!`Œ!`Œ!ž¥tB¿&à!`Œ!ž§jB¿&á!`Œ!`Œ*AšªI¨Al™LL+ÿþ8@Û!`Œ!`Œ!žÉjB¿&à!`Œ!`Œ!`Œ!`Œ!`ŒP•moovlmvhdèYð@&>trak\tkhdYØ@Ð$edtselstYØ%¶mdia mdhd2~UÄ-hdlrvideVideoHandler%aminfvmhd$dinfdref url %!stbl­stsdavc1ÐHHÿÿ7avcCdÿágd¬Ù@P» ñƒ`hëãË"Àýøøpaspstts?stssûõøctts=                                                                                                                                              (stsc stsz?Š(%%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%-×(%%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%-×(%%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.'%%.% stco>0ù*[ŒÀó$U‰¼íR…¶áNªäBs­à < v £ Ô  ? l Î  5 f — Ë þ / ` ” Ç ø # ] Á ì&Yеï"M~¸ëG®ßJw¨Ù@q¢Ö :kŸÒ.h›Ì÷1d•Àú-X‰Ãö!RŒ¹êU‚³äK|­áEvªÝ9s¦×<o Ë8c”Î,]—Êõ&`¾ï ) V ‡ ¸ ì!0!a!’!Æ!ù"*"[""Â"ó##X#‹#¼#ç$!$T$$°$ê%%H%y%³%æ&&B&|&©&Ú' 'E'r'£'Ô((;(l((Ñ))5)f)š)Í)þ*)*c*–*Ç*ò+,+_+Š+»+õ,(,S,„,¾,ñ--M-‡-´-å..P.}.®.ß//F/w/¨/Ü00@0q0¥0Ø1 141n1¡1Ò1ý272j2•2Æ3333^33É3ü4'4X4‘5n5¢5Ó66/6i6œ6Ç6ø727e77Á7û8.8Y8Š8Ä8ñ9"9S99º9ë::V:ƒ:´:å;;L;};®;â<>=>p>›>Ý??J?u?¦?à@ @>@o@©@ÖAA8ArAŸAÐBB5BhB™BÊBþC1CbCCÇCúD+DVDDÃDôEEYEŒE·EèF"FUF€F±FëGGIGzG´GáHHCH}HªHÛI I@IsI¤IÕJ J<JmJ˜JÒKK6KaK›KÎKÿL*LdL—LÂLóM-M`M‹M¼MöN#NTN…N¿NìOONOˆOµOæPPKP~P¯PàQQGQxQ©QÝRRARlR¦RÙS S5SoS¢SÍSþT8TkT–TÇUU4U_UUÊU÷V(VYV“VÀVñW"WVW‰WºWëXXRXƒX´XèYYLYwY±YäZZ@ZzZ­ZØ[ [C[v[¡[Ò\\P\{\¬\æ]]D]u]¯]Ü^ ^>^r^¥^Ö__;_n_Ÿ_Ð``7`h`“`Íaa1a\a–aÉaôb%b_b’b½bîc(c[c†c·cñddOd€dºdçeeIe}e°eáffEg(g\gg¸géh#hPhh²hìiiJi{i¯iâjjDjxj«jÜk kAktk¥kÐl l=lnl™lÓmm7mbmœmÏmún+nen˜nÃnôo.o[oŒo½o÷p$pUp†pÀpíqqOqƒq¶qçrrLrr°rÛssHsys¤sÞttBtmt§tÚuu6up)trak\tkhdYð@$edtselstYØ(ùmdia mdhd¬D~UÄ-hdlrsounSoundHandler(¤minfsmhd$dinfdref url (hstbljstsdZmp4a¬D6esds€€€%€€€@ô+€€€V倀€ sttsßstsc?    "#&')*-.014589;<?@BCFGJKMNQRTUXY\]_`cdfgjknoqruvxy|}€ƒ„‡ˆ‹ŒŽ’“•–™šž ¡¤¥§¨«¬¯°²³¶·¹º½¾ÁÂÄÅÈÉËÌÏÐÓÔÖ×ÚÛÝÞáâåæèéìíïðóô÷øúûþÿ    "#&')*-.014589;<?@BCFGJKMNQRTUXY\]_`cdfgjknoqruvxy|}€ƒ„‡ˆ‹ŒŽ’“•–™šž ¡¤¥§¨«¬¯°²³¶·¹º½¾ÁÂÄÅÈÉËÌÏÐÓÔÖ×ÚÛÝÞáâåæèéìíïðóô÷øúûþÿ    "#&')*-.014589;<>”stszà stco>âO€ºçIƒ°áFyªÛBs¤Ø <g¡Ô  0 j È ù 3 f ‘  ü / Z ‹ Å ò # T Ž » ì  Q „ µ æM~¯ãGr¬ß;u¨Ó>qœÍ:e–Ðý._™Æ÷(\Àñ%X‰ºî!R}·êF€³ÞI|§ØEp¡Û9j¤Ñ3gšËü0c”Åù,]ˆÂõ&Q‹¾ïT‡²ã  P { ¬ æ!!U!†!À!í""O"‰"¶"ç##L##°#á$$H$y$¤$Þ%%B%m%§%Ú& &6&p&£&Î&ÿ'9'l'—'È((/(`(‘(Ë(ø)))Z)”)Á)ò*#*W*Š*»*ì+ +S+„+¯+é,,M,x,²,å--A-{-®-Ù. .D.w.¢.Ó/ /:/k/œ/Ö0040e0Ÿ0Ì0ý1.1b1•1Æ1÷2+2^22º2ô3'3X3ƒ3½3ð4!4L4…5h5–5Ç5ø6)6]66Á6ì7&7Y7Š7µ7ï8"8S8~8¸8ë99G99´9ß::J:}:¨:Ù;;@;q;¢;Ü< <:1>d>•>À? ?>?o?š?Ô@@2@c@@Ð@ûA,AfA™AÄAõB/B\BB¾BøC%CVC‡C»CîDDPD„D·DèEEME€E±EÜFFIFzF¥FßGG=GnG¨GÛHH7HqH¤HÏII:IgI˜IÉJJ0JaJ’JÆJùK*K[KKÂKóL$LXL‹L¼LçM!MTM…M°MêNNHNyN³NæOOBO|O¯OÚP PEPrP£PÔQQ;QlQQ×RR5RfRšRÍRþS/ScS–SÇSòT,T_TT»TõU(UYU„U¾UñVVMV‡VºVåWWPW}W®WßXXFXwX¨XâYY@YqY¥YØZ Z:ZnZ¡ZÒZý[7[j[›[Æ\\D\u\ \Ú] ]8]i]£]Ö^^2^l^™^Ê^û_5_b_“_Ä_þ`+`\``Á`ôa%aVaŠa½aîbbSb†b·bâccOc€c«cåddCdtd®dáe e=ewe¤eÕff?ggPgg²gÝhhJhuh¦hàii>ioi©iÖjj8jrjŸjÐkk;khk™kÊkþl1lbl“lÇlúm+m\mmÃmônnYnŒn½nèo"oUo€o±oëppIpzp´pçqqCq}qªqÛr rFrsr¤rÕs s<smsžsÒtt6tgt›tÎtÿu*udu•sgpdrollÿÿsbgprollàbudtaZmeta!hdlrmdirappl-ilst%©toodataLavf58.45.100uiprotect-6.1.0/tests/sample_data/sample_chime.json000066400000000000000000000023561467310220200224740ustar00rootroot00000000000000{ "mac": "BEEEE2FBE413", "host": "192.168.144.146", "connectionHost": "192.168.234.27", "type": "UP Chime", "name": "Xaorvu Tvsv", "upSince": 1651882870009, "uptime": 567870, "lastSeen": 1652450740009, "connectedSince": 1652448904587, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.3.4", "latestFirmwareVersion": "1.3.4", "firmwareBuild": "58bd350.220401.1859", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "volume": 100, "isProbingForWifi": false, "apMac": null, "apRssi": null, "elementInfo": null, "lastRing": 1652116059940, "isWirelessUplinkEnabled": true, "wiredConnectionState": { "phyRate": null }, "wifiConnectionState": { "channel": null, "frequency": null, "phyRate": null, "signalQuality": 100, "signalStrength": -44, "ssid": null }, "cameraIds": [], "id": "cf1a330397c08f919d02bd7c", "isConnected": true, "marketName": "UP Chime", "modelKey": "chime" } uiprotect-6.1.0/tests/sample_data/sample_constants.json000066400000000000000000000014331467310220200234160ustar00rootroot00000000000000{ "server_name": "Uiiji Ryoyo", "server_id": "4B8290F6D7A3", "server_version": "1.21.0-beta.3", "server_ip": "192.168.102.63", "server_model": "UNVR-PRO", "last_update_id": "ebf25bac-d5a1-4f1d-a0ee-74c15981eb70", "user_id": "4c5f03a8c8bd48ad8e066285", "counts": { "camera": 11, "user": 7, "group": 2, "liveview": 5, "viewer": 1, "display": 0, "light": 1, "bridge": 2, "sensor": 4, "doorlock": 1, "chime": 0, "schedule": 0 }, "time": "2022-01-24T20:23:32.433278+00:00", "event_count": 1374, "camera_thumbnail": "e-90f051ebf085214d331644a5", "camera_heatmap": "e-90f051ebf085214d331644a5", "camera_video_length": 23, "camera_online": true } uiprotect-6.1.0/tests/sample_data/sample_doorlock.json000066400000000000000000000030771467310220200232240ustar00rootroot00000000000000{ "mac": "F10599AB6955", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-LOCK-R", "name": "Wkltg Qcjxv", "upSince": 1643050461849, "uptime": null, "lastSeen": 1643052750858, "connectedSince": 1643052765849, "state": "CONNECTED", "hardwareRevision": 7, "firmwareVersion": "1.2.0", "latestFirmwareVersion": "1.2.0", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "credentials": "955756200c7f43936df9d5f7865f058e1528945aac0f0cb27cef960eb58f17db", "lockStatus": "CLOSING", "enableHomekit": false, "autoCloseTimeMs": 15000, "wiredConnectionState": { "phyRate": null }, "ledSettings": { "isEnabled": true }, "bluetoothConnectionState": { "signalQuality": 62, "signalStrength": -65 }, "batteryStatus": { "percentage": 100, "isLow": false }, "bridge": "61b3f5c90050a703e700042a", "camera": "e2ff0ade6be0f2a2beb61869", "bridgeCandidates": [], "id": "1c812e80fd693ab51535be38", "isConnected": true, "hasHomekit": false, "marketName": "UP DoorLock", "modelKey": "doorlock", "privateToken": "MsjIV0UUpMWuAQZvJnCOfC1K9UAfgqDKCIcWtANWIuW66OXLwSgMbNEG2MEkL2TViSkMbJvFxAQEyHU0EJeVCWzY6dGHGuKXFXZMqJWZivBGDC8JoXiRxNIBqHZtXQKXZIoXWKLmhBL7SDxLoFNYEYNNLUGKGFBBGX2oNLi8KRW3SDSUTTWJZNwAUs8GKeJJ" } uiprotect-6.1.0/tests/sample_data/sample_event_smart_track.json000066400000000000000000000211641467310220200251200ustar00rootroot00000000000000{ "id": "2cfadb80b0b93e3135bc9ee0", "payload": [ { "id": "1191", "timestamp": 1643055487431, "level": 82, "coord": [ 341, 466, 39, 133 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 464 }, { "id": "1191", "timestamp": 1643055487929, "level": 80, "coord": [ 354, 461, 39, 144 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 472 }, { "id": "1191", "timestamp": 1643055488264, "level": 80, "coord": [ 360, 458, 39, 155 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 465 }, { "id": "1191", "timestamp": 1643055488764, "level": 86, "coord": [ 364, 461, 47, 158 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 473 }, { "id": "1191", "timestamp": 1643055489105, "level": 83, "coord": [ 372, 461, 47, 161 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 464 }, { "id": "1191", "timestamp": 1643055489606, "level": 85, "coord": [ 372, 463, 50, 166 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 473 }, { "id": "1191", "timestamp": 1643055490105, "level": 84, "coord": [ 370, 461, 56, 180 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 480 }, { "id": "1191", "timestamp": 1643055490606, "level": 87, "coord": [ 362, 449, 62, 200 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 487 }, { "id": "1191", "timestamp": 1643055491106, "level": 86, "coord": [ 352, 449, 68, 219 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 493 }, { "id": "1191", "timestamp": 1643055491606, "level": 88, "coord": [ 339, 447, 75, 249 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 500 }, { "id": "1191", "timestamp": 1643055492106, "level": 92, "coord": [ 322, 436, 83, 280 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 504 }, { "id": "1191", "timestamp": 1643055492607, "level": 90, "coord": [ 306, 427, 97, 327 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 508 }, { "id": "1191", "timestamp": 1643055493110, "level": 92, "coord": [ 281, 408, 114, 375 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 515 }, { "id": "1191", "timestamp": 1643055493609, "level": 91, "coord": [ 268, 386, 135, 427 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 526 }, { "id": "1191", "timestamp": 1643055494109, "level": 92, "coord": [ 222, 369, 166, 511 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 536 }, { "id": "1191", "timestamp": 1643055494609, "level": 91, "coord": [ 177, 338, 210, 591 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 537 }, { "id": "1191", "timestamp": 1643055495111, "level": 93, "coord": [ 87, 283, 283, 669 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 538 }, { "id": "1191", "timestamp": 1643055495610, "level": 90, "coord": [ 39, 224, 333, 738 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 539 }, { "id": "1191", "timestamp": 1643055495946, "level": 93, "coord": [ 25, 191, 381, 786 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 523 }, { "id": "1191", "timestamp": 1643055496447, "level": 89, "coord": [ 108, 163, 368, 824 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 525 }, { "id": "1191", "timestamp": 1643055496946, "level": 89, "coord": [ 47, 94, 406, 886 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 530 }, { "id": "1191", "timestamp": 1643055497452, "level": 45, "coord": [ 6, 44, 410, 938 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 532 }, { "id": "1191", "timestamp": 1643055497951, "level": 35, "coord": [ -33, 16, 372, 977 ], "objectType": "person", "zones": [ 1 ], "lines": [], "duration": 533 } ], "camera": "1c9a2db4df6efda47a3509be", "event": "e46822b9b3d12873255725a5" } uiprotect-6.1.0/tests/sample_data/sample_light.json000066400000000000000000000026161467310220200225150ustar00rootroot00000000000000{ "mac": "534D8B001B14", "host": "192.168.234.163", "connectionHost": "192.168.102.63", "type": "UP FloodLight", "name": "Pjq Osgvztd", "upSince": 1638128967900, "uptime": 4926792, "lastSeen": 1643055759900, "connectedSince": 1642902624923, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.9.3", "latestFirmwareVersion": "1.9.3", "firmwareBuild": "g990c553.211105.251", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": true, "canAdopt": false, "isAttemptingToConnect": false, "isPirMotionDetected": true, "lastMotion": 1643055813957, "isDark": false, "isLightOn": false, "isLocating": false, "wiredConnectionState": { "phyRate": 100 }, "lightDeviceSettings": { "isIndicatorEnabled": false, "ledLevel": 6, "luxSensitivity": "medium", "pirDuration": 120000, "pirSensitivity": 46 }, "lightOnSettings": { "isLedForceOn": false }, "lightModeSettings": { "mode": "off", "enableAt": "fulltime" }, "camera": "1c9a2db4df6efda47a3509be", "id": "3ada785d6626c88d7d52446a", "isConnected": true, "isCameraPaired": true, "marketName": "UP FloodLight", "modelKey": "light" } uiprotect-6.1.0/tests/sample_data/sample_liveview.json000066400000000000000000000011431467310220200232320ustar00rootroot00000000000000{ "name": "Txvocs Vwn", "isDefault": false, "isGlobal": true, "layout": 1, "slots": [ { "cameras": [ "1c9a2db4df6efda47a3509be", "e2ff0ade6be0f2a2beb61869", "c462c07dbd63ad805a7318c7", "4a333d993fe8e2e8472bc901", "ab3e27f2d55fad817dac7bb9", "f0cd15b8bed9e38899286a8c" ], "cycleMode": "motion", "cycleInterval": 10 } ], "owner": "4c5f03a8c8bd48ad8e066285", "id": "d65bb41c14d6aa92bfa4a6d1", "modelKey": "liveview" } uiprotect-6.1.0/tests/sample_data/sample_raw_events.json000066400000000000000000032050631467310220200235670ustar00rootroot00000000000000[ { "id": "a4547d7347c9f2f7c901988d", "type": "disconnect", "start": 1640293400462, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "56401f80d300adad7123c864", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-a4547d7347c9f2f7c901988d", "heatmap": "e-a4547d7347c9f2f7c901988d", "modelKey": "event", "timestamp": null }, { "id": "cea535e766680356430f25b4", "type": "disconnect", "start": 1641666397056, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "76227b20c37b2ff0abdc9d4d", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-cea535e766680356430f25b4", "heatmap": "e-cea535e766680356430f25b4", "modelKey": "event", "timestamp": null }, { "id": "aa2f815bf67a423d4c2023b3", "type": "disconnect", "start": 1642023134544, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "0777b5d342302079dc6b793d", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-aa2f815bf67a423d4c2023b3", "heatmap": "e-aa2f815bf67a423d4c2023b3", "modelKey": "event", "timestamp": null }, { "id": "f2c0797cef2295cf94d7258d", "type": "access", "start": 1642968399418, "end": 1643038112717, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "8593657a25b7826a4288b6af", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-f2c0797cef2295cf94d7258d", "heatmap": "e-f2c0797cef2295cf94d7258d", "modelKey": "event", "timestamp": null }, { "id": "a6ec856c4b526f1ef75581e0", "type": "sensorMotion", "start": 1642969428282, "end": 1642969428282, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vgsyvl Cmhwjm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a6ec856c4b526f1ef75581e0", "heatmap": "e-a6ec856c4b526f1ef75581e0", "modelKey": "event", "timestamp": null }, { "id": "76a8fcddad901f3a47f6a491", "type": "motion", "start": 1642969868951, "end": 1642969890117, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-76a8fcddad901f3a47f6a491", "heatmap": "e-76a8fcddad901f3a47f6a491", "modelKey": "event", "timestamp": 1642968718587 }, { "id": "b6c283115a45b90e01328374", "type": "motion", "start": 1642969874623, "end": 1642969896127, "score": 14, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b6c283115a45b90e01328374", "heatmap": "e-b6c283115a45b90e01328374", "modelKey": "event", "timestamp": 1642968718587 }, { "id": "bfebf7927fa4a3b687de2527", "type": "motion", "start": 1642969903129, "end": 1642969924659, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bfebf7927fa4a3b687de2527", "heatmap": "e-bfebf7927fa4a3b687de2527", "modelKey": "event", "timestamp": 1642968718587 }, { "id": "59a11e87ae1bc1eb8ca1dbc1", "type": "motion", "start": 1642970259299, "end": 1642970280812, "score": 15, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-59a11e87ae1bc1eb8ca1dbc1", "heatmap": "e-59a11e87ae1bc1eb8ca1dbc1", "modelKey": "event", "timestamp": 1642968718587 }, { "id": "147c9b78f6d9a811f2d48dfd", "type": "motion", "start": 1642970489439, "end": 1642970513605, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-147c9b78f6d9a811f2d48dfd", "heatmap": "e-147c9b78f6d9a811f2d48dfd", "modelKey": "event", "timestamp": 1642970500627 }, { "id": "90b5f83de271cf5b34506da6", "type": "smartDetectZone", "start": 1642970928482, "end": 1642970963990, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-90b5f83de271cf5b34506da6", "heatmap": "e-90b5f83de271cf5b34506da6", "modelKey": "event", "timestamp": 1642970944768 }, { "id": "99230d645167538098e365eb", "type": "motion", "start": 1642970931827, "end": 1642971007950, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "90b5f83de271cf5b34506da6", "36b65fab5f89cc9652a0f6b0" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-99230d645167538098e365eb", "heatmap": "e-99230d645167538098e365eb", "modelKey": "event", "timestamp": 1642970952728 }, { "id": "639099f2212638678bb27231", "type": "sensorMotion", "start": 1642970939192, "end": 1642970939192, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fditv Oxbauug" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-639099f2212638678bb27231", "heatmap": "e-639099f2212638678bb27231", "modelKey": "event", "timestamp": null }, { "id": "ccd6073e052e881e963884c3", "type": "sensorMotion", "start": 1642970943587, "end": 1642970943587, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mhero Taqxk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ccd6073e052e881e963884c3", "heatmap": "e-ccd6073e052e881e963884c3", "modelKey": "event", "timestamp": null }, { "id": "2b8cb85a7b74da054bd1bca9", "type": "sensorMotion", "start": 1642970948381, "end": 1642970948381, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Crjk Ejgvfx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2b8cb85a7b74da054bd1bca9", "heatmap": "e-2b8cb85a7b74da054bd1bca9", "modelKey": "event", "timestamp": null }, { "id": "e62363b2fd25cf4d1ad9ca6b", "type": "sensorOpened", "start": 1642970949997, "end": 1642970949997, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zmyjfgs Vbyymv" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-e62363b2fd25cf4d1ad9ca6b", "heatmap": "e-e62363b2fd25cf4d1ad9ca6b", "modelKey": "event", "timestamp": null }, { "id": "d5e5d32a31234eb7753cbb15", "type": "sensorClosed", "start": 1642970950021, "end": 1642970950021, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vsgsn Ckw" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-d5e5d32a31234eb7753cbb15", "heatmap": "e-d5e5d32a31234eb7753cbb15", "modelKey": "event", "timestamp": null }, { "id": "9f3d21186207f627581dbf4c", "type": "sensorOpened", "start": 1642970950402, "end": 1642970950402, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ogljqg Ontles" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-9f3d21186207f627581dbf4c", "heatmap": "e-9f3d21186207f627581dbf4c", "modelKey": "event", "timestamp": null }, { "id": "36b65fab5f89cc9652a0f6b0", "type": "smartDetectZone", "start": 1642970950948, "end": 1642971005543, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-36b65fab5f89cc9652a0f6b0", "heatmap": "e-36b65fab5f89cc9652a0f6b0", "modelKey": "event", "timestamp": 1642970981322 }, { "id": "094944af2a857993e8140e93", "type": "motion", "start": 1642970978656, "end": 1642971021242, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "5dc2b46d9f249f0bd3e93de7" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-094944af2a857993e8140e93", "heatmap": "e-094944af2a857993e8140e93", "modelKey": "event", "timestamp": 1642971007728 }, { "id": "c180db39a745d0bf3d250f2f", "type": "motion", "start": 1642970979366, "end": 1642971044266, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "2c5b2080101e04b3394740e7", "fbe1f10258c159e7773de819" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c180db39a745d0bf3d250f2f", "heatmap": "e-c180db39a745d0bf3d250f2f", "modelKey": "event", "timestamp": 1642971031743 }, { "id": "5dc2b46d9f249f0bd3e93de7", "type": "smartDetectZone", "start": 1642970980151, "end": 1642971020687, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5dc2b46d9f249f0bd3e93de7", "heatmap": "e-5dc2b46d9f249f0bd3e93de7", "modelKey": "event", "timestamp": 1642970991930 }, { "id": "2c5b2080101e04b3394740e7", "type": "smartDetectZone", "start": 1642970980367, "end": 1642971016216, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2c5b2080101e04b3394740e7", "heatmap": "e-2c5b2080101e04b3394740e7", "modelKey": "event", "timestamp": 1642970996542 }, { "id": "b5453036147383d640bc831c", "type": "sensorMotion", "start": 1642970982969, "end": 1642970982969, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yyfnr Zbdnoy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b5453036147383d640bc831c", "heatmap": "e-b5453036147383d640bc831c", "modelKey": "event", "timestamp": null }, { "id": "8a535a04d0f9cb1ee12f9c86", "type": "sensorMotion", "start": 1642970985931, "end": 1642970985931, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dkslbh Qugymxx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8a535a04d0f9cb1ee12f9c86", "heatmap": "e-8a535a04d0f9cb1ee12f9c86", "modelKey": "event", "timestamp": null }, { "id": "680b24361a238bdceee31657", "type": "lightMotion", "start": 1642970990146, "end": 1642970990146, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-680b24361a238bdceee31657", "heatmap": "e-680b24361a238bdceee31657", "modelKey": "event", "timestamp": null }, { "id": "22189a674143cca569b373e2", "type": "sensorMotion", "start": 1642970990180, "end": 1642970990180, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dbajl Fbmomnj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-22189a674143cca569b373e2", "heatmap": "e-22189a674143cca569b373e2", "modelKey": "event", "timestamp": null }, { "id": "5c222d0ac5376329261573e5", "type": "smartDetectZone", "start": 1642971010015, "end": 1642971042632, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5c222d0ac5376329261573e5", "heatmap": "e-5c222d0ac5376329261573e5", "modelKey": "event", "timestamp": 1642971021015 }, { "id": "90f494c398886c01afdbbcfc", "type": "motion", "start": 1642971010439, "end": 1642971044933, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "5c222d0ac5376329261573e5" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-90f494c398886c01afdbbcfc", "heatmap": "e-90f494c398886c01afdbbcfc", "modelKey": "event", "timestamp": 1642971027770 }, { "id": "f7ac41cb3d37bb083bb52663", "type": "motion", "start": 1642971013105, "end": 1642971053157, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f7ac41cb3d37bb083bb52663", "heatmap": "e-f7ac41cb3d37bb083bb52663", "modelKey": "event", "timestamp": 1642971024297 }, { "id": "fbe1f10258c159e7773de819", "type": "smartDetectZone", "start": 1642971019743, "end": 1642971042412, "score": 46, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fbe1f10258c159e7773de819", "heatmap": "e-fbe1f10258c159e7773de819", "modelKey": "event", "timestamp": 1642971031743 }, { "id": "37801af120fc618260e9a14f", "type": "motion", "start": 1642971022158, "end": 1642971051470, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-37801af120fc618260e9a14f", "heatmap": "e-37801af120fc618260e9a14f", "modelKey": "event", "timestamp": 1642971041136 }, { "id": "8a6c94a2f536e639e67bc613", "type": "motion", "start": 1642971159137, "end": 1642971189464, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8a6c94a2f536e639e67bc613", "heatmap": "e-8a6c94a2f536e639e67bc613", "modelKey": "event", "timestamp": 1642971170303 }, { "id": "0261b5d0a3c5aa89fff90da1", "type": "motion", "start": 1642971206968, "end": 1642971230968, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0261b5d0a3c5aa89fff90da1", "heatmap": "e-0261b5d0a3c5aa89fff90da1", "modelKey": "event", "timestamp": 1642971219985 }, { "id": "cf460d8e1dfc1825515ff249", "type": "motion", "start": 1642971279557, "end": 1642971307396, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cf460d8e1dfc1825515ff249", "heatmap": "e-cf460d8e1dfc1825515ff249", "modelKey": "event", "timestamp": 1642971292390 }, { "id": "1dc21a9e6c6f594cbb6aa2af", "type": "motion", "start": 1642971281949, "end": 1642971305450, "score": 63, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1dc21a9e6c6f594cbb6aa2af", "heatmap": "e-1dc21a9e6c6f594cbb6aa2af", "modelKey": "event", "timestamp": 1642971293116 }, { "id": "ffdc15c15414a055583b1fef", "type": "motion", "start": 1642971283365, "end": 1642971335906, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "48d274fa97f10ba71fd7eeb2" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ffdc15c15414a055583b1fef", "heatmap": "e-ffdc15c15414a055583b1fef", "modelKey": "event", "timestamp": 1642971306931 }, { "id": "b3d5719b23f3effb1b0cc7f6", "type": "motion", "start": 1642971283855, "end": 1642971316515, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "8cf9c508f775aae4b972f70f" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b3d5719b23f3effb1b0cc7f6", "heatmap": "e-b3d5719b23f3effb1b0cc7f6", "modelKey": "event", "timestamp": 1642971299353 }, { "id": "7b30f823378cbb6f724a7198", "type": "sensorMotion", "start": 1642971285451, "end": 1642971285451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fhnfl Ihedgt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7b30f823378cbb6f724a7198", "heatmap": "e-7b30f823378cbb6f724a7198", "modelKey": "event", "timestamp": null }, { "id": "8cf9c508f775aae4b972f70f", "type": "smartDetectZone", "start": 1642971286843, "end": 1642971316520, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8cf9c508f775aae4b972f70f", "heatmap": "e-8cf9c508f775aae4b972f70f", "modelKey": "event", "timestamp": 1642971297843 }, { "id": "30b12a8b3ecf98170e0c0498", "type": "sensorMotion", "start": 1642971290708, "end": 1642971290708, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Thihyq Qwwlx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-30b12a8b3ecf98170e0c0498", "heatmap": "e-30b12a8b3ecf98170e0c0498", "modelKey": "event", "timestamp": null }, { "id": "bd3a7b062a7e8a0ab641dc6b", "type": "motion", "start": 1642971293932, "end": 1642971341141, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "3e6e8f89dd8c461ea162046b" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bd3a7b062a7e8a0ab641dc6b", "heatmap": "e-bd3a7b062a7e8a0ab641dc6b", "modelKey": "event", "timestamp": 1642971318776 }, { "id": "d9a193b82b0e411f0721a58b", "type": "sensorMotion", "start": 1642971295096, "end": 1642971295096, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kseka Gkquuj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d9a193b82b0e411f0721a58b", "heatmap": "e-d9a193b82b0e411f0721a58b", "modelKey": "event", "timestamp": null }, { "id": "48d274fa97f10ba71fd7eeb2", "type": "smartDetectZone", "start": 1642971299816, "end": 1642971335976, "score": 81, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-48d274fa97f10ba71fd7eeb2", "heatmap": "e-48d274fa97f10ba71fd7eeb2", "modelKey": "event", "timestamp": 1642971319511 }, { "id": "3e6e8f89dd8c461ea162046b", "type": "smartDetectZone", "start": 1642971301111, "end": 1642971333123, "score": 80, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3e6e8f89dd8c461ea162046b", "heatmap": "e-3e6e8f89dd8c461ea162046b", "modelKey": "event", "timestamp": 1642971318776 }, { "id": "0232f3174c88afa5d93119c9", "type": "smartDetectZone", "start": 1642971302380, "end": 1642971364595, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0232f3174c88afa5d93119c9", "heatmap": "e-0232f3174c88afa5d93119c9", "modelKey": "event", "timestamp": 1642971321933 }, { "id": "973c3f681014430600fc60c8", "type": "motion", "start": 1642971304683, "end": 1642971379018, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "0232f3174c88afa5d93119c9", "772c86a44847b4a080733ef9" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-973c3f681014430600fc60c8", "heatmap": "e-973c3f681014430600fc60c8", "modelKey": "event", "timestamp": 1642971343688 }, { "id": "d6e00a52e1bde37828d2d056", "type": "motion", "start": 1642971306287, "end": 1642971327456, "score": 38, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d6e00a52e1bde37828d2d056", "heatmap": "e-d6e00a52e1bde37828d2d056", "modelKey": "event", "timestamp": 1642971292390 }, { "id": "a6d3ef50843f0e8cbd9a060b", "type": "sensorMotion", "start": 1642971326104, "end": 1642971326104, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jspaad Zagme" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a6d3ef50843f0e8cbd9a060b", "heatmap": "e-a6d3ef50843f0e8cbd9a060b", "modelKey": "event", "timestamp": null }, { "id": "85da046c40551f9fd47a3fb8", "type": "smartDetectZone", "start": 1642971328388, "end": 1642971350942, "score": 64, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-85da046c40551f9fd47a3fb8", "heatmap": "e-85da046c40551f9fd47a3fb8", "modelKey": "event", "timestamp": 1642971339388 }, { "id": "edd6bb1d8c561ec90adf30a1", "type": "lightMotion", "start": 1642971330286, "end": 1642971330286, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-edd6bb1d8c561ec90adf30a1", "heatmap": "e-edd6bb1d8c561ec90adf30a1", "modelKey": "event", "timestamp": null }, { "id": "489078a3602cb1757cb4ba81", "type": "sensorMotion", "start": 1642971330865, "end": 1642971330865, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dgyyxll Itzsx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-489078a3602cb1757cb4ba81", "heatmap": "e-489078a3602cb1757cb4ba81", "modelKey": "event", "timestamp": null }, { "id": "735ba7aad60320bf3fff23d9", "type": "sensorMotion", "start": 1642971334129, "end": 1642971334129, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cgcwks Vvj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-735ba7aad60320bf3fff23d9", "heatmap": "e-735ba7aad60320bf3fff23d9", "modelKey": "event", "timestamp": null }, { "id": "ee6c19df4ba6b1240bb2225b", "type": "lightMotion", "start": 1642971335326, "end": 1642971335326, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-ee6c19df4ba6b1240bb2225b", "heatmap": "e-ee6c19df4ba6b1240bb2225b", "modelKey": "event", "timestamp": null }, { "id": "a5c1b4d467bbddb8d8c7bb95", "type": "sensorMotion", "start": 1642971338091, "end": 1642971338091, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wbil Gpz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a5c1b4d467bbddb8d8c7bb95", "heatmap": "e-a5c1b4d467bbddb8d8c7bb95", "modelKey": "event", "timestamp": null }, { "id": "00ae57975d7e0eb1cadf053c", "type": "smartDetectZone", "start": 1642971338473, "end": 1642971365819, "score": 77, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-00ae57975d7e0eb1cadf053c", "heatmap": "e-00ae57975d7e0eb1cadf053c", "modelKey": "event", "timestamp": 1642971352813 }, { "id": "cf595972044315730f993db9", "type": "motion", "start": 1642971338806, "end": 1642971379843, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "00ae57975d7e0eb1cadf053c", "af5049cfd3c9bd4b70ba302c" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cf595972044315730f993db9", "heatmap": "e-cf595972044315730f993db9", "modelKey": "event", "timestamp": 1642971361995 }, { "id": "2ad44df8ad24d31a4bc3920f", "type": "smartDetectZone", "start": 1642971338907, "end": 1642971368428, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2ad44df8ad24d31a4bc3920f", "heatmap": "e-2ad44df8ad24d31a4bc3920f", "modelKey": "event", "timestamp": 1642971350594 }, { "id": "919024d3dd8ce164e018d9ae", "type": "motion", "start": 1642971338983, "end": 1642971381696, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "2ad44df8ad24d31a4bc3920f", "e1ab7b742518bf833d9b4b1b" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-919024d3dd8ce164e018d9ae", "heatmap": "e-919024d3dd8ce164e018d9ae", "modelKey": "event", "timestamp": 1642971356847 }, { "id": "22b4716513f08bedc66e8a8e", "type": "sensorMotion", "start": 1642971343370, "end": 1642971343370, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jtyut Grtw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-22b4716513f08bedc66e8a8e", "heatmap": "e-22b4716513f08bedc66e8a8e", "modelKey": "event", "timestamp": null }, { "id": "772c86a44847b4a080733ef9", "type": "smartDetectZone", "start": 1642971348293, "end": 1642971377131, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-772c86a44847b4a080733ef9", "heatmap": "e-772c86a44847b4a080733ef9", "modelKey": "event", "timestamp": 1642971359293 }, { "id": "4778bbb4c2dfd9f93fa89565", "type": "lightMotion", "start": 1642971348466, "end": 1642971348466, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-4778bbb4c2dfd9f93fa89565", "heatmap": "e-4778bbb4c2dfd9f93fa89565", "modelKey": "event", "timestamp": null }, { "id": "85827cdbcc1d7a60cc560809", "type": "sensorMotion", "start": 1642971349035, "end": 1642971349035, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kstnf Xmqh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-85827cdbcc1d7a60cc560809", "heatmap": "e-85827cdbcc1d7a60cc560809", "modelKey": "event", "timestamp": null }, { "id": "af5049cfd3c9bd4b70ba302c", "type": "smartDetectZone", "start": 1642971350495, "end": 1642971379172, "score": 74, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-af5049cfd3c9bd4b70ba302c", "heatmap": "e-af5049cfd3c9bd4b70ba302c", "modelKey": "event", "timestamp": 1642971361995 }, { "id": "fc414d661e7b3e453c0172fb", "type": "motion", "start": 1642971352362, "end": 1642971376033, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fc414d661e7b3e453c0172fb", "heatmap": "e-fc414d661e7b3e453c0172fb", "modelKey": "event", "timestamp": 1642971365364 }, { "id": "e1ab7b742518bf833d9b4b1b", "type": "smartDetectZone", "start": 1642971355661, "end": 1642971378446, "score": 67, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e1ab7b742518bf833d9b4b1b", "heatmap": "e-e1ab7b742518bf833d9b4b1b", "modelKey": "event", "timestamp": 1642971367371 }, { "id": "2656e4a4b308306e6c1983b0", "type": "motion", "start": 1642971357435, "end": 1642971385111, "score": 99, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2656e4a4b308306e6c1983b0", "heatmap": "e-2656e4a4b308306e6c1983b0", "modelKey": "event", "timestamp": 1642971374777 }, { "id": "f8956de35442322b06a516f2", "type": "motion", "start": 1642971368123, "end": 1642971392127, "score": 61, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f8956de35442322b06a516f2", "heatmap": "e-f8956de35442322b06a516f2", "modelKey": "event", "timestamp": 1642971381960 }, { "id": "a61abdf353802201690d2088", "type": "motion", "start": 1642971376188, "end": 1642971409359, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a61abdf353802201690d2088", "heatmap": "e-a61abdf353802201690d2088", "modelKey": "event", "timestamp": 1642971388025 }, { "id": "da617885fad3f1099c33efb2", "type": "sensorMotion", "start": 1642971380311, "end": 1642971380311, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zjnyd Hzlpkpe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-da617885fad3f1099c33efb2", "heatmap": "e-da617885fad3f1099c33efb2", "modelKey": "event", "timestamp": null }, { "id": "504042c8f77d425b0286ddce", "type": "sensorMotion", "start": 1642971387785, "end": 1642971387785, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vgsi Hroyg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-504042c8f77d425b0286ddce", "heatmap": "e-504042c8f77d425b0286ddce", "modelKey": "event", "timestamp": null }, { "id": "42f561171e59c9df83bb16f4", "type": "motion", "start": 1642971392254, "end": 1642971414757, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-42f561171e59c9df83bb16f4", "heatmap": "e-42f561171e59c9df83bb16f4", "modelKey": "event", "timestamp": 1642971403420 }, { "id": "df10f9ad7531be18eec24cb6", "type": "motion", "start": 1642971393499, "end": 1642971468001, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "358bba7a750fad9eb4da5eff" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-df10f9ad7531be18eec24cb6", "heatmap": "e-df10f9ad7531be18eec24cb6", "modelKey": "event", "timestamp": 1642971434496 }, { "id": "89e0ad7acfe18a68b7c3eca5", "type": "motion", "start": 1642971394225, "end": 1642971470287, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "e4c46240db245e60c0e1bc57", "0bdd51c0ea591a7b708e8efc" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-89e0ad7acfe18a68b7c3eca5", "heatmap": "e-89e0ad7acfe18a68b7c3eca5", "modelKey": "event", "timestamp": 1642971447604 }, { "id": "6ce97e116393595fdfe38d21", "type": "sensorMotion", "start": 1642971394852, "end": 1642971394852, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mqktc Jafqdg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6ce97e116393595fdfe38d21", "heatmap": "e-6ce97e116393595fdfe38d21", "modelKey": "event", "timestamp": null }, { "id": "358bba7a750fad9eb4da5eff", "type": "smartDetectZone", "start": 1642971395500, "end": 1642971466134, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-358bba7a750fad9eb4da5eff", "heatmap": "e-358bba7a750fad9eb4da5eff", "modelKey": "event", "timestamp": 1642971424713 }, { "id": "e979d87856bebc4857311125", "type": "motion", "start": 1642971398569, "end": 1642971464451, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "1f6af0ad14994903afc1ac38" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e979d87856bebc4857311125", "heatmap": "e-e979d87856bebc4857311125", "modelKey": "event", "timestamp": 1642971441581 }, { "id": "e4c46240db245e60c0e1bc57", "type": "smartDetectZone", "start": 1642971403402, "end": 1642971442982, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e4c46240db245e60c0e1bc57", "heatmap": "e-e4c46240db245e60c0e1bc57", "modelKey": "event", "timestamp": 1642971422474 }, { "id": "1f6af0ad14994903afc1ac38", "type": "smartDetectZone", "start": 1642971404572, "end": 1642971463768, "score": 79, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1f6af0ad14994903afc1ac38", "heatmap": "e-1f6af0ad14994903afc1ac38", "modelKey": "event", "timestamp": 1642971441581 }, { "id": "0bdd51c0ea591a7b708e8efc", "type": "smartDetectZone", "start": 1642971428338, "end": 1642971467642, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0bdd51c0ea591a7b708e8efc", "heatmap": "e-0bdd51c0ea591a7b708e8efc", "modelKey": "event", "timestamp": 1642971439674 }, { "id": "73aa187fd17daeb27bf8dda7", "type": "sensorMotion", "start": 1642971433226, "end": 1642971433226, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fmw Nbtfx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-73aa187fd17daeb27bf8dda7", "heatmap": "e-73aa187fd17daeb27bf8dda7", "modelKey": "event", "timestamp": null }, { "id": "73d051569fda04094e99083a", "type": "lightMotion", "start": 1642971433306, "end": 1642971433306, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-73d051569fda04094e99083a", "heatmap": "e-73d051569fda04094e99083a", "modelKey": "event", "timestamp": null }, { "id": "1944f3437979e475b44dc263", "type": "motion", "start": 1642971446628, "end": 1642971471442, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1944f3437979e475b44dc263", "heatmap": "e-1944f3437979e475b44dc263", "modelKey": "event", "timestamp": 1642971460942 }, { "id": "705c65e29ea344a7d078817d", "type": "motion", "start": 1642971477439, "end": 1642971505103, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-705c65e29ea344a7d078817d", "heatmap": "e-705c65e29ea344a7d078817d", "modelKey": "event", "timestamp": 1642971488606 }, { "id": "5b07b673cf2e503de43186f8", "type": "motion", "start": 1642971482149, "end": 1642971546473, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "9f589b7f58e04b579cad760a" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5b07b673cf2e503de43186f8", "heatmap": "e-5b07b673cf2e503de43186f8", "modelKey": "event", "timestamp": 1642971511641 }, { "id": "d81bb5bab1dd58db4fc63790", "type": "motion", "start": 1642971483670, "end": 1642971522243, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "74dd5e3a52fd4f86f07ef89f" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d81bb5bab1dd58db4fc63790", "heatmap": "e-d81bb5bab1dd58db4fc63790", "modelKey": "event", "timestamp": 1642971499515 }, { "id": "9f589b7f58e04b579cad760a", "type": "smartDetectZone", "start": 1642971483767, "end": 1642971544474, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9f589b7f58e04b579cad760a", "heatmap": "e-9f589b7f58e04b579cad760a", "modelKey": "event", "timestamp": 1642971507283 }, { "id": "c219a5a517e650fef87feba1", "type": "smartDetectZone", "start": 1642971487148, "end": 1642971622065, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c219a5a517e650fef87feba1", "heatmap": "e-c219a5a517e650fef87feba1", "modelKey": "event", "timestamp": 1642971608049 }, { "id": "bb88cfefa65b82744cd32789", "type": "motion", "start": 1642971487649, "end": 1642971627570, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "c219a5a517e650fef87feba1" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bb88cfefa65b82744cd32789", "heatmap": "e-bb88cfefa65b82744cd32789", "modelKey": "event", "timestamp": 1642971608049 }, { "id": "74dd5e3a52fd4f86f07ef89f", "type": "smartDetectZone", "start": 1642971490967, "end": 1642971521107, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-74dd5e3a52fd4f86f07ef89f", "heatmap": "e-74dd5e3a52fd4f86f07ef89f", "modelKey": "event", "timestamp": 1642971507995 }, { "id": "7599dea2c7cc88d4e3224a94", "type": "motion", "start": 1642971508597, "end": 1642971624217, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "4f50b25130a6a955b0aba44d" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7599dea2c7cc88d4e3224a94", "heatmap": "e-7599dea2c7cc88d4e3224a94", "modelKey": "event", "timestamp": 1642971536177 }, { "id": "4f50b25130a6a955b0aba44d", "type": "smartDetectZone", "start": 1642971509088, "end": 1642971622840, "score": 88, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4f50b25130a6a955b0aba44d", "heatmap": "e-4f50b25130a6a955b0aba44d", "modelKey": "event", "timestamp": 1642971569002 }, { "id": "c2ca10ce6d9ecaa67f44f26d", "type": "lightMotion", "start": 1642971511366, "end": 1642971511366, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-c2ca10ce6d9ecaa67f44f26d", "heatmap": "e-c2ca10ce6d9ecaa67f44f26d", "modelKey": "event", "timestamp": null }, { "id": "6b3425e97c545540f52ccc9b", "type": "sensorMotion", "start": 1642971512297, "end": 1642971512297, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xflyjre Nqved" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6b3425e97c545540f52ccc9b", "heatmap": "e-6b3425e97c545540f52ccc9b", "modelKey": "event", "timestamp": null }, { "id": "d9346d97231453f1c07b4c6e", "type": "lightMotion", "start": 1642971519046, "end": 1642971519046, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-d9346d97231453f1c07b4c6e", "heatmap": "e-d9346d97231453f1c07b4c6e", "modelKey": "event", "timestamp": null }, { "id": "ddb8496120736cb79bf291c3", "type": "smartDetectZone", "start": 1642971530090, "end": 1642971559122, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ddb8496120736cb79bf291c3", "heatmap": "e-ddb8496120736cb79bf291c3", "modelKey": "event", "timestamp": 1642971547473 }, { "id": "9aa4cdea1c95f21701403a9a", "type": "motion", "start": 1642971530976, "end": 1642971561303, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [ "ddb8496120736cb79bf291c3" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9aa4cdea1c95f21701403a9a", "heatmap": "e-9aa4cdea1c95f21701403a9a", "modelKey": "event", "timestamp": 1642971542811 }, { "id": "b9e85db3d3c9461ab357308b", "type": "smartDetectZone", "start": 1642971545284, "end": 1642971571310, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b9e85db3d3c9461ab357308b", "heatmap": "e-b9e85db3d3c9461ab357308b", "modelKey": "event", "timestamp": 1642971556284 }, { "id": "1f521c055c026fe29d6f340d", "type": "motion", "start": 1642971545971, "end": 1642971573133, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [ "b9e85db3d3c9461ab357308b" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1f521c055c026fe29d6f340d", "heatmap": "e-1f521c055c026fe29d6f340d", "modelKey": "event", "timestamp": 1642971558805 }, { "id": "46e3fcc73ad55b67903a31f6", "type": "smartDetectZone", "start": 1642971559423, "end": 1642971586580, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-46e3fcc73ad55b67903a31f6", "heatmap": "e-46e3fcc73ad55b67903a31f6", "modelKey": "event", "timestamp": 1642971572184 }, { "id": "2ae7c65f9476ff5ec4af19fa", "type": "motion", "start": 1642971560630, "end": 1642971588458, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "46e3fcc73ad55b67903a31f6" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2ae7c65f9476ff5ec4af19fa", "heatmap": "e-2ae7c65f9476ff5ec4af19fa", "modelKey": "event", "timestamp": 1642971573796 }, { "id": "423f0f8c9334da581225fdb1", "type": "smartDetectZone", "start": 1642971570883, "end": 1642971595469, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-423f0f8c9334da581225fdb1", "heatmap": "e-423f0f8c9334da581225fdb1", "modelKey": "event", "timestamp": 1642971582179 }, { "id": "d7c58fab68268f828279b6bb", "type": "motion", "start": 1642971572125, "end": 1642971596788, "score": 59, "smartDetectTypes": [], "smartDetectEvents": [ "423f0f8c9334da581225fdb1" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d7c58fab68268f828279b6bb", "heatmap": "e-d7c58fab68268f828279b6bb", "modelKey": "event", "timestamp": 1642971583458 }, { "id": "d5c569eca01774a7ff4a451c", "type": "smartDetectZone", "start": 1642971588627, "end": 1642971612363, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d5c569eca01774a7ff4a451c", "heatmap": "e-d5c569eca01774a7ff4a451c", "modelKey": "event", "timestamp": 1642971599779 }, { "id": "82f329e0d7db1bc02a939c9f", "type": "smartDetectZone", "start": 1642971599689, "end": 1642971694082, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-82f329e0d7db1bc02a939c9f", "heatmap": "e-82f329e0d7db1bc02a939c9f", "modelKey": "event", "timestamp": 1642971636718 }, { "id": "43e1cc4bae9d5e46dd1c3a76", "type": "motion", "start": 1642971601623, "end": 1642971694619, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "82f329e0d7db1bc02a939c9f" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-43e1cc4bae9d5e46dd1c3a76", "heatmap": "e-43e1cc4bae9d5e46dd1c3a76", "modelKey": "event", "timestamp": 1642971622293 }, { "id": "e3501358f03519d4c2330d41", "type": "lightMotion", "start": 1642971612406, "end": 1642971612406, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-e3501358f03519d4c2330d41", "heatmap": "e-e3501358f03519d4c2330d41", "modelKey": "event", "timestamp": null }, { "id": "b9f693e3a6ba8aa038901ffe", "type": "sensorMotion", "start": 1642971614051, "end": 1642971614051, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jyxb Bmhpnx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b9f693e3a6ba8aa038901ffe", "heatmap": "e-b9f693e3a6ba8aa038901ffe", "modelKey": "event", "timestamp": null }, { "id": "2ad8629c899c112993fa3f9f", "type": "sensorMotion", "start": 1642971619938, "end": 1642971619938, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ewfhpui Nihhi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2ad8629c899c112993fa3f9f", "heatmap": "e-2ad8629c899c112993fa3f9f", "modelKey": "event", "timestamp": null }, { "id": "c5e892ecc19c30bcccaf0e25", "type": "sensorMotion", "start": 1642971627277, "end": 1642971627277, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vysgadq Pwp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c5e892ecc19c30bcccaf0e25", "heatmap": "e-c5e892ecc19c30bcccaf0e25", "modelKey": "event", "timestamp": null }, { "id": "41eb5e620d7175c3e0c8cf86", "type": "motion", "start": 1642971654238, "end": 1642971676249, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-41eb5e620d7175c3e0c8cf86", "heatmap": "e-41eb5e620d7175c3e0c8cf86", "modelKey": "event", "timestamp": 1642971666246 }, { "id": "958a30aee213970a9c1b8cbf", "type": "smartDetectZone", "start": 1642971662873, "end": 1642971698280, "score": 80, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-958a30aee213970a9c1b8cbf", "heatmap": "e-958a30aee213970a9c1b8cbf", "modelKey": "event", "timestamp": 1642971673873 }, { "id": "92e8869ef3150fea5f116457", "type": "sensorClosed", "start": 1642971671562, "end": 1642971671562, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nvhhq Ibcpte" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-92e8869ef3150fea5f116457", "heatmap": "e-92e8869ef3150fea5f116457", "modelKey": "event", "timestamp": null }, { "id": "3abbf1bd927ed480d92a1843", "type": "sensorMotion", "start": 1642971672971, "end": 1642971672971, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ylkli Fpdfkhb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3abbf1bd927ed480d92a1843", "heatmap": "e-3abbf1bd927ed480d92a1843", "modelKey": "event", "timestamp": null }, { "id": "7c75fe660a8b513e8244a45a", "type": "sensorMotion", "start": 1642971676963, "end": 1642971676963, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tam Nuof" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7c75fe660a8b513e8244a45a", "heatmap": "e-7c75fe660a8b513e8244a45a", "modelKey": "event", "timestamp": null }, { "id": "f2e3d4b62b5877d814ea7bce", "type": "motion", "start": 1642971744786, "end": 1642971797868, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "f6de81ce1ae82c60fda29301" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f2e3d4b62b5877d814ea7bce", "heatmap": "e-f2e3d4b62b5877d814ea7bce", "modelKey": "event", "timestamp": 1642971779982 }, { "id": "e693749aeeb2791336ecbbb0", "type": "motion", "start": 1642971746414, "end": 1642971773415, "score": 42, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e693749aeeb2791336ecbbb0", "heatmap": "e-e693749aeeb2791336ecbbb0", "modelKey": "event", "timestamp": 1642971762248 }, { "id": "f6de81ce1ae82c60fda29301", "type": "smartDetectZone", "start": 1642971746817, "end": 1642971815800, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f6de81ce1ae82c60fda29301", "heatmap": "e-f6de81ce1ae82c60fda29301", "modelKey": "event", "timestamp": 1642971798667 }, { "id": "3d434e907ce11c149a74a612", "type": "smartDetectZone", "start": 1642971748179, "end": 1642971798204, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3d434e907ce11c149a74a612", "heatmap": "e-3d434e907ce11c149a74a612", "modelKey": "event", "timestamp": 1642971765882 }, { "id": "098f498460712660b0d17753", "type": "motion", "start": 1642971749012, "end": 1642971817518, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "3d434e907ce11c149a74a612", "d358505fa19cd9f488a53c5a" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-098f498460712660b0d17753", "heatmap": "e-098f498460712660b0d17753", "modelKey": "event", "timestamp": 1642971799169 }, { "id": "a0b545733d56a61edbbdc69d", "type": "sensorMotion", "start": 1642971754583, "end": 1642971754583, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vohd Jatyu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a0b545733d56a61edbbdc69d", "heatmap": "e-a0b545733d56a61edbbdc69d", "modelKey": "event", "timestamp": null }, { "id": "3df5b08e64bff93d8ad41603", "type": "motion", "start": 1642971760412, "end": 1642971782410, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3df5b08e64bff93d8ad41603", "heatmap": "e-3df5b08e64bff93d8ad41603", "modelKey": "event", "timestamp": 1642971771588 }, { "id": "b37bf2886aa7e039eb899d47", "type": "ring", "start": 1642971766763, "end": 1642971767763, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b37bf2886aa7e039eb899d47", "heatmap": "e-b37bf2886aa7e039eb899d47", "modelKey": "event", "timestamp": null }, { "id": "d358505fa19cd9f488a53c5a", "type": "smartDetectZone", "start": 1642971784271, "end": 1642971816848, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d358505fa19cd9f488a53c5a", "heatmap": "e-d358505fa19cd9f488a53c5a", "modelKey": "event", "timestamp": 1642971799169 }, { "id": "c5a1efa20961104f0d0eddb2", "type": "motion", "start": 1642971786376, "end": 1642971828066, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c5a1efa20961104f0d0eddb2", "heatmap": "e-c5a1efa20961104f0d0eddb2", "modelKey": "event", "timestamp": 1642971812391 }, { "id": "308eee69243d9577cd609b61", "type": "motion", "start": 1642971787213, "end": 1642971820737, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "f6de81ce1ae82c60fda29301" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-308eee69243d9577cd609b61", "heatmap": "e-308eee69243d9577cd609b61", "modelKey": "event", "timestamp": 1642971798379 }, { "id": "025e9fcdbf09e65056be5c41", "type": "sensorMotion", "start": 1642971795268, "end": 1642971795268, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Owyvc Khzcd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-025e9fcdbf09e65056be5c41", "heatmap": "e-025e9fcdbf09e65056be5c41", "modelKey": "event", "timestamp": null }, { "id": "c655c88f1655d94aa9c34686", "type": "motion", "start": 1642971797061, "end": 1642971823894, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c655c88f1655d94aa9c34686", "heatmap": "e-c655c88f1655d94aa9c34686", "modelKey": "event", "timestamp": 1642971809893 }, { "id": "4af02328e5872315f2220781", "type": "sensorMotion", "start": 1642971800418, "end": 1642971800418, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oho Miwn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4af02328e5872315f2220781", "heatmap": "e-4af02328e5872315f2220781", "modelKey": "event", "timestamp": null }, { "id": "9952a2d2c1bc8b1e65499afb", "type": "sensorMotion", "start": 1642971811104, "end": 1642971811104, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jco Jqlqunm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9952a2d2c1bc8b1e65499afb", "heatmap": "e-9952a2d2c1bc8b1e65499afb", "modelKey": "event", "timestamp": null }, { "id": "40030811ec656ce6d307087a", "type": "motion", "start": 1642971813063, "end": 1642971842744, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-40030811ec656ce6d307087a", "heatmap": "e-40030811ec656ce6d307087a", "modelKey": "event", "timestamp": 1642971827897 }, { "id": "e0a1d458532fd231cc67a086", "type": "motion", "start": 1642971813327, "end": 1642971840499, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e0a1d458532fd231cc67a086", "heatmap": "e-e0a1d458532fd231cc67a086", "modelKey": "event", "timestamp": 1642971824993 }, { "id": "5590bcf47cdd896b555a48d0", "type": "sensorMotion", "start": 1642971813551, "end": 1642971813551, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xkwblrl Eagkxz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5590bcf47cdd896b555a48d0", "heatmap": "e-5590bcf47cdd896b555a48d0", "modelKey": "event", "timestamp": null }, { "id": "b67e44cd17ffc192312d2732", "type": "motion", "start": 1642971819103, "end": 1642971863972, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "6d5117e79f85a1695e90dcb1" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b67e44cd17ffc192312d2732", "heatmap": "e-b67e44cd17ffc192312d2732", "modelKey": "event", "timestamp": 1642971837106 }, { "id": "b0cd4ed8cf63e76650a341eb", "type": "sensorMotion", "start": 1642971819731, "end": 1642971819731, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ibe Cnmey" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b0cd4ed8cf63e76650a341eb", "heatmap": "e-b0cd4ed8cf63e76650a341eb", "modelKey": "event", "timestamp": null }, { "id": "6d5117e79f85a1695e90dcb1", "type": "smartDetectZone", "start": 1642971822705, "end": 1642971862287, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6d5117e79f85a1695e90dcb1", "heatmap": "e-6d5117e79f85a1695e90dcb1", "modelKey": "event", "timestamp": 1642971844117 }, { "id": "3d7c307796c9d8bfbbbc1407", "type": "smartDetectZone", "start": 1642971825631, "end": 1642971859994, "score": 80, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3d7c307796c9d8bfbbbc1407", "heatmap": "e-3d7c307796c9d8bfbbbc1407", "modelKey": "event", "timestamp": 1642971846149 }, { "id": "7db2150e16b8e85860f056c4", "type": "motion", "start": 1642971826465, "end": 1642971863553, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "3d7c307796c9d8bfbbbc1407" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7db2150e16b8e85860f056c4", "heatmap": "e-7db2150e16b8e85860f056c4", "modelKey": "event", "timestamp": 1642971846149 }, { "id": "3c57c30c50663ba089f3286f", "type": "motion", "start": 1642971827212, "end": 1642971850213, "score": 25, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3c57c30c50663ba089f3286f", "heatmap": "e-3c57c30c50663ba089f3286f", "modelKey": "event", "timestamp": 1642971824993 }, { "id": "24f81c0bf29a752a1173eccf", "type": "motion", "start": 1642971836278, "end": 1642971880232, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-24f81c0bf29a752a1173eccf", "heatmap": "e-24f81c0bf29a752a1173eccf", "modelKey": "event", "timestamp": 1642971860399 }, { "id": "9ffe994f37196ae790999efd", "type": "sensorMotion", "start": 1642971838658, "end": 1642971838658, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zvfeyv Ppp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9ffe994f37196ae790999efd", "heatmap": "e-9ffe994f37196ae790999efd", "modelKey": "event", "timestamp": null }, { "id": "9cd2bbfcdbe592c4d20bea28", "type": "motion", "start": 1642971840404, "end": 1642971863752, "score": 96, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9cd2bbfcdbe592c4d20bea28", "heatmap": "e-9cd2bbfcdbe592c4d20bea28", "modelKey": "event", "timestamp": 1642971852905 }, { "id": "a97d2482e33eafb2b116a73a", "type": "motion", "start": 1642971847574, "end": 1642971890796, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a97d2482e33eafb2b116a73a", "heatmap": "e-a97d2482e33eafb2b116a73a", "modelKey": "event", "timestamp": 1642971874117 }, { "id": "06b9ab8e26a5b0555fde368a", "type": "sensorOpened", "start": 1642971853465, "end": 1642971853465, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qef Nqm" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-06b9ab8e26a5b0555fde368a", "heatmap": "e-06b9ab8e26a5b0555fde368a", "modelKey": "event", "timestamp": null }, { "id": "18a6d846c2536581f99ac713", "type": "sensorMotion", "start": 1642971854506, "end": 1642971854506, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qel Znsvg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-18a6d846c2536581f99ac713", "heatmap": "e-18a6d846c2536581f99ac713", "modelKey": "event", "timestamp": null }, { "id": "8583927a2a4441adf06aeb2b", "type": "motion", "start": 1642971860079, "end": 1642971885914, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8583927a2a4441adf06aeb2b", "heatmap": "e-8583927a2a4441adf06aeb2b", "modelKey": "event", "timestamp": 1642971875082 }, { "id": "a49c6f262405a8d3b6e22330", "type": "motion", "start": 1642971870995, "end": 1642971902499, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a49c6f262405a8d3b6e22330", "heatmap": "e-a49c6f262405a8d3b6e22330", "modelKey": "event", "timestamp": 1642971887335 }, { "id": "ab0c153766e793e1052486f2", "type": "motion", "start": 1642971902735, "end": 1642971931573, "score": 53, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ab0c153766e793e1052486f2", "heatmap": "e-ab0c153766e793e1052486f2", "modelKey": "event", "timestamp": 1642971917234 }, { "id": "862e6305b6c5b3032e1fc3ae", "type": "motion", "start": 1642971905378, "end": 1642971948714, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-862e6305b6c5b3032e1fc3ae", "heatmap": "e-862e6305b6c5b3032e1fc3ae", "modelKey": "event", "timestamp": 1642971927881 }, { "id": "ba0b198f5cbbdbc3886764c6", "type": "motion", "start": 1642971924164, "end": 1642971947166, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ba0b198f5cbbdbc3886764c6", "heatmap": "e-ba0b198f5cbbdbc3886764c6", "modelKey": "event", "timestamp": 1642971935330 }, { "id": "4791adbe02fedfdbe110e85d", "type": "motion", "start": 1642971932925, "end": 1642971961098, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4791adbe02fedfdbe110e85d", "heatmap": "e-4791adbe02fedfdbe110e85d", "modelKey": "event", "timestamp": 1642971944088 }, { "id": "422d426b68d105657b76195e", "type": "motion", "start": 1642971967666, "end": 1642971995674, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-422d426b68d105657b76195e", "heatmap": "e-422d426b68d105657b76195e", "modelKey": "event", "timestamp": 1642971979168 }, { "id": "4215d96713147364e5c461dd", "type": "motion", "start": 1642971971931, "end": 1642972007999, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "92b59fcd91f519f35ab23a8e" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4215d96713147364e5c461dd", "heatmap": "e-4215d96713147364e5c461dd", "modelKey": "event", "timestamp": 1642971987600 }, { "id": "92b59fcd91f519f35ab23a8e", "type": "smartDetectZone", "start": 1642971974099, "end": 1642972005798, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-92b59fcd91f519f35ab23a8e", "heatmap": "e-92b59fcd91f519f35ab23a8e", "modelKey": "event", "timestamp": 1642971987600 }, { "id": "ba1dd4ed9a1fe2279e788f81", "type": "motion", "start": 1642971983894, "end": 1642972008565, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ba1dd4ed9a1fe2279e788f81", "heatmap": "e-ba1dd4ed9a1fe2279e788f81", "modelKey": "event", "timestamp": 1642971995395 }, { "id": "f9882d9bbdeaf925bc6702eb", "type": "motion", "start": 1642971994293, "end": 1642972017298, "score": 22, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f9882d9bbdeaf925bc6702eb", "heatmap": "e-f9882d9bbdeaf925bc6702eb", "modelKey": "event", "timestamp": 1642971995395 }, { "id": "aa3100e1aa3785a1bdfa6dec", "type": "sensorMotion", "start": 1642972003771, "end": 1642972003771, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Avbznt Tfa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aa3100e1aa3785a1bdfa6dec", "heatmap": "e-aa3100e1aa3785a1bdfa6dec", "modelKey": "event", "timestamp": null }, { "id": "52f1a2060791a3ea0ae8eb3a", "type": "motion", "start": 1642972008370, "end": 1642972032035, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-52f1a2060791a3ea0ae8eb3a", "heatmap": "e-52f1a2060791a3ea0ae8eb3a", "modelKey": "event", "timestamp": 1642972020702 }, { "id": "47c81874aac04233eced56b3", "type": "sensorMotion", "start": 1642972008744, "end": 1642972008744, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ott Qxmrnh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-47c81874aac04233eced56b3", "heatmap": "e-47c81874aac04233eced56b3", "modelKey": "event", "timestamp": null }, { "id": "c494aa647385d8e10439f467", "type": "sensorClosed", "start": 1642972008873, "end": 1642972008873, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Dxvwv Nwdqxre" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-c494aa647385d8e10439f467", "heatmap": "e-c494aa647385d8e10439f467", "modelKey": "event", "timestamp": null }, { "id": "032b72a3322494fb450a7868", "type": "motion", "start": 1642972023429, "end": 1642972049443, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-032b72a3322494fb450a7868", "heatmap": "e-032b72a3322494fb450a7868", "modelKey": "event", "timestamp": 1642972035596 }, { "id": "b4b5c913135f16f107552c93", "type": "sensorMotion", "start": 1642972038358, "end": 1642972038358, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Sztgtvs Blsfywn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b4b5c913135f16f107552c93", "heatmap": "e-b4b5c913135f16f107552c93", "modelKey": "event", "timestamp": null }, { "id": "6dfcc9accdefdf9e761b8526", "type": "sensorMotion", "start": 1642972045826, "end": 1642972045826, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qdcjgsj Tkaae" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6dfcc9accdefdf9e761b8526", "heatmap": "e-6dfcc9accdefdf9e761b8526", "modelKey": "event", "timestamp": null }, { "id": "3bab8e4d54b1be5a79a480b0", "type": "motion", "start": 1642972053027, "end": 1642972076192, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3bab8e4d54b1be5a79a480b0", "heatmap": "e-3bab8e4d54b1be5a79a480b0", "modelKey": "event", "timestamp": 1642972035596 }, { "id": "ace89d1a9693ca0d8beb15a2", "type": "sensorMotion", "start": 1642972059860, "end": 1642972059860, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qgrhh Jqifq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ace89d1a9693ca0d8beb15a2", "heatmap": "e-ace89d1a9693ca0d8beb15a2", "modelKey": "event", "timestamp": null }, { "id": "8a0b26d47559ff0283e29ce8", "type": "sensorOpened", "start": 1642972064242, "end": 1642972064242, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jfa Zqdho" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-8a0b26d47559ff0283e29ce8", "heatmap": "e-8a0b26d47559ff0283e29ce8", "modelKey": "event", "timestamp": null }, { "id": "aaf5117407ae60074b0b4e92", "type": "sensorMotion", "start": 1642972065268, "end": 1642972065268, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Omc Chv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aaf5117407ae60074b0b4e92", "heatmap": "e-aaf5117407ae60074b0b4e92", "modelKey": "event", "timestamp": null }, { "id": "97689912929cd8c6c9ad0597", "type": "motion", "start": 1642972106913, "end": 1642972179348, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "71cc58639f74df66134f1ef5", "604a0a7c8c001f6514a45cf0" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-97689912929cd8c6c9ad0597", "heatmap": "e-97689912929cd8c6c9ad0597", "modelKey": "event", "timestamp": 1642972160653 }, { "id": "71cc58639f74df66134f1ef5", "type": "smartDetectZone", "start": 1642972110751, "end": 1642972165798, "score": 87, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-71cc58639f74df66134f1ef5", "heatmap": "e-71cc58639f74df66134f1ef5", "modelKey": "event", "timestamp": 1642972144106 }, { "id": "602f8b9a1e04a5ac2ed923a7", "type": "smartDetectZone", "start": 1642972110962, "end": 1642972182619, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-602f8b9a1e04a5ac2ed923a7", "heatmap": "e-602f8b9a1e04a5ac2ed923a7", "modelKey": "event", "timestamp": 1642972130149 }, { "id": "10077ed621c7bc61a63b74b1", "type": "motion", "start": 1642972112973, "end": 1642972136966, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-10077ed621c7bc61a63b74b1", "heatmap": "e-10077ed621c7bc61a63b74b1", "modelKey": "event", "timestamp": 1642972124136 }, { "id": "ae38346fa0bbcc7c192c1557", "type": "motion", "start": 1642972113211, "end": 1642972183293, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "602f8b9a1e04a5ac2ed923a7" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ae38346fa0bbcc7c192c1557", "heatmap": "e-ae38346fa0bbcc7c192c1557", "modelKey": "event", "timestamp": 1642972125388 }, { "id": "78a7566590f46e98d14a4d23", "type": "motion", "start": 1642972131860, "end": 1642972154696, "score": 35, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-78a7566590f46e98d14a4d23", "heatmap": "e-78a7566590f46e98d14a4d23", "modelKey": "event", "timestamp": 1642972144027 }, { "id": "d9ba954a7f645c9d38a87bfc", "type": "lightMotion", "start": 1642972131886, "end": 1642972131886, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-d9ba954a7f645c9d38a87bfc", "heatmap": "e-d9ba954a7f645c9d38a87bfc", "modelKey": "event", "timestamp": null }, { "id": "604a0a7c8c001f6514a45cf0", "type": "smartDetectZone", "start": 1642972149653, "end": 1642972178681, "score": 77, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-604a0a7c8c001f6514a45cf0", "heatmap": "e-604a0a7c8c001f6514a45cf0", "modelKey": "event", "timestamp": 1642972160653 }, { "id": "266771d1e0dcf2f086e86898", "type": "motion", "start": 1642972160669, "end": 1642972185696, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-266771d1e0dcf2f086e86898", "heatmap": "e-266771d1e0dcf2f086e86898", "modelKey": "event", "timestamp": 1642972174334 }, { "id": "fd888edb4c70986cd9dff6f3", "type": "motion", "start": 1642972252481, "end": 1642972282478, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fd888edb4c70986cd9dff6f3", "heatmap": "e-fd888edb4c70986cd9dff6f3", "modelKey": "event", "timestamp": 1642972263813 }, { "id": "b4d5244a2a288cd8a0a557b5", "type": "motion", "start": 1642972254058, "end": 1642972306765, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "605fd60ec7b7a6d6d4cee3af" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b4d5244a2a288cd8a0a557b5", "heatmap": "e-b4d5244a2a288cd8a0a557b5", "modelKey": "event", "timestamp": 1642972289760 }, { "id": "605fd60ec7b7a6d6d4cee3af", "type": "smartDetectZone", "start": 1642972263116, "end": 1642972305294, "score": 80, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-605fd60ec7b7a6d6d4cee3af", "heatmap": "e-605fd60ec7b7a6d6d4cee3af", "modelKey": "event", "timestamp": 1642972288392 }, { "id": "1054af780ebff0661ac18117", "type": "smartDetectZone", "start": 1642972266502, "end": 1642972306077, "score": 87, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1054af780ebff0661ac18117", "heatmap": "e-1054af780ebff0661ac18117", "modelKey": "event", "timestamp": 1642972290184 }, { "id": "1e610ab6e19c33e22cbccd90", "type": "motion", "start": 1642972270504, "end": 1642972309247, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "1054af780ebff0661ac18117" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1e610ab6e19c33e22cbccd90", "heatmap": "e-1e610ab6e19c33e22cbccd90", "modelKey": "event", "timestamp": 1642972290184 }, { "id": "3f777cac6969041926e5f498", "type": "motion", "start": 1642972279127, "end": 1642972322799, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3f777cac6969041926e5f498", "heatmap": "e-3f777cac6969041926e5f498", "modelKey": "event", "timestamp": 1642972300464 }, { "id": "6b11c326cacfa3304992cb63", "type": "motion", "start": 1642972320399, "end": 1642972344904, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6b11c326cacfa3304992cb63", "heatmap": "e-6b11c326cacfa3304992cb63", "modelKey": "event", "timestamp": 1642972333735 }, { "id": "2aff2758c898f84a00ea02fc", "type": "sensorClosed", "start": 1642972387613, "end": 1642972387613, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Uto Lijw" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-2aff2758c898f84a00ea02fc", "heatmap": "e-2aff2758c898f84a00ea02fc", "modelKey": "event", "timestamp": null }, { "id": "9f6c6e754933297d6f3ebec4", "type": "motion", "start": 1642972498593, "end": 1642972519758, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9f6c6e754933297d6f3ebec4", "heatmap": "e-9f6c6e754933297d6f3ebec4", "modelKey": "event", "timestamp": 1642972263813 }, { "id": "cad31ca0af0d903bc9440966", "type": "motion", "start": 1642972872880, "end": 1642972893881, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cad31ca0af0d903bc9440966", "heatmap": "e-cad31ca0af0d903bc9440966", "modelKey": "event", "timestamp": 1642972289760 }, { "id": "db73a2d33821a7707928bd0e", "type": "sensorMotion", "start": 1642973081739, "end": 1642973081739, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Wdvztl Vae" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-db73a2d33821a7707928bd0e", "heatmap": "e-db73a2d33821a7707928bd0e", "modelKey": "event", "timestamp": null }, { "id": "1eb8bfc7ac4d1c92246dde87", "type": "access", "start": 1642973181923, "end": 1642989919913, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "bc3dd633553907952a6fe20d", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-1eb8bfc7ac4d1c92246dde87", "heatmap": "e-1eb8bfc7ac4d1c92246dde87", "modelKey": "event", "timestamp": null }, { "id": "be28bd2a91dfcebd856f4984", "type": "motion", "start": 1642974054187, "end": 1642974075688, "score": 14, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-be28bd2a91dfcebd856f4984", "heatmap": "e-be28bd2a91dfcebd856f4984", "modelKey": "event", "timestamp": 1642972263813 }, { "id": "c1c31ce4872288f538436402", "type": "motion", "start": 1642974675138, "end": 1642974696806, "score": 87, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c1c31ce4872288f538436402", "heatmap": "e-c1c31ce4872288f538436402", "modelKey": "event", "timestamp": 1642974686637 }, { "id": "03ead0390d5c67737cf6fde9", "type": "motion", "start": 1642974784706, "end": 1642974806551, "score": 97, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-03ead0390d5c67737cf6fde9", "heatmap": "e-03ead0390d5c67737cf6fde9", "modelKey": "event", "timestamp": 1642974796546 }, { "id": "ae2ff99503565dca3be1fdfb", "type": "motion", "start": 1642974889280, "end": 1642974910763, "score": 3, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ae2ff99503565dca3be1fdfb", "heatmap": "e-ae2ff99503565dca3be1fdfb", "modelKey": "event", "timestamp": 1642972263813 }, { "id": "8f4765f3db75891b7234fedf", "type": "motion", "start": 1642975139690, "end": 1642975161854, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8f4765f3db75891b7234fedf", "heatmap": "e-8f4765f3db75891b7234fedf", "modelKey": "event", "timestamp": 1642975150855 }, { "id": "ca3ded0a55309cd814240315", "type": "motion", "start": 1642975258054, "end": 1642975279886, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ca3ded0a55309cd814240315", "heatmap": "e-ca3ded0a55309cd814240315", "modelKey": "event", "timestamp": 1642975269880 }, { "id": "9dd3092889c42647616d40d3", "type": "motion", "start": 1642975348736, "end": 1642975374906, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9dd3092889c42647616d40d3", "heatmap": "e-9dd3092889c42647616d40d3", "modelKey": "event", "timestamp": 1642975362902 }, { "id": "88649f087a86105299e774cc", "type": "motion", "start": 1642975353402, "end": 1642975380398, "score": 83, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-88649f087a86105299e774cc", "heatmap": "e-88649f087a86105299e774cc", "modelKey": "event", "timestamp": 1642975368402 }, { "id": "5f0e8c51fe97e27e3e0ffb4d", "type": "motion", "start": 1642975362488, "end": 1642975389675, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5f0e8c51fe97e27e3e0ffb4d", "heatmap": "e-5f0e8c51fe97e27e3e0ffb4d", "modelKey": "event", "timestamp": 1642975379502 }, { "id": "ce758dcc73234fb72ff48c64", "type": "motion", "start": 1642975492774, "end": 1642975514969, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ce758dcc73234fb72ff48c64", "heatmap": "e-ce758dcc73234fb72ff48c64", "modelKey": "event", "timestamp": 1642975504946 }, { "id": "1a0aec27076467e2b40f9fae", "type": "motion", "start": 1642975629971, "end": 1642975660683, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1a0aec27076467e2b40f9fae", "heatmap": "e-1a0aec27076467e2b40f9fae", "modelKey": "event", "timestamp": 1642975648667 }, { "id": "ac667d1f0c5df682863c5d53", "type": "motion", "start": 1642975639795, "end": 1642975669482, "score": 93, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ac667d1f0c5df682863c5d53", "heatmap": "e-ac667d1f0c5df682863c5d53", "modelKey": "event", "timestamp": 1642975650961 }, { "id": "8458d14868730f2b30f88902", "type": "motion", "start": 1642975678103, "end": 1642975705810, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8458d14868730f2b30f88902", "heatmap": "e-8458d14868730f2b30f88902", "modelKey": "event", "timestamp": 1642975694967 }, { "id": "fad5bd9e8e2e07460a7af921", "type": "motion", "start": 1642975732438, "end": 1642975758616, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fad5bd9e8e2e07460a7af921", "heatmap": "e-fad5bd9e8e2e07460a7af921", "modelKey": "event", "timestamp": 1642975744105 }, { "id": "eeb6414d0bef8771517dcf3f", "type": "motion", "start": 1642976145764, "end": 1642976167767, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-eeb6414d0bef8771517dcf3f", "heatmap": "e-eeb6414d0bef8771517dcf3f", "modelKey": "event", "timestamp": 1642976157766 }, { "id": "b7d221f5f2cbae2567266011", "type": "motion", "start": 1642976245984, "end": 1642976267652, "score": 61, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b7d221f5f2cbae2567266011", "heatmap": "e-b7d221f5f2cbae2567266011", "modelKey": "event", "timestamp": 1642976257487 }, { "id": "5d67e0b49f3bfcc4a3d6c337", "type": "motion", "start": 1642976314112, "end": 1642976335614, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5d67e0b49f3bfcc4a3d6c337", "heatmap": "e-5d67e0b49f3bfcc4a3d6c337", "modelKey": "event", "timestamp": 1642976325279 }, { "id": "f075714a4f011c36a97e905d", "type": "motion", "start": 1642976317278, "end": 1642976338609, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f075714a4f011c36a97e905d", "heatmap": "e-f075714a4f011c36a97e905d", "modelKey": "event", "timestamp": 1642975368402 }, { "id": "b3229ad94ab275344a0aa07e", "type": "motion", "start": 1642976405778, "end": 1642976427280, "score": 15, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b3229ad94ab275344a0aa07e", "heatmap": "e-b3229ad94ab275344a0aa07e", "modelKey": "event", "timestamp": 1642975368402 }, { "id": "3587b69833fea7e765d2e7f4", "type": "motion", "start": 1642976854052, "end": 1642976875718, "score": 3, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3587b69833fea7e765d2e7f4", "heatmap": "e-3587b69833fea7e765d2e7f4", "modelKey": "event", "timestamp": 1642975368402 }, { "id": "1fd3e7683a1a8e40ab3f905b", "type": "motion", "start": 1642976915894, "end": 1642976936899, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1fd3e7683a1a8e40ab3f905b", "heatmap": "e-1fd3e7683a1a8e40ab3f905b", "modelKey": "event", "timestamp": 1642975368402 }, { "id": "27fb0746f7a2a4c90ba49a56", "type": "motion", "start": 1642977000383, "end": 1642977024243, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-27fb0746f7a2a4c90ba49a56", "heatmap": "e-27fb0746f7a2a4c90ba49a56", "modelKey": "event", "timestamp": 1642977013392 }, { "id": "dd65b60d3b1f338f8b549321", "type": "motion", "start": 1642977034265, "end": 1642977055433, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-dd65b60d3b1f338f8b549321", "heatmap": "e-dd65b60d3b1f338f8b549321", "modelKey": "event", "timestamp": 1642977045431 }, { "id": "c122006a53b258edfa93d494", "type": "motion", "start": 1642977071631, "end": 1642977093134, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c122006a53b258edfa93d494", "heatmap": "e-c122006a53b258edfa93d494", "modelKey": "event", "timestamp": 1642977083132 }, { "id": "a2b14c9c3d393da58a2a0398", "type": "motion", "start": 1642977130901, "end": 1642977154249, "score": 50, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a2b14c9c3d393da58a2a0398", "heatmap": "e-a2b14c9c3d393da58a2a0398", "modelKey": "event", "timestamp": 1642977144236 }, { "id": "32841ab7ec7134d14b55c917", "type": "motion", "start": 1642977242055, "end": 1642977264721, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-32841ab7ec7134d14b55c917", "heatmap": "e-32841ab7ec7134d14b55c917", "modelKey": "event", "timestamp": 1642977254556 }, { "id": "de23b0e92145f04c791711f8", "type": "motion", "start": 1642977436447, "end": 1642977458778, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-de23b0e92145f04c791711f8", "heatmap": "e-de23b0e92145f04c791711f8", "modelKey": "event", "timestamp": 1642977447613 }, { "id": "17106adce9147df87b035cd2", "type": "motion", "start": 1642977610587, "end": 1642977639095, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-17106adce9147df87b035cd2", "heatmap": "e-17106adce9147df87b035cd2", "modelKey": "event", "timestamp": 1642977621921 }, { "id": "c8a57e19237f018fb20d694b", "type": "motion", "start": 1642977615207, "end": 1642977640883, "score": 57, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c8a57e19237f018fb20d694b", "heatmap": "e-c8a57e19237f018fb20d694b", "modelKey": "event", "timestamp": 1642977626374 }, { "id": "b0800e87be918f9cf7353ea5", "type": "motion", "start": 1642977629634, "end": 1642977651142, "score": 15, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b0800e87be918f9cf7353ea5", "heatmap": "e-b0800e87be918f9cf7353ea5", "modelKey": "event", "timestamp": 1642977447613 }, { "id": "33a335643e48585e0a3a9260", "type": "motion", "start": 1642977829040, "end": 1642977851217, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-33a335643e48585e0a3a9260", "heatmap": "e-33a335643e48585e0a3a9260", "modelKey": "event", "timestamp": 1642977841207 }, { "id": "6f5d96c3a515e310b4dbe486", "type": "motion", "start": 1642977838400, "end": 1642977860752, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6f5d96c3a515e310b4dbe486", "heatmap": "e-6f5d96c3a515e310b4dbe486", "modelKey": "event", "timestamp": 1642977850734 }, { "id": "85c9621a7e08eb240e68d4f6", "type": "access", "start": 1642977896829, "end": 1642977897647, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "bc3dd633553907952a6fe20d", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-85c9621a7e08eb240e68d4f6", "heatmap": "e-85c9621a7e08eb240e68d4f6", "modelKey": "event", "timestamp": null }, { "id": "6a9e8a746b3362055d2d4f8b", "type": "motion", "start": 1642978058262, "end": 1642978079767, "score": 97, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a9e8a746b3362055d2d4f8b", "heatmap": "e-6a9e8a746b3362055d2d4f8b", "modelKey": "event", "timestamp": 1642978069763 }, { "id": "4e0948c189f36df1e865a45c", "type": "sensorMotion", "start": 1642978102224, "end": 1642978102224, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "d3cdb13d7b66b7d67763cd2e" }, "sensorName": { "text": "Rbezboy Hjrgz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4e0948c189f36df1e865a45c", "heatmap": "e-4e0948c189f36df1e865a45c", "modelKey": "event", "timestamp": null }, { "id": "0a0073b1e7e8750dfc7b3e1a", "type": "motion", "start": 1642978317996, "end": 1642978339163, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0a0073b1e7e8750dfc7b3e1a", "heatmap": "e-0a0073b1e7e8750dfc7b3e1a", "modelKey": "event", "timestamp": 1642978069763 }, { "id": "808029f1340fcdd3dee49582", "type": "motion", "start": 1642978352564, "end": 1642978373704, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-808029f1340fcdd3dee49582", "heatmap": "e-808029f1340fcdd3dee49582", "modelKey": "event", "timestamp": 1642978069763 }, { "id": "7e59c8685c430b8f7dbb463e", "type": "smartDetectZone", "start": 1642978465859, "end": 1642978532379, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7e59c8685c430b8f7dbb463e", "heatmap": "e-7e59c8685c430b8f7dbb463e", "modelKey": "event", "timestamp": 1642978507025 }, { "id": "398dcd21eedb6a5681177f0a", "type": "motion", "start": 1642978468754, "end": 1642978490300, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "7e59c8685c430b8f7dbb463e" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-398dcd21eedb6a5681177f0a", "heatmap": "e-398dcd21eedb6a5681177f0a", "modelKey": "event", "timestamp": 1642978481087 }, { "id": "f9201b227bdfa0fa99b687f2", "type": "sensorMotion", "start": 1642978477311, "end": 1642978477311, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Crjerhp Jnkrfmn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f9201b227bdfa0fa99b687f2", "heatmap": "e-f9201b227bdfa0fa99b687f2", "modelKey": "event", "timestamp": null }, { "id": "6209a271636149b4b4560f34", "type": "sensorMotion", "start": 1642978481302, "end": 1642978481302, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gee Goaod" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6209a271636149b4b4560f34", "heatmap": "e-6209a271636149b4b4560f34", "modelKey": "event", "timestamp": null }, { "id": "2694ba9e96dc9002051644e1", "type": "sensorMotion", "start": 1642978485036, "end": 1642978485036, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pcde Dpqoid" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2694ba9e96dc9002051644e1", "heatmap": "e-2694ba9e96dc9002051644e1", "modelKey": "event", "timestamp": null }, { "id": "436c3de821f2b03a1179ff31", "type": "motion", "start": 1642978495301, "end": 1642978533449, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "7e59c8685c430b8f7dbb463e" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-436c3de821f2b03a1179ff31", "heatmap": "e-436c3de821f2b03a1179ff31", "modelKey": "event", "timestamp": 1642978507300 }, { "id": "30ca50d4daa9a2834dfdc4bd", "type": "sensorMotion", "start": 1642978506303, "end": 1642978506303, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yqiz Kcwdtvq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-30ca50d4daa9a2834dfdc4bd", "heatmap": "e-30ca50d4daa9a2834dfdc4bd", "modelKey": "event", "timestamp": null }, { "id": "087f4e80f84c5de8c1e46aa9", "type": "sensorMotion", "start": 1642978511302, "end": 1642978511302, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mrorsm Inps" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-087f4e80f84c5de8c1e46aa9", "heatmap": "e-087f4e80f84c5de8c1e46aa9", "modelKey": "event", "timestamp": null }, { "id": "876800d5a96efa74c1c7da9e", "type": "sensorMotion", "start": 1642978515293, "end": 1642978515293, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pmexc Ggna" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-876800d5a96efa74c1c7da9e", "heatmap": "e-876800d5a96efa74c1c7da9e", "modelKey": "event", "timestamp": null }, { "id": "8bfa6615112c158aa6a7a44d", "type": "sensorMotion", "start": 1642978519285, "end": 1642978519285, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wpxkcco Oyt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8bfa6615112c158aa6a7a44d", "heatmap": "e-8bfa6615112c158aa6a7a44d", "modelKey": "event", "timestamp": null }, { "id": "fb8bb0b9baddcde8d5dbcadf", "type": "sensorMotion", "start": 1642978523276, "end": 1642978523276, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ztk Rpiek" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fb8bb0b9baddcde8d5dbcadf", "heatmap": "e-fb8bb0b9baddcde8d5dbcadf", "modelKey": "event", "timestamp": null }, { "id": "06811d49b3b67d73737ee249", "type": "motion", "start": 1642978595601, "end": 1642978616779, "score": 93, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-06811d49b3b67d73737ee249", "heatmap": "e-06811d49b3b67d73737ee249", "modelKey": "event", "timestamp": 1642978606767 }, { "id": "4c526e8ae35f2f77fb55495c", "type": "motion", "start": 1642978681543, "end": 1642978705213, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4c526e8ae35f2f77fb55495c", "heatmap": "e-4c526e8ae35f2f77fb55495c", "modelKey": "event", "timestamp": 1642978692709 }, { "id": "b0edd28c08a2e5a35c368aec", "type": "motion", "start": 1642978933757, "end": 1642978955088, "score": 73, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b0edd28c08a2e5a35c368aec", "heatmap": "e-b0edd28c08a2e5a35c368aec", "modelKey": "event", "timestamp": 1642978944938 }, { "id": "a2893ac92d081e3ae4643765", "type": "motion", "start": 1642979292234, "end": 1642979313905, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a2893ac92d081e3ae4643765", "heatmap": "e-a2893ac92d081e3ae4643765", "modelKey": "event", "timestamp": 1642979303400 }, { "id": "9df31e89eb39db23fe76942c", "type": "motion", "start": 1642979345888, "end": 1642979368916, "score": 35, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9df31e89eb39db23fe76942c", "heatmap": "e-9df31e89eb39db23fe76942c", "modelKey": "event", "timestamp": 1642979359722 }, { "id": "df57d0675d4b097a36412a00", "type": "motion", "start": 1642979348133, "end": 1642979379675, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-df57d0675d4b097a36412a00", "heatmap": "e-df57d0675d4b097a36412a00", "modelKey": "event", "timestamp": 1642979367671 }, { "id": "8a36358b15dbb2111748f733", "type": "motion", "start": 1642979353892, "end": 1642979375396, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8a36358b15dbb2111748f733", "heatmap": "e-8a36358b15dbb2111748f733", "modelKey": "event", "timestamp": 1642979365393 }, { "id": "2e69c58e38d886753b7c9226", "type": "motion", "start": 1642979447431, "end": 1642979468766, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2e69c58e38d886753b7c9226", "heatmap": "e-2e69c58e38d886753b7c9226", "modelKey": "event", "timestamp": 1642979458741 }, { "id": "b543c8e41379324564ca0aad", "type": "motion", "start": 1642979450451, "end": 1642979471646, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b543c8e41379324564ca0aad", "heatmap": "e-b543c8e41379324564ca0aad", "modelKey": "event", "timestamp": 1642979461618 }, { "id": "59df2295b8f602c8ed0ec82a", "type": "smartDetectZone", "start": 1642979519273, "end": 1642979703149, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-59df2295b8f602c8ed0ec82a", "heatmap": "e-59df2295b8f602c8ed0ec82a", "modelKey": "event", "timestamp": 1642979530739 }, { "id": "10e5bef8bebbfb943adf76e5", "type": "motion", "start": 1642979519547, "end": 1642979541725, "score": 81, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-10e5bef8bebbfb943adf76e5", "heatmap": "e-10e5bef8bebbfb943adf76e5", "modelKey": "event", "timestamp": 1642979530714 }, { "id": "09251c45f7e53b74055ea0d3", "type": "motion", "start": 1642979520998, "end": 1642979543335, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "59df2295b8f602c8ed0ec82a" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-09251c45f7e53b74055ea0d3", "heatmap": "e-09251c45f7e53b74055ea0d3", "modelKey": "event", "timestamp": 1642979534168 }, { "id": "863f269c4a9b5d5e782b32dc", "type": "sensorMotion", "start": 1642979530451, "end": 1642979530451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Asyxgo Xwjal" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-863f269c4a9b5d5e782b32dc", "heatmap": "e-863f269c4a9b5d5e782b32dc", "modelKey": "event", "timestamp": null }, { "id": "0ecec233d8b9414256bddea0", "type": "sensorMotion", "start": 1642979534908, "end": 1642979534908, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cryie Cnsvwgt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0ecec233d8b9414256bddea0", "heatmap": "e-0ecec233d8b9414256bddea0", "modelKey": "event", "timestamp": null }, { "id": "2b96889acb024024e495c47c", "type": "sensorMotion", "start": 1642979540025, "end": 1642979540025, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mknjlb Gacpq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2b96889acb024024e495c47c", "heatmap": "e-2b96889acb024024e495c47c", "modelKey": "event", "timestamp": null }, { "id": "0149db821144673168e2fc24", "type": "motion", "start": 1642979540063, "end": 1642979561754, "score": 55, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0149db821144673168e2fc24", "heatmap": "e-0149db821144673168e2fc24", "modelKey": "event", "timestamp": 1642979551232 }, { "id": "989f17a114109d278e3b6882", "type": "sensorMotion", "start": 1642979543549, "end": 1642979543549, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nufcrk Icbrdco" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-989f17a114109d278e3b6882", "heatmap": "e-989f17a114109d278e3b6882", "modelKey": "event", "timestamp": null }, { "id": "383c38bff5e78d279adade9f", "type": "sensorMotion", "start": 1642979545996, "end": 1642979545996, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gsil Whuw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-383c38bff5e78d279adade9f", "heatmap": "e-383c38bff5e78d279adade9f", "modelKey": "event", "timestamp": null }, { "id": "3b1d85e0b1a40459b6672446", "type": "sensorMotion", "start": 1642979549991, "end": 1642979549991, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Swstbp Cdjri" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3b1d85e0b1a40459b6672446", "heatmap": "e-3b1d85e0b1a40459b6672446", "modelKey": "event", "timestamp": null }, { "id": "cd41002cd98136f8a7cc7eb9", "type": "sensorMotion", "start": 1642979555896, "end": 1642979555896, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pfq Pqrz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cd41002cd98136f8a7cc7eb9", "heatmap": "e-cd41002cd98136f8a7cc7eb9", "modelKey": "event", "timestamp": null }, { "id": "4ba067855995b9c8ec94c083", "type": "sensorMotion", "start": 1642979559372, "end": 1642979559372, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iwsduo Dbrq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4ba067855995b9c8ec94c083", "heatmap": "e-4ba067855995b9c8ec94c083", "modelKey": "event", "timestamp": null }, { "id": "891f2f98359abadefab305c0", "type": "sensorMotion", "start": 1642979563121, "end": 1642979563121, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nbrhneo Yvsubr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-891f2f98359abadefab305c0", "heatmap": "e-891f2f98359abadefab305c0", "modelKey": "event", "timestamp": null }, { "id": "d9d95d0ca78f95608ebb8574", "type": "sensorMotion", "start": 1642979567237, "end": 1642979567237, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wfucpum Fyhekko" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d9d95d0ca78f95608ebb8574", "heatmap": "e-d9d95d0ca78f95608ebb8574", "modelKey": "event", "timestamp": null }, { "id": "c81cbc14dceaef98d6f2cf5e", "type": "sensorMotion", "start": 1642979571990, "end": 1642979571990, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rgmwf Cxfj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c81cbc14dceaef98d6f2cf5e", "heatmap": "e-c81cbc14dceaef98d6f2cf5e", "modelKey": "event", "timestamp": null }, { "id": "b8fee3cd536842130f75af92", "type": "sensorMotion", "start": 1642979575724, "end": 1642979575724, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bnqdj Pwpk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b8fee3cd536842130f75af92", "heatmap": "e-b8fee3cd536842130f75af92", "modelKey": "event", "timestamp": null }, { "id": "d34702e5598d6bba0caab1f9", "type": "sensorMotion", "start": 1642979585123, "end": 1642979585123, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xhmyuej Xkfou" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d34702e5598d6bba0caab1f9", "heatmap": "e-d34702e5598d6bba0caab1f9", "modelKey": "event", "timestamp": null }, { "id": "bbf0a77e7d5d571285c0881b", "type": "sensorMotion", "start": 1642979589115, "end": 1642979589115, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zsol Qwdmxl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bbf0a77e7d5d571285c0881b", "heatmap": "e-bbf0a77e7d5d571285c0881b", "modelKey": "event", "timestamp": null }, { "id": "a0351b58da162badb91ddab9", "type": "sensorMotion", "start": 1642979593234, "end": 1642979593234, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Baeztl Sdwwo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a0351b58da162badb91ddab9", "heatmap": "e-a0351b58da162badb91ddab9", "modelKey": "event", "timestamp": null }, { "id": "5dee731f98d4d1254294c5de", "type": "sensorMotion", "start": 1642979597548, "end": 1642979597548, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yvoeu Gywcdhu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5dee731f98d4d1254294c5de", "heatmap": "e-5dee731f98d4d1254294c5de", "modelKey": "event", "timestamp": null }, { "id": "787844f67e6d44c4db3b84ac", "type": "sensorMotion", "start": 1642979601475, "end": 1642979601475, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Glppy Mjgg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-787844f67e6d44c4db3b84ac", "heatmap": "e-787844f67e6d44c4db3b84ac", "modelKey": "event", "timestamp": null }, { "id": "39a0673d57228d9e5a671078", "type": "sensorMotion", "start": 1642979605466, "end": 1642979605466, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fsz Gryfx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-39a0673d57228d9e5a671078", "heatmap": "e-39a0673d57228d9e5a671078", "modelKey": "event", "timestamp": null }, { "id": "fa436575786083c52c93c9c8", "type": "sensorMotion", "start": 1642979611518, "end": 1642979611518, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xuet Zguq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fa436575786083c52c93c9c8", "heatmap": "e-fa436575786083c52c93c9c8", "modelKey": "event", "timestamp": null }, { "id": "7657e49101da91cee440716d", "type": "sensorMotion", "start": 1642979617440, "end": 1642979617440, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Aja Lak" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7657e49101da91cee440716d", "heatmap": "e-7657e49101da91cee440716d", "modelKey": "event", "timestamp": null }, { "id": "0f8c08c7ebfce25ee3bfd35b", "type": "sensorMotion", "start": 1642979626196, "end": 1642979626196, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Risuq Ckw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0f8c08c7ebfce25ee3bfd35b", "heatmap": "e-0f8c08c7ebfce25ee3bfd35b", "modelKey": "event", "timestamp": null }, { "id": "a7104fc66503e4bf3d2f05e2", "type": "sensorMotion", "start": 1642979630961, "end": 1642979630961, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gjtdwe Drm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a7104fc66503e4bf3d2f05e2", "heatmap": "e-a7104fc66503e4bf3d2f05e2", "modelKey": "event", "timestamp": null }, { "id": "899dccb50862e258deec7040", "type": "motion", "start": 1642979635036, "end": 1642979656369, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-899dccb50862e258deec7040", "heatmap": "e-899dccb50862e258deec7040", "modelKey": "event", "timestamp": 1642979646369 }, { "id": "d528eeafefe698ac2a27bf7d", "type": "motion", "start": 1642979637995, "end": 1642979661492, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "59df2295b8f602c8ed0ec82a" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d528eeafefe698ac2a27bf7d", "heatmap": "e-d528eeafefe698ac2a27bf7d", "modelKey": "event", "timestamp": 1642979651159 }, { "id": "5523fb7b25679097b77d576f", "type": "sensorMotion", "start": 1642979638428, "end": 1642979638428, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mfjvf Lubmu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5523fb7b25679097b77d576f", "heatmap": "e-5523fb7b25679097b77d576f", "modelKey": "event", "timestamp": null }, { "id": "e915a0896f233b0af3d1a522", "type": "sensorMotion", "start": 1642979643450, "end": 1642979643450, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kqk Gcfdw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e915a0896f233b0af3d1a522", "heatmap": "e-e915a0896f233b0af3d1a522", "modelKey": "event", "timestamp": null }, { "id": "8994aef61f77c99c48fac147", "type": "sensorMotion", "start": 1642979647955, "end": 1642979647955, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ewblhdq Wrwkg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8994aef61f77c99c48fac147", "heatmap": "e-8994aef61f77c99c48fac147", "modelKey": "event", "timestamp": null }, { "id": "78243d5dbf5e6ab9830a0ebd", "type": "sensorMotion", "start": 1642979650533, "end": 1642979650533, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Askfh Tvje" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-78243d5dbf5e6ab9830a0ebd", "heatmap": "e-78243d5dbf5e6ab9830a0ebd", "modelKey": "event", "timestamp": null }, { "id": "0df41198323f929f8dc94ae1", "type": "sensorMotion", "start": 1642979658643, "end": 1642979658643, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ppqpspe Kodsvut" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0df41198323f929f8dc94ae1", "heatmap": "e-0df41198323f929f8dc94ae1", "modelKey": "event", "timestamp": null }, { "id": "62de790fe8f456613006605c", "type": "sensorMotion", "start": 1642979663149, "end": 1642979663149, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qsvp Dccc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-62de790fe8f456613006605c", "heatmap": "e-62de790fe8f456613006605c", "modelKey": "event", "timestamp": null }, { "id": "b1c1e8b5b7b8f4ede9b54720", "type": "sensorMotion", "start": 1642979667011, "end": 1642979667011, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Eugnluq Ymrrsqu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b1c1e8b5b7b8f4ede9b54720", "heatmap": "e-b1c1e8b5b7b8f4ede9b54720", "modelKey": "event", "timestamp": null }, { "id": "97aff8d980ffa82ba6fdfe4c", "type": "sensorMotion", "start": 1642979671518, "end": 1642979671518, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cpdfxed Ulcgjlq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-97aff8d980ffa82ba6fdfe4c", "heatmap": "e-97aff8d980ffa82ba6fdfe4c", "modelKey": "event", "timestamp": null }, { "id": "aae4a7f10fedfac3eae036b4", "type": "sensorMotion", "start": 1642979674994, "end": 1642979674994, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nwk Sybcwv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aae4a7f10fedfac3eae036b4", "heatmap": "e-aae4a7f10fedfac3eae036b4", "modelKey": "event", "timestamp": null }, { "id": "17066c1d89dc60fc4c50e450", "type": "sensorMotion", "start": 1642979680284, "end": 1642979680284, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Phn Yvcvgmp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-17066c1d89dc60fc4c50e450", "heatmap": "e-17066c1d89dc60fc4c50e450", "modelKey": "event", "timestamp": null }, { "id": "79851ffab5d129ea5afdf57b", "type": "sensorMotion", "start": 1642979682978, "end": 1642979682978, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uaoqz Wvgeis" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-79851ffab5d129ea5afdf57b", "heatmap": "e-79851ffab5d129ea5afdf57b", "modelKey": "event", "timestamp": null }, { "id": "1e35567d2c6afe74f7419a8c", "type": "sensorMotion", "start": 1642979687273, "end": 1642979687273, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hbyt Yewwid" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1e35567d2c6afe74f7419a8c", "heatmap": "e-1e35567d2c6afe74f7419a8c", "modelKey": "event", "timestamp": null }, { "id": "e307a4ba2cb2650b088db105", "type": "sensorMotion", "start": 1642979695209, "end": 1642979695209, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cjbiws Senki" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e307a4ba2cb2650b088db105", "heatmap": "e-e307a4ba2cb2650b088db105", "modelKey": "event", "timestamp": null }, { "id": "33aa2cc2ba2d70ae4bf84575", "type": "sensorMotion", "start": 1642979700617, "end": 1642979700617, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sfo Cdqd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-33aa2cc2ba2d70ae4bf84575", "heatmap": "e-33aa2cc2ba2d70ae4bf84575", "modelKey": "event", "timestamp": null }, { "id": "0070932c00112a891cd7d84a", "type": "sensorMotion", "start": 1642979704994, "end": 1642979704994, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ncaykcn Lnly" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0070932c00112a891cd7d84a", "heatmap": "e-0070932c00112a891cd7d84a", "modelKey": "event", "timestamp": null }, { "id": "012f74810039927bf4d4aae3", "type": "motion", "start": 1642979780217, "end": 1642979803232, "score": 6, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-012f74810039927bf4d4aae3", "heatmap": "e-012f74810039927bf4d4aae3", "modelKey": "event", "timestamp": 1642979551232 }, { "id": "efd419551a60f2352c587716", "type": "motion", "start": 1642979901163, "end": 1642979922329, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-efd419551a60f2352c587716", "heatmap": "e-efd419551a60f2352c587716", "modelKey": "event", "timestamp": 1642979646369 }, { "id": "46ee2925d684c7181def7671", "type": "motion", "start": 1642979903355, "end": 1642979926388, "score": 14, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-46ee2925d684c7181def7671", "heatmap": "e-46ee2925d684c7181def7671", "modelKey": "event", "timestamp": 1642979551232 }, { "id": "ef3816c94c5a3b3f0bc8bfc9", "type": "motion", "start": 1642980041292, "end": 1642980069728, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ef3816c94c5a3b3f0bc8bfc9", "heatmap": "e-ef3816c94c5a3b3f0bc8bfc9", "modelKey": "event", "timestamp": 1642980055410 }, { "id": "72dd8f7f55b9ec55d921a998", "type": "motion", "start": 1642980045300, "end": 1642980067632, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-72dd8f7f55b9ec55d921a998", "heatmap": "e-72dd8f7f55b9ec55d921a998", "modelKey": "event", "timestamp": 1642980057466 }, { "id": "804ae3f4ac357f690be7edc5", "type": "motion", "start": 1642980053163, "end": 1642980075162, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-804ae3f4ac357f690be7edc5", "heatmap": "e-804ae3f4ac357f690be7edc5", "modelKey": "event", "timestamp": 1642979551232 }, { "id": "027d78b9ee38e776b6caee91", "type": "sensorMotion", "start": 1642980053586, "end": 1642980053586, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Prb Rycs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-027d78b9ee38e776b6caee91", "heatmap": "e-027d78b9ee38e776b6caee91", "modelKey": "event", "timestamp": null }, { "id": "ddaec34412b6f9972b83fe30", "type": "sensorMotion", "start": 1642980057597, "end": 1642980057597, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rncjw Ore" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ddaec34412b6f9972b83fe30", "heatmap": "e-ddaec34412b6f9972b83fe30", "modelKey": "event", "timestamp": null }, { "id": "3997a67b585b2b1573f1740e", "type": "sensorMotion", "start": 1642980062504, "end": 1642980062504, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Udnjvx Ewgl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3997a67b585b2b1573f1740e", "heatmap": "e-3997a67b585b2b1573f1740e", "modelKey": "event", "timestamp": null }, { "id": "5f23dad0a12dd83a778a8aa9", "type": "sensorMotion", "start": 1642980068249, "end": 1642980068249, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hxmwmaf Lzsj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5f23dad0a12dd83a778a8aa9", "heatmap": "e-5f23dad0a12dd83a778a8aa9", "modelKey": "event", "timestamp": null }, { "id": "d08390d5f920b1b1ef59151b", "type": "motion", "start": 1642980071754, "end": 1642980093560, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d08390d5f920b1b1ef59151b", "heatmap": "e-d08390d5f920b1b1ef59151b", "modelKey": "event", "timestamp": 1642979551232 }, { "id": "bab0fedeb65e4ca2f7d8bc1e", "type": "smartDetectZone", "start": 1642980079109, "end": 1642980281265, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bab0fedeb65e4ca2f7d8bc1e", "heatmap": "e-bab0fedeb65e4ca2f7d8bc1e", "modelKey": "event", "timestamp": 1642980113071 }, { "id": "32193c85c1f69df89c7f68d1", "type": "motion", "start": 1642980081980, "end": 1642980103396, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "bab0fedeb65e4ca2f7d8bc1e" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-32193c85c1f69df89c7f68d1", "heatmap": "e-32193c85c1f69df89c7f68d1", "modelKey": "event", "timestamp": 1642980094479 }, { "id": "c2e8544dd0d5e59889369115", "type": "sensorMotion", "start": 1642980089716, "end": 1642980089716, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qbdkh Gvteug" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c2e8544dd0d5e59889369115", "heatmap": "e-c2e8544dd0d5e59889369115", "modelKey": "event", "timestamp": null }, { "id": "7399cfd7a5c33672ce1fa268", "type": "motion", "start": 1642980092802, "end": 1642980114460, "score": 99, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7399cfd7a5c33672ce1fa268", "heatmap": "e-7399cfd7a5c33672ce1fa268", "modelKey": "event", "timestamp": 1642980104128 }, { "id": "9509c5a2c26a6368fcbdc2c9", "type": "motion", "start": 1642980093226, "end": 1642980115321, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9509c5a2c26a6368fcbdc2c9", "heatmap": "e-9509c5a2c26a6368fcbdc2c9", "modelKey": "event", "timestamp": 1642980106394 }, { "id": "1f56103deb53f188130b1c41", "type": "sensorMotion", "start": 1642980095381, "end": 1642980095381, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Durjnea Alh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1f56103deb53f188130b1c41", "heatmap": "e-1f56103deb53f188130b1c41", "modelKey": "event", "timestamp": null }, { "id": "d7510485cc0f912117514a0b", "type": "sensorMotion", "start": 1642980098987, "end": 1642980098987, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dpcf Gru" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d7510485cc0f912117514a0b", "heatmap": "e-d7510485cc0f912117514a0b", "modelKey": "event", "timestamp": null }, { "id": "ba92048cba21931f5f4e2239", "type": "sensorMotion", "start": 1642980104786, "end": 1642980104786, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gtw Wfxtj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ba92048cba21931f5f4e2239", "heatmap": "e-ba92048cba21931f5f4e2239", "modelKey": "event", "timestamp": null }, { "id": "e3db9867110c2df2a792dbb1", "type": "sensorMotion", "start": 1642980108658, "end": 1642980108658, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ttjv Eooqbrh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e3db9867110c2df2a792dbb1", "heatmap": "e-e3db9867110c2df2a792dbb1", "modelKey": "event", "timestamp": null }, { "id": "9f55ff4c4c76927c3790e83b", "type": "sensorMotion", "start": 1642980111748, "end": 1642980111748, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Adzgi Kag" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9f55ff4c4c76927c3790e83b", "heatmap": "e-9f55ff4c4c76927c3790e83b", "modelKey": "event", "timestamp": null }, { "id": "6f84b35eb6816bfe4731238f", "type": "sensorMotion", "start": 1642980116255, "end": 1642980116255, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zoalcpm Wxivqw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6f84b35eb6816bfe4731238f", "heatmap": "e-6f84b35eb6816bfe4731238f", "modelKey": "event", "timestamp": null }, { "id": "5d5de998abb19d9c05f06b11", "type": "sensorMotion", "start": 1642980120747, "end": 1642980120747, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vve Hjgnogu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5d5de998abb19d9c05f06b11", "heatmap": "e-5d5de998abb19d9c05f06b11", "modelKey": "event", "timestamp": null }, { "id": "224dac5d37aa03b649683242", "type": "sensorMotion", "start": 1642980123851, "end": 1642980123851, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Etq Sqny" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-224dac5d37aa03b649683242", "heatmap": "e-224dac5d37aa03b649683242", "modelKey": "event", "timestamp": null }, { "id": "673432c66ef08cfbccd87fdf", "type": "sensorMotion", "start": 1642980131818, "end": 1642980131818, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wvsiz Eixs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-673432c66ef08cfbccd87fdf", "heatmap": "e-673432c66ef08cfbccd87fdf", "modelKey": "event", "timestamp": null }, { "id": "613a3eceee5443e2b05c2589", "type": "sensorMotion", "start": 1642980135939, "end": 1642980135939, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dcw Cwob" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-613a3eceee5443e2b05c2589", "heatmap": "e-613a3eceee5443e2b05c2589", "modelKey": "event", "timestamp": null }, { "id": "58fb6ce5f8a46ef713144da2", "type": "sensorMotion", "start": 1642980140026, "end": 1642980140026, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zwwppl Kznbxoo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-58fb6ce5f8a46ef713144da2", "heatmap": "e-58fb6ce5f8a46ef713144da2", "modelKey": "event", "timestamp": null }, { "id": "692ed11c9f6b5545761c8a01", "type": "sensorMotion", "start": 1642980147701, "end": 1642980147701, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ydxcr Vubjepl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-692ed11c9f6b5545761c8a01", "heatmap": "e-692ed11c9f6b5545761c8a01", "modelKey": "event", "timestamp": null }, { "id": "2ae0464f74b21be5ae619104", "type": "sensorMotion", "start": 1642980152520, "end": 1642980152520, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dpkja Grtga" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2ae0464f74b21be5ae619104", "heatmap": "e-2ae0464f74b21be5ae619104", "modelKey": "event", "timestamp": null }, { "id": "6a3a3532ef1a84d610fb1ce4", "type": "motion", "start": 1642980153636, "end": 1642980175304, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a3a3532ef1a84d610fb1ce4", "heatmap": "e-6a3a3532ef1a84d610fb1ce4", "modelKey": "event", "timestamp": 1642980165137 }, { "id": "52620998ba9314f34415a19e", "type": "sensorMotion", "start": 1642980156894, "end": 1642980156894, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cchgrq Mylsqyp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-52620998ba9314f34415a19e", "heatmap": "e-52620998ba9314f34415a19e", "modelKey": "event", "timestamp": null }, { "id": "b271eedcbfe979c918a0188c", "type": "sensorMotion", "start": 1642980161530, "end": 1642980161530, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nvu Lrpuk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b271eedcbfe979c918a0188c", "heatmap": "e-b271eedcbfe979c918a0188c", "modelKey": "event", "timestamp": null }, { "id": "29856a237202fff20cdac328", "type": "sensorMotion", "start": 1642980165907, "end": 1642980165907, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ozvjz Wsala" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-29856a237202fff20cdac328", "heatmap": "e-29856a237202fff20cdac328", "modelKey": "event", "timestamp": null }, { "id": "e310528236a9745d32128da6", "type": "sensorMotion", "start": 1642980170451, "end": 1642980170451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gnc Ubsg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e310528236a9745d32128da6", "heatmap": "e-e310528236a9745d32128da6", "modelKey": "event", "timestamp": null }, { "id": "170bb5ba868e3ba0604a0f53", "type": "sensorMotion", "start": 1642980173504, "end": 1642980173504, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wokipn Xzqy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-170bb5ba868e3ba0604a0f53", "heatmap": "e-170bb5ba868e3ba0604a0f53", "modelKey": "event", "timestamp": null }, { "id": "4044b2272420da05a9b27bf0", "type": "sensorMotion", "start": 1642980190931, "end": 1642980190931, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dqy Agdmwm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4044b2272420da05a9b27bf0", "heatmap": "e-4044b2272420da05a9b27bf0", "modelKey": "event", "timestamp": null }, { "id": "0de3fa15d0c1dd9e2c124ea9", "type": "sensorMotion", "start": 1642980195906, "end": 1642980195906, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Anw Rwpejhk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0de3fa15d0c1dd9e2c124ea9", "heatmap": "e-0de3fa15d0c1dd9e2c124ea9", "modelKey": "event", "timestamp": null }, { "id": "32cee6034cee45a5c077fc8b", "type": "sensorMotion", "start": 1642980203117, "end": 1642980203117, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Etvjjmq Ggz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-32cee6034cee45a5c077fc8b", "heatmap": "e-32cee6034cee45a5c077fc8b", "modelKey": "event", "timestamp": null }, { "id": "6da684e6813e32cdc3ab3c60", "type": "sensorMotion", "start": 1642980207113, "end": 1642980207113, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dnu Kifvr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6da684e6813e32cdc3ab3c60", "heatmap": "e-6da684e6813e32cdc3ab3c60", "modelKey": "event", "timestamp": null }, { "id": "1e5e7221280bc21ad147de02", "type": "sensorMotion", "start": 1642980213418, "end": 1642980213418, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ssevukv Ocxfh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1e5e7221280bc21ad147de02", "heatmap": "e-1e5e7221280bc21ad147de02", "modelKey": "event", "timestamp": null }, { "id": "aeeb5be2ef200f7d12c6dc5f", "type": "sensorMotion", "start": 1642980217411, "end": 1642980217411, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vjcredc Ims" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aeeb5be2ef200f7d12c6dc5f", "heatmap": "e-aeeb5be2ef200f7d12c6dc5f", "modelKey": "event", "timestamp": null }, { "id": "abfa4a1b7b220b3b5bd05e9b", "type": "sensorMotion", "start": 1642980223460, "end": 1642980223460, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wojqdb Mbizzwt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-abfa4a1b7b220b3b5bd05e9b", "heatmap": "e-abfa4a1b7b220b3b5bd05e9b", "modelKey": "event", "timestamp": null }, { "id": "e6572172ffc753871770489f", "type": "sensorMotion", "start": 1642980230542, "end": 1642980230542, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tpgpn Vxiyj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e6572172ffc753871770489f", "heatmap": "e-e6572172ffc753871770489f", "modelKey": "event", "timestamp": null }, { "id": "c004d26f89049017a82644e4", "type": "sensorMotion", "start": 1642980234533, "end": 1642980234533, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fus Pdmhb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c004d26f89049017a82644e4", "heatmap": "e-c004d26f89049017a82644e4", "modelKey": "event", "timestamp": null }, { "id": "beb5235526975c27cc8b5e01", "type": "sensorMotion", "start": 1642980238654, "end": 1642980238654, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oewjezs Atlvqns" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-beb5235526975c27cc8b5e01", "heatmap": "e-beb5235526975c27cc8b5e01", "modelKey": "event", "timestamp": null }, { "id": "536f58e951f77fb327180949", "type": "sensorMotion", "start": 1642980242645, "end": 1642980242645, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cjhz Coksvoa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-536f58e951f77fb327180949", "heatmap": "e-536f58e951f77fb327180949", "modelKey": "event", "timestamp": null }, { "id": "abe7805281caaf79eae92dd9", "type": "motion", "start": 1642980243637, "end": 1642980268306, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-abe7805281caaf79eae92dd9", "heatmap": "e-abe7805281caaf79eae92dd9", "modelKey": "event", "timestamp": 1642980256304 }, { "id": "df23e3b0136ebaa195295595", "type": "motion", "start": 1642980247233, "end": 1642980276293, "score": 83, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-df23e3b0136ebaa195295595", "heatmap": "e-df23e3b0136ebaa195295595", "modelKey": "event", "timestamp": 1642980265120 }, { "id": "b84f6cad517fac4d3a0cdc77", "type": "sensorMotion", "start": 1642980247932, "end": 1642980247932, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Anbcs Hygd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b84f6cad517fac4d3a0cdc77", "heatmap": "e-b84f6cad517fac4d3a0cdc77", "modelKey": "event", "timestamp": null }, { "id": "2a17cd129d52642572680eac", "type": "sensorMotion", "start": 1642980252173, "end": 1642980252173, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Prte Admwnhj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2a17cd129d52642572680eac", "heatmap": "e-2a17cd129d52642572680eac", "modelKey": "event", "timestamp": null }, { "id": "9fb6e60b33c888935a1740fe", "type": "sensorMotion", "start": 1642980256036, "end": 1642980256036, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qcudy Osylzp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9fb6e60b33c888935a1740fe", "heatmap": "e-9fb6e60b33c888935a1740fe", "modelKey": "event", "timestamp": null }, { "id": "e792a74724e965a90613ff29", "type": "motion", "start": 1642980256733, "end": 1642980280901, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "bab0fedeb65e4ca2f7d8bc1e" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e792a74724e965a90613ff29", "heatmap": "e-e792a74724e965a90613ff29", "modelKey": "event", "timestamp": 1642980267900 }, { "id": "daf40e5546f21769f8e0f0de", "type": "sensorMotion", "start": 1642980260156, "end": 1642980260156, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Khfg Ewsouz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-daf40e5546f21769f8e0f0de", "heatmap": "e-daf40e5546f21769f8e0f0de", "modelKey": "event", "timestamp": null }, { "id": "287b5b1ac3dccf9f49ea3ebc", "type": "sensorMotion", "start": 1642980264147, "end": 1642980264147, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Niaenc Gpno" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-287b5b1ac3dccf9f49ea3ebc", "heatmap": "e-287b5b1ac3dccf9f49ea3ebc", "modelKey": "event", "timestamp": null }, { "id": "b9400469d12a3ed367bcf110", "type": "sensorMotion", "start": 1642980268181, "end": 1642980268181, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Lvuahx Cfhpuo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b9400469d12a3ed367bcf110", "heatmap": "e-b9400469d12a3ed367bcf110", "modelKey": "event", "timestamp": null }, { "id": "c3af9d3bddd1fab315d6e2d5", "type": "sensorMotion", "start": 1642980272774, "end": 1642980272774, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uufuy Qpbnscb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c3af9d3bddd1fab315d6e2d5", "heatmap": "e-c3af9d3bddd1fab315d6e2d5", "modelKey": "event", "timestamp": null }, { "id": "86e3fdb96b1a5af5f687dedd", "type": "sensorMotion", "start": 1642980275993, "end": 1642980275993, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yihsei Qgo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-86e3fdb96b1a5af5f687dedd", "heatmap": "e-86e3fdb96b1a5af5f687dedd", "modelKey": "event", "timestamp": null }, { "id": "d1a404c444315cad5ecb2252", "type": "sensorMotion", "start": 1642980279727, "end": 1642980279727, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ksx Ntav" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d1a404c444315cad5ecb2252", "heatmap": "e-d1a404c444315cad5ecb2252", "modelKey": "event", "timestamp": null }, { "id": "b9b8dfb4e28cebde4a317e92", "type": "sensorMotion", "start": 1642980285263, "end": 1642980285263, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wlskxq Fzhcmh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b9b8dfb4e28cebde4a317e92", "heatmap": "e-b9b8dfb4e28cebde4a317e92", "modelKey": "event", "timestamp": null }, { "id": "9670d8e96d745c4cc0204f61", "type": "smartDetectZone", "start": 1642980287051, "end": 1642980324622, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9670d8e96d745c4cc0204f61", "heatmap": "e-9670d8e96d745c4cc0204f61", "modelKey": "event", "timestamp": 1642980298051 }, { "id": "af5b6785aa4e7e7710c18e3f", "type": "sensorMotion", "start": 1642980292216, "end": 1642980292216, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rduk Bzmzh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-af5b6785aa4e7e7710c18e3f", "heatmap": "e-af5b6785aa4e7e7710c18e3f", "modelKey": "event", "timestamp": null }, { "id": "1c9521222088145b10e361b5", "type": "sensorMotion", "start": 1642980296207, "end": 1642980296207, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kjsdko Gex" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1c9521222088145b10e361b5", "heatmap": "e-1c9521222088145b10e361b5", "modelKey": "event", "timestamp": null }, { "id": "1a22d566430aa2e12f7ec641", "type": "motion", "start": 1642980297760, "end": 1642980336380, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "9670d8e96d745c4cc0204f61", "19ea4764cc26fda4b87f01db" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1a22d566430aa2e12f7ec641", "heatmap": "e-1a22d566430aa2e12f7ec641", "modelKey": "event", "timestamp": 1642980309758 }, { "id": "0c414f26ffb5c3888be64a66", "type": "sensorMotion", "start": 1642980302001, "end": 1642980302001, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vkvxbxe Dfayzr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0c414f26ffb5c3888be64a66", "heatmap": "e-0c414f26ffb5c3888be64a66", "modelKey": "event", "timestamp": null }, { "id": "19673d8f416b4ed8eafec3a2", "type": "sensorMotion", "start": 1642980305994, "end": 1642980305994, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tfhht Hrgrumu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-19673d8f416b4ed8eafec3a2", "heatmap": "e-19673d8f416b4ed8eafec3a2", "modelKey": "event", "timestamp": null }, { "id": "19ea4764cc26fda4b87f01db", "type": "smartDetectZone", "start": 1642980309065, "end": 1642980336652, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-19ea4764cc26fda4b87f01db", "heatmap": "e-19ea4764cc26fda4b87f01db", "modelKey": "event", "timestamp": 1642980320374 }, { "id": "070a287f7af0b1494785facf", "type": "sensorMotion", "start": 1642980309984, "end": 1642980309984, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Alprs Kotu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-070a287f7af0b1494785facf", "heatmap": "e-070a287f7af0b1494785facf", "modelKey": "event", "timestamp": null }, { "id": "71a928bf5b3db2d034e5b7ae", "type": "sensorMotion", "start": 1642980314104, "end": 1642980314104, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ibglt Zkwl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-71a928bf5b3db2d034e5b7ae", "heatmap": "e-71a928bf5b3db2d034e5b7ae", "modelKey": "event", "timestamp": null }, { "id": "90781c6cc79618bd779c0272", "type": "sensorMotion", "start": 1642980321315, "end": 1642980321315, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Aoi Kxrech" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-90781c6cc79618bd779c0272", "heatmap": "e-90781c6cc79618bd779c0272", "modelKey": "event", "timestamp": null }, { "id": "3e52845e6e40c69490d40230", "type": "sensorMotion", "start": 1642980325950, "end": 1642980325950, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fpj Uzsd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3e52845e6e40c69490d40230", "heatmap": "e-3e52845e6e40c69490d40230", "modelKey": "event", "timestamp": null }, { "id": "8150c7143469c1c121c77df5", "type": "motion", "start": 1642980431941, "end": 1642980452943, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8150c7143469c1c121c77df5", "heatmap": "e-8150c7143469c1c121c77df5", "modelKey": "event", "timestamp": 1642980165137 }, { "id": "6f0b3b1c4ddbcc681a54df2b", "type": "motion", "start": 1642980509738, "end": 1642980530905, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6f0b3b1c4ddbcc681a54df2b", "heatmap": "e-6f0b3b1c4ddbcc681a54df2b", "modelKey": "event", "timestamp": 1642980520905 }, { "id": "f1b5ae2f1ca54abc5f341ac9", "type": "motion", "start": 1642980514042, "end": 1642980535376, "score": 83, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f1b5ae2f1ca54abc5f341ac9", "heatmap": "e-f1b5ae2f1ca54abc5f341ac9", "modelKey": "event", "timestamp": 1642980525375 }, { "id": "0d8275f8c19fa12ab785171d", "type": "motion", "start": 1642980635259, "end": 1642980656263, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0d8275f8c19fa12ab785171d", "heatmap": "e-0d8275f8c19fa12ab785171d", "modelKey": "event", "timestamp": 1642980525375 }, { "id": "166f1c76c4cd0635b3813ec1", "type": "motion", "start": 1642980825897, "end": 1642980847241, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-166f1c76c4cd0635b3813ec1", "heatmap": "e-166f1c76c4cd0635b3813ec1", "modelKey": "event", "timestamp": 1642980837063 }, { "id": "5b10fb2707ae67ec9406972a", "type": "motion", "start": 1642980899280, "end": 1642980925445, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5b10fb2707ae67ec9406972a", "heatmap": "e-5b10fb2707ae67ec9406972a", "modelKey": "event", "timestamp": 1642980910447 }, { "id": "98798623f1bf984ee021cad8", "type": "motion", "start": 1642980900551, "end": 1642980925229, "score": 62, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-98798623f1bf984ee021cad8", "heatmap": "e-98798623f1bf984ee021cad8", "modelKey": "event", "timestamp": 1642980915221 }, { "id": "14bf578052f06a797b5be779", "type": "motion", "start": 1642981026841, "end": 1642981048003, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-14bf578052f06a797b5be779", "heatmap": "e-14bf578052f06a797b5be779", "modelKey": "event", "timestamp": 1642980915221 }, { "id": "c9f295c7f1d13648f8435d97", "type": "motion", "start": 1642981032345, "end": 1642981055349, "score": 17, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c9f295c7f1d13648f8435d97", "heatmap": "e-c9f295c7f1d13648f8435d97", "modelKey": "event", "timestamp": 1642980915221 }, { "id": "5adc6ae2565bfc450c9215e4", "type": "motion", "start": 1642981254777, "end": 1642981276775, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5adc6ae2565bfc450c9215e4", "heatmap": "e-5adc6ae2565bfc450c9215e4", "modelKey": "event", "timestamp": 1642981266611 }, { "id": "21f98ef036f1693a968026e6", "type": "motion", "start": 1642981257883, "end": 1642981280893, "score": 26, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-21f98ef036f1693a968026e6", "heatmap": "e-21f98ef036f1693a968026e6", "modelKey": "event", "timestamp": 1642980915221 }, { "id": "d70cae6008807ab3d2ee82a2", "type": "motion", "start": 1642981270912, "end": 1642981293246, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d70cae6008807ab3d2ee82a2", "heatmap": "e-d70cae6008807ab3d2ee82a2", "modelKey": "event", "timestamp": 1642981282079 }, { "id": "df6855995b8808c3dd9b011a", "type": "motion", "start": 1642981279106, "end": 1642981304103, "score": 59, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-df6855995b8808c3dd9b011a", "heatmap": "e-df6855995b8808c3dd9b011a", "modelKey": "event", "timestamp": 1642981291789 }, { "id": "73c23fca3795c231337a69a7", "type": "motion", "start": 1642981385090, "end": 1642981408101, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-73c23fca3795c231337a69a7", "heatmap": "e-73c23fca3795c231337a69a7", "modelKey": "event", "timestamp": 1642981282079 }, { "id": "2f14cdec98df8d44efe4b203", "type": "motion", "start": 1642981481705, "end": 1642981504706, "score": 22, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2f14cdec98df8d44efe4b203", "heatmap": "e-2f14cdec98df8d44efe4b203", "modelKey": "event", "timestamp": 1642980106394 }, { "id": "2658cb19358043d508e2c03d", "type": "motion", "start": 1642981499119, "end": 1642981533697, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2658cb19358043d508e2c03d", "heatmap": "e-2658cb19358043d508e2c03d", "modelKey": "event", "timestamp": 1642981520474 }, { "id": "6e56bf7601cc699fe4a02023", "type": "motion", "start": 1642981500325, "end": 1642981524004, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6e56bf7601cc699fe4a02023", "heatmap": "e-6e56bf7601cc699fe4a02023", "modelKey": "event", "timestamp": 1642981511492 }, { "id": "396bbb455a8272a4bed796cc", "type": "motion", "start": 1642981593969, "end": 1642981615309, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-396bbb455a8272a4bed796cc", "heatmap": "e-396bbb455a8272a4bed796cc", "modelKey": "event", "timestamp": 1642981605136 }, { "id": "338940810c2cf850e466d172", "type": "sensorMotion", "start": 1642981645292, "end": 1642981645292, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qfkpck Vwobrlv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-338940810c2cf850e466d172", "heatmap": "e-338940810c2cf850e466d172", "modelKey": "event", "timestamp": null }, { "id": "77565f29622fe31b9ec5ae81", "type": "motion", "start": 1642981842676, "end": 1642981864014, "score": 82, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-77565f29622fe31b9ec5ae81", "heatmap": "e-77565f29622fe31b9ec5ae81", "modelKey": "event", "timestamp": 1642981853842 }, { "id": "97238e951e7fcd04fd715f7b", "type": "motion", "start": 1642981929145, "end": 1642981950811, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-97238e951e7fcd04fd715f7b", "heatmap": "e-97238e951e7fcd04fd715f7b", "modelKey": "event", "timestamp": 1642981520474 }, { "id": "157527ac0a0cb729b1b7a3fb", "type": "motion", "start": 1642981935050, "end": 1642981959900, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-157527ac0a0cb729b1b7a3fb", "heatmap": "e-157527ac0a0cb729b1b7a3fb", "modelKey": "event", "timestamp": 1642981947884 }, { "id": "1b26dc0a8d5c3e7049df2924", "type": "motion", "start": 1642981977983, "end": 1642982011359, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1b26dc0a8d5c3e7049df2924", "heatmap": "e-1b26dc0a8d5c3e7049df2924", "modelKey": "event", "timestamp": 1642981989651 }, { "id": "4881660fc5c799f9a529c9f1", "type": "sensorMotion", "start": 1642981981952, "end": 1642981981952, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Pyqxiy Vayq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4881660fc5c799f9a529c9f1", "heatmap": "e-4881660fc5c799f9a529c9f1", "modelKey": "event", "timestamp": null }, { "id": "c024bf026307942aaa96d8f2", "type": "motion", "start": 1642981985632, "end": 1642982009626, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c024bf026307942aaa96d8f2", "heatmap": "e-c024bf026307942aaa96d8f2", "modelKey": "event", "timestamp": 1642981998964 }, { "id": "722a50aac30b732bb4dada38", "type": "sensorMotion", "start": 1642981992148, "end": 1642981992148, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ldfj Gsffkqr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-722a50aac30b732bb4dada38", "heatmap": "e-722a50aac30b732bb4dada38", "modelKey": "event", "timestamp": null }, { "id": "4f731a4e0ff3dd1b568fe378", "type": "motion", "start": 1642981999643, "end": 1642982023292, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4f731a4e0ff3dd1b568fe378", "heatmap": "e-4f731a4e0ff3dd1b568fe378", "modelKey": "event", "timestamp": 1642982012458 }, { "id": "6a63b13a26a8901173dc0f5e", "type": "motion", "start": 1642982015598, "end": 1642982038272, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a63b13a26a8901173dc0f5e", "heatmap": "e-6a63b13a26a8901173dc0f5e", "modelKey": "event", "timestamp": 1642982026765 }, { "id": "f93510309c6fe98ba19fde92", "type": "motion", "start": 1642982119526, "end": 1642982141691, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f93510309c6fe98ba19fde92", "heatmap": "e-f93510309c6fe98ba19fde92", "modelKey": "event", "timestamp": 1642982131359 }, { "id": "e089fdba33554bb14400c6a2", "type": "motion", "start": 1642982125007, "end": 1642982148175, "score": 60, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e089fdba33554bb14400c6a2", "heatmap": "e-e089fdba33554bb14400c6a2", "modelKey": "event", "timestamp": 1642982136174 }, { "id": "685375fbcd98b281b35ff2ab", "type": "motion", "start": 1642982159385, "end": 1642982184552, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-685375fbcd98b281b35ff2ab", "heatmap": "e-685375fbcd98b281b35ff2ab", "modelKey": "event", "timestamp": 1642982170873 }, { "id": "594fae76fd3fcac08da9b15d", "type": "motion", "start": 1642982183763, "end": 1642982206954, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-594fae76fd3fcac08da9b15d", "heatmap": "e-594fae76fd3fcac08da9b15d", "modelKey": "event", "timestamp": 1642982194930 }, { "id": "d4ee04ddf038bddd26b0fe3c", "type": "motion", "start": 1642982216030, "end": 1642982238027, "score": 99, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d4ee04ddf038bddd26b0fe3c", "heatmap": "e-d4ee04ddf038bddd26b0fe3c", "modelKey": "event", "timestamp": 1642982227196 }, { "id": "705a25209a7461e974c55d08", "type": "motion", "start": 1642982257395, "end": 1642982281890, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-705a25209a7461e974c55d08", "heatmap": "e-705a25209a7461e974c55d08", "modelKey": "event", "timestamp": 1642982268555 }, { "id": "606a1813d54bcbaa4199a9f5", "type": "sensorMotion", "start": 1642982260061, "end": 1642982260061, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cbjgil Pzbe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-606a1813d54bcbaa4199a9f5", "heatmap": "e-606a1813d54bcbaa4199a9f5", "modelKey": "event", "timestamp": null }, { "id": "783fd42848c83e3bbb02cbcf", "type": "motion", "start": 1642982262451, "end": 1642982288996, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-783fd42848c83e3bbb02cbcf", "heatmap": "e-783fd42848c83e3bbb02cbcf", "modelKey": "event", "timestamp": 1642982274119 }, { "id": "a59b9fe2c6a699221e95d97d", "type": "motion", "start": 1642982264675, "end": 1642982287179, "score": 45, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a59b9fe2c6a699221e95d97d", "heatmap": "e-a59b9fe2c6a699221e95d97d", "modelKey": "event", "timestamp": 1642982277176 }, { "id": "bf085ff0f0924a4531050d54", "type": "motion", "start": 1642982264933, "end": 1642982297101, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bf085ff0f0924a4531050d54", "heatmap": "e-bf085ff0f0924a4531050d54", "modelKey": "event", "timestamp": 1642982278433 }, { "id": "29aba140021d330c03e3bd4e", "type": "motion", "start": 1642982282586, "end": 1642982303924, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-29aba140021d330c03e3bd4e", "heatmap": "e-29aba140021d330c03e3bd4e", "modelKey": "event", "timestamp": 1642982293757 }, { "id": "b1b949177b3ff37b97884569", "type": "sensorMotion", "start": 1642982291864, "end": 1642982291864, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Eqedyrh Oakrojx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b1b949177b3ff37b97884569", "heatmap": "e-b1b949177b3ff37b97884569", "modelKey": "event", "timestamp": null }, { "id": "cf040d8bf08d41da2fb51985", "type": "sensorMotion", "start": 1642982303111, "end": 1642982303111, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Avb Xmc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cf040d8bf08d41da2fb51985", "heatmap": "e-cf040d8bf08d41da2fb51985", "modelKey": "event", "timestamp": null }, { "id": "965f824ddc7e089aa7582271", "type": "motion", "start": 1642982417061, "end": 1642982438226, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-965f824ddc7e089aa7582271", "heatmap": "e-965f824ddc7e089aa7582271", "modelKey": "event", "timestamp": 1642982278433 }, { "id": "1f8265b1f42a1eeb09cb83fc", "type": "motion", "start": 1642982539108, "end": 1642982567446, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1f8265b1f42a1eeb09cb83fc", "heatmap": "e-1f8265b1f42a1eeb09cb83fc", "modelKey": "event", "timestamp": 1642982554296 }, { "id": "def81c71bc3afd50524a8efa", "type": "motion", "start": 1642982850463, "end": 1642982872467, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-def81c71bc3afd50524a8efa", "heatmap": "e-def81c71bc3afd50524a8efa", "modelKey": "event", "timestamp": 1642982274119 }, { "id": "823ff4b6194cbbc9e0d7d573", "type": "motion", "start": 1642982988832, "end": 1642983011171, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-823ff4b6194cbbc9e0d7d573", "heatmap": "e-823ff4b6194cbbc9e0d7d573", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "05e74d0906958befe61d9241", "type": "motion", "start": 1642983114674, "end": 1642983136853, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-05e74d0906958befe61d9241", "heatmap": "e-05e74d0906958befe61d9241", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "3eb11b86e61a2dd1d7fd8253", "type": "motion", "start": 1642983699438, "end": 1642983720451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3eb11b86e61a2dd1d7fd8253", "heatmap": "e-3eb11b86e61a2dd1d7fd8253", "modelKey": "event", "timestamp": 1642982554296 }, { "id": "1343fd48b7221d66fc230d2d", "type": "motion", "start": 1642983711807, "end": 1642983734836, "score": 10, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1343fd48b7221d66fc230d2d", "heatmap": "e-1343fd48b7221d66fc230d2d", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "71b69378760b0a5a8831e229", "type": "motion", "start": 1642983814944, "end": 1642983835945, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-71b69378760b0a5a8831e229", "heatmap": "e-71b69378760b0a5a8831e229", "modelKey": "event", "timestamp": 1642982554296 }, { "id": "b17827872a73c8088dadd8c4", "type": "motion", "start": 1642983817014, "end": 1642983840015, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b17827872a73c8088dadd8c4", "heatmap": "e-b17827872a73c8088dadd8c4", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "5f119008d9ea5e1297a1ee7d", "type": "motion", "start": 1642984565894, "end": 1642984587396, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5f119008d9ea5e1297a1ee7d", "heatmap": "e-5f119008d9ea5e1297a1ee7d", "modelKey": "event", "timestamp": 1642984577061 }, { "id": "09b20ec083856e76d9eb9a68", "type": "motion", "start": 1642984710334, "end": 1642984731337, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-09b20ec083856e76d9eb9a68", "heatmap": "e-09b20ec083856e76d9eb9a68", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "1e7bc754027dab321db5149e", "type": "motion", "start": 1642984756783, "end": 1642984779119, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1e7bc754027dab321db5149e", "heatmap": "e-1e7bc754027dab321db5149e", "modelKey": "event", "timestamp": 1642984767950 }, { "id": "971b583f180105390e54093a", "type": "sensorMotion", "start": 1642984861839, "end": 1642984861839, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Yhzqai Jkjs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-971b583f180105390e54093a", "heatmap": "e-971b583f180105390e54093a", "modelKey": "event", "timestamp": null }, { "id": "28eca793d4fda155d349d174", "type": "motion", "start": 1642984930210, "end": 1642984959045, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-28eca793d4fda155d349d174", "heatmap": "e-28eca793d4fda155d349d174", "modelKey": "event", "timestamp": 1642984946712 }, { "id": "6863bea0c634fb1de3751b72", "type": "sensorMotion", "start": 1642984939161, "end": 1642984939161, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hlh Vtfoe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6863bea0c634fb1de3751b72", "heatmap": "e-6863bea0c634fb1de3751b72", "modelKey": "event", "timestamp": null }, { "id": "c61d1de7284b362d2410cd9e", "type": "sensorMotion", "start": 1642984946931, "end": 1642984946931, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vqbarl Sniu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c61d1de7284b362d2410cd9e", "heatmap": "e-c61d1de7284b362d2410cd9e", "modelKey": "event", "timestamp": null }, { "id": "0c10f186c8f8c9f985d1fdbf", "type": "motion", "start": 1642985297940, "end": 1642985320940, "score": 3, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0c10f186c8f8c9f985d1fdbf", "heatmap": "e-0c10f186c8f8c9f985d1fdbf", "modelKey": "event", "timestamp": 1642982999998 }, { "id": "cb32f49c542aa7f0ab6a21c1", "type": "motion", "start": 1642985815351, "end": 1642985839379, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cb32f49c542aa7f0ab6a21c1", "heatmap": "e-cb32f49c542aa7f0ab6a21c1", "modelKey": "event", "timestamp": 1642985827184 }, { "id": "05bbaf7a4e20c633ed41bea6", "type": "motion", "start": 1642985816148, "end": 1642985841151, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-05bbaf7a4e20c633ed41bea6", "heatmap": "e-05bbaf7a4e20c633ed41bea6", "modelKey": "event", "timestamp": 1642985827480 }, { "id": "626bab23b500dbd3d6784651", "type": "motion", "start": 1642985966228, "end": 1642985991395, "score": 61, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-626bab23b500dbd3d6784651", "heatmap": "e-626bab23b500dbd3d6784651", "modelKey": "event", "timestamp": 1642985978061 }, { "id": "07e29f5bb93f799de79f3e83", "type": "motion", "start": 1642985969384, "end": 1642985995903, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-07e29f5bb93f799de79f3e83", "heatmap": "e-07e29f5bb93f799de79f3e83", "modelKey": "event", "timestamp": 1642985980551 }, { "id": "54248175f82e8161c8be29f9", "type": "motion", "start": 1642985970916, "end": 1642985995100, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-54248175f82e8161c8be29f9", "heatmap": "e-54248175f82e8161c8be29f9", "modelKey": "event", "timestamp": 1642985982082 }, { "id": "51ad137854ea015e404ea532", "type": "motion", "start": 1642986106412, "end": 1642986137941, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-51ad137854ea015e404ea532", "heatmap": "e-51ad137854ea015e404ea532", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "3543790e952c01ca244ae773", "type": "motion", "start": 1642986288723, "end": 1642986314222, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3543790e952c01ca244ae773", "heatmap": "e-3543790e952c01ca244ae773", "modelKey": "event", "timestamp": 1642986302389 }, { "id": "7a3cbbda8feba32d65189f00", "type": "motion", "start": 1642986290243, "end": 1642986311913, "score": 55, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7a3cbbda8feba32d65189f00", "heatmap": "e-7a3cbbda8feba32d65189f00", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "ddc4f02ad4f664ee29b147a0", "type": "motion", "start": 1642986386777, "end": 1642986408451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ddc4f02ad4f664ee29b147a0", "heatmap": "e-ddc4f02ad4f664ee29b147a0", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "5b8145d61cf676d9132596e2", "type": "motion", "start": 1642986493459, "end": 1642986514624, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5b8145d61cf676d9132596e2", "heatmap": "e-5b8145d61cf676d9132596e2", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "f25e3d3396ab6ded8f3ece27", "type": "motion", "start": 1642986684541, "end": 1642986706540, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f25e3d3396ab6ded8f3ece27", "heatmap": "e-f25e3d3396ab6ded8f3ece27", "modelKey": "event", "timestamp": 1642986696225 }, { "id": "b3a7a2cb57daae850923e91d", "type": "motion", "start": 1642986926123, "end": 1642986947292, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b3a7a2cb57daae850923e91d", "heatmap": "e-b3a7a2cb57daae850923e91d", "modelKey": "event", "timestamp": 1642986937290 }, { "id": "c44e8af978f568335ae8fadd", "type": "motion", "start": 1642986977664, "end": 1642986999329, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c44e8af978f568335ae8fadd", "heatmap": "e-c44e8af978f568335ae8fadd", "modelKey": "event", "timestamp": 1642986988831 }, { "id": "477b1b2203e09b24d660b968", "type": "motion", "start": 1642987131125, "end": 1642987153294, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-477b1b2203e09b24d660b968", "heatmap": "e-477b1b2203e09b24d660b968", "modelKey": "event", "timestamp": 1642987142292 }, { "id": "f0887d644a754cc76e8c3236", "type": "motion", "start": 1642987374087, "end": 1642987395919, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f0887d644a754cc76e8c3236", "heatmap": "e-f0887d644a754cc76e8c3236", "modelKey": "event", "timestamp": 1642987385754 }, { "id": "97495d6526e3c7cca7c0dd6b", "type": "motion", "start": 1642987661997, "end": 1642987683672, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-97495d6526e3c7cca7c0dd6b", "heatmap": "e-97495d6526e3c7cca7c0dd6b", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "2925d7ed4af26f388d985673", "type": "motion", "start": 1642987663577, "end": 1642987685923, "score": 63, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2925d7ed4af26f388d985673", "heatmap": "e-2925d7ed4af26f388d985673", "modelKey": "event", "timestamp": 1642987674743 }, { "id": "2e8c2c758dc12aa55672adc7", "type": "motion", "start": 1642987795674, "end": 1642987817508, "score": 25, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2e8c2c758dc12aa55672adc7", "heatmap": "e-2e8c2c758dc12aa55672adc7", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "e4f8ff70ab3292b5832ba56d", "type": "motion", "start": 1642987876405, "end": 1642987897737, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e4f8ff70ab3292b5832ba56d", "heatmap": "e-e4f8ff70ab3292b5832ba56d", "modelKey": "event", "timestamp": 1642987887572 }, { "id": "937d37ea0ce05557f9ea4b55", "type": "motion", "start": 1642988787437, "end": 1642988810450, "score": 7, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-937d37ea0ce05557f9ea4b55", "heatmap": "e-937d37ea0ce05557f9ea4b55", "modelKey": "event", "timestamp": 1642986121247 }, { "id": "cd8a4b760b76d5a4e4cac7d2", "type": "motion", "start": 1642988807164, "end": 1642988828844, "score": 73, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cd8a4b760b76d5a4e4cac7d2", "heatmap": "e-cd8a4b760b76d5a4e4cac7d2", "modelKey": "event", "timestamp": 1642988818332 }, { "id": "4aff5f3f58d0b7dd77c63a38", "type": "motion", "start": 1642989033282, "end": 1642989055102, "score": 3, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4aff5f3f58d0b7dd77c63a38", "heatmap": "e-4aff5f3f58d0b7dd77c63a38", "modelKey": "event", "timestamp": 1642987674743 }, { "id": "ba859fe2f33022cac0fafcaa", "type": "motion", "start": 1642989684099, "end": 1642989709598, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ba859fe2f33022cac0fafcaa", "heatmap": "e-ba859fe2f33022cac0fafcaa", "modelKey": "event", "timestamp": 1642989698766 }, { "id": "010c6df0fb4e10885c705ae0", "type": "motion", "start": 1642989956563, "end": 1642989978396, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-010c6df0fb4e10885c705ae0", "heatmap": "e-010c6df0fb4e10885c705ae0", "modelKey": "event", "timestamp": 1642989967730 }, { "id": "e9c92094888c3cb9e7d091b0", "type": "motion", "start": 1642989963060, "end": 1642989989738, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e9c92094888c3cb9e7d091b0", "heatmap": "e-e9c92094888c3cb9e7d091b0", "modelKey": "event", "timestamp": 1642989974226 }, { "id": "e4f6505889319cf9dfc369f1", "type": "motion", "start": 1642989971727, "end": 1642989995561, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e4f6505889319cf9dfc369f1", "heatmap": "e-e4f6505889319cf9dfc369f1", "modelKey": "event", "timestamp": 1642989985394 }, { "id": "59303cab874fd7b7ac9557c6", "type": "motion", "start": 1642990071356, "end": 1642990092867, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-59303cab874fd7b7ac9557c6", "heatmap": "e-59303cab874fd7b7ac9557c6", "modelKey": "event", "timestamp": 1642988818332 }, { "id": "14d5c76bc5c26ab11e298391", "type": "motion", "start": 1642990098360, "end": 1642990119544, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-14d5c76bc5c26ab11e298391", "heatmap": "e-14d5c76bc5c26ab11e298391", "modelKey": "event", "timestamp": 1642989985394 }, { "id": "2bf353750e888039105a2962", "type": "motion", "start": 1642990638754, "end": 1642990660419, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2bf353750e888039105a2962", "heatmap": "e-2bf353750e888039105a2962", "modelKey": "event", "timestamp": 1642990650254 }, { "id": "b2715fffa15b84578d1bc6ca", "type": "motion", "start": 1642990640789, "end": 1642990663799, "score": 13, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b2715fffa15b84578d1bc6ca", "heatmap": "e-b2715fffa15b84578d1bc6ca", "modelKey": "event", "timestamp": 1642988818332 }, { "id": "c85fb3660dd72626440ee820", "type": "motion", "start": 1642991365990, "end": 1642991387322, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c85fb3660dd72626440ee820", "heatmap": "e-c85fb3660dd72626440ee820", "modelKey": "event", "timestamp": 1642990650254 }, { "id": "3d1f1c0c06af22705c38c78c", "type": "motion", "start": 1642991661449, "end": 1642991682615, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3d1f1c0c06af22705c38c78c", "heatmap": "e-3d1f1c0c06af22705c38c78c", "modelKey": "event", "timestamp": 1642990650254 }, { "id": "64b0d74996601b9938ee9d04", "type": "motion", "start": 1642991666729, "end": 1642991689729, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-64b0d74996601b9938ee9d04", "heatmap": "e-64b0d74996601b9938ee9d04", "modelKey": "event", "timestamp": 1642991677895 }, { "id": "71b6485c5b27d91ee58b4e53", "type": "motion", "start": 1642991784620, "end": 1642991806454, "score": 14, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-71b6485c5b27d91ee58b4e53", "heatmap": "e-71b6485c5b27d91ee58b4e53", "modelKey": "event", "timestamp": 1642990650254 }, { "id": "6de9e4ba570b12d96d05076a", "type": "motion", "start": 1642992048883, "end": 1642992069883, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6de9e4ba570b12d96d05076a", "heatmap": "e-6de9e4ba570b12d96d05076a", "modelKey": "event", "timestamp": 1642990650254 }, { "id": "04c9883fd8000ffa24b1fcec", "type": "motion", "start": 1642992503198, "end": 1642992524873, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-04c9883fd8000ffa24b1fcec", "heatmap": "e-04c9883fd8000ffa24b1fcec", "modelKey": "event", "timestamp": 1642988818332 }, { "id": "a16b0181776518463f0c4c80", "type": "motion", "start": 1642993027479, "end": 1642993051146, "score": 99, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a16b0181776518463f0c4c80", "heatmap": "e-a16b0181776518463f0c4c80", "modelKey": "event", "timestamp": 1642993038644 }, { "id": "a6d8b4043244526e8f97a0f1", "type": "sensorMotion", "start": 1642993035845, "end": 1642993035845, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mhprgo Drrcma" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a6d8b4043244526e8f97a0f1", "heatmap": "e-a6d8b4043244526e8f97a0f1", "modelKey": "event", "timestamp": null }, { "id": "abf951246af4242311a5d73d", "type": "motion", "start": 1642993065683, "end": 1642993088521, "score": 50, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-abf951246af4242311a5d73d", "heatmap": "e-abf951246af4242311a5d73d", "modelKey": "event", "timestamp": 1642993038644 }, { "id": "26b867a2db0eba6d2c63b7a9", "type": "motion", "start": 1642993121455, "end": 1642993146617, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-26b867a2db0eba6d2c63b7a9", "heatmap": "e-26b867a2db0eba6d2c63b7a9", "modelKey": "event", "timestamp": 1642993134452 }, { "id": "0c7e615733ea56b10cd4ee99", "type": "motion", "start": 1642993126445, "end": 1642993147947, "score": 63, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0c7e615733ea56b10cd4ee99", "heatmap": "e-0c7e615733ea56b10cd4ee99", "modelKey": "event", "timestamp": 1642993137946 }, { "id": "701ee12baac34fdfde6103ab", "type": "motion", "start": 1642993130332, "end": 1642993152333, "score": 61, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-701ee12baac34fdfde6103ab", "heatmap": "e-701ee12baac34fdfde6103ab", "modelKey": "event", "timestamp": 1642993141504 }, { "id": "945fc8cc02df3b8ab47b8714", "type": "motion", "start": 1642993132907, "end": 1642993154905, "score": 58, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-945fc8cc02df3b8ab47b8714", "heatmap": "e-945fc8cc02df3b8ab47b8714", "modelKey": "event", "timestamp": 1642993144074 }, { "id": "d436f7241ca003450d8f28f2", "type": "sensorMotion", "start": 1642993139247, "end": 1642993139247, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xrnoxf Ckf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d436f7241ca003450d8f28f2", "heatmap": "e-d436f7241ca003450d8f28f2", "modelKey": "event", "timestamp": null }, { "id": "99ac512db462febf9c6b1b19", "type": "motion", "start": 1642993817185, "end": 1642993838517, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-99ac512db462febf9c6b1b19", "heatmap": "e-99ac512db462febf9c6b1b19", "modelKey": "event", "timestamp": 1642993144074 }, { "id": "faa6f6fb3499607193b7212e", "type": "motion", "start": 1642994062660, "end": 1642994102658, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "307184b29ddaa51fd0a4061a" ], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-faa6f6fb3499607193b7212e", "heatmap": "e-faa6f6fb3499607193b7212e", "modelKey": "event", "timestamp": 1642994081992 }, { "id": "82f45a1831cfe67b4d0aacff", "type": "motion", "start": 1642994066247, "end": 1642994109851, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-82f45a1831cfe67b4d0aacff", "heatmap": "e-82f45a1831cfe67b4d0aacff", "modelKey": "event", "timestamp": 1642994081582 }, { "id": "d5c7e68cf13686cf822b63d7", "type": "motion", "start": 1642994066475, "end": 1642994101820, "score": 73, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d5c7e68cf13686cf822b63d7", "heatmap": "e-d5c7e68cf13686cf822b63d7", "modelKey": "event", "timestamp": 1642994081310 }, { "id": "5844a2ac28d9d0266c6ca377", "type": "motion", "start": 1642994071913, "end": 1642994116719, "score": 79, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5844a2ac28d9d0266c6ca377", "heatmap": "e-5844a2ac28d9d0266c6ca377", "modelKey": "event", "timestamp": 1642994083580 }, { "id": "307184b29ddaa51fd0a4061a", "type": "smartDetectZone", "start": 1642994075970, "end": 1642994098206, "score": 69, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-307184b29ddaa51fd0a4061a", "heatmap": "e-307184b29ddaa51fd0a4061a", "modelKey": "event", "timestamp": 1642994086970 }, { "id": "c5f9ef0838df1c77913743b7", "type": "smartDetectZone", "start": 1642994108580, "end": 1642994135643, "score": 87, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c5f9ef0838df1c77913743b7", "heatmap": "e-c5f9ef0838df1c77913743b7", "modelKey": "event", "timestamp": 1642994124803 }, { "id": "0306b37fa3dc62e82050a006", "type": "motion", "start": 1642994110419, "end": 1642994169539, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "c5f9ef0838df1c77913743b7" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0306b37fa3dc62e82050a006", "heatmap": "e-0306b37fa3dc62e82050a006", "modelKey": "event", "timestamp": 1642994124803 }, { "id": "130d832e7922dc9df2681016", "type": "motion", "start": 1642994110746, "end": 1642994155921, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-130d832e7922dc9df2681016", "heatmap": "e-130d832e7922dc9df2681016", "modelKey": "event", "timestamp": 1642994121915 }, { "id": "0b100cd1f2dfaef6693665e9", "type": "motion", "start": 1642994113488, "end": 1642994159500, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "ff89a74900f3fc17ead52908" ], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0b100cd1f2dfaef6693665e9", "heatmap": "e-0b100cd1f2dfaef6693665e9", "modelKey": "event", "timestamp": 1642994138991 }, { "id": "ff89a74900f3fc17ead52908", "type": "smartDetectZone", "start": 1642994114899, "end": 1642994136426, "score": 42, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ff89a74900f3fc17ead52908", "heatmap": "e-ff89a74900f3fc17ead52908", "modelKey": "event", "timestamp": 1642994126167 }, { "id": "d8c3a07f0e98ce8790cec883", "type": "motion", "start": 1642994126043, "end": 1642994148148, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d8c3a07f0e98ce8790cec883", "heatmap": "e-d8c3a07f0e98ce8790cec883", "modelKey": "event", "timestamp": 1642993134452 }, { "id": "9e6affe579c8108de5c8b4b6", "type": "sensorOpened", "start": 1642994129455, "end": 1642994129455, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Tiptoi Mzpt" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-9e6affe579c8108de5c8b4b6", "heatmap": "e-9e6affe579c8108de5c8b4b6", "modelKey": "event", "timestamp": null }, { "id": "81dfac8e28b4393408b89454", "type": "sensorMotion", "start": 1642994130987, "end": 1642994130987, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zsyjxm Eujh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-81dfac8e28b4393408b89454", "heatmap": "e-81dfac8e28b4393408b89454", "modelKey": "event", "timestamp": null }, { "id": "c4e1bf269fdea0ab4b76fac5", "type": "sensorMotion", "start": 1642994136135, "end": 1642994136135, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Calbkqz Zmpo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c4e1bf269fdea0ab4b76fac5", "heatmap": "e-c4e1bf269fdea0ab4b76fac5", "modelKey": "event", "timestamp": null }, { "id": "b83fc69604ec20d9925a669d", "type": "sensorMotion", "start": 1642994140657, "end": 1642994140657, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Spbtc Ynnem" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b83fc69604ec20d9925a669d", "heatmap": "e-b83fc69604ec20d9925a669d", "modelKey": "event", "timestamp": null }, { "id": "aac3bbdd7d679e7256bf95e1", "type": "sensorClosed", "start": 1642994140674, "end": 1642994140674, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Wnzenvc Ykfejhn" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-aac3bbdd7d679e7256bf95e1", "heatmap": "e-aac3bbdd7d679e7256bf95e1", "modelKey": "event", "timestamp": null }, { "id": "b4d00aef13c9315f4a469873", "type": "sensorMotion", "start": 1642994147466, "end": 1642994147466, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Yribd Almq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b4d00aef13c9315f4a469873", "heatmap": "e-b4d00aef13c9315f4a469873", "modelKey": "event", "timestamp": null }, { "id": "e027cfc4a27f50412b5968ea", "type": "motion", "start": 1642994466390, "end": 1642994491484, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e027cfc4a27f50412b5968ea", "heatmap": "e-e027cfc4a27f50412b5968ea", "modelKey": "event", "timestamp": 1642994477889 }, { "id": "c84b747af672fa0925b0ea46", "type": "sensorMotion", "start": 1642994467161, "end": 1642994467161, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ipdetv Iolk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c84b747af672fa0925b0ea46", "heatmap": "e-c84b747af672fa0925b0ea46", "modelKey": "event", "timestamp": null }, { "id": "8853b32897778e28b11db628", "type": "sensorMotion", "start": 1642994471925, "end": 1642994471925, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Clmfuvh Nmwwp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8853b32897778e28b11db628", "heatmap": "e-8853b32897778e28b11db628", "modelKey": "event", "timestamp": null }, { "id": "c9f4dc47b8e8364c500cddb0", "type": "sensorMotion", "start": 1642994475659, "end": 1642994475659, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Bmxggsy Ogzftt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c9f4dc47b8e8364c500cddb0", "heatmap": "e-c9f4dc47b8e8364c500cddb0", "modelKey": "event", "timestamp": null }, { "id": "0770bd45b5f90b1d068d58bd", "type": "sensorMotion", "start": 1642994480294, "end": 1642994480294, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xnzbfdf Nlt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0770bd45b5f90b1d068d58bd", "heatmap": "e-0770bd45b5f90b1d068d58bd", "modelKey": "event", "timestamp": null }, { "id": "4a869d32e04831cb46f2f8c6", "type": "sensorMotion", "start": 1642994485959, "end": 1642994485959, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Uurhe Uqs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4a869d32e04831cb46f2f8c6", "heatmap": "e-4a869d32e04831cb46f2f8c6", "modelKey": "event", "timestamp": null }, { "id": "41c2399b4009375df964f0b0", "type": "sensorMotion", "start": 1642994489565, "end": 1642994489565, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ywbawb Jrgz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-41c2399b4009375df964f0b0", "heatmap": "e-41c2399b4009375df964f0b0", "modelKey": "event", "timestamp": null }, { "id": "eec74db5c99f55786db47959", "type": "sensorMotion", "start": 1642994493427, "end": 1642994493427, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qgl Pgqscp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eec74db5c99f55786db47959", "heatmap": "e-eec74db5c99f55786db47959", "modelKey": "event", "timestamp": null }, { "id": "812c3f73b35a6701340d522e", "type": "sensorMotion", "start": 1642994498320, "end": 1642994498320, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Voqph Snk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-812c3f73b35a6701340d522e", "heatmap": "e-812c3f73b35a6701340d522e", "modelKey": "event", "timestamp": null }, { "id": "2ced0f1aa7f9bcb3ff59f32c", "type": "sensorMotion", "start": 1642994669562, "end": 1642994669562, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vbx Goonzg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2ced0f1aa7f9bcb3ff59f32c", "heatmap": "e-2ced0f1aa7f9bcb3ff59f32c", "modelKey": "event", "timestamp": null }, { "id": "cfba804d544753d5bcbf25a2", "type": "motion", "start": 1642994827294, "end": 1642994850980, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cfba804d544753d5bcbf25a2", "heatmap": "e-cfba804d544753d5bcbf25a2", "modelKey": "event", "timestamp": 1642994838972 }, { "id": "931fa73e04e05570abdcbf2b", "type": "motion", "start": 1642994834387, "end": 1642994866267, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-931fa73e04e05570abdcbf2b", "heatmap": "e-931fa73e04e05570abdcbf2b", "modelKey": "event", "timestamp": 1642994849836 }, { "id": "4baabcef90df5120926be9c2", "type": "sensorMotion", "start": 1642994836340, "end": 1642994836340, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Pmrmo Ogwiv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4baabcef90df5120926be9c2", "heatmap": "e-4baabcef90df5120926be9c2", "modelKey": "event", "timestamp": null }, { "id": "befa578cd0b4771a3f929667", "type": "sensorMotion", "start": 1642994839830, "end": 1642994839830, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cebcoae Juj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-befa578cd0b4771a3f929667", "heatmap": "e-befa578cd0b4771a3f929667", "modelKey": "event", "timestamp": null }, { "id": "5ec7fdaac7382a5783dd2745", "type": "sensorMotion", "start": 1642994845867, "end": 1642994845867, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cyug Qxgo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5ec7fdaac7382a5783dd2745", "heatmap": "e-5ec7fdaac7382a5783dd2745", "modelKey": "event", "timestamp": null }, { "id": "dfd8d85056ddf65177dc444a", "type": "sensorMotion", "start": 1642994849988, "end": 1642994849988, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Gqwx Dvvnqx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dfd8d85056ddf65177dc444a", "heatmap": "e-dfd8d85056ddf65177dc444a", "modelKey": "event", "timestamp": null }, { "id": "e47c18ce34aa5abb29df58b6", "type": "sensorMotion", "start": 1642994873420, "end": 1642994873420, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Bakye Xhlfhb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e47c18ce34aa5abb29df58b6", "heatmap": "e-e47c18ce34aa5abb29df58b6", "modelKey": "event", "timestamp": null }, { "id": "5965b13dea09f9ac0a2a9107", "type": "sensorMotion", "start": 1642994951461, "end": 1642994951461, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Myis Iar" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5965b13dea09f9ac0a2a9107", "heatmap": "e-5965b13dea09f9ac0a2a9107", "modelKey": "event", "timestamp": null }, { "id": "9aeb184b57b9704c7a295029", "type": "motion", "start": 1642995267219, "end": 1642995289716, "score": 44, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9aeb184b57b9704c7a295029", "heatmap": "e-9aeb184b57b9704c7a295029", "modelKey": "event", "timestamp": 1642995279551 }, { "id": "11ca83891271c45d6e006152", "type": "motion", "start": 1642995321773, "end": 1642995343444, "score": 16, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-11ca83891271c45d6e006152", "heatmap": "e-11ca83891271c45d6e006152", "modelKey": "event", "timestamp": 1642994121915 }, { "id": "74c99708b75af1ae200855dc", "type": "sensorMotion", "start": 1642995369914, "end": 1642995369914, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nbkmf Vxd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-74c99708b75af1ae200855dc", "heatmap": "e-74c99708b75af1ae200855dc", "modelKey": "event", "timestamp": null }, { "id": "1b5fcd17e0ad70ad9db0f2a9", "type": "sensorMotion", "start": 1642995968608, "end": 1642995968608, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mlmncsw Skldql" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1b5fcd17e0ad70ad9db0f2a9", "heatmap": "e-1b5fcd17e0ad70ad9db0f2a9", "modelKey": "event", "timestamp": null }, { "id": "7bb3cf02bf038bb713f1af47", "type": "sensorMotion", "start": 1642995972114, "end": 1642995972114, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kjvdqkp Tvbae" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7bb3cf02bf038bb713f1af47", "heatmap": "e-7bb3cf02bf038bb713f1af47", "modelKey": "event", "timestamp": null }, { "id": "ef1ff6f8af9ed7e122dc122d", "type": "sensorMotion", "start": 1642995980196, "end": 1642995980196, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oqcolys Gwctu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ef1ff6f8af9ed7e122dc122d", "heatmap": "e-ef1ff6f8af9ed7e122dc122d", "modelKey": "event", "timestamp": null }, { "id": "908436460d5bfa73a01761dc", "type": "sensorMotion", "start": 1642995989080, "end": 1642995989080, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zynkqa Pvg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-908436460d5bfa73a01761dc", "heatmap": "e-908436460d5bfa73a01761dc", "modelKey": "event", "timestamp": null }, { "id": "24a7df91db6204403051919b", "type": "access", "start": 1642996600044, "end": 1643038112723, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "dcaef9cb8aed05c7db658a46", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-24a7df91db6204403051919b", "heatmap": "e-24a7df91db6204403051919b", "modelKey": "event", "timestamp": null }, { "id": "fe67f8aea2088ea5145f46b4", "type": "motion", "start": 1642997046034, "end": 1642997073699, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fe67f8aea2088ea5145f46b4", "heatmap": "e-fe67f8aea2088ea5145f46b4", "modelKey": "event", "timestamp": 1642997057700 }, { "id": "a3a33ffabb046faaacf77193", "type": "sensorMotion", "start": 1642997051820, "end": 1642997051820, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ldkqybx Dsuz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a3a33ffabb046faaacf77193", "heatmap": "e-a3a33ffabb046faaacf77193", "modelKey": "event", "timestamp": null }, { "id": "4c4d3d07e849da3250c174ce", "type": "sensorMotion", "start": 1642997058000, "end": 1642997058000, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qkn Ebhjvi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4c4d3d07e849da3250c174ce", "heatmap": "e-4c4d3d07e849da3250c174ce", "modelKey": "event", "timestamp": null }, { "id": "177e3b85b57c126b9389247e", "type": "motion", "start": 1642997082438, "end": 1642997109417, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-177e3b85b57c126b9389247e", "heatmap": "e-177e3b85b57c126b9389247e", "modelKey": "event", "timestamp": 1642997094938 }, { "id": "b9a3d737bc6777dc058dba6c", "type": "sensorMotion", "start": 1642997088831, "end": 1642997088831, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zbocirl Hafw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b9a3d737bc6777dc058dba6c", "heatmap": "e-b9a3d737bc6777dc058dba6c", "modelKey": "event", "timestamp": null }, { "id": "6590e6bb21930a4825effc3b", "type": "sensorMotion", "start": 1642997094953, "end": 1642997094953, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Upusd Bgjexlp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6590e6bb21930a4825effc3b", "heatmap": "e-6590e6bb21930a4825effc3b", "modelKey": "event", "timestamp": null }, { "id": "5bf008b5572454f554b0f2e8", "type": "sensorMotion", "start": 1642997099459, "end": 1642997099459, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rryhn Ebcuwjp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5bf008b5572454f554b0f2e8", "heatmap": "e-5bf008b5572454f554b0f2e8", "modelKey": "event", "timestamp": null }, { "id": "2411d11320c4fa6bf97c189f", "type": "motion", "start": 1642997173710, "end": 1642997240234, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2411d11320c4fa6bf97c189f", "heatmap": "e-2411d11320c4fa6bf97c189f", "modelKey": "event", "timestamp": 1642997201565 }, { "id": "5d4bc3794a5490b248947bc4", "type": "sensorMotion", "start": 1642997187528, "end": 1642997187528, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Setr Xjmrne" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5d4bc3794a5490b248947bc4", "heatmap": "e-5d4bc3794a5490b248947bc4", "modelKey": "event", "timestamp": null }, { "id": "f1ef1c7ab87ba35357e15c15", "type": "sensorMotion", "start": 1642997198086, "end": 1642997198086, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fwef Hzqqdl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f1ef1c7ab87ba35357e15c15", "heatmap": "e-f1ef1c7ab87ba35357e15c15", "modelKey": "event", "timestamp": null }, { "id": "47a5a261fcad5799dfc8c59d", "type": "sensorMotion", "start": 1642997202493, "end": 1642997202493, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jivwyw Smgboxv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-47a5a261fcad5799dfc8c59d", "heatmap": "e-47a5a261fcad5799dfc8c59d", "modelKey": "event", "timestamp": null }, { "id": "e7848170e5f9afea8f972bde", "type": "sensorMotion", "start": 1642997206970, "end": 1642997206970, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Goaybq Zaejd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e7848170e5f9afea8f972bde", "heatmap": "e-e7848170e5f9afea8f972bde", "modelKey": "event", "timestamp": null }, { "id": "707f523643d835c05a1c197f", "type": "sensorMotion", "start": 1642997210590, "end": 1642997210590, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xqjudn Pii" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-707f523643d835c05a1c197f", "heatmap": "e-707f523643d835c05a1c197f", "modelKey": "event", "timestamp": null }, { "id": "fb3241969987de8cf83b00ca", "type": "motion", "start": 1642997224315, "end": 1642997250327, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fb3241969987de8cf83b00ca", "heatmap": "e-fb3241969987de8cf83b00ca", "modelKey": "event", "timestamp": 1642997235482 }, { "id": "c275d62dfe2f966e6ca1768d", "type": "sensorMotion", "start": 1642997231563, "end": 1642997231563, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Lljni Kmifm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c275d62dfe2f966e6ca1768d", "heatmap": "e-c275d62dfe2f966e6ca1768d", "modelKey": "event", "timestamp": null }, { "id": "c175229287d87839e7a97e73", "type": "sensorMotion", "start": 1642997658901, "end": 1642997658901, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oqiax Pjby" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c175229287d87839e7a97e73", "heatmap": "e-c175229287d87839e7a97e73", "modelKey": "event", "timestamp": null }, { "id": "efb2ad8525781bd557054efe", "type": "motion", "start": 1642997746733, "end": 1642997772405, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-efb2ad8525781bd557054efe", "heatmap": "e-efb2ad8525781bd557054efe", "modelKey": "event", "timestamp": 1642997757895 }, { "id": "de8613a3211f835e3e205f96", "type": "sensorMotion", "start": 1642997754955, "end": 1642997754955, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Uhxqik Gytv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-de8613a3211f835e3e205f96", "heatmap": "e-de8613a3211f835e3e205f96", "modelKey": "event", "timestamp": null }, { "id": "50d3af30f0b848543ac19606", "type": "motion", "start": 1642997782513, "end": 1642997804848, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-50d3af30f0b848543ac19606", "heatmap": "e-50d3af30f0b848543ac19606", "modelKey": "event", "timestamp": 1642997794349 }, { "id": "0d8ca1835184b119c7015d78", "type": "sensorMotion", "start": 1643001163454, "end": 1643001163454, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xepef Kehbpgm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0d8ca1835184b119c7015d78", "heatmap": "e-0d8ca1835184b119c7015d78", "modelKey": "event", "timestamp": null }, { "id": "798376b6f31393a3f68cfb77", "type": "sensorMotion", "start": 1643001166315, "end": 1643001166315, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Smn Etp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-798376b6f31393a3f68cfb77", "heatmap": "e-798376b6f31393a3f68cfb77", "modelKey": "event", "timestamp": null }, { "id": "589c767035fbb9bb1aafd71d", "type": "sensorMotion", "start": 1643001198346, "end": 1643001198346, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xellh Kapat" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-589c767035fbb9bb1aafd71d", "heatmap": "e-589c767035fbb9bb1aafd71d", "modelKey": "event", "timestamp": null }, { "id": "2bf2c48a207756b40bda877e", "type": "sensorMotion", "start": 1643001201822, "end": 1643001201822, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nxjr Cwoez" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2bf2c48a207756b40bda877e", "heatmap": "e-2bf2c48a207756b40bda877e", "modelKey": "event", "timestamp": null }, { "id": "fce6c1753d2197663a1b9daa", "type": "motion", "start": 1643001229218, "end": 1643001252720, "score": 54, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fce6c1753d2197663a1b9daa", "heatmap": "e-fce6c1753d2197663a1b9daa", "modelKey": "event", "timestamp": 1643001240552 }, { "id": "707787e90b39c7f049c572b6", "type": "motion", "start": 1643001371915, "end": 1643001393917, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-707787e90b39c7f049c572b6", "heatmap": "e-707787e90b39c7f049c572b6", "modelKey": "event", "timestamp": 1643001383915 }, { "id": "7b5b9a512949060828803ac8", "type": "motion", "start": 1643005296896, "end": 1643005318068, "score": 4, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7b5b9a512949060828803ac8", "heatmap": "e-7b5b9a512949060828803ac8", "modelKey": "event", "timestamp": 1643001383915 }, { "id": "a639ba30ca5a6b26048444d6", "type": "motion", "start": 1643006365832, "end": 1643006388000, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a639ba30ca5a6b26048444d6", "heatmap": "e-a639ba30ca5a6b26048444d6", "modelKey": "event", "timestamp": 1643006376998 }, { "id": "21ec6b1ce60a02741c89cdb3", "type": "motion", "start": 1643006426490, "end": 1643006452464, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-21ec6b1ce60a02741c89cdb3", "heatmap": "e-21ec6b1ce60a02741c89cdb3", "modelKey": "event", "timestamp": 1643006437659 }, { "id": "b6a95a52395ed5294d8d175b", "type": "motion", "start": 1643007389211, "end": 1643007418374, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b6a95a52395ed5294d8d175b", "heatmap": "e-b6a95a52395ed5294d8d175b", "modelKey": "event", "timestamp": 1643007400378 }, { "id": "7f0f76aa5d7dcefbb634d684", "type": "motion", "start": 1643007408535, "end": 1643007429702, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7f0f76aa5d7dcefbb634d684", "heatmap": "e-7f0f76aa5d7dcefbb634d684", "modelKey": "event", "timestamp": 1643007419701 }, { "id": "1578edbcd12b5d2d3ab8b84b", "type": "motion", "start": 1643007410802, "end": 1643007433841, "score": 5, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1578edbcd12b5d2d3ab8b84b", "heatmap": "e-1578edbcd12b5d2d3ab8b84b", "modelKey": "event", "timestamp": 1642994121915 }, { "id": "c8ef3d4b96ce9e157302effa", "type": "motion", "start": 1643007412428, "end": 1643007433930, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c8ef3d4b96ce9e157302effa", "heatmap": "e-c8ef3d4b96ce9e157302effa", "modelKey": "event", "timestamp": 1643007423596 }, { "id": "d5298d08aa83dc096045c4a7", "type": "motion", "start": 1643008062482, "end": 1643008086650, "score": 34, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "4a333d993fe8e2e8472bc901", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d5298d08aa83dc096045c4a7", "heatmap": "e-d5298d08aa83dc096045c4a7", "modelKey": "event", "timestamp": 1643008073982 }, { "id": "01fdbf75ad7c63a5f34f1f26", "type": "motion", "start": 1643008232203, "end": 1643008255365, "score": 22, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-01fdbf75ad7c63a5f34f1f26", "heatmap": "e-01fdbf75ad7c63a5f34f1f26", "modelKey": "event", "timestamp": 1643006437659 }, { "id": "3d66b5a6df01a0e018a9aba0", "type": "motion", "start": 1643008448394, "end": 1643008470900, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3d66b5a6df01a0e018a9aba0", "heatmap": "e-3d66b5a6df01a0e018a9aba0", "modelKey": "event", "timestamp": 1643008459561 }, { "id": "fc05bcb972300bb6e98ee669", "type": "motion", "start": 1643009641858, "end": 1643009664029, "score": 81, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fc05bcb972300bb6e98ee669", "heatmap": "e-fc05bcb972300bb6e98ee669", "modelKey": "event", "timestamp": 1643009653525 }, { "id": "763c24be4506ec329bcf603b", "type": "sensorMotion", "start": 1643010456660, "end": 1643010456660, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qiyhkhh Fiovv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-763c24be4506ec329bcf603b", "heatmap": "e-763c24be4506ec329bcf603b", "modelKey": "event", "timestamp": null }, { "id": "05c3d80d19bff600c0bd68b7", "type": "motion", "start": 1643010493170, "end": 1643010514348, "score": 38, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-05c3d80d19bff600c0bd68b7", "heatmap": "e-05c3d80d19bff600c0bd68b7", "modelKey": "event", "timestamp": 1643006437659 }, { "id": "e2834ca1fa886ecba1a202b3", "type": "sensorMotion", "start": 1643010501452, "end": 1643010501452, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Newj Ryyutiw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e2834ca1fa886ecba1a202b3", "heatmap": "e-e2834ca1fa886ecba1a202b3", "modelKey": "event", "timestamp": null }, { "id": "a13eb1bc1a4ec395fc92c4a6", "type": "motion", "start": 1643011681159, "end": 1643011702986, "score": 40, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a13eb1bc1a4ec395fc92c4a6", "heatmap": "e-a13eb1bc1a4ec395fc92c4a6", "modelKey": "event", "timestamp": 1643011692649 }, { "id": "6fee13e39a572c68c00381d3", "type": "motion", "start": 1643012537532, "end": 1643012563363, "score": 55, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6fee13e39a572c68c00381d3", "heatmap": "e-6fee13e39a572c68c00381d3", "modelKey": "event", "timestamp": 1643012549534 }, { "id": "5f7eb895dc1bdb504814602e", "type": "motion", "start": 1643012539207, "end": 1643012560891, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5f7eb895dc1bdb504814602e", "heatmap": "e-5f7eb895dc1bdb504814602e", "modelKey": "event", "timestamp": 1643011692649 }, { "id": "ac3619a594fdafb41d24fa98", "type": "motion", "start": 1643014450108, "end": 1643014477613, "score": 82, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ac3619a594fdafb41d24fa98", "heatmap": "e-ac3619a594fdafb41d24fa98", "modelKey": "event", "timestamp": 1643014461442 }, { "id": "c46c01012c680f317ca56ef3", "type": "motion", "start": 1643014478977, "end": 1643014505485, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c46c01012c680f317ca56ef3", "heatmap": "e-c46c01012c680f317ca56ef3", "modelKey": "event", "timestamp": 1643014492816 }, { "id": "743fd83d4f5b67a620f786c5", "type": "motion", "start": 1643014481892, "end": 1643014504061, "score": 2, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-743fd83d4f5b67a620f786c5", "heatmap": "e-743fd83d4f5b67a620f786c5", "modelKey": "event", "timestamp": 1643011692649 }, { "id": "28650e8e2f0b67d70b8af67d", "type": "motion", "start": 1643014483740, "end": 1643014506578, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-28650e8e2f0b67d70b8af67d", "heatmap": "e-28650e8e2f0b67d70b8af67d", "modelKey": "event", "timestamp": 1643014495073 }, { "id": "d3cc5e7e3ad4b1b1126bd461", "type": "motion", "start": 1643014566937, "end": 1643014589604, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d3cc5e7e3ad4b1b1126bd461", "heatmap": "e-d3cc5e7e3ad4b1b1126bd461", "modelKey": "event", "timestamp": 1643014578104 }, { "id": "c253cd9503167ba8e76d5e3b", "type": "motion", "start": 1643015412882, "end": 1643015437724, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c253cd9503167ba8e76d5e3b", "heatmap": "e-c253cd9503167ba8e76d5e3b", "modelKey": "event", "timestamp": 1643015425215 }, { "id": "ed8bb79d67ef26d5509a5a7e", "type": "motion", "start": 1643015413891, "end": 1643015435727, "score": 6, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ed8bb79d67ef26d5509a5a7e", "heatmap": "e-ed8bb79d67ef26d5509a5a7e", "modelKey": "event", "timestamp": 1643011692649 }, { "id": "8b07009fee6e93c723515249", "type": "motion", "start": 1643016520939, "end": 1643016542941, "score": 58, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8b07009fee6e93c723515249", "heatmap": "e-8b07009fee6e93c723515249", "modelKey": "event", "timestamp": 1643016532106 }, { "id": "57f9da605dd445edd59ba73f", "type": "motion", "start": 1643016560076, "end": 1643016591903, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-57f9da605dd445edd59ba73f", "heatmap": "e-57f9da605dd445edd59ba73f", "modelKey": "event", "timestamp": 1643016581736 }, { "id": "7723485677eef23fee5c1c93", "type": "motion", "start": 1643018880494, "end": 1643018904009, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7723485677eef23fee5c1c93", "heatmap": "e-7723485677eef23fee5c1c93", "modelKey": "event", "timestamp": 1643018891994 }, { "id": "caba849e487e5142d92fb628", "type": "motion", "start": 1643018884512, "end": 1643018906011, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-caba849e487e5142d92fb628", "heatmap": "e-caba849e487e5142d92fb628", "modelKey": "event", "timestamp": 1643016581736 }, { "id": "af887e4f69645274149af5ef", "type": "sensorMotion", "start": 1643018898259, "end": 1643018898259, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kqgehnm Zmvikh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-af887e4f69645274149af5ef", "heatmap": "e-af887e4f69645274149af5ef", "modelKey": "event", "timestamp": null }, { "id": "7dc4a84c4330a10763d099ae", "type": "sensorMotion", "start": 1643018902253, "end": 1643018902253, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Smrscv Jcjbvu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7dc4a84c4330a10763d099ae", "heatmap": "e-7dc4a84c4330a10763d099ae", "modelKey": "event", "timestamp": null }, { "id": "05a76f1fa860478e2a8600d9", "type": "sensorMotion", "start": 1643018917701, "end": 1643018917701, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rwud Juze" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-05a76f1fa860478e2a8600d9", "heatmap": "e-05a76f1fa860478e2a8600d9", "modelKey": "event", "timestamp": null }, { "id": "ff143d25f979badbcea918af", "type": "sensorMotion", "start": 1643018923639, "end": 1643018923639, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ixqeeaw Nzibndy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ff143d25f979badbcea918af", "heatmap": "e-ff143d25f979badbcea918af", "modelKey": "event", "timestamp": null }, { "id": "bcec598772a30cdc475ef6e5", "type": "motion", "start": 1643018971751, "end": 1643018997583, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bcec598772a30cdc475ef6e5", "heatmap": "e-bcec598772a30cdc475ef6e5", "modelKey": "event", "timestamp": 1643018984232 }, { "id": "1009d233056d6b9cc303064d", "type": "sensorMotion", "start": 1643018980276, "end": 1643018980276, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cerusr Bgidwn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1009d233056d6b9cc303064d", "heatmap": "e-1009d233056d6b9cc303064d", "modelKey": "event", "timestamp": null }, { "id": "f29eb275a70516bb064fbba2", "type": "sensorMotion", "start": 1643018985055, "end": 1643018985055, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fgthco Rmxo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f29eb275a70516bb064fbba2", "heatmap": "e-f29eb275a70516bb064fbba2", "modelKey": "event", "timestamp": null }, { "id": "3a25f568c802d8a4143ea877", "type": "motion", "start": 1643019110973, "end": 1643019146333, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3a25f568c802d8a4143ea877", "heatmap": "e-3a25f568c802d8a4143ea877", "modelKey": "event", "timestamp": 1643019123128 }, { "id": "938c78abee36b25e13cdc253", "type": "motion", "start": 1643019123996, "end": 1643019153664, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-938c78abee36b25e13cdc253", "heatmap": "e-938c78abee36b25e13cdc253", "modelKey": "event", "timestamp": 1643019135329 }, { "id": "32805898c08279a702b9b9ff", "type": "motion", "start": 1643019127223, "end": 1643019150910, "score": 79, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-32805898c08279a702b9b9ff", "heatmap": "e-32805898c08279a702b9b9ff", "modelKey": "event", "timestamp": 1643019138396 }, { "id": "45bcd2910f012e86feade4d5", "type": "motion", "start": 1643019219467, "end": 1643019240469, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-45bcd2910f012e86feade4d5", "heatmap": "e-45bcd2910f012e86feade4d5", "modelKey": "event", "timestamp": 1643019123128 }, { "id": "c7345d40f982e803c0069735", "type": "motion", "start": 1643019226290, "end": 1643019249316, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c7345d40f982e803c0069735", "heatmap": "e-c7345d40f982e803c0069735", "modelKey": "event", "timestamp": 1643019237457 }, { "id": "1cdd5b9dce8696d326968ab1", "type": "motion", "start": 1643020263002, "end": 1643020286678, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1cdd5b9dce8696d326968ab1", "heatmap": "e-1cdd5b9dce8696d326968ab1", "modelKey": "event", "timestamp": 1643020274667 }, { "id": "67b9c97ad1f96e5930903a1b", "type": "motion", "start": 1643020264105, "end": 1643020287120, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-67b9c97ad1f96e5930903a1b", "heatmap": "e-67b9c97ad1f96e5930903a1b", "modelKey": "event", "timestamp": 1643020275271 }, { "id": "8dbe7e2f8d2dff25e7e829b6", "type": "motion", "start": 1643020623105, "end": 1643020647449, "score": 24, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8dbe7e2f8d2dff25e7e829b6", "heatmap": "e-8dbe7e2f8d2dff25e7e829b6", "modelKey": "event", "timestamp": 1643020635109 }, { "id": "cadb11f4d963e2553b798456", "type": "motion", "start": 1643020626205, "end": 1643020649542, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cadb11f4d963e2553b798456", "heatmap": "e-cadb11f4d963e2553b798456", "modelKey": "event", "timestamp": 1643020637371 }, { "id": "069398be4df97e45160facba", "type": "motion", "start": 1643021273918, "end": 1643021295561, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-069398be4df97e45160facba", "heatmap": "e-069398be4df97e45160facba", "modelKey": "event", "timestamp": 1643021285061 }, { "id": "68688f33edfd4110dc1bf356", "type": "motion", "start": 1643021571111, "end": 1643021594960, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-68688f33edfd4110dc1bf356", "heatmap": "e-68688f33edfd4110dc1bf356", "modelKey": "event", "timestamp": 1643021582278 }, { "id": "74959da68761f09a025a2026", "type": "motion", "start": 1643021577898, "end": 1643021600246, "score": 23, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-74959da68761f09a025a2026", "heatmap": "e-74959da68761f09a025a2026", "modelKey": "event", "timestamp": 1643021589565 }, { "id": "1c4572f19f445776bca3378f", "type": "motion", "start": 1643021884484, "end": 1643021909319, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1c4572f19f445776bca3378f", "heatmap": "e-1c4572f19f445776bca3378f", "modelKey": "event", "timestamp": 1643021897650 }, { "id": "b3a20498a09994fc2b8978a6", "type": "motion", "start": 1643021884916, "end": 1643021906588, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b3a20498a09994fc2b8978a6", "heatmap": "e-b3a20498a09994fc2b8978a6", "modelKey": "event", "timestamp": 1643021285061 }, { "id": "dc857b7559ba32e8e4567a85", "type": "motion", "start": 1643021945465, "end": 1643021967299, "score": 16, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-dc857b7559ba32e8e4567a85", "heatmap": "e-dc857b7559ba32e8e4567a85", "modelKey": "event", "timestamp": 1643021897650 }, { "id": "8dc1234f46bd59177dc5a5c1", "type": "motion", "start": 1643022055181, "end": 1643022077356, "score": 73, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8dc1234f46bd59177dc5a5c1", "heatmap": "e-8dc1234f46bd59177dc5a5c1", "modelKey": "event", "timestamp": 1643022066515 }, { "id": "7965278aa93d664c550a5f49", "type": "motion", "start": 1643022058488, "end": 1643022080159, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7965278aa93d664c550a5f49", "heatmap": "e-7965278aa93d664c550a5f49", "modelKey": "event", "timestamp": 1643021285061 }, { "id": "80eb0b9267b0f7f5c41b4e3a", "type": "motion", "start": 1643022060626, "end": 1643022082605, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-80eb0b9267b0f7f5c41b4e3a", "heatmap": "e-80eb0b9267b0f7f5c41b4e3a", "modelKey": "event", "timestamp": 1643022071939 }, { "id": "8ac15b29547dd5d6f5f281c8", "type": "motion", "start": 1643023418261, "end": 1643023439926, "score": 96, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8ac15b29547dd5d6f5f281c8", "heatmap": "e-8ac15b29547dd5d6f5f281c8", "modelKey": "event", "timestamp": 1643023429761 }, { "id": "aa5c106a83a9754ba13cf207", "type": "motion", "start": 1643023504233, "end": 1643023527399, "score": 60, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-aa5c106a83a9754ba13cf207", "heatmap": "e-aa5c106a83a9754ba13cf207", "modelKey": "event", "timestamp": 1643023429761 }, { "id": "80749db534c119b158cb244e", "type": "motion", "start": 1643023919063, "end": 1643023941396, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-80749db534c119b158cb244e", "heatmap": "e-80749db534c119b158cb244e", "modelKey": "event", "timestamp": 1643023930230 }, { "id": "13e460054cd3b176f7f65ef1", "type": "motion", "start": 1643024126932, "end": 1643024149431, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-13e460054cd3b176f7f65ef1", "heatmap": "e-13e460054cd3b176f7f65ef1", "modelKey": "event", "timestamp": 1643024138120 }, { "id": "2ca5797d591d12c597178f61", "type": "motion", "start": 1643024473328, "end": 1643024496816, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2ca5797d591d12c597178f61", "heatmap": "e-2ca5797d591d12c597178f61", "modelKey": "event", "timestamp": 1643024485485 }, { "id": "6f465ef86b69e1a51dc395b7", "type": "motion", "start": 1643024485814, "end": 1643024508646, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6f465ef86b69e1a51dc395b7", "heatmap": "e-6f465ef86b69e1a51dc395b7", "modelKey": "event", "timestamp": 1643024498480 }, { "id": "18849868b6f5ed3c0d510c72", "type": "motion", "start": 1643024576619, "end": 1643024598119, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-18849868b6f5ed3c0d510c72", "heatmap": "e-18849868b6f5ed3c0d510c72", "modelKey": "event", "timestamp": 1643024587969 }, { "id": "915720d06e00ca831d4e20d7", "type": "motion", "start": 1643024890107, "end": 1643024912774, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-915720d06e00ca831d4e20d7", "heatmap": "e-915720d06e00ca831d4e20d7", "modelKey": "event", "timestamp": 1643023930230 }, { "id": "a1d6a0fc781f540e9a90d9e4", "type": "motion", "start": 1643024956510, "end": 1643024978180, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a1d6a0fc781f540e9a90d9e4", "heatmap": "e-a1d6a0fc781f540e9a90d9e4", "modelKey": "event", "timestamp": 1643024968010 }, { "id": "f40c66145cbc171ccd49aa2b", "type": "motion", "start": 1643024961303, "end": 1643024983467, "score": 37, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f40c66145cbc171ccd49aa2b", "heatmap": "e-f40c66145cbc171ccd49aa2b", "modelKey": "event", "timestamp": 1643024587969 }, { "id": "938aa1a8ffe29797856721d0", "type": "motion", "start": 1643024973636, "end": 1643024995135, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-938aa1a8ffe29797856721d0", "heatmap": "e-938aa1a8ffe29797856721d0", "modelKey": "event", "timestamp": 1643024984802 }, { "id": "b398cf999f9aecff761c5d26", "type": "motion", "start": 1643025294301, "end": 1643025316155, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b398cf999f9aecff761c5d26", "heatmap": "e-b398cf999f9aecff761c5d26", "modelKey": "event", "timestamp": 1643024138120 }, { "id": "e8781cd372cac079d8cf4d33", "type": "motion", "start": 1643025363354, "end": 1643025384374, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e8781cd372cac079d8cf4d33", "heatmap": "e-e8781cd372cac079d8cf4d33", "modelKey": "event", "timestamp": 1643024968010 }, { "id": "f9e395f3c78c514df4a13683", "type": "motion", "start": 1643025365448, "end": 1643025390613, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f9e395f3c78c514df4a13683", "heatmap": "e-f9e395f3c78c514df4a13683", "modelKey": "event", "timestamp": 1643025378448 }, { "id": "abe8b878f0ffb1c763d711d4", "type": "motion", "start": 1643025365813, "end": 1643025387992, "score": 58, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-abe8b878f0ffb1c763d711d4", "heatmap": "e-abe8b878f0ffb1c763d711d4", "modelKey": "event", "timestamp": 1643025377313 }, { "id": "0a502694d911a60ce91e6208", "type": "motion", "start": 1643025769427, "end": 1643025790926, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0a502694d911a60ce91e6208", "heatmap": "e-0a502694d911a60ce91e6208", "modelKey": "event", "timestamp": 1643025780782 }, { "id": "3bb1024a36a8647f95a93242", "type": "motion", "start": 1643025828614, "end": 1643025851962, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3bb1024a36a8647f95a93242", "heatmap": "e-3bb1024a36a8647f95a93242", "modelKey": "event", "timestamp": 1643025841954 }, { "id": "29f3899ed794731554ec1563", "type": "motion", "start": 1643026097386, "end": 1643026122414, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-29f3899ed794731554ec1563", "heatmap": "e-29f3899ed794731554ec1563", "modelKey": "event", "timestamp": 1643026110398 }, { "id": "7d910dc93447dd13a93ebd48", "type": "motion", "start": 1643026100242, "end": 1643026125430, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7d910dc93447dd13a93ebd48", "heatmap": "e-7d910dc93447dd13a93ebd48", "modelKey": "event", "timestamp": 1643026111417 }, { "id": "e561cee0a258d2fdbaddc984", "type": "motion", "start": 1643026246758, "end": 1643026268091, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e561cee0a258d2fdbaddc984", "heatmap": "e-e561cee0a258d2fdbaddc984", "modelKey": "event", "timestamp": 1643026257925 }, { "id": "19d7471e7f27b5cc691730b6", "type": "motion", "start": 1643026249579, "end": 1643026272746, "score": 44, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-19d7471e7f27b5cc691730b6", "heatmap": "e-19d7471e7f27b5cc691730b6", "modelKey": "event", "timestamp": 1643026110398 }, { "id": "0834d4884f7215dbb1066cc9", "type": "motion", "start": 1643026319929, "end": 1643026346601, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0834d4884f7215dbb1066cc9", "heatmap": "e-0834d4884f7215dbb1066cc9", "modelKey": "event", "timestamp": 1643026331096 }, { "id": "21be71658c55852e659f7f89", "type": "motion", "start": 1643026653612, "end": 1643026675613, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-21be71658c55852e659f7f89", "heatmap": "e-21be71658c55852e659f7f89", "modelKey": "event", "timestamp": 1643026664779 }, { "id": "d910e127f3c05ac61ba69c64", "type": "motion", "start": 1643026669803, "end": 1643026691287, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d910e127f3c05ac61ba69c64", "heatmap": "e-d910e127f3c05ac61ba69c64", "modelKey": "event", "timestamp": 1643026681280 }, { "id": "5a1fc76e6934260cf0b567d6", "type": "motion", "start": 1643026673022, "end": 1643026694523, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5a1fc76e6934260cf0b567d6", "heatmap": "e-5a1fc76e6934260cf0b567d6", "modelKey": "event", "timestamp": 1643026684522 }, { "id": "9e8d740fc77ba557003f9547", "type": "motion", "start": 1643026890926, "end": 1643026912597, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9e8d740fc77ba557003f9547", "heatmap": "e-9e8d740fc77ba557003f9547", "modelKey": "event", "timestamp": 1643026684522 }, { "id": "1dfb99491602ddac5ea6dbe4", "type": "motion", "start": 1643027925877, "end": 1643027948878, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1dfb99491602ddac5ea6dbe4", "heatmap": "e-1dfb99491602ddac5ea6dbe4", "modelKey": "event", "timestamp": 1643026684522 }, { "id": "568194dcce8ebb898f206004", "type": "motion", "start": 1643028009466, "end": 1643028035131, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-568194dcce8ebb898f206004", "heatmap": "e-568194dcce8ebb898f206004", "modelKey": "event", "timestamp": 1643028024799 }, { "id": "0c9ef5f85f77519807c51196", "type": "motion", "start": 1643028041377, "end": 1643028063075, "score": 7, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0c9ef5f85f77519807c51196", "heatmap": "e-0c9ef5f85f77519807c51196", "modelKey": "event", "timestamp": 1643028053044 }, { "id": "340ef7d5c97aa8c9365f02cd", "type": "motion", "start": 1643028155769, "end": 1643028177269, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-340ef7d5c97aa8c9365f02cd", "heatmap": "e-340ef7d5c97aa8c9365f02cd", "modelKey": "event", "timestamp": 1643028024799 }, { "id": "ecb6203a3952abcb458f0dfc", "type": "motion", "start": 1643028196590, "end": 1643028222756, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ecb6203a3952abcb458f0dfc", "heatmap": "e-ecb6203a3952abcb458f0dfc", "modelKey": "event", "timestamp": 1643028211423 }, { "id": "a54236ce506f19ce8ec38a01", "type": "motion", "start": 1643029253211, "end": 1643029279209, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a54236ce506f19ce8ec38a01", "heatmap": "e-a54236ce506f19ce8ec38a01", "modelKey": "event", "timestamp": 1643029267711 }, { "id": "b77b342399723f7125f8d6cd", "type": "motion", "start": 1643029255075, "end": 1643029277086, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b77b342399723f7125f8d6cd", "heatmap": "e-b77b342399723f7125f8d6cd", "modelKey": "event", "timestamp": 1643029266243 }, { "id": "2a45b4862dd308bd43181cc7", "type": "motion", "start": 1643029257403, "end": 1643029279068, "score": 16, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2a45b4862dd308bd43181cc7", "heatmap": "e-2a45b4862dd308bd43181cc7", "modelKey": "event", "timestamp": 1643028211423 }, { "id": "143a9692f442e3724cee7e64", "type": "motion", "start": 1643029481466, "end": 1643029507809, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-143a9692f442e3724cee7e64", "heatmap": "e-143a9692f442e3724cee7e64", "modelKey": "event", "timestamp": 1643029492800 }, { "id": "dfc8249c592e8bc6bd03e897", "type": "sensorMotion", "start": 1643029491996, "end": 1643029491996, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ffkxsru Lqyvl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dfc8249c592e8bc6bd03e897", "heatmap": "e-dfc8249c592e8bc6bd03e897", "modelKey": "event", "timestamp": null }, { "id": "134eee3f7e0c2bf8eae39de6", "type": "sensorMotion", "start": 1643029495472, "end": 1643029495472, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hajc Ykubo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-134eee3f7e0c2bf8eae39de6", "heatmap": "e-134eee3f7e0c2bf8eae39de6", "modelKey": "event", "timestamp": null }, { "id": "2146d82e2fc305411613b651", "type": "motion", "start": 1643029734116, "end": 1643029755130, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2146d82e2fc305411613b651", "heatmap": "e-2146d82e2fc305411613b651", "modelKey": "event", "timestamp": 1643028211423 }, { "id": "58fe20f374709a3121a694f4", "type": "motion", "start": 1643029736331, "end": 1643029758666, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-58fe20f374709a3121a694f4", "heatmap": "e-58fe20f374709a3121a694f4", "modelKey": "event", "timestamp": 1643029747998 }, { "id": "0fb57201b4dc62e841aee675", "type": "motion", "start": 1643029738612, "end": 1643029761957, "score": 83, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0fb57201b4dc62e841aee675", "heatmap": "e-0fb57201b4dc62e841aee675", "modelKey": "event", "timestamp": 1643029749778 }, { "id": "647ad5d7ef311325114ad636", "type": "motion", "start": 1643030119149, "end": 1643030146321, "score": 62, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-647ad5d7ef311325114ad636", "heatmap": "e-647ad5d7ef311325114ad636", "modelKey": "event", "timestamp": 1643030134488 }, { "id": "186cb722e46f1779527161b0", "type": "motion", "start": 1643030121296, "end": 1643030145301, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-186cb722e46f1779527161b0", "heatmap": "e-186cb722e46f1779527161b0", "modelKey": "event", "timestamp": 1643030132463 }, { "id": "e1c2e26c7a950ba761d10ee3", "type": "motion", "start": 1643030123928, "end": 1643030148094, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e1c2e26c7a950ba761d10ee3", "heatmap": "e-e1c2e26c7a950ba761d10ee3", "modelKey": "event", "timestamp": 1643030135094 }, { "id": "41f2e78dc438643eeac7c781", "type": "motion", "start": 1643030486204, "end": 1643030508043, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-41f2e78dc438643eeac7c781", "heatmap": "e-41f2e78dc438643eeac7c781", "modelKey": "event", "timestamp": 1643030497371 }, { "id": "c5a3cceafb37b84b55ad9ca4", "type": "motion", "start": 1643030705062, "end": 1643030726229, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c5a3cceafb37b84b55ad9ca4", "heatmap": "e-c5a3cceafb37b84b55ad9ca4", "modelKey": "event", "timestamp": 1643030716229 }, { "id": "981d38669fbbfd1b3149d231", "type": "motion", "start": 1643031171943, "end": 1643031193448, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-981d38669fbbfd1b3149d231", "heatmap": "e-981d38669fbbfd1b3149d231", "modelKey": "event", "timestamp": 1643030497371 }, { "id": "a207ccc7036ae31769a50528", "type": "motion", "start": 1643031324907, "end": 1643031345920, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a207ccc7036ae31769a50528", "heatmap": "e-a207ccc7036ae31769a50528", "modelKey": "event", "timestamp": 1643030497371 }, { "id": "6e51c85e0fc6610703d93fad", "type": "motion", "start": 1643031351403, "end": 1643031373752, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6e51c85e0fc6610703d93fad", "heatmap": "e-6e51c85e0fc6610703d93fad", "modelKey": "event", "timestamp": 1643031362570 }, { "id": "3751e9bd97680365609f07d5", "type": "motion", "start": 1643031604081, "end": 1643031625588, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3751e9bd97680365609f07d5", "heatmap": "e-3751e9bd97680365609f07d5", "modelKey": "event", "timestamp": 1643031615581 }, { "id": "06f5c13cff4c9d368679d72a", "type": "motion", "start": 1643031635291, "end": 1643031656469, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-06f5c13cff4c9d368679d72a", "heatmap": "e-06f5c13cff4c9d368679d72a", "modelKey": "event", "timestamp": 1643031646458 }, { "id": "a42e08821b8b7daba434dc39", "type": "motion", "start": 1643031689563, "end": 1643031710591, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a42e08821b8b7daba434dc39", "heatmap": "e-a42e08821b8b7daba434dc39", "modelKey": "event", "timestamp": 1643031646458 }, { "id": "d525093c9f35e8d8f6c99834", "type": "motion", "start": 1643031831386, "end": 1643031853222, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d525093c9f35e8d8f6c99834", "heatmap": "e-d525093c9f35e8d8f6c99834", "modelKey": "event", "timestamp": 1643031842540 }, { "id": "929c8e8fa80989b5b8900d3e", "type": "motion", "start": 1643031837228, "end": 1643031859228, "score": 97, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-929c8e8fa80989b5b8900d3e", "heatmap": "e-929c8e8fa80989b5b8900d3e", "modelKey": "event", "timestamp": 1643031848395 }, { "id": "29a4bfac383ed0d5ee0c5b64", "type": "motion", "start": 1643032196000, "end": 1643032217670, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-29a4bfac383ed0d5ee0c5b64", "heatmap": "e-29a4bfac383ed0d5ee0c5b64", "modelKey": "event", "timestamp": 1643031848395 }, { "id": "860c39b8f2d23248b22f73d1", "type": "motion", "start": 1643032222220, "end": 1643032244387, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-860c39b8f2d23248b22f73d1", "heatmap": "e-860c39b8f2d23248b22f73d1", "modelKey": "event", "timestamp": 1643032233386 }, { "id": "72b34820449f34d651921f05", "type": "motion", "start": 1643032224232, "end": 1643032246237, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-72b34820449f34d651921f05", "heatmap": "e-72b34820449f34d651921f05", "modelKey": "event", "timestamp": 1643032235399 }, { "id": "2412b843afcf242fa4978872", "type": "motion", "start": 1643032224765, "end": 1643032246264, "score": 6, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2412b843afcf242fa4978872", "heatmap": "e-2412b843afcf242fa4978872", "modelKey": "event", "timestamp": 1643031362570 }, { "id": "f39232651905d46129578ec0", "type": "motion", "start": 1643032275486, "end": 1643032296489, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f39232651905d46129578ec0", "heatmap": "e-f39232651905d46129578ec0", "modelKey": "event", "timestamp": 1643032233386 }, { "id": "c0f39c18c1f509b720135edf", "type": "motion", "start": 1643032455067, "end": 1643032476232, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c0f39c18c1f509b720135edf", "heatmap": "e-c0f39c18c1f509b720135edf", "modelKey": "event", "timestamp": 1643032233386 }, { "id": "6d0ac7415f8f140589c7e413", "type": "motion", "start": 1643032494453, "end": 1643032515968, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6d0ac7415f8f140589c7e413", "heatmap": "e-6d0ac7415f8f140589c7e413", "modelKey": "event", "timestamp": 1643032233386 }, { "id": "ee06497462565b5ca859fecd", "type": "motion", "start": 1643032681305, "end": 1643032702641, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ee06497462565b5ca859fecd", "heatmap": "e-ee06497462565b5ca859fecd", "modelKey": "event", "timestamp": 1643032692638 }, { "id": "1da29e98b2c366aa0b9e0cbd", "type": "motion", "start": 1643032868542, "end": 1643032889707, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1da29e98b2c366aa0b9e0cbd", "heatmap": "e-1da29e98b2c366aa0b9e0cbd", "modelKey": "event", "timestamp": 1643032692638 }, { "id": "0f10b68b6dd840c25c2ea218", "type": "motion", "start": 1643032881593, "end": 1643032902593, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0f10b68b6dd840c25c2ea218", "heatmap": "e-0f10b68b6dd840c25c2ea218", "modelKey": "event", "timestamp": 1643032692638 }, { "id": "92f374446316b31e4002dd23", "type": "motion", "start": 1643032895617, "end": 1643032918285, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-92f374446316b31e4002dd23", "heatmap": "e-92f374446316b31e4002dd23", "modelKey": "event", "timestamp": 1643032906784 }, { "id": "f550c3599bb8f157bd01d366", "type": "motion", "start": 1643032970483, "end": 1643032991985, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f550c3599bb8f157bd01d366", "heatmap": "e-f550c3599bb8f157bd01d366", "modelKey": "event", "timestamp": 1643032981983 }, { "id": "89277cc7eee6bb274a670bce", "type": "motion", "start": 1643033076130, "end": 1643033097634, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-89277cc7eee6bb274a670bce", "heatmap": "e-89277cc7eee6bb274a670bce", "modelKey": "event", "timestamp": 1643032981983 }, { "id": "fdf04353176575711ebd6f0d", "type": "motion", "start": 1643033126529, "end": 1643033147697, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fdf04353176575711ebd6f0d", "heatmap": "e-fdf04353176575711ebd6f0d", "modelKey": "event", "timestamp": 1643033137696 }, { "id": "0cf2d9d4827f9f340f98a530", "type": "motion", "start": 1643033190244, "end": 1643033211746, "score": 81, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0cf2d9d4827f9f340f98a530", "heatmap": "e-0cf2d9d4827f9f340f98a530", "modelKey": "event", "timestamp": 1643033201411 }, { "id": "ab2e21305f7c8cec9d18e91a", "type": "motion", "start": 1643033514082, "end": 1643033536103, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ab2e21305f7c8cec9d18e91a", "heatmap": "e-ab2e21305f7c8cec9d18e91a", "modelKey": "event", "timestamp": 1643033525249 }, { "id": "847cab13d1d2381059a25c5d", "type": "motion", "start": 1643033687810, "end": 1643033711981, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-847cab13d1d2381059a25c5d", "heatmap": "e-847cab13d1d2381059a25c5d", "modelKey": "event", "timestamp": 1643033698976 }, { "id": "bf82ebd69f7198988f899813", "type": "sensorMotion", "start": 1643033701310, "end": 1643033701310, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cflvghf Yrgh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bf82ebd69f7198988f899813", "heatmap": "e-bf82ebd69f7198988f899813", "modelKey": "event", "timestamp": null }, { "id": "8faff5c361580abe696c7175", "type": "motion", "start": 1643033726548, "end": 1643033747881, "score": 73, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8faff5c361580abe696c7175", "heatmap": "e-8faff5c361580abe696c7175", "modelKey": "event", "timestamp": 1643033737714 }, { "id": "3939f399baafb1120b91c54e", "type": "sensorMotion", "start": 1643033735186, "end": 1643033735186, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oriaxs Vubg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3939f399baafb1120b91c54e", "heatmap": "e-3939f399baafb1120b91c54e", "modelKey": "event", "timestamp": null }, { "id": "dba914ff1f093bc03456938f", "type": "sensorMotion", "start": 1643033739743, "end": 1643033739743, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zdewi Eetkell" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dba914ff1f093bc03456938f", "heatmap": "e-dba914ff1f093bc03456938f", "modelKey": "event", "timestamp": null }, { "id": "e0c2654b3b681f4b73d1f974", "type": "motion", "start": 1643033818082, "end": 1643033839943, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e0c2654b3b681f4b73d1f974", "heatmap": "e-e0c2654b3b681f4b73d1f974", "modelKey": "event", "timestamp": 1643033829249 }, { "id": "45cee07fc50632c93876db44", "type": "smartDetectZone", "start": 1643033852026, "end": 1643033892130, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-45cee07fc50632c93876db44", "heatmap": "e-45cee07fc50632c93876db44", "modelKey": "event", "timestamp": 1643033874960 }, { "id": "21180d58d2a96a760d9c9d38", "type": "motion", "start": 1643033852836, "end": 1643033892235, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "45cee07fc50632c93876db44" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-21180d58d2a96a760d9c9d38", "heatmap": "e-21180d58d2a96a760d9c9d38", "modelKey": "event", "timestamp": 1643033877402 }, { "id": "8aec51731fc04583d67d2702", "type": "sensorMotion", "start": 1643033863540, "end": 1643033863540, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oslftrc Lzok" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8aec51731fc04583d67d2702", "heatmap": "e-8aec51731fc04583d67d2702", "modelKey": "event", "timestamp": null }, { "id": "3fc3e994c431b451f9cd093f", "type": "sensorMotion", "start": 1643033866902, "end": 1643033866902, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hsnlnz Hmxkk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3fc3e994c431b451f9cd093f", "heatmap": "e-3fc3e994c431b451f9cd093f", "modelKey": "event", "timestamp": null }, { "id": "baa6b464779b1f2b2f118f98", "type": "sensorMotion", "start": 1643033871909, "end": 1643033871909, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jgzjgna Ueek" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-baa6b464779b1f2b2f118f98", "heatmap": "e-baa6b464779b1f2b2f118f98", "modelKey": "event", "timestamp": null }, { "id": "59d0b44991c733a5f21ee9ab", "type": "sensorMotion", "start": 1643033875514, "end": 1643033875514, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rvgjz Pqm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-59d0b44991c733a5f21ee9ab", "heatmap": "e-59d0b44991c733a5f21ee9ab", "modelKey": "event", "timestamp": null }, { "id": "fda5c28f94c00301b9afb496", "type": "sensorMotion", "start": 1643033878891, "end": 1643033878891, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xdwami Unxntaq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fda5c28f94c00301b9afb496", "heatmap": "e-fda5c28f94c00301b9afb496", "modelKey": "event", "timestamp": null }, { "id": "dd488fa4d39aef142ecee7c8", "type": "sensorMotion", "start": 1643033883755, "end": 1643033883755, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Awdfcs Dohtckv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dd488fa4d39aef142ecee7c8", "heatmap": "e-dd488fa4d39aef142ecee7c8", "modelKey": "event", "timestamp": null }, { "id": "03b8ba7d3784827d3b16cd4d", "type": "motion", "start": 1643033887045, "end": 1643033915416, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-03b8ba7d3784827d3b16cd4d", "heatmap": "e-03b8ba7d3784827d3b16cd4d", "modelKey": "event", "timestamp": 1643033900580 }, { "id": "891e72365b48c6db5d3c0483", "type": "sensorMotion", "start": 1643033899218, "end": 1643033899218, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Lpdm Rlks" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-891e72365b48c6db5d3c0483", "heatmap": "e-891e72365b48c6db5d3c0483", "modelKey": "event", "timestamp": null }, { "id": "809ab96de9f09ac17037b650", "type": "sensorMotion", "start": 1643033903324, "end": 1643033903324, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oliavn Nrho" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-809ab96de9f09ac17037b650", "heatmap": "e-809ab96de9f09ac17037b650", "modelKey": "event", "timestamp": null }, { "id": "d390e0125e4e7d42237f6150", "type": "motion", "start": 1643033965476, "end": 1643033991845, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d390e0125e4e7d42237f6150", "heatmap": "e-d390e0125e4e7d42237f6150", "modelKey": "event", "timestamp": 1643033979312 }, { "id": "6b03ec8f7b45a65b426db5c7", "type": "motion", "start": 1643034077118, "end": 1643034100459, "score": 82, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6b03ec8f7b45a65b426db5c7", "heatmap": "e-6b03ec8f7b45a65b426db5c7", "modelKey": "event", "timestamp": 1643034088452 }, { "id": "c8cdcb657f26c7dc94ae68d7", "type": "motion", "start": 1643034098609, "end": 1643034138356, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c8cdcb657f26c7dc94ae68d7", "heatmap": "e-c8cdcb657f26c7dc94ae68d7", "modelKey": "event", "timestamp": 1643034122183 }, { "id": "0cc717c27849fd217ec0f0cd", "type": "smartDetectZone", "start": 1643034106100, "end": 1643034130317, "score": 78, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0cc717c27849fd217ec0f0cd", "heatmap": "e-0cc717c27849fd217ec0f0cd", "modelKey": "event", "timestamp": 1643034118926 }, { "id": "30aafe16438a6ad1b8a08d8c", "type": "motion", "start": 1643034107122, "end": 1643034134122, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "86a54e56ad0cff6d21456bfd" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-30aafe16438a6ad1b8a08d8c", "heatmap": "e-30aafe16438a6ad1b8a08d8c", "modelKey": "event", "timestamp": 1643034123788 }, { "id": "cbf1d93f35a9ea7aaeb8704b", "type": "motion", "start": 1643034108435, "end": 1643034131435, "score": 24, "smartDetectTypes": [], "smartDetectEvents": [ "0cc717c27849fd217ec0f0cd" ], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cbf1d93f35a9ea7aaeb8704b", "heatmap": "e-cbf1d93f35a9ea7aaeb8704b", "modelKey": "event", "timestamp": 1643034088452 }, { "id": "86a54e56ad0cff6d21456bfd", "type": "smartDetectZone", "start": 1643034109250, "end": 1643034174195, "score": 86, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-86a54e56ad0cff6d21456bfd", "heatmap": "e-86a54e56ad0cff6d21456bfd", "modelKey": "event", "timestamp": 1643034142295 }, { "id": "6a07abd05bc50d48c2459c35", "type": "motion", "start": 1643034110328, "end": 1643034134864, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "bd039053c283bc5e52ef56ec" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a07abd05bc50d48c2459c35", "heatmap": "e-6a07abd05bc50d48c2459c35", "modelKey": "event", "timestamp": 1643034124164 }, { "id": "bd039053c283bc5e52ef56ec", "type": "smartDetectZone", "start": 1643034111495, "end": 1643034134166, "score": 74, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bd039053c283bc5e52ef56ec", "heatmap": "e-bd039053c283bc5e52ef56ec", "modelKey": "event", "timestamp": 1643034124164 }, { "id": "f8486ad6cf5e4f7009ef00e7", "type": "sensorMotion", "start": 1643034112181, "end": 1643034112181, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Emz Rgejv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f8486ad6cf5e4f7009ef00e7", "heatmap": "e-f8486ad6cf5e4f7009ef00e7", "modelKey": "event", "timestamp": null }, { "id": "b3b85a9d8680c187fde04313", "type": "sensorMotion", "start": 1643034115106, "end": 1643034115106, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ivi Adoxj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b3b85a9d8680c187fde04313", "heatmap": "e-b3b85a9d8680c187fde04313", "modelKey": "event", "timestamp": null }, { "id": "85807c1988e3812b4a856939", "type": "sensorMotion", "start": 1643034119736, "end": 1643034119736, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Gbkykkm Tdjzgua" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-85807c1988e3812b4a856939", "heatmap": "e-85807c1988e3812b4a856939", "modelKey": "event", "timestamp": null }, { "id": "e5dd98980376dfbd3a2ac42d", "type": "sensorMotion", "start": 1643034124243, "end": 1643034124243, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Agpknne Omvc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e5dd98980376dfbd3a2ac42d", "heatmap": "e-e5dd98980376dfbd3a2ac42d", "modelKey": "event", "timestamp": null }, { "id": "b9ebb1d9a6469c23b95190cc", "type": "smartDetectZone", "start": 1643034124578, "end": 1643034179294, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b9ebb1d9a6469c23b95190cc", "heatmap": "e-b9ebb1d9a6469c23b95190cc", "modelKey": "event", "timestamp": 1643034142258 }, { "id": "5a5467476bee50071c40fb60", "type": "motion", "start": 1643034125457, "end": 1643034153983, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "86a54e56ad0cff6d21456bfd" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5a5467476bee50071c40fb60", "heatmap": "e-5a5467476bee50071c40fb60", "modelKey": "event", "timestamp": 1643034142965 }, { "id": "07e2a6d5ff5e1f43d3345738", "type": "sensorMotion", "start": 1643034127720, "end": 1643034127720, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Inf Kdhj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-07e2a6d5ff5e1f43d3345738", "heatmap": "e-07e2a6d5ff5e1f43d3345738", "modelKey": "event", "timestamp": null }, { "id": "e8f38bb78f605824dc1d0bf0", "type": "motion", "start": 1643034128925, "end": 1643034180310, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "b9ebb1d9a6469c23b95190cc" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e8f38bb78f605824dc1d0bf0", "heatmap": "e-e8f38bb78f605824dc1d0bf0", "modelKey": "event", "timestamp": 1643034142258 }, { "id": "9741fe944c9df1fed9177a9a", "type": "smartDetectZone", "start": 1643034141746, "end": 1643034209062, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9741fe944c9df1fed9177a9a", "heatmap": "e-9741fe944c9df1fed9177a9a", "modelKey": "event", "timestamp": 1643034163010 }, { "id": "87c8669a40b01193b67289f8", "type": "motion", "start": 1643034142686, "end": 1643034210463, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "9741fe944c9df1fed9177a9a" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-87c8669a40b01193b67289f8", "heatmap": "e-87c8669a40b01193b67289f8", "modelKey": "event", "timestamp": 1643034155685 }, { "id": "0ba7eb3323160b8090dd2425", "type": "motion", "start": 1643034148023, "end": 1643034173867, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "86a54e56ad0cff6d21456bfd" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0ba7eb3323160b8090dd2425", "heatmap": "e-0ba7eb3323160b8090dd2425", "modelKey": "event", "timestamp": 1643034159190 }, { "id": "3316c7d2cc43578f09b84a18", "type": "sensorMotion", "start": 1643034153908, "end": 1643034153908, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kxjmg Eostbf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3316c7d2cc43578f09b84a18", "heatmap": "e-3316c7d2cc43578f09b84a18", "modelKey": "event", "timestamp": null }, { "id": "152d23f2c318810f7dfea61d", "type": "sensorMotion", "start": 1643034157100, "end": 1643034157100, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xrarpt Zbspzhc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-152d23f2c318810f7dfea61d", "heatmap": "e-152d23f2c318810f7dfea61d", "modelKey": "event", "timestamp": null }, { "id": "1e1ce63795411aca543bf8b3", "type": "sensorOpened", "start": 1643034160051, "end": 1643034160051, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wxb Yckpr" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-1e1ce63795411aca543bf8b3", "heatmap": "e-1e1ce63795411aca543bf8b3", "modelKey": "event", "timestamp": null }, { "id": "0e2cb7fc08eca54782480a41", "type": "lightMotion", "start": 1643034164127, "end": 1643034164127, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-0e2cb7fc08eca54782480a41", "heatmap": "e-0e2cb7fc08eca54782480a41", "modelKey": "event", "timestamp": null }, { "id": "81dc3eb205d3872ffab10223", "type": "sensorMotion", "start": 1643034164954, "end": 1643034164954, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Arcatf Pfonr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-81dc3eb205d3872ffab10223", "heatmap": "e-81dc3eb205d3872ffab10223", "modelKey": "event", "timestamp": null }, { "id": "2cb88c644af5e6f594b8405e", "type": "sensorMotion", "start": 1643034168688, "end": 1643034168688, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Lbcmeq Por" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2cb88c644af5e6f594b8405e", "heatmap": "e-2cb88c644af5e6f594b8405e", "modelKey": "event", "timestamp": null }, { "id": "78642d47d67c8059f726b53a", "type": "motion", "start": 1643034169816, "end": 1643034214815, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-78642d47d67c8059f726b53a", "heatmap": "e-78642d47d67c8059f726b53a", "modelKey": "event", "timestamp": 1643034184314 }, { "id": "f139f8d88f6403dbc7175d63", "type": "sensorMotion", "start": 1643034171625, "end": 1643034171625, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ssir Pvuemwg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f139f8d88f6403dbc7175d63", "heatmap": "e-f139f8d88f6403dbc7175d63", "modelKey": "event", "timestamp": null }, { "id": "27bcd86abc6291f32aaa2f60", "type": "sensorMotion", "start": 1643034171922, "end": 1643034171922, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Voxzwog Xrxsl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-27bcd86abc6291f32aaa2f60", "heatmap": "e-27bcd86abc6291f32aaa2f60", "modelKey": "event", "timestamp": null }, { "id": "240301c1951062c734fa27c6", "type": "motion", "start": 1643034173695, "end": 1643034245269, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "51e5bc44be6e4094b9fac289", "e37ba8bf3f0c7a9affbeed0d" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-240301c1951062c734fa27c6", "heatmap": "e-240301c1951062c734fa27c6", "modelKey": "event", "timestamp": 1643034231092 }, { "id": "51e5bc44be6e4094b9fac289", "type": "smartDetectZone", "start": 1643034174527, "end": 1643034225060, "score": 79, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-51e5bc44be6e4094b9fac289", "heatmap": "e-51e5bc44be6e4094b9fac289", "modelKey": "event", "timestamp": 1643034196867 }, { "id": "63614bad49ba229fbccc18e5", "type": "motion", "start": 1643034175454, "end": 1643034212649, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "66ea30438debb8943163d24c" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-63614bad49ba229fbccc18e5", "heatmap": "e-63614bad49ba229fbccc18e5", "modelKey": "event", "timestamp": 1643034191622 }, { "id": "66ea30438debb8943163d24c", "type": "smartDetectZone", "start": 1643034175486, "end": 1643034242415, "score": 84, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-66ea30438debb8943163d24c", "heatmap": "e-66ea30438debb8943163d24c", "modelKey": "event", "timestamp": 1643034227590 }, { "id": "21987b70dffbdad398da2710", "type": "sensorMotion", "start": 1643034176689, "end": 1643034176689, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mlg Kaeai" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-21987b70dffbdad398da2710", "heatmap": "e-21987b70dffbdad398da2710", "modelKey": "event", "timestamp": null }, { "id": "98dd21f591734590696386d9", "type": "sensorMotion", "start": 1643034177032, "end": 1643034177032, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Eysfyii Qxgb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-98dd21f591734590696386d9", "heatmap": "e-98dd21f591734590696386d9", "modelKey": "event", "timestamp": null }, { "id": "586a727dcfe1ed52b83b54e0", "type": "sensorMotion", "start": 1643034181895, "end": 1643034181895, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ywuaaj Cstft" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-586a727dcfe1ed52b83b54e0", "heatmap": "e-586a727dcfe1ed52b83b54e0", "modelKey": "event", "timestamp": null }, { "id": "8e4cb37918d1a4503ee7b410", "type": "sensorMotion", "start": 1643034183985, "end": 1643034183985, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Psfltms Uqf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8e4cb37918d1a4503ee7b410", "heatmap": "e-8e4cb37918d1a4503ee7b410", "modelKey": "event", "timestamp": null }, { "id": "f3252e0d59b35df3709bafb0", "type": "lightMotion", "start": 1643034185067, "end": 1643034185067, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-f3252e0d59b35df3709bafb0", "heatmap": "e-f3252e0d59b35df3709bafb0", "modelKey": "event", "timestamp": null }, { "id": "565f4261f2c7dff49b9226ba", "type": "sensorMotion", "start": 1643034185684, "end": 1643034185684, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Veptfg Noyyfqd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-565f4261f2c7dff49b9226ba", "heatmap": "e-565f4261f2c7dff49b9226ba", "modelKey": "event", "timestamp": null }, { "id": "f6bbcea80cd163670c29326d", "type": "sensorMotion", "start": 1643034189807, "end": 1643034189807, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Refhi Mfklien" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f6bbcea80cd163670c29326d", "heatmap": "e-f6bbcea80cd163670c29326d", "modelKey": "event", "timestamp": null }, { "id": "be4465c6857195708bf346af", "type": "sensorMotion", "start": 1643034190165, "end": 1643034190165, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Llp Jzklyd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-be4465c6857195708bf346af", "heatmap": "e-be4465c6857195708bf346af", "modelKey": "event", "timestamp": null }, { "id": "38b6ad02dc08bd5b87c3db04", "type": "lightMotion", "start": 1643034191787, "end": 1643034191787, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-38b6ad02dc08bd5b87c3db04", "heatmap": "e-38b6ad02dc08bd5b87c3db04", "modelKey": "event", "timestamp": null }, { "id": "1c6a751bd4526fe1247302a6", "type": "sensorMotion", "start": 1643034194028, "end": 1643034194028, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fozaine Tiw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1c6a751bd4526fe1247302a6", "heatmap": "e-1c6a751bd4526fe1247302a6", "modelKey": "event", "timestamp": null }, { "id": "37a2d26f6d7d2155f54e69de", "type": "sensorMotion", "start": 1643034195598, "end": 1643034195598, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Krjwwbe Zcl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-37a2d26f6d7d2155f54e69de", "heatmap": "e-37a2d26f6d7d2155f54e69de", "modelKey": "event", "timestamp": null }, { "id": "9d6be81af13bab06dde81347", "type": "sensorMotion", "start": 1643034197504, "end": 1643034197504, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kfatshw Ntym" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9d6be81af13bab06dde81347", "heatmap": "e-9d6be81af13bab06dde81347", "modelKey": "event", "timestamp": null }, { "id": "2152b8d701b31b4249224518", "type": "sensorMotion", "start": 1643034201367, "end": 1643034201367, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hqzwlkv Tqlx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2152b8d701b31b4249224518", "heatmap": "e-2152b8d701b31b4249224518", "modelKey": "event", "timestamp": null }, { "id": "e37ba8bf3f0c7a9affbeed0d", "type": "smartDetectZone", "start": 1643034214254, "end": 1643034244596, "score": 79, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e37ba8bf3f0c7a9affbeed0d", "heatmap": "e-e37ba8bf3f0c7a9affbeed0d", "modelKey": "event", "timestamp": 1643034231092 }, { "id": "46847c426ae880c6f67f1cc8", "type": "smartDetectZone", "start": 1643034214691, "end": 1643034321096, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-46847c426ae880c6f67f1cc8", "heatmap": "e-46847c426ae880c6f67f1cc8", "modelKey": "event", "timestamp": 1643034237732 }, { "id": "40b94b67c4f3b293952b8ddf", "type": "motion", "start": 1643034215956, "end": 1643034282001, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "46847c426ae880c6f67f1cc8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-40b94b67c4f3b293952b8ddf", "heatmap": "e-40b94b67c4f3b293952b8ddf", "modelKey": "event", "timestamp": 1643034243006 }, { "id": "abd2f032eb0720328d48088f", "type": "motion", "start": 1643034216013, "end": 1643034242206, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "66ea30438debb8943163d24c" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-abd2f032eb0720328d48088f", "heatmap": "e-abd2f032eb0720328d48088f", "modelKey": "event", "timestamp": 1643034230515 }, { "id": "306fafeb26009d23a5b2e4fa", "type": "sensorMotion", "start": 1643034224439, "end": 1643034224439, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sajow Ryrwtvj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-306fafeb26009d23a5b2e4fa", "heatmap": "e-306fafeb26009d23a5b2e4fa", "modelKey": "event", "timestamp": null }, { "id": "151ee6311d83e32aaf790418", "type": "lightMotion", "start": 1643034229347, "end": 1643034229347, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-151ee6311d83e32aaf790418", "heatmap": "e-151ee6311d83e32aaf790418", "modelKey": "event", "timestamp": null }, { "id": "1a06ea7c2ea6743e1c83ff39", "type": "sensorMotion", "start": 1643034230619, "end": 1643034230619, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Njie Tnw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1a06ea7c2ea6743e1c83ff39", "heatmap": "e-1a06ea7c2ea6743e1c83ff39", "modelKey": "event", "timestamp": null }, { "id": "82f97bd199f90a5bbec4651a", "type": "sensorMotion", "start": 1643034233466, "end": 1643034233466, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rdhjv Xiqgcd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-82f97bd199f90a5bbec4651a", "heatmap": "e-82f97bd199f90a5bbec4651a", "modelKey": "event", "timestamp": null }, { "id": "5458eb82161fd350b7a5017b", "type": "motion", "start": 1643034235829, "end": 1643034274201, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "09d790c39c9bb29861f0a0b5" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5458eb82161fd350b7a5017b", "heatmap": "e-5458eb82161fd350b7a5017b", "modelKey": "event", "timestamp": 1643034258684 }, { "id": "09d790c39c9bb29861f0a0b5", "type": "smartDetectZone", "start": 1643034236162, "end": 1643034271689, "score": 79, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-09d790c39c9bb29861f0a0b5", "heatmap": "e-09d790c39c9bb29861f0a0b5", "modelKey": "event", "timestamp": 1643034258684 }, { "id": "1653f7a8268f75bd07331e58", "type": "smartDetectZone", "start": 1643034237050, "end": 1643034275519, "score": 84, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1653f7a8268f75bd07331e58", "heatmap": "e-1653f7a8268f75bd07331e58", "modelKey": "event", "timestamp": 1643034255311 }, { "id": "92e51b29fda9f0971a677439", "type": "motion", "start": 1643034237079, "end": 1643034268934, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "1653f7a8268f75bd07331e58" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-92e51b29fda9f0971a677439", "heatmap": "e-92e51b29fda9f0971a677439", "modelKey": "event", "timestamp": 1643034258084 }, { "id": "70f9b930386ee8239bf59402", "type": "sensorMotion", "start": 1643034237459, "end": 1643034237459, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mdwvs Kigqvy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-70f9b930386ee8239bf59402", "heatmap": "e-70f9b930386ee8239bf59402", "modelKey": "event", "timestamp": null }, { "id": "df4358235d5c33e62cd2beca", "type": "sensorMotion", "start": 1643034241707, "end": 1643034241707, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nitb Tlvd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-df4358235d5c33e62cd2beca", "heatmap": "e-df4358235d5c33e62cd2beca", "modelKey": "event", "timestamp": null }, { "id": "a3223b11965b5fcba1d20a16", "type": "sensorMotion", "start": 1643034245570, "end": 1643034245570, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Blramsn Ymawl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a3223b11965b5fcba1d20a16", "heatmap": "e-a3223b11965b5fcba1d20a16", "modelKey": "event", "timestamp": null }, { "id": "5017b196c4855a19ff072781", "type": "lightMotion", "start": 1643034247057, "end": 1643034247057, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-5017b196c4855a19ff072781", "heatmap": "e-5017b196c4855a19ff072781", "modelKey": "event", "timestamp": null }, { "id": "aba0eae7854a9825b3626655", "type": "sensorMotion", "start": 1643034249697, "end": 1643034249697, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kgffofj Qisiqw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aba0eae7854a9825b3626655", "heatmap": "e-aba0eae7854a9825b3626655", "modelKey": "event", "timestamp": null }, { "id": "ac9fb7c03c661956d1967e5e", "type": "lightMotion", "start": 1643034252217, "end": 1643034252217, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-ac9fb7c03c661956d1967e5e", "heatmap": "e-ac9fb7c03c661956d1967e5e", "modelKey": "event", "timestamp": null }, { "id": "ce6232d5cb8e1066483da558", "type": "sensorMotion", "start": 1643034254698, "end": 1643034254698, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yvpk Csn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ce6232d5cb8e1066483da558", "heatmap": "e-ce6232d5cb8e1066483da558", "modelKey": "event", "timestamp": null }, { "id": "89b2b8af9734f042e1c11789", "type": "sensorMotion", "start": 1643034263451, "end": 1643034263451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Scxquy Kvaqnw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-89b2b8af9734f042e1c11789", "heatmap": "e-89b2b8af9734f042e1c11789", "modelKey": "event", "timestamp": null }, { "id": "209466155a92260257ea1c8c", "type": "smartDetectZone", "start": 1643034264214, "end": 1643034473688, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-209466155a92260257ea1c8c", "heatmap": "e-209466155a92260257ea1c8c", "modelKey": "event", "timestamp": 1643034460522 }, { "id": "3fa8c60def39861c184a3bea", "type": "motion", "start": 1643034266222, "end": 1643034476529, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "209466155a92260257ea1c8c" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3fa8c60def39861c184a3bea", "heatmap": "e-3fa8c60def39861c184a3bea", "modelKey": "event", "timestamp": 1643034460522 }, { "id": "56c7d026c78b6a609082dc42", "type": "motion", "start": 1643034268334, "end": 1643034323169, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "46847c426ae880c6f67f1cc8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-56c7d026c78b6a609082dc42", "heatmap": "e-56c7d026c78b6a609082dc42", "modelKey": "event", "timestamp": 1643034288840 }, { "id": "f587c17eb9423b0d5f90e02d", "type": "sensorMotion", "start": 1643034272981, "end": 1643034272981, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ode Tlhmd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f587c17eb9423b0d5f90e02d", "heatmap": "e-f587c17eb9423b0d5f90e02d", "modelKey": "event", "timestamp": null }, { "id": "90d74c51a8520aadca948cf3", "type": "motion", "start": 1643034273269, "end": 1643034295268, "score": 85, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-90d74c51a8520aadca948cf3", "heatmap": "e-90d74c51a8520aadca948cf3", "modelKey": "event", "timestamp": 1643034285103 }, { "id": "7dec1c6d22a8d13e24f412ed", "type": "smartDetectZone", "start": 1643034273545, "end": 1643034473800, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7dec1c6d22a8d13e24f412ed", "heatmap": "e-7dec1c6d22a8d13e24f412ed", "modelKey": "event", "timestamp": 1643034286975 }, { "id": "ee700e8225243ff0f60f1378", "type": "sensorMotion", "start": 1643034278001, "end": 1643034278001, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qhnpv Fokab" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ee700e8225243ff0f60f1378", "heatmap": "e-ee700e8225243ff0f60f1378", "modelKey": "event", "timestamp": null }, { "id": "6ec98d3280b12d822fc298c5", "type": "motion", "start": 1643034278029, "end": 1643034301375, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [ "7dec1c6d22a8d13e24f412ed" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6ec98d3280b12d822fc298c5", "heatmap": "e-6ec98d3280b12d822fc298c5", "modelKey": "event", "timestamp": 1643034289195 }, { "id": "9382cc9045d59b1e0a671038", "type": "motion", "start": 1643034295383, "end": 1643034318415, "score": 36, "smartDetectTypes": [], "smartDetectEvents": [ "7dec1c6d22a8d13e24f412ed" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9382cc9045d59b1e0a671038", "heatmap": "e-9382cc9045d59b1e0a671038", "modelKey": "event", "timestamp": 1643034289195 }, { "id": "4d33dcff80d9d4f5139b9774", "type": "motion", "start": 1643034304267, "end": 1643034326270, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4d33dcff80d9d4f5139b9774", "heatmap": "e-4d33dcff80d9d4f5139b9774", "modelKey": "event", "timestamp": 1643034315434 }, { "id": "a1b88d4b26e1da2a75b7ecd8", "type": "smartDetectZone", "start": 1643034310700, "end": 1643034533166, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a1b88d4b26e1da2a75b7ecd8", "heatmap": "e-a1b88d4b26e1da2a75b7ecd8", "modelKey": "event", "timestamp": 1643034504481 }, { "id": "8fe3efc643d4cbb15bf38bc5", "type": "sensorMotion", "start": 1643034335787, "end": 1643034335787, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Yskd Mxf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8fe3efc643d4cbb15bf38bc5", "heatmap": "e-8fe3efc643d4cbb15bf38bc5", "modelKey": "event", "timestamp": null }, { "id": "a3ad114bd008c28650a56f13", "type": "motion", "start": 1643034340011, "end": 1643034364186, "score": 42, "smartDetectTypes": [], "smartDetectEvents": [ "7dec1c6d22a8d13e24f412ed" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a3ad114bd008c28650a56f13", "heatmap": "e-a3ad114bd008c28650a56f13", "modelKey": "event", "timestamp": 1643034289195 }, { "id": "3caadaf5c08c776d2537e196", "type": "motion", "start": 1643034350192, "end": 1643034376552, "score": 85, "smartDetectTypes": [], "smartDetectEvents": [ "7dec1c6d22a8d13e24f412ed" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3caadaf5c08c776d2537e196", "heatmap": "e-3caadaf5c08c776d2537e196", "modelKey": "event", "timestamp": 1643034362861 }, { "id": "7763476eaefed8866393cab6", "type": "motion", "start": 1643034403200, "end": 1643034430056, "score": 26, "smartDetectTypes": [], "smartDetectEvents": [ "7dec1c6d22a8d13e24f412ed" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7763476eaefed8866393cab6", "heatmap": "e-7763476eaefed8866393cab6", "modelKey": "event", "timestamp": 1643034362861 }, { "id": "fb8e264cb3040b9324a4a086", "type": "motion", "start": 1643034455122, "end": 1643034478140, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fb8e264cb3040b9324a4a086", "heatmap": "e-fb8e264cb3040b9324a4a086", "modelKey": "event", "timestamp": 1643034467124 }, { "id": "7bf5078bbf0f90ad9c3c7086", "type": "smartDetectZone", "start": 1643034492395, "end": 1643034515877, "score": 76, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7bf5078bbf0f90ad9c3c7086", "heatmap": "e-7bf5078bbf0f90ad9c3c7086", "modelKey": "event", "timestamp": 1643034505821 }, { "id": "9c9f85c23e24465ebd47359d", "type": "motion", "start": 1643034492421, "end": 1643034516269, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "7bf5078bbf0f90ad9c3c7086" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9c9f85c23e24465ebd47359d", "heatmap": "e-9c9f85c23e24465ebd47359d", "modelKey": "event", "timestamp": 1643034503587 }, { "id": "040e5519c2752459a3256f2c", "type": "smartDetectZone", "start": 1643034492633, "end": 1643034518826, "score": 78, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-040e5519c2752459a3256f2c", "heatmap": "e-040e5519c2752459a3256f2c", "modelKey": "event", "timestamp": 1643034506143 }, { "id": "b38afdd13745aebecca45c76", "type": "motion", "start": 1643034492799, "end": 1643034525329, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "040e5519c2752459a3256f2c" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b38afdd13745aebecca45c76", "heatmap": "e-b38afdd13745aebecca45c76", "modelKey": "event", "timestamp": 1643034506143 }, { "id": "20e0a77e3d59fc594c72b4b5", "type": "motion", "start": 1643034494305, "end": 1643034544356, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "a1b88d4b26e1da2a75b7ecd8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-20e0a77e3d59fc594c72b4b5", "heatmap": "e-20e0a77e3d59fc594c72b4b5", "modelKey": "event", "timestamp": 1643034507638 }, { "id": "3d44489f1d418a0b549bbfb7", "type": "lightMotion", "start": 1643034504337, "end": 1643034504337, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-3d44489f1d418a0b549bbfb7", "heatmap": "e-3d44489f1d418a0b549bbfb7", "modelKey": "event", "timestamp": null }, { "id": "3dc1a02ec2a032c1b37fc652", "type": "sensorMotion", "start": 1643034505783, "end": 1643034505783, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Enmboru Angh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3dc1a02ec2a032c1b37fc652", "heatmap": "e-3dc1a02ec2a032c1b37fc652", "modelKey": "event", "timestamp": null }, { "id": "c245296944dc152bd5e48981", "type": "motion", "start": 1643034512488, "end": 1643034536498, "score": 58, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c245296944dc152bd5e48981", "heatmap": "e-c245296944dc152bd5e48981", "modelKey": "event", "timestamp": 1643034523655 }, { "id": "92ffcc521268b11cba706f42", "type": "motion", "start": 1643034512932, "end": 1643034539248, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "a1561674672c0b261ccddf68" ], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-92ffcc521268b11cba706f42", "heatmap": "e-92ffcc521268b11cba706f42", "modelKey": "event", "timestamp": 1643034527413 }, { "id": "a1561674672c0b261ccddf68", "type": "smartDetectZone", "start": 1643034514462, "end": 1643034535865, "score": 79, "smartDetectTypes": [ "vehicle" ], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a1561674672c0b261ccddf68", "heatmap": "e-a1561674672c0b261ccddf68", "modelKey": "event", "timestamp": 1643034525462 }, { "id": "d4375332199ac173b163eb25", "type": "sensorClosed", "start": 1643034521741, "end": 1643034521741, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Allfkcn Ljqjtmf" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-d4375332199ac173b163eb25", "heatmap": "e-d4375332199ac173b163eb25", "modelKey": "event", "timestamp": null }, { "id": "b3e0e0bc55af5839453a9fff", "type": "sensorMotion", "start": 1643034522650, "end": 1643034522650, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Amdieiv Dkj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b3e0e0bc55af5839453a9fff", "heatmap": "e-b3e0e0bc55af5839453a9fff", "modelKey": "event", "timestamp": null }, { "id": "396bba8dab73f7ece6065dc7", "type": "motion", "start": 1643034523081, "end": 1643034545924, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-396bba8dab73f7ece6065dc7", "heatmap": "e-396bba8dab73f7ece6065dc7", "modelKey": "event", "timestamp": 1643034534248 }, { "id": "72d0a74c193d2729208e52ff", "type": "sensorMotion", "start": 1643034651880, "end": 1643034651880, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ndfxolu Ylkh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-72d0a74c193d2729208e52ff", "heatmap": "e-72d0a74c193d2729208e52ff", "modelKey": "event", "timestamp": null }, { "id": "68507083223c1faff68c5331", "type": "motion", "start": 1643034745568, "end": 1643034766910, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-68507083223c1faff68c5331", "heatmap": "e-68507083223c1faff68c5331", "modelKey": "event", "timestamp": 1643034756901 }, { "id": "347d3ea944e3a2715f4ace55", "type": "motion", "start": 1643035340318, "end": 1643035362827, "score": 82, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-347d3ea944e3a2715f4ace55", "heatmap": "e-347d3ea944e3a2715f4ace55", "modelKey": "event", "timestamp": 1643035351486 }, { "id": "1147e5e521c5fec8969bd3cb", "type": "motion", "start": 1643035343244, "end": 1643035364743, "score": 9, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1147e5e521c5fec8969bd3cb", "heatmap": "e-1147e5e521c5fec8969bd3cb", "modelKey": "event", "timestamp": 1643034534248 }, { "id": "74363b23a1e9e6de27950b86", "type": "motion", "start": 1643035359897, "end": 1643035381724, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-74363b23a1e9e6de27950b86", "heatmap": "e-74363b23a1e9e6de27950b86", "modelKey": "event", "timestamp": 1643035351486 }, { "id": "57131678d0ae3f61acd2a52b", "type": "motion", "start": 1643035611387, "end": 1643035633899, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-57131678d0ae3f61acd2a52b", "heatmap": "e-57131678d0ae3f61acd2a52b", "modelKey": "event", "timestamp": 1643035622563 }, { "id": "a363cf26ad14ee10a9973337", "type": "motion", "start": 1643036049491, "end": 1643036072162, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a363cf26ad14ee10a9973337", "heatmap": "e-a363cf26ad14ee10a9973337", "modelKey": "event", "timestamp": 1643036060658 }, { "id": "8731495d403021bbc2412bad", "type": "motion", "start": 1643036204181, "end": 1643036226015, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8731495d403021bbc2412bad", "heatmap": "e-8731495d403021bbc2412bad", "modelKey": "event", "timestamp": 1643036215347 }, { "id": "d7f91a1249291d4ce0970395", "type": "motion", "start": 1643036437273, "end": 1643036458943, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d7f91a1249291d4ce0970395", "heatmap": "e-d7f91a1249291d4ce0970395", "modelKey": "event", "timestamp": 1643036215347 }, { "id": "378a20292406ee2d20a56f27", "type": "motion", "start": 1643037018033, "end": 1643037039527, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-378a20292406ee2d20a56f27", "heatmap": "e-378a20292406ee2d20a56f27", "modelKey": "event", "timestamp": 1643037029194 }, { "id": "cd3a4d0443e0c393706ee70f", "type": "sensorMotion", "start": 1643037030880, "end": 1643037030880, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hxme Pjvd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cd3a4d0443e0c393706ee70f", "heatmap": "e-cd3a4d0443e0c393706ee70f", "modelKey": "event", "timestamp": null }, { "id": "99d62ec53eef45464d11b8e4", "type": "sensorMotion", "start": 1643037036416, "end": 1643037036416, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xthbn Gplbc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-99d62ec53eef45464d11b8e4", "heatmap": "e-99d62ec53eef45464d11b8e4", "modelKey": "event", "timestamp": null }, { "id": "1d50b893fb0fa21b6156ab9f", "type": "motion", "start": 1643037233934, "end": 1643037255433, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1d50b893fb0fa21b6156ab9f", "heatmap": "e-1d50b893fb0fa21b6156ab9f", "modelKey": "event", "timestamp": 1643037245267 }, { "id": "42ae4ac889974c03d795e670", "type": "motion", "start": 1643037763765, "end": 1643037784767, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-42ae4ac889974c03d795e670", "heatmap": "e-42ae4ac889974c03d795e670", "modelKey": "event", "timestamp": 1643037245267 }, { "id": "b64f526dee4974658a6aa80f", "type": "motion", "start": 1643037962976, "end": 1643037985144, "score": 81, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b64f526dee4974658a6aa80f", "heatmap": "e-b64f526dee4974658a6aa80f", "modelKey": "event", "timestamp": 1643037975144 }, { "id": "be264e27e6b256bcf2f9381b", "type": "motion", "start": 1643038079173, "end": 1643038100677, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-be264e27e6b256bcf2f9381b", "heatmap": "e-be264e27e6b256bcf2f9381b", "modelKey": "event", "timestamp": 1643037975144 }, { "id": "3a2a9a47705436037cbd3b7a", "type": "access", "start": 1643038136835, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "8593657a25b7826a4288b6af", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-3a2a9a47705436037cbd3b7a", "heatmap": "e-3a2a9a47705436037cbd3b7a", "modelKey": "event", "timestamp": null }, { "id": "32ba93a82da7e973aa99b01d", "type": "access", "start": 1643038145947, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "dcaef9cb8aed05c7db658a46", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-32ba93a82da7e973aa99b01d", "heatmap": "e-32ba93a82da7e973aa99b01d", "modelKey": "event", "timestamp": null }, { "id": "9fb10aee5cb8c3fac2cb3e4e", "type": "motion", "start": 1643038443103, "end": 1643038464939, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9fb10aee5cb8c3fac2cb3e4e", "heatmap": "e-9fb10aee5cb8c3fac2cb3e4e", "modelKey": "event", "timestamp": 1643038454769 }, { "id": "9bcc0355bf06dfd40960cc32", "type": "smartDetectZone", "start": 1643038851191, "end": 1643038872941, "score": 31, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9bcc0355bf06dfd40960cc32", "heatmap": "e-9bcc0355bf06dfd40960cc32", "modelKey": "event", "timestamp": 1643038862483 }, { "id": "34730af354700c6729f331de", "type": "motion", "start": 1643038851268, "end": 1643038873769, "score": 39, "smartDetectTypes": [], "smartDetectEvents": [ "9bcc0355bf06dfd40960cc32" ], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-34730af354700c6729f331de", "heatmap": "e-34730af354700c6729f331de", "modelKey": "event", "timestamp": 1642994081310 }, { "id": "18c81712f6616b9e5461ff94", "type": "motion", "start": 1643038858374, "end": 1643038880200, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-18c81712f6616b9e5461ff94", "heatmap": "e-18c81712f6616b9e5461ff94", "modelKey": "event", "timestamp": 1643038869530 }, { "id": "fdb40a9eeba05ac5355d6d19", "type": "motion", "start": 1643040017026, "end": 1643040038197, "score": 87, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fdb40a9eeba05ac5355d6d19", "heatmap": "e-fdb40a9eeba05ac5355d6d19", "modelKey": "event", "timestamp": 1643040028193 }, { "id": "50949a93485e2efd056881cd", "type": "motion", "start": 1643040326743, "end": 1643040355322, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-50949a93485e2efd056881cd", "heatmap": "e-50949a93485e2efd056881cd", "modelKey": "event", "timestamp": 1643040340478 }, { "id": "daef7eaaa4f5105d4c3120ef", "type": "sensorMotion", "start": 1643040338973, "end": 1643040338973, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qnvss Zun" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-daef7eaaa4f5105d4c3120ef", "heatmap": "e-daef7eaaa4f5105d4c3120ef", "modelKey": "event", "timestamp": null }, { "id": "a01760bea04587eec85b459a", "type": "motion", "start": 1643040368090, "end": 1643040395009, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a01760bea04587eec85b459a", "heatmap": "e-a01760bea04587eec85b459a", "modelKey": "event", "timestamp": 1643040380455 }, { "id": "62bb9e3f214093567445abcd", "type": "sensorMotion", "start": 1643040379916, "end": 1643040379916, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nsj Yedwcgz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-62bb9e3f214093567445abcd", "heatmap": "e-62bb9e3f214093567445abcd", "modelKey": "event", "timestamp": null }, { "id": "ad3a3af0e57df6c179e1824f", "type": "sensorMotion", "start": 1643040383779, "end": 1643040383779, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hqlmg Ues" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ad3a3af0e57df6c179e1824f", "heatmap": "e-ad3a3af0e57df6c179e1824f", "modelKey": "event", "timestamp": null }, { "id": "4f842f170531d4b724b1dafc", "type": "motion", "start": 1643040531297, "end": 1643040552464, "score": 82, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4f842f170531d4b724b1dafc", "heatmap": "e-4f842f170531d4b724b1dafc", "modelKey": "event", "timestamp": 1643040542464 }, { "id": "b39227de7be73afeb756cc5a", "type": "motion", "start": 1643040915109, "end": 1643040937120, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b39227de7be73afeb756cc5a", "heatmap": "e-b39227de7be73afeb756cc5a", "modelKey": "event", "timestamp": 1643040926277 }, { "id": "527d526e5c1e57363981c23f", "type": "motion", "start": 1643040920136, "end": 1643040943310, "score": 5, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-527d526e5c1e57363981c23f", "heatmap": "e-527d526e5c1e57363981c23f", "modelKey": "event", "timestamp": 1643035622563 }, { "id": "c50f4e55db5f9419882b26ed", "type": "access", "start": 1643041036221, "end": 1643041334668, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "adec5334b69f56f6a6c47520", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-c50f4e55db5f9419882b26ed", "heatmap": "e-c50f4e55db5f9419882b26ed", "modelKey": "event", "timestamp": null }, { "id": "24e70a137cbc481fe514f1e4", "type": "motion", "start": 1643041118901, "end": 1643041140401, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-24e70a137cbc481fe514f1e4", "heatmap": "e-24e70a137cbc481fe514f1e4", "modelKey": "event", "timestamp": 1643041130235 }, { "id": "ce54930b351bb34b8eebe01a", "type": "motion", "start": 1643041487524, "end": 1643041508704, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ce54930b351bb34b8eebe01a", "heatmap": "e-ce54930b351bb34b8eebe01a", "modelKey": "event", "timestamp": 1643041130235 }, { "id": "8795e3fc9fcdbceaa26def5a", "type": "motion", "start": 1643041564516, "end": 1643041586705, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8795e3fc9fcdbceaa26def5a", "heatmap": "e-8795e3fc9fcdbceaa26def5a", "modelKey": "event", "timestamp": 1643041575682 }, { "id": "315d10575a7d928f5a017792", "type": "motion", "start": 1643041631463, "end": 1643041652629, "score": 85, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-315d10575a7d928f5a017792", "heatmap": "e-315d10575a7d928f5a017792", "modelKey": "event", "timestamp": 1643041642630 }, { "id": "7efd506681db68df7c517373", "type": "motion", "start": 1643041638348, "end": 1643041665212, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7efd506681db68df7c517373", "heatmap": "e-7efd506681db68df7c517373", "modelKey": "event", "timestamp": 1643041650710 }, { "id": "b26556fbb76e1794c308f2a3", "type": "sensorMotion", "start": 1643041649309, "end": 1643041649309, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Dtjjwt Weeqs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b26556fbb76e1794c308f2a3", "heatmap": "e-b26556fbb76e1794c308f2a3", "modelKey": "event", "timestamp": null }, { "id": "75dc4cb27a11b5ee774f33b7", "type": "sensorMotion", "start": 1643041652915, "end": 1643041652915, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Usyi Bjxle" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-75dc4cb27a11b5ee774f33b7", "heatmap": "e-75dc4cb27a11b5ee774f33b7", "modelKey": "event", "timestamp": null }, { "id": "f3d18cd9f3cfedffef1c4664", "type": "motion", "start": 1643041865628, "end": 1643041894513, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f3d18cd9f3cfedffef1c4664", "heatmap": "e-f3d18cd9f3cfedffef1c4664", "modelKey": "event", "timestamp": 1643041879293 }, { "id": "cbf039a94065de2e3c9d373d", "type": "sensorMotion", "start": 1643041877978, "end": 1643041877978, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Abcmyne Jsvcpiy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cbf039a94065de2e3c9d373d", "heatmap": "e-cbf039a94065de2e3c9d373d", "modelKey": "event", "timestamp": null }, { "id": "64f8024715e2db112c79a548", "type": "sensorMotion", "start": 1643041882870, "end": 1643041882870, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rdwo Usljlq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-64f8024715e2db112c79a548", "heatmap": "e-64f8024715e2db112c79a548", "modelKey": "event", "timestamp": null }, { "id": "09a6a79146b3e97fc71fb20a", "type": "motion", "start": 1643042042177, "end": 1643042064350, "score": 80, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-09a6a79146b3e97fc71fb20a", "heatmap": "e-09a6a79146b3e97fc71fb20a", "modelKey": "event", "timestamp": 1643042053511 }, { "id": "1a6d457296f65658459b9731", "type": "motion", "start": 1643042088441, "end": 1643042109621, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1a6d457296f65658459b9731", "heatmap": "e-1a6d457296f65658459b9731", "modelKey": "event", "timestamp": 1643042099621 }, { "id": "092e7e2c439f37c4481d6b6f", "type": "motion", "start": 1643042327401, "end": 1643042350403, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-092e7e2c439f37c4481d6b6f", "heatmap": "e-092e7e2c439f37c4481d6b6f", "modelKey": "event", "timestamp": 1643042340259 }, { "id": "2e7a29d1d2ee9c6b4e959283", "type": "motion", "start": 1643042662604, "end": 1643042683940, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2e7a29d1d2ee9c6b4e959283", "heatmap": "e-2e7a29d1d2ee9c6b4e959283", "modelKey": "event", "timestamp": 1643042673937 }, { "id": "013230719a2f622d6dd62f2e", "type": "motion", "start": 1643042663853, "end": 1643042689689, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-013230719a2f622d6dd62f2e", "heatmap": "e-013230719a2f622d6dd62f2e", "modelKey": "event", "timestamp": 1643042679353 }, { "id": "c654d9582e7c5aabcbf0acb9", "type": "motion", "start": 1643042668943, "end": 1643042690778, "score": 7, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c654d9582e7c5aabcbf0acb9", "heatmap": "e-c654d9582e7c5aabcbf0acb9", "modelKey": "event", "timestamp": 1643042673937 }, { "id": "894395a64c4bdef76bb6d00c", "type": "motion", "start": 1643042698732, "end": 1643042721568, "score": 72, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-894395a64c4bdef76bb6d00c", "heatmap": "e-894395a64c4bdef76bb6d00c", "modelKey": "event", "timestamp": 1643042709900 }, { "id": "f1cea2328fdc8794dfd51acb", "type": "motion", "start": 1643042750613, "end": 1643042773452, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f1cea2328fdc8794dfd51acb", "heatmap": "e-f1cea2328fdc8794dfd51acb", "modelKey": "event", "timestamp": 1643042761779 }, { "id": "42126853c5e30601a8db577f", "type": "motion", "start": 1643042754022, "end": 1643042775525, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-42126853c5e30601a8db577f", "heatmap": "e-42126853c5e30601a8db577f", "modelKey": "event", "timestamp": 1643042765522 }, { "id": "c05aa053c6f719f718e008e4", "type": "motion", "start": 1643042764871, "end": 1643042787210, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c05aa053c6f719f718e008e4", "heatmap": "e-c05aa053c6f719f718e008e4", "modelKey": "event", "timestamp": 1643042776038 }, { "id": "b5713b2abad42a3b22938d15", "type": "motion", "start": 1643042827488, "end": 1643042848659, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b5713b2abad42a3b22938d15", "heatmap": "e-b5713b2abad42a3b22938d15", "modelKey": "event", "timestamp": 1643042838655 }, { "id": "67c87489edcd1bd6ce54253e", "type": "motion", "start": 1643042933418, "end": 1643042954787, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-67c87489edcd1bd6ce54253e", "heatmap": "e-67c87489edcd1bd6ce54253e", "modelKey": "event", "timestamp": 1643042944585 }, { "id": "ec925b4a1b8ff94e5aa60bc7", "type": "motion", "start": 1643042945769, "end": 1643042969942, "score": 20, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ec925b4a1b8ff94e5aa60bc7", "heatmap": "e-ec925b4a1b8ff94e5aa60bc7", "modelKey": "event", "timestamp": 1643042959774 }, { "id": "1c9bbfaa903b0c37cbd7f83c", "type": "motion", "start": 1643043243382, "end": 1643043270552, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1c9bbfaa903b0c37cbd7f83c", "heatmap": "e-1c9bbfaa903b0c37cbd7f83c", "modelKey": "event", "timestamp": 1643043258223 }, { "id": "09721a527552f14fa01c9d5c", "type": "motion", "start": 1643043245340, "end": 1643043266528, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-09721a527552f14fa01c9d5c", "heatmap": "e-09721a527552f14fa01c9d5c", "modelKey": "event", "timestamp": 1643043256507 }, { "id": "c388a5ed945ea3854b228cf9", "type": "sensorMotion", "start": 1643043252689, "end": 1643043252689, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qcmzii Iea" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c388a5ed945ea3854b228cf9", "heatmap": "e-c388a5ed945ea3854b228cf9", "modelKey": "event", "timestamp": null }, { "id": "b0da8a423ebd70bf5fe1d9e5", "type": "sensorMotion", "start": 1643043259255, "end": 1643043259255, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kbqyz Dlg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b0da8a423ebd70bf5fe1d9e5", "heatmap": "e-b0da8a423ebd70bf5fe1d9e5", "modelKey": "event", "timestamp": null }, { "id": "e4c06ef076ea15a8a3de3f29", "type": "motion", "start": 1643043264465, "end": 1643043292139, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e4c06ef076ea15a8a3de3f29", "heatmap": "e-e4c06ef076ea15a8a3de3f29", "modelKey": "event", "timestamp": 1643043275633 }, { "id": "081bc51b8b883756d51a9a14", "type": "sensorMotion", "start": 1643043274963, "end": 1643043274963, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Dcexjo Rnrysve" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-081bc51b8b883756d51a9a14", "heatmap": "e-081bc51b8b883756d51a9a14", "modelKey": "event", "timestamp": null }, { "id": "caa11c323792f753b5ccd267", "type": "motion", "start": 1643043279050, "end": 1643043306390, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-caa11c323792f753b5ccd267", "heatmap": "e-caa11c323792f753b5ccd267", "modelKey": "event", "timestamp": 1643043290382 }, { "id": "0ac3945755441c01792a9275", "type": "sensorMotion", "start": 1643043279470, "end": 1643043279470, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Wrg Zwlw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0ac3945755441c01792a9275", "heatmap": "e-0ac3945755441c01792a9275", "modelKey": "event", "timestamp": null }, { "id": "46a3db31e1d81f20fdae7e9a", "type": "motion", "start": 1643043285782, "end": 1643043308925, "score": 19, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-46a3db31e1d81f20fdae7e9a", "heatmap": "e-46a3db31e1d81f20fdae7e9a", "modelKey": "event", "timestamp": 1643042959774 }, { "id": "585cab5d98964d5c2320b58b", "type": "motion", "start": 1643043305153, "end": 1643043332999, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-585cab5d98964d5c2320b58b", "heatmap": "e-585cab5d98964d5c2320b58b", "modelKey": "event", "timestamp": 1643043319320 }, { "id": "4520caf27925a5190a2f0a96", "type": "sensorMotion", "start": 1643043356351, "end": 1643043356351, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vykvcf Iippvl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4520caf27925a5190a2f0a96", "heatmap": "e-4520caf27925a5190a2f0a96", "modelKey": "event", "timestamp": null }, { "id": "84f89d11b3e7da4ef003edc5", "type": "sensorMotion", "start": 1643043501416, "end": 1643043501416, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Snflcgv Vvux" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-84f89d11b3e7da4ef003edc5", "heatmap": "e-84f89d11b3e7da4ef003edc5", "modelKey": "event", "timestamp": null }, { "id": "0c6b1ce8cb22b13baa77ae80", "type": "sensorMotion", "start": 1643043526413, "end": 1643043526413, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Oks Yfryvop" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0c6b1ce8cb22b13baa77ae80", "heatmap": "e-0c6b1ce8cb22b13baa77ae80", "modelKey": "event", "timestamp": null }, { "id": "cb6f6f9d0291ea10aba8ba37", "type": "smartDetectZone", "start": 1643043533442, "end": 1643043584506, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cb6f6f9d0291ea10aba8ba37", "heatmap": "e-cb6f6f9d0291ea10aba8ba37", "modelKey": "event", "timestamp": 1643043558380 }, { "id": "47c1d3918dabe8153e68405b", "type": "motion", "start": 1643043537466, "end": 1643043584403, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "cb6f6f9d0291ea10aba8ba37" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-47c1d3918dabe8153e68405b", "heatmap": "e-47c1d3918dabe8153e68405b", "modelKey": "event", "timestamp": 1643043569184 }, { "id": "adb70d6c5346a5297543febf", "type": "motion", "start": 1643043537562, "end": 1643043558909, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-adb70d6c5346a5297543febf", "heatmap": "e-adb70d6c5346a5297543febf", "modelKey": "event", "timestamp": 1643043548907 }, { "id": "2f0a20c1b346f32d75487ceb", "type": "sensorMotion", "start": 1643043544616, "end": 1643043544616, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dccr Etrlgcy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2f0a20c1b346f32d75487ceb", "heatmap": "e-2f0a20c1b346f32d75487ceb", "modelKey": "event", "timestamp": null }, { "id": "ba5c700c719332ac4e15815f", "type": "sensorMotion", "start": 1643043547434, "end": 1643043547434, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ooddau Lvpxif" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ba5c700c719332ac4e15815f", "heatmap": "e-ba5c700c719332ac4e15815f", "modelKey": "event", "timestamp": null }, { "id": "778d7845364cac274ea23fc8", "type": "sensorMotion", "start": 1643043551615, "end": 1643043551615, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dntl Cjcl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-778d7845364cac274ea23fc8", "heatmap": "e-778d7845364cac274ea23fc8", "modelKey": "event", "timestamp": null }, { "id": "1b94907f506c6dfcec6e5b95", "type": "sensorMotion", "start": 1643043556847, "end": 1643043556847, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ykxz Fpued" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1b94907f506c6dfcec6e5b95", "heatmap": "e-1b94907f506c6dfcec6e5b95", "modelKey": "event", "timestamp": null }, { "id": "2ea2a757e2d9f0b1eb76c6b5", "type": "sensorMotion", "start": 1643043563785, "end": 1643043563785, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uehrmcl Ifcn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2ea2a757e2d9f0b1eb76c6b5", "heatmap": "e-2ea2a757e2d9f0b1eb76c6b5", "modelKey": "event", "timestamp": null }, { "id": "cc51886812b5e17bd63c0d9d", "type": "sensorMotion", "start": 1643043568806, "end": 1643043568806, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pntwatr Mer" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cc51886812b5e17bd63c0d9d", "heatmap": "e-cc51886812b5e17bd63c0d9d", "modelKey": "event", "timestamp": null }, { "id": "d812cd195204db7c8660f139", "type": "sensorMotion", "start": 1643043572928, "end": 1643043572928, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Alnqfki Zxwowmg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d812cd195204db7c8660f139", "heatmap": "e-d812cd195204db7c8660f139", "modelKey": "event", "timestamp": null }, { "id": "9187fe8b6ca7c94637c749d2", "type": "motion", "start": 1643043582174, "end": 1643043603344, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9187fe8b6ca7c94637c749d2", "heatmap": "e-9187fe8b6ca7c94637c749d2", "modelKey": "event", "timestamp": 1643043593341 }, { "id": "9310fae8e786cec462ee350b", "type": "sensorMotion", "start": 1643043610470, "end": 1643043610470, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Iulxm Ajatut" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9310fae8e786cec462ee350b", "heatmap": "e-9310fae8e786cec462ee350b", "modelKey": "event", "timestamp": null }, { "id": "32dee6c21329ef16d61158e8", "type": "sensorMotion", "start": 1643043640084, "end": 1643043640084, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nglp Pteqae" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-32dee6c21329ef16d61158e8", "heatmap": "e-32dee6c21329ef16d61158e8", "modelKey": "event", "timestamp": null }, { "id": "2f70df8ab84f077a0665c1c4", "type": "sensorMotion", "start": 1643043766650, "end": 1643043766650, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fmqvl Ojf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2f70df8ab84f077a0665c1c4", "heatmap": "e-2f70df8ab84f077a0665c1c4", "modelKey": "event", "timestamp": null }, { "id": "87d90402a0dd7c60da1eb842", "type": "motion", "start": 1643043781871, "end": 1643043803206, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-87d90402a0dd7c60da1eb842", "heatmap": "e-87d90402a0dd7c60da1eb842", "modelKey": "event", "timestamp": 1643043793206 }, { "id": "81f79c2901e5544b07bdf154", "type": "motion", "start": 1643043789742, "end": 1643043810744, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-81f79c2901e5544b07bdf154", "heatmap": "e-81f79c2901e5544b07bdf154", "modelKey": "event", "timestamp": 1643043793206 }, { "id": "a6137bb8a65cce32f43d1b3b", "type": "motion", "start": 1643043906128, "end": 1643043934299, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a6137bb8a65cce32f43d1b3b", "heatmap": "e-a6137bb8a65cce32f43d1b3b", "modelKey": "event", "timestamp": 1643043918462 }, { "id": "45626b14429c717df0e6ea4a", "type": "sensorMotion", "start": 1643043917164, "end": 1643043917164, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rpc Wxzgbp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-45626b14429c717df0e6ea4a", "heatmap": "e-45626b14429c717df0e6ea4a", "modelKey": "event", "timestamp": null }, { "id": "cce220edaf841d8a4c7faa6c", "type": "sensorMotion", "start": 1643043921568, "end": 1643043921568, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Vhuk Hybx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cce220edaf841d8a4c7faa6c", "heatmap": "e-cce220edaf841d8a4c7faa6c", "modelKey": "event", "timestamp": null }, { "id": "f018b446975b212e806b469e", "type": "motion", "start": 1643044066134, "end": 1643044092987, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f018b446975b212e806b469e", "heatmap": "e-f018b446975b212e806b469e", "modelKey": "event", "timestamp": 1643044080316 }, { "id": "914017a15d80941d577a08a7", "type": "sensorMotion", "start": 1643044078921, "end": 1643044078921, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "His Fqdo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-914017a15d80941d577a08a7", "heatmap": "e-914017a15d80941d577a08a7", "modelKey": "event", "timestamp": null }, { "id": "226b267c7a62d4b466a8c1e6", "type": "sensorMotion", "start": 1643044083773, "end": 1643044083773, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Toqd Aldgn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-226b267c7a62d4b466a8c1e6", "heatmap": "e-226b267c7a62d4b466a8c1e6", "modelKey": "event", "timestamp": null }, { "id": "a94051c9a170dbec7728c5e6", "type": "motion", "start": 1643044220323, "end": 1643044241323, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a94051c9a170dbec7728c5e6", "heatmap": "e-a94051c9a170dbec7728c5e6", "modelKey": "event", "timestamp": 1643043793206 }, { "id": "e9763dee00986e40dc0d3b29", "type": "motion", "start": 1643044381550, "end": 1643044407218, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e9763dee00986e40dc0d3b29", "heatmap": "e-e9763dee00986e40dc0d3b29", "modelKey": "event", "timestamp": 1643044397053 }, { "id": "c8ee9cb66e1930bfe980e344", "type": "motion", "start": 1643044381893, "end": 1643044403058, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c8ee9cb66e1930bfe980e344", "heatmap": "e-c8ee9cb66e1930bfe980e344", "modelKey": "event", "timestamp": 1643042959774 }, { "id": "d2095c91472f83246d16a3b9", "type": "motion", "start": 1643044566653, "end": 1643044588337, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d2095c91472f83246d16a3b9", "heatmap": "e-d2095c91472f83246d16a3b9", "modelKey": "event", "timestamp": 1643044578328 }, { "id": "2d69383264993c13751076d0", "type": "motion", "start": 1643044781316, "end": 1643044803141, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2d69383264993c13751076d0", "heatmap": "e-2d69383264993c13751076d0", "modelKey": "event", "timestamp": 1643044792467 }, { "id": "743a8b495bceaa3438d4fffd", "type": "motion", "start": 1643044929719, "end": 1643044951057, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-743a8b495bceaa3438d4fffd", "heatmap": "e-743a8b495bceaa3438d4fffd", "modelKey": "event", "timestamp": 1643044941053 }, { "id": "54baab571604547c39f650e6", "type": "motion", "start": 1643045044370, "end": 1643045065709, "score": 97, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-54baab571604547c39f650e6", "heatmap": "e-54baab571604547c39f650e6", "modelKey": "event", "timestamp": 1643045055703 }, { "id": "2cca5fbae2b03099df7f26f8", "type": "motion", "start": 1643045268635, "end": 1643045293488, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2cca5fbae2b03099df7f26f8", "heatmap": "e-2cca5fbae2b03099df7f26f8", "modelKey": "event", "timestamp": 1643045283478 }, { "id": "99577e3e0fceecb89af6e401", "type": "motion", "start": 1643045337297, "end": 1643045358298, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-99577e3e0fceecb89af6e401", "heatmap": "e-99577e3e0fceecb89af6e401", "modelKey": "event", "timestamp": 1643045055703 }, { "id": "613250db7fa351d66eeb4106", "type": "motion", "start": 1643045373155, "end": 1643045394485, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-613250db7fa351d66eeb4106", "heatmap": "e-613250db7fa351d66eeb4106", "modelKey": "event", "timestamp": 1643045384303 }, { "id": "8fc03d02b66e891979910823", "type": "motion", "start": 1643045377283, "end": 1643045399783, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8fc03d02b66e891979910823", "heatmap": "e-8fc03d02b66e891979910823", "modelKey": "event", "timestamp": 1643045388449 }, { "id": "6650cff19be71c53f4e3f8b7", "type": "motion", "start": 1643045433502, "end": 1643045455181, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6650cff19be71c53f4e3f8b7", "heatmap": "e-6650cff19be71c53f4e3f8b7", "modelKey": "event", "timestamp": 1643045445173 }, { "id": "51f6ec394067bb1cf3aad739", "type": "motion", "start": 1643045581981, "end": 1643045603829, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-51f6ec394067bb1cf3aad739", "heatmap": "e-51f6ec394067bb1cf3aad739", "modelKey": "event", "timestamp": 1643045593819 }, { "id": "baf8f53a54c3004c9a120224", "type": "motion", "start": 1643045631897, "end": 1643045653897, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-baf8f53a54c3004c9a120224", "heatmap": "e-baf8f53a54c3004c9a120224", "modelKey": "event", "timestamp": 1643045643897 }, { "id": "085f6a7fbf57ba1c816485ba", "type": "motion", "start": 1643045633986, "end": 1643045655322, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-085f6a7fbf57ba1c816485ba", "heatmap": "e-085f6a7fbf57ba1c816485ba", "modelKey": "event", "timestamp": 1643045645152 }, { "id": "b1d520cb773fb9b165ec0d7e", "type": "motion", "start": 1643045706464, "end": 1643045729132, "score": 89, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b1d520cb773fb9b165ec0d7e", "heatmap": "e-b1d520cb773fb9b165ec0d7e", "modelKey": "event", "timestamp": 1643045718981 }, { "id": "1a903b2bc17ae48388b000f4", "type": "motion", "start": 1643045838063, "end": 1643045865576, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1a903b2bc17ae48388b000f4", "heatmap": "e-1a903b2bc17ae48388b000f4", "modelKey": "event", "timestamp": 1643045849565 }, { "id": "ff1b0ff69def8985f105b298", "type": "motion", "start": 1643045845573, "end": 1643045867752, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ff1b0ff69def8985f105b298", "heatmap": "e-ff1b0ff69def8985f105b298", "modelKey": "event", "timestamp": 1643045856741 }, { "id": "70dbeb8d86a2b287e5aca4c4", "type": "motion", "start": 1643045846115, "end": 1643045867943, "score": 16, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-70dbeb8d86a2b287e5aca4c4", "heatmap": "e-70dbeb8d86a2b287e5aca4c4", "modelKey": "event", "timestamp": 1643045718981 }, { "id": "fd24864609166e8a2734925f", "type": "motion", "start": 1643046055795, "end": 1643046077489, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fd24864609166e8a2734925f", "heatmap": "e-fd24864609166e8a2734925f", "modelKey": "event", "timestamp": 1643046067462 }, { "id": "56c57cc137bd9d46b3c274b2", "type": "motion", "start": 1643046349411, "end": 1643046370912, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-56c57cc137bd9d46b3c274b2", "heatmap": "e-56c57cc137bd9d46b3c274b2", "modelKey": "event", "timestamp": 1643046067462 }, { "id": "ad94271589103861abae6483", "type": "motion", "start": 1643046430549, "end": 1643046452049, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ad94271589103861abae6483", "heatmap": "e-ad94271589103861abae6483", "modelKey": "event", "timestamp": 1643046441715 }, { "id": "1680f2f5037262fdfd3815e3", "type": "motion", "start": 1643046501009, "end": 1643046522180, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1680f2f5037262fdfd3815e3", "heatmap": "e-1680f2f5037262fdfd3815e3", "modelKey": "event", "timestamp": 1643046512176 }, { "id": "eb46c5bff33b0da40194fd6b", "type": "motion", "start": 1643046635460, "end": 1643046656626, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-eb46c5bff33b0da40194fd6b", "heatmap": "e-eb46c5bff33b0da40194fd6b", "modelKey": "event", "timestamp": 1643046646626 }, { "id": "26499ac13aef98f0bf013e2e", "type": "motion", "start": 1643047088734, "end": 1643047111397, "score": 71, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-26499ac13aef98f0bf013e2e", "heatmap": "e-26499ac13aef98f0bf013e2e", "modelKey": "event", "timestamp": 1643047101233 }, { "id": "fd7461809a38fe0748cd549d", "type": "motion", "start": 1643047125717, "end": 1643047156894, "score": 61, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fd7461809a38fe0748cd549d", "heatmap": "e-fd7461809a38fe0748cd549d", "modelKey": "event", "timestamp": 1643047144721 }, { "id": "86688e418f645ad384617d1a", "type": "motion", "start": 1643047141449, "end": 1643047164453, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-86688e418f645ad384617d1a", "heatmap": "e-86688e418f645ad384617d1a", "modelKey": "event", "timestamp": 1643045856741 }, { "id": "4f7ca6875a14891db8ebdb5a", "type": "motion", "start": 1643047273197, "end": 1643047294536, "score": 83, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4f7ca6875a14891db8ebdb5a", "heatmap": "e-4f7ca6875a14891db8ebdb5a", "modelKey": "event", "timestamp": 1643047284524 }, { "id": "4e367a5dce9c82bbddf7fd3b", "type": "motion", "start": 1643047365566, "end": 1643047387899, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4e367a5dce9c82bbddf7fd3b", "heatmap": "e-4e367a5dce9c82bbddf7fd3b", "modelKey": "event", "timestamp": 1643047376733 }, { "id": "28ebab635aaa5723851e349e", "type": "motion", "start": 1643047369153, "end": 1643047390819, "score": 9, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-28ebab635aaa5723851e349e", "heatmap": "e-28ebab635aaa5723851e349e", "modelKey": "event", "timestamp": 1643047284524 }, { "id": "5b2ff0ec2a6fc2d5914714cb", "type": "motion", "start": 1643047541993, "end": 1643047564164, "score": 80, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5b2ff0ec2a6fc2d5914714cb", "heatmap": "e-5b2ff0ec2a6fc2d5914714cb", "modelKey": "event", "timestamp": 1643047553160 }, { "id": "4cc2fb95cbab184b4b7341f0", "type": "motion", "start": 1643047575139, "end": 1643047596997, "score": 87, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4cc2fb95cbab184b4b7341f0", "heatmap": "e-4cc2fb95cbab184b4b7341f0", "modelKey": "event", "timestamp": 1643047586806 }, { "id": "bafb3f1ad9f1f9b85dbae28d", "type": "motion", "start": 1643047578422, "end": 1643047601112, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bafb3f1ad9f1f9b85dbae28d", "heatmap": "e-bafb3f1ad9f1f9b85dbae28d", "modelKey": "event", "timestamp": 1643047591090 }, { "id": "748202b40359a6b5613e9f10", "type": "motion", "start": 1643047581064, "end": 1643047602905, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-748202b40359a6b5613e9f10", "heatmap": "e-748202b40359a6b5613e9f10", "modelKey": "event", "timestamp": 1643047592231 }, { "id": "0b15336632a01d38ece6e246", "type": "motion", "start": 1643047940008, "end": 1643047961672, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0b15336632a01d38ece6e246", "heatmap": "e-0b15336632a01d38ece6e246", "modelKey": "event", "timestamp": 1643047951177 }, { "id": "cc4c6e783944ce40cce154f5", "type": "sensorMotion", "start": 1643047951314, "end": 1643047951314, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ezcftgx Dzget" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cc4c6e783944ce40cce154f5", "heatmap": "e-cc4c6e783944ce40cce154f5", "modelKey": "event", "timestamp": null }, { "id": "8ffcd0fcbd895b9d45220f9e", "type": "motion", "start": 1643048001106, "end": 1643048023456, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8ffcd0fcbd895b9d45220f9e", "heatmap": "e-8ffcd0fcbd895b9d45220f9e", "modelKey": "event", "timestamp": 1643048012273 }, { "id": "a8f0121953212fa938c05fa9", "type": "motion", "start": 1643048003973, "end": 1643048025305, "score": 10, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a8f0121953212fa938c05fa9", "heatmap": "e-a8f0121953212fa938c05fa9", "modelKey": "event", "timestamp": 1643047586806 }, { "id": "026b578aca8fab9e10dac514", "type": "sensorMotion", "start": 1643048095261, "end": 1643048095261, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Kbf Gdap" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-026b578aca8fab9e10dac514", "heatmap": "e-026b578aca8fab9e10dac514", "modelKey": "event", "timestamp": null }, { "id": "ffdc941680621e430f0b4423", "type": "sensorMotion", "start": 1643048098866, "end": 1643048098866, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cxssct Qdrknq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ffdc941680621e430f0b4423", "heatmap": "e-ffdc941680621e430f0b4423", "modelKey": "event", "timestamp": null }, { "id": "10c8c022f4106338b0d9b139", "type": "motion", "start": 1643048288593, "end": 1643048309753, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-10c8c022f4106338b0d9b139", "heatmap": "e-10c8c022f4106338b0d9b139", "modelKey": "event", "timestamp": 1643047586806 }, { "id": "4f80c1bd8605f4a6a744da8e", "type": "motion", "start": 1643048291550, "end": 1643048313383, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4f80c1bd8605f4a6a744da8e", "heatmap": "e-4f80c1bd8605f4a6a744da8e", "modelKey": "event", "timestamp": 1643048303217 }, { "id": "41ca76cef16a79c411f9956b", "type": "motion", "start": 1643048293104, "end": 1643048314771, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-41ca76cef16a79c411f9956b", "heatmap": "e-41ca76cef16a79c411f9956b", "modelKey": "event", "timestamp": 1643048304438 }, { "id": "eda6d68885051a32bb3bdfb5", "type": "motion", "start": 1643048325406, "end": 1643048347071, "score": 11, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-eda6d68885051a32bb3bdfb5", "heatmap": "e-eda6d68885051a32bb3bdfb5", "modelKey": "event", "timestamp": 1643047586806 }, { "id": "743b98708c0bda74c3e6e745", "type": "motion", "start": 1643048338569, "end": 1643048363237, "score": 67, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-743b98708c0bda74c3e6e745", "heatmap": "e-743b98708c0bda74c3e6e745", "modelKey": "event", "timestamp": 1643048349735 }, { "id": "0076fbd58b598c65e588736d", "type": "motion", "start": 1643048339643, "end": 1643048384185, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "cfa08131ebe90be900a4e15a" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0076fbd58b598c65e588736d", "heatmap": "e-0076fbd58b598c65e588736d", "modelKey": "event", "timestamp": 1643048362986 }, { "id": "cfa08131ebe90be900a4e15a", "type": "smartDetectZone", "start": 1643048339913, "end": 1643048377745, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cfa08131ebe90be900a4e15a", "heatmap": "e-cfa08131ebe90be900a4e15a", "modelKey": "event", "timestamp": 1643048357566 }, { "id": "428a4ca8bc4a87b23572aed8", "type": "smartDetectZone", "start": 1643048341509, "end": 1643048380132, "score": 86, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-428a4ca8bc4a87b23572aed8", "heatmap": "e-428a4ca8bc4a87b23572aed8", "modelKey": "event", "timestamp": 1643048359360 }, { "id": "014e9beebbaa097448839968", "type": "motion", "start": 1643048344186, "end": 1643048382316, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "428a4ca8bc4a87b23572aed8" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-014e9beebbaa097448839968", "heatmap": "e-014e9beebbaa097448839968", "modelKey": "event", "timestamp": 1643048359360 }, { "id": "62858f0ec4bef764e921bbd7", "type": "motion", "start": 1643048360234, "end": 1643048383397, "score": 79, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-62858f0ec4bef764e921bbd7", "heatmap": "e-62858f0ec4bef764e921bbd7", "modelKey": "event", "timestamp": 1643048373233 }, { "id": "4fe527c017a047b196ac231a", "type": "motion", "start": 1643048370842, "end": 1643048433554, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4fe527c017a047b196ac231a", "heatmap": "e-4fe527c017a047b196ac231a", "modelKey": "event", "timestamp": 1643048384697 }, { "id": "520627843e21cc6f837ea43e", "type": "sensorMotion", "start": 1643048380968, "end": 1643048380968, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mus Vpv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-520627843e21cc6f837ea43e", "heatmap": "e-520627843e21cc6f837ea43e", "modelKey": "event", "timestamp": null }, { "id": "0a0635fa2ecfe75b318fcb1c", "type": "motion", "start": 1643048384068, "end": 1643048406741, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0a0635fa2ecfe75b318fcb1c", "heatmap": "e-0a0635fa2ecfe75b318fcb1c", "modelKey": "event", "timestamp": 1643048396736 }, { "id": "76c80e7528199b092cccff18", "type": "sensorMotion", "start": 1643048385989, "end": 1643048385989, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cppvuqo Jqjqwxp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-76c80e7528199b092cccff18", "heatmap": "e-76c80e7528199b092cccff18", "modelKey": "event", "timestamp": null }, { "id": "f1d28a3cd982b0417437699a", "type": "motion", "start": 1643048386031, "end": 1643048407367, "score": 66, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f1d28a3cd982b0417437699a", "heatmap": "e-f1d28a3cd982b0417437699a", "modelKey": "event", "timestamp": 1643048397364 }, { "id": "8a0d7e827f9352464ee0b941", "type": "sensorMotion", "start": 1643048392312, "end": 1643048392312, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hhuigey Obcjmn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8a0d7e827f9352464ee0b941", "heatmap": "e-8a0d7e827f9352464ee0b941", "modelKey": "event", "timestamp": null }, { "id": "1be0fd2931567d970bcefdc5", "type": "motion", "start": 1643048392751, "end": 1643048415594, "score": 80, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1be0fd2931567d970bcefdc5", "heatmap": "e-1be0fd2931567d970bcefdc5", "modelKey": "event", "timestamp": 1643048404418 }, { "id": "43aa2d1dc6ff81f1834bc73d", "type": "sensorOpened", "start": 1643048397319, "end": 1643048397319, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ifibm Lkidw" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-43aa2d1dc6ff81f1834bc73d", "heatmap": "e-43aa2d1dc6ff81f1834bc73d", "modelKey": "event", "timestamp": null }, { "id": "73e05af6bbd3de919dd625d6", "type": "sensorMotion", "start": 1643048398349, "end": 1643048398349, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Uvenapn Fawa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-73e05af6bbd3de919dd625d6", "heatmap": "e-73e05af6bbd3de919dd625d6", "modelKey": "event", "timestamp": null }, { "id": "092af620938fd03b6ba747ed", "type": "sensorMotion", "start": 1643048408393, "end": 1643048408393, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hscfefl Wkfqpj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-092af620938fd03b6ba747ed", "heatmap": "e-092af620938fd03b6ba747ed", "modelKey": "event", "timestamp": null }, { "id": "0bb4ecc16bbd72a9385182a1", "type": "sensorClosed", "start": 1643048411096, "end": 1643048411096, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zhboojs Azo" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-0bb4ecc16bbd72a9385182a1", "heatmap": "e-0bb4ecc16bbd72a9385182a1", "modelKey": "event", "timestamp": null }, { "id": "7ac514da03f1939ede1b4fc2", "type": "sensorMotion", "start": 1643048412383, "end": 1643048412383, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Zppjnix Oszo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7ac514da03f1939ede1b4fc2", "heatmap": "e-7ac514da03f1939ede1b4fc2", "modelKey": "event", "timestamp": null }, { "id": "8355c6972c4ac1cc4d0f80bf", "type": "sensorMotion", "start": 1643048414959, "end": 1643048414959, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qgs Ulh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8355c6972c4ac1cc4d0f80bf", "heatmap": "e-8355c6972c4ac1cc4d0f80bf", "modelKey": "event", "timestamp": null }, { "id": "87c3164ac978e2f0d848ac4c", "type": "sensorMotion", "start": 1643048418950, "end": 1643048418950, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hbto Xphpn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-87c3164ac978e2f0d848ac4c", "heatmap": "e-87c3164ac978e2f0d848ac4c", "modelKey": "event", "timestamp": null }, { "id": "c0eddebfffa8dae821ef7656", "type": "sensorMotion", "start": 1643048423843, "end": 1643048423843, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Srzc Bdp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c0eddebfffa8dae821ef7656", "heatmap": "e-c0eddebfffa8dae821ef7656", "modelKey": "event", "timestamp": null }, { "id": "aababda1b1001fcb24123937", "type": "motion", "start": 1643048424644, "end": 1643048446315, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-aababda1b1001fcb24123937", "heatmap": "e-aababda1b1001fcb24123937", "modelKey": "event", "timestamp": 1643048435818 }, { "id": "0b3751504a2d25a0c0c969af", "type": "sensorMotion", "start": 1643048435450, "end": 1643048435450, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Gvugwrq Wikbp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0b3751504a2d25a0c0c969af", "heatmap": "e-0b3751504a2d25a0c0c969af", "modelKey": "event", "timestamp": null }, { "id": "647dca54f3be0efdc57c20b0", "type": "access", "start": 1643048554291, "end": 1643048808612, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "bc3dd633553907952a6fe20d", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-647dca54f3be0efdc57c20b0", "heatmap": "e-647dca54f3be0efdc57c20b0", "modelKey": "event", "timestamp": null }, { "id": "6a01a2513b9941149fc476ab", "type": "motion", "start": 1643048577222, "end": 1643048601721, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a01a2513b9941149fc476ab", "heatmap": "e-6a01a2513b9941149fc476ab", "modelKey": "event", "timestamp": 1643048591556 }, { "id": "f534ebbe5c23743b2b078f2f", "type": "motion", "start": 1643048741866, "end": 1643048763705, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f534ebbe5c23743b2b078f2f", "heatmap": "e-f534ebbe5c23743b2b078f2f", "modelKey": "event", "timestamp": 1643048753534 }, { "id": "a2da1425168ce09b451bd2ac", "type": "sensorMotion", "start": 1643048754485, "end": 1643048754485, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ssaems Idtoq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a2da1425168ce09b451bd2ac", "heatmap": "e-a2da1425168ce09b451bd2ac", "modelKey": "event", "timestamp": null }, { "id": "2ed6a93b60d62805fab92fd9", "type": "sensorMotion", "start": 1643048758352, "end": 1643048758352, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Piy Bfpx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2ed6a93b60d62805fab92fd9", "heatmap": "e-2ed6a93b60d62805fab92fd9", "modelKey": "event", "timestamp": null }, { "id": "c17351e903f4d2801b68950d", "type": "motion", "start": 1643048837006, "end": 1643048916525, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c17351e903f4d2801b68950d", "heatmap": "e-c17351e903f4d2801b68950d", "modelKey": "event", "timestamp": 1643048854175 }, { "id": "f2e0bc91908514354d4c25cb", "type": "access", "start": 1643048849515, "end": 1643049055412, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "android" }, "thumbnail": "e-f2e0bc91908514354d4c25cb", "heatmap": "e-f2e0bc91908514354d4c25cb", "modelKey": "event", "timestamp": null }, { "id": "e5ae42d7225f658667eba4dc", "type": "sensorMotion", "start": 1643048851051, "end": 1643048851051, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Sladu Ouvtdl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e5ae42d7225f658667eba4dc", "heatmap": "e-e5ae42d7225f658667eba4dc", "modelKey": "event", "timestamp": null }, { "id": "10e41d54819f61be82fe67e1", "type": "sensorMotion", "start": 1643048855560, "end": 1643048855560, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ujdnjwk Jwjopbu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-10e41d54819f61be82fe67e1", "heatmap": "e-10e41d54819f61be82fe67e1", "modelKey": "event", "timestamp": null }, { "id": "eecf0e2dc2f5a6024ca4f11c", "type": "sensorMotion", "start": 1643048860579, "end": 1643048860579, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Szcq Paohybo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eecf0e2dc2f5a6024ca4f11c", "heatmap": "e-eecf0e2dc2f5a6024ca4f11c", "modelKey": "event", "timestamp": null }, { "id": "1eafa4f0edf1daff35cae332", "type": "deviceAdopted", "start": 1643048867031, "end": 1643048867031, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "deviceId": { "text": "61eeeeef03de9d03e700c532" }, "mac": { "text": "74ACB94616F5" }, "host": { "text": null }, "type": { "text": "UFP-LOCK-R" }, "name": { "text": "UP DoorLock" }, "firmwareVersion": { "text": "unknown" }, "anonymousDeviceId": { "text": "unknown" }, "nvrId": { "text": "573efde7-efb9-41c2-b715-bd8f53892cbe" }, "nvrName": { "text": "Mortis NVR" }, "adoptedBy": { "text": "Christopher Bailey" }, "clickUrl": { "url": "protect/devices" } }, "thumbnail": "e-1eafa4f0edf1daff35cae332", "heatmap": "e-1eafa4f0edf1daff35cae332", "modelKey": "event", "timestamp": null }, { "id": "625003efe5a7569715643941", "type": "access", "start": 1643048886218, "end": 1643048981573, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-625003efe5a7569715643941", "heatmap": "e-625003efe5a7569715643941", "modelKey": "event", "timestamp": null }, { "id": "25d7a06faa04feaf55e1a2e6", "type": "sensorMotion", "start": 1643048897451, "end": 1643048897451, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jzkyxis Wzegsq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-25d7a06faa04feaf55e1a2e6", "heatmap": "e-25d7a06faa04feaf55e1a2e6", "modelKey": "event", "timestamp": null }, { "id": "b30fc9147a900415a381c360", "type": "motion", "start": 1643048916309, "end": 1643048943908, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b30fc9147a900415a381c360", "heatmap": "e-b30fc9147a900415a381c360", "modelKey": "event", "timestamp": 1643048928070 }, { "id": "0bdfba4fbe102173d8c5f9b7", "type": "sensorMotion", "start": 1643048917232, "end": 1643048917232, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Icnbohg Chuhx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0bdfba4fbe102173d8c5f9b7", "heatmap": "e-0bdfba4fbe102173d8c5f9b7", "modelKey": "event", "timestamp": null }, { "id": "f22258ef072ca8fa8f79cc11", "type": "motion", "start": 1643048919750, "end": 1643048957388, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "2026ced9ce8af63e34d927cf" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f22258ef072ca8fa8f79cc11", "heatmap": "e-f22258ef072ca8fa8f79cc11", "modelKey": "event", "timestamp": 1643048935358 }, { "id": "1df205b3715ca42479776a7a", "type": "smartDetectZone", "start": 1643048921740, "end": 1643048954339, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1df205b3715ca42479776a7a", "heatmap": "e-1df205b3715ca42479776a7a", "modelKey": "event", "timestamp": 1643048934978 }, { "id": "2026ced9ce8af63e34d927cf", "type": "smartDetectZone", "start": 1643048921932, "end": 1643048956871, "score": 82, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2026ced9ce8af63e34d927cf", "heatmap": "e-2026ced9ce8af63e34d927cf", "modelKey": "event", "timestamp": 1643048935358 }, { "id": "c99f938f1d3a5cfb8aefc047", "type": "motion", "start": 1643048922255, "end": 1643048957128, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "1df205b3715ca42479776a7a" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c99f938f1d3a5cfb8aefc047", "heatmap": "e-c99f938f1d3a5cfb8aefc047", "modelKey": "event", "timestamp": 1643048933921 }, { "id": "19b46ab54fff82dfd60038e4", "type": "sensorMotion", "start": 1643048925345, "end": 1643048925345, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Gexvd Kppcj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-19b46ab54fff82dfd60038e4", "heatmap": "e-19b46ab54fff82dfd60038e4", "modelKey": "event", "timestamp": null }, { "id": "2714130bddc1b99dab98f965", "type": "sensorOpened", "start": 1643048928433, "end": 1643048928433, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Omnod Obn" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-2714130bddc1b99dab98f965", "heatmap": "e-2714130bddc1b99dab98f965", "modelKey": "event", "timestamp": null }, { "id": "821b017bda764d645891954c", "type": "sensorMotion", "start": 1643048929592, "end": 1643048929592, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Iueux Ohz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-821b017bda764d645891954c", "heatmap": "e-821b017bda764d645891954c", "modelKey": "event", "timestamp": null }, { "id": "c10ee5cb00906814a13bd4dc", "type": "sensorClosed", "start": 1643048931689, "end": 1643048931689, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Pew Sxq" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-c10ee5cb00906814a13bd4dc", "heatmap": "e-c10ee5cb00906814a13bd4dc", "modelKey": "event", "timestamp": null }, { "id": "9e8eaae86ecd3ff79499ee07", "type": "motion", "start": 1643048933304, "end": 1643048961148, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9e8eaae86ecd3ff79499ee07", "heatmap": "e-9e8eaae86ecd3ff79499ee07", "modelKey": "event", "timestamp": 1643048946970 }, { "id": "1565855930169cc19511ebe8", "type": "motion", "start": 1643048950629, "end": 1643048974962, "score": 69, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1565855930169cc19511ebe8", "heatmap": "e-1565855930169cc19511ebe8", "modelKey": "event", "timestamp": 1643048961796 }, { "id": "cb63480f78dd68856b914550", "type": "access", "start": 1643048984443, "end": 1643055110923, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-cb63480f78dd68856b914550", "heatmap": "e-cb63480f78dd68856b914550", "modelKey": "event", "timestamp": null }, { "id": "7be00752498a8bee26b5e21f", "type": "motion", "start": 1643049033627, "end": 1643049054771, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7be00752498a8bee26b5e21f", "heatmap": "e-7be00752498a8bee26b5e21f", "modelKey": "event", "timestamp": 1643049044771 }, { "id": "9e1cf7a2dc52be25363f9076", "type": "motion", "start": 1643049074998, "end": 1643049095999, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9e1cf7a2dc52be25363f9076", "heatmap": "e-9e1cf7a2dc52be25363f9076", "modelKey": "event", "timestamp": 1643048933921 }, { "id": "7eb0ba38d860a37b874fb3d6", "type": "access", "start": 1643049228238, "end": 1643049464896, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "bc3dd633553907952a6fe20d", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-7eb0ba38d860a37b874fb3d6", "heatmap": "e-7eb0ba38d860a37b874fb3d6", "modelKey": "event", "timestamp": null }, { "id": "1b25c133a67eb8a5d6c0c7d2", "type": "motion", "start": 1643049261918, "end": 1643049284086, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1b25c133a67eb8a5d6c0c7d2", "heatmap": "e-1b25c133a67eb8a5d6c0c7d2", "modelKey": "event", "timestamp": 1643048933921 }, { "id": "d860737f7241e63b34dfe1e4", "type": "motion", "start": 1643049265736, "end": 1643049287565, "score": 7, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d860737f7241e63b34dfe1e4", "heatmap": "e-d860737f7241e63b34dfe1e4", "modelKey": "event", "timestamp": 1643049044771 }, { "id": "78ea48f6a4aacc6c9d1fd162", "type": "smartDetectZone", "start": 1643049359846, "end": 1643049560224, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-78ea48f6a4aacc6c9d1fd162", "heatmap": "e-78ea48f6a4aacc6c9d1fd162", "modelKey": "event", "timestamp": 1643049371146 }, { "id": "55fb6d97956a04e4061c1905", "type": "motion", "start": 1643049364621, "end": 1643049415174, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "78ea48f6a4aacc6c9d1fd162" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-55fb6d97956a04e4061c1905", "heatmap": "e-55fb6d97956a04e4061c1905", "modelKey": "event", "timestamp": 1643049380002 }, { "id": "9f59279c69dd9588476ba07d", "type": "sensorMotion", "start": 1643049371803, "end": 1643049371803, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fboxzup Tfbn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9f59279c69dd9588476ba07d", "heatmap": "e-9f59279c69dd9588476ba07d", "modelKey": "event", "timestamp": null }, { "id": "4249db1ab2ddd683a0fcae43", "type": "sensorMotion", "start": 1643049374749, "end": 1643049374749, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mlzftun Kzrr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4249db1ab2ddd683a0fcae43", "heatmap": "e-4249db1ab2ddd683a0fcae43", "modelKey": "event", "timestamp": null }, { "id": "95f386de7c18dfccb34a6f4c", "type": "sensorMotion", "start": 1643049378869, "end": 1643049378869, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Inouzly Visryg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-95f386de7c18dfccb34a6f4c", "heatmap": "e-95f386de7c18dfccb34a6f4c", "modelKey": "event", "timestamp": null }, { "id": "1c114f40179581ddc533a055", "type": "sensorMotion", "start": 1643049384024, "end": 1643049384024, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uut Icosq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1c114f40179581ddc533a055", "heatmap": "e-1c114f40179581ddc533a055", "modelKey": "event", "timestamp": null }, { "id": "6835c80114734fb24fe91e89", "type": "sensorMotion", "start": 1643049388011, "end": 1643049388011, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nysxro Ghsgcb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6835c80114734fb24fe91e89", "heatmap": "e-6835c80114734fb24fe91e89", "modelKey": "event", "timestamp": null }, { "id": "8c479993adb022079f02e93b", "type": "sensorMotion", "start": 1643049392146, "end": 1643049392146, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zhbecty Wrcv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8c479993adb022079f02e93b", "heatmap": "e-8c479993adb022079f02e93b", "modelKey": "event", "timestamp": null }, { "id": "1b9d6dc68606edf48eae675d", "type": "sensorMotion", "start": 1643049396123, "end": 1643049396123, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Joqpzo Uvki" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1b9d6dc68606edf48eae675d", "heatmap": "e-1b9d6dc68606edf48eae675d", "modelKey": "event", "timestamp": null }, { "id": "f3a9c47131f8872db0b8e9d4", "type": "sensorMotion", "start": 1643049401674, "end": 1643049401674, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yzeonk Mujyz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f3a9c47131f8872db0b8e9d4", "heatmap": "e-f3a9c47131f8872db0b8e9d4", "modelKey": "event", "timestamp": null }, { "id": "f54315a164f806e5e26e541f", "type": "sensorMotion", "start": 1643049405794, "end": 1643049405794, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xnsrx Gnp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f54315a164f806e5e26e541f", "heatmap": "e-f54315a164f806e5e26e541f", "modelKey": "event", "timestamp": null }, { "id": "1ff8b8277812ed0576d4b9b3", "type": "sensorMotion", "start": 1643049409899, "end": 1643049409899, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Avlypbh Nqsfbx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1ff8b8277812ed0576d4b9b3", "heatmap": "e-1ff8b8277812ed0576d4b9b3", "modelKey": "event", "timestamp": null }, { "id": "c1ab25732e98e110b9a91e6c", "type": "motion", "start": 1643049413997, "end": 1643049439997, "score": 48, "smartDetectTypes": [], "smartDetectEvents": [ "78ea48f6a4aacc6c9d1fd162" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c1ab25732e98e110b9a91e6c", "heatmap": "e-c1ab25732e98e110b9a91e6c", "modelKey": "event", "timestamp": 1643049425830 }, { "id": "7e4131d660d70acfe66ef623", "type": "sensorMotion", "start": 1643049416347, "end": 1643049416347, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hppe Shjdi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7e4131d660d70acfe66ef623", "heatmap": "e-7e4131d660d70acfe66ef623", "modelKey": "event", "timestamp": null }, { "id": "3186573fd28371837c56abf7", "type": "sensorMotion", "start": 1643049420199, "end": 1643049420199, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vvh Zbyhuc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3186573fd28371837c56abf7", "heatmap": "e-3186573fd28371837c56abf7", "modelKey": "event", "timestamp": null }, { "id": "7bca591460599f2b03c9ee21", "type": "sensorMotion", "start": 1643049424206, "end": 1643049424206, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xjp Lahc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7bca591460599f2b03c9ee21", "heatmap": "e-7bca591460599f2b03c9ee21", "modelKey": "event", "timestamp": null }, { "id": "aab18472a217ebae7233fb69", "type": "sensorMotion", "start": 1643049428197, "end": 1643049428197, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dlwmovy Efpd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-aab18472a217ebae7233fb69", "heatmap": "e-aab18472a217ebae7233fb69", "modelKey": "event", "timestamp": null }, { "id": "7c7eee71c99dddf04c133aa8", "type": "sensorMotion", "start": 1643049431288, "end": 1643049431288, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nvp Pxcvdv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7c7eee71c99dddf04c133aa8", "heatmap": "e-7c7eee71c99dddf04c133aa8", "modelKey": "event", "timestamp": null }, { "id": "f7548d8fd5c0bddb569f46a4", "type": "sensorMotion", "start": 1643049435311, "end": 1643049435311, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pxrut Hej" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f7548d8fd5c0bddb569f46a4", "heatmap": "e-f7548d8fd5c0bddb569f46a4", "modelKey": "event", "timestamp": null }, { "id": "e4d68c46d88073437abb8330", "type": "sensorMotion", "start": 1643049439400, "end": 1643049439400, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Olmnl Rzmzhcg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e4d68c46d88073437abb8330", "heatmap": "e-e4d68c46d88073437abb8330", "modelKey": "event", "timestamp": null }, { "id": "cfe45b986b91f1fd70f78207", "type": "motion", "start": 1643049443008, "end": 1643049464672, "score": 12, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cfe45b986b91f1fd70f78207", "heatmap": "e-cfe45b986b91f1fd70f78207", "modelKey": "event", "timestamp": 1643049044771 }, { "id": "c235ab4811d4d9214a97697d", "type": "sensorMotion", "start": 1643049444092, "end": 1643049444092, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ndlhy Qcsfkf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c235ab4811d4d9214a97697d", "heatmap": "e-c235ab4811d4d9214a97697d", "modelKey": "event", "timestamp": null }, { "id": "c2908f3f1463a4373c2b9e96", "type": "sensorMotion", "start": 1643049448169, "end": 1643049448169, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hzm Aqt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c2908f3f1463a4373c2b9e96", "heatmap": "e-c2908f3f1463a4373c2b9e96", "modelKey": "event", "timestamp": null }, { "id": "c6edf33f59b1d011c03c0799", "type": "sensorMotion", "start": 1643049452002, "end": 1643049452002, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kijyqi Pcmxqd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c6edf33f59b1d011c03c0799", "heatmap": "e-c6edf33f59b1d011c03c0799", "modelKey": "event", "timestamp": null }, { "id": "2cc623c94f7ed1e414c1a4ce", "type": "motion", "start": 1643049455556, "end": 1643049477066, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2cc623c94f7ed1e414c1a4ce", "heatmap": "e-2cc623c94f7ed1e414c1a4ce", "modelKey": "event", "timestamp": 1643049467065 }, { "id": "26fab3d7bd5577e69564b878", "type": "sensorMotion", "start": 1643049464764, "end": 1643049464764, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pcjqwv Mgv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-26fab3d7bd5577e69564b878", "heatmap": "e-26fab3d7bd5577e69564b878", "modelKey": "event", "timestamp": null }, { "id": "22dc1d06c1fb9cabacf44e02", "type": "sensorMotion", "start": 1643049467711, "end": 1643049467711, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zpv Qud" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-22dc1d06c1fb9cabacf44e02", "heatmap": "e-22dc1d06c1fb9cabacf44e02", "modelKey": "event", "timestamp": null }, { "id": "9f50b74c7d99f71053cb4b99", "type": "sensorMotion", "start": 1643049472216, "end": 1643049472216, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qrjuo Pluo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9f50b74c7d99f71053cb4b99", "heatmap": "e-9f50b74c7d99f71053cb4b99", "modelKey": "event", "timestamp": null }, { "id": "cc0f7dc383f7eb70d2df70bc", "type": "sensorMotion", "start": 1643049479555, "end": 1643049479555, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qysajb Cvqntqa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cc0f7dc383f7eb70d2df70bc", "heatmap": "e-cc0f7dc383f7eb70d2df70bc", "modelKey": "event", "timestamp": null }, { "id": "ffe3b8a3f89c41225bbed6e7", "type": "sensorMotion", "start": 1643049485366, "end": 1643049485366, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dax Gnwxjcx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ffe3b8a3f89c41225bbed6e7", "heatmap": "e-ffe3b8a3f89c41225bbed6e7", "modelKey": "event", "timestamp": null }, { "id": "92375fd4935d4edecbde05f8", "type": "sensorMotion", "start": 1643049495907, "end": 1643049495907, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kwb Cknuh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-92375fd4935d4edecbde05f8", "heatmap": "e-92375fd4935d4edecbde05f8", "modelKey": "event", "timestamp": null }, { "id": "95124b836dcea004f3e91ee0", "type": "sensorMotion", "start": 1643049504037, "end": 1643049504037, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hpioq Mvqwh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-95124b836dcea004f3e91ee0", "heatmap": "e-95124b836dcea004f3e91ee0", "modelKey": "event", "timestamp": null }, { "id": "a05fca21f0c03ea381eb78ba", "type": "sensorMotion", "start": 1643049517409, "end": 1643049517409, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sle Ilpvd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a05fca21f0c03ea381eb78ba", "heatmap": "e-a05fca21f0c03ea381eb78ba", "modelKey": "event", "timestamp": null }, { "id": "e9568db2904b45986a214c6e", "type": "sensorMotion", "start": 1643049522302, "end": 1643049522302, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Lqn Ajhotd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e9568db2904b45986a214c6e", "heatmap": "e-e9568db2904b45986a214c6e", "modelKey": "event", "timestamp": null }, { "id": "6c6436b885db6eaf627222ee", "type": "sensorMotion", "start": 1643049531958, "end": 1643049531958, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Aktfamn Okvs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6c6436b885db6eaf627222ee", "heatmap": "e-6c6436b885db6eaf627222ee", "modelKey": "event", "timestamp": null }, { "id": "7e9256d4dcb7ee73196bc01e", "type": "sensorMotion", "start": 1643049534935, "end": 1643049534935, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ozax Bbr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7e9256d4dcb7ee73196bc01e", "heatmap": "e-7e9256d4dcb7ee73196bc01e", "modelKey": "event", "timestamp": null }, { "id": "a2c6ca86f180898dbfd081b2", "type": "sensorMotion", "start": 1643049540070, "end": 1643049540070, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bvofskh Ymic" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a2c6ca86f180898dbfd081b2", "heatmap": "e-a2c6ca86f180898dbfd081b2", "modelKey": "event", "timestamp": null }, { "id": "69c897fd9662a786f08b54ad", "type": "sensorMotion", "start": 1643049557881, "end": 1643049557881, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ajs Lhfn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-69c897fd9662a786f08b54ad", "heatmap": "e-69c897fd9662a786f08b54ad", "modelKey": "event", "timestamp": null }, { "id": "50f78c58330963829f50f961", "type": "smartDetectZone", "start": 1643049557936, "end": 1643049615082, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-50f78c58330963829f50f961", "heatmap": "e-50f78c58330963829f50f961", "modelKey": "event", "timestamp": 1643049594374 }, { "id": "ddb6a2982b7f988ab24ce449", "type": "motion", "start": 1643049560474, "end": 1643049589307, "score": 40, "smartDetectTypes": [], "smartDetectEvents": [ "50f78c58330963829f50f961" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ddb6a2982b7f988ab24ce449", "heatmap": "e-ddb6a2982b7f988ab24ce449", "modelKey": "event", "timestamp": 1643049425830 }, { "id": "e5e84ff450d183479d70a81a", "type": "sensorMotion", "start": 1643049560815, "end": 1643049560815, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gyxopx Ibzapc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e5e84ff450d183479d70a81a", "heatmap": "e-e5e84ff450d183479d70a81a", "modelKey": "event", "timestamp": null }, { "id": "20cba43b4162a01fbcea8dbe", "type": "sensorMotion", "start": 1643049564809, "end": 1643049564809, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zhy Bzlgyqf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-20cba43b4162a01fbcea8dbe", "heatmap": "e-20cba43b4162a01fbcea8dbe", "modelKey": "event", "timestamp": null }, { "id": "fcd38f91506364c60c7a912e", "type": "sensorMotion", "start": 1643049569941, "end": 1643049569941, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sezeupw Tagmnh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fcd38f91506364c60c7a912e", "heatmap": "e-fcd38f91506364c60c7a912e", "modelKey": "event", "timestamp": null }, { "id": "360121dc28a21f30b8ed6aa8", "type": "sensorMotion", "start": 1643049572903, "end": 1643049572903, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mxbysf Wyqaa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-360121dc28a21f30b8ed6aa8", "heatmap": "e-360121dc28a21f30b8ed6aa8", "modelKey": "event", "timestamp": null }, { "id": "512f567e181470443f7f7c2b", "type": "motion", "start": 1643049581135, "end": 1643049614967, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "50f78c58330963829f50f961" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-512f567e181470443f7f7c2b", "heatmap": "e-512f567e181470443f7f7c2b", "modelKey": "event", "timestamp": 1643049597303 }, { "id": "f41e9a3d0c7e96f854e63fa3", "type": "sensorMotion", "start": 1643049586808, "end": 1643049586808, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bexvnzm Iwozf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f41e9a3d0c7e96f854e63fa3", "heatmap": "e-f41e9a3d0c7e96f854e63fa3", "modelKey": "event", "timestamp": null }, { "id": "962c67fc84d31396195d8e9c", "type": "sensorMotion", "start": 1643049590928, "end": 1643049590928, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ncswptc Bce" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-962c67fc84d31396195d8e9c", "heatmap": "e-962c67fc84d31396195d8e9c", "modelKey": "event", "timestamp": null }, { "id": "63a89b4ac70152afd3867ec3", "type": "sensorMotion", "start": 1643049594934, "end": 1643049594934, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iprpcz Tvnx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-63a89b4ac70152afd3867ec3", "heatmap": "e-63a89b4ac70152afd3867ec3", "modelKey": "event", "timestamp": null }, { "id": "eb816b811366b46d877a4429", "type": "sensorMotion", "start": 1643049595828, "end": 1643049595828, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Srxpu Zefd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eb816b811366b46d877a4429", "heatmap": "e-eb816b811366b46d877a4429", "modelKey": "event", "timestamp": null }, { "id": "4d6cfccdc5fd14891ac528da", "type": "sensorMotion", "start": 1643049598404, "end": 1643049598404, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xamd Ijalr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4d6cfccdc5fd14891ac528da", "heatmap": "e-4d6cfccdc5fd14891ac528da", "modelKey": "event", "timestamp": null }, { "id": "b0d957e984dee31b170b0eba", "type": "sensorMotion", "start": 1643049598926, "end": 1643049598926, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Poxfvgm Gavqcj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b0d957e984dee31b170b0eba", "heatmap": "e-b0d957e984dee31b170b0eba", "modelKey": "event", "timestamp": null }, { "id": "ddc22e01a46d71cec64d5411", "type": "sensorMotion", "start": 1643049604447, "end": 1643049604447, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dgyoyt Sat" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ddc22e01a46d71cec64d5411", "heatmap": "e-ddc22e01a46d71cec64d5411", "modelKey": "event", "timestamp": null }, { "id": "483d47f8812cc5ffb92c44ac", "type": "motion", "start": 1643049608427, "end": 1643049637626, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-483d47f8812cc5ffb92c44ac", "heatmap": "e-483d47f8812cc5ffb92c44ac", "modelKey": "event", "timestamp": 1643049621125 }, { "id": "a5a065748670b92e8562cf5c", "type": "sensorMotion", "start": 1643049616429, "end": 1643049616429, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Lhd Ycmwd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a5a065748670b92e8562cf5c", "heatmap": "e-a5a065748670b92e8562cf5c", "modelKey": "event", "timestamp": null }, { "id": "10942d825c9a7bf61424366e", "type": "sensorMotion", "start": 1643049620306, "end": 1643049620306, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qka Sfalsd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-10942d825c9a7bf61424366e", "heatmap": "e-10942d825c9a7bf61424366e", "modelKey": "event", "timestamp": null }, { "id": "e2ac44be35667c163e9227a3", "type": "access", "start": 1643049916630, "end": 1643050366598, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "android" }, "thumbnail": "e-e2ac44be35667c163e9227a3", "heatmap": "e-e2ac44be35667c163e9227a3", "modelKey": "event", "timestamp": null }, { "id": "73ae4f31d7df35867e73903b", "type": "smartDetectZone", "start": 1643049935729, "end": 1643050052325, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-73ae4f31d7df35867e73903b", "heatmap": "e-73ae4f31d7df35867e73903b", "modelKey": "event", "timestamp": 1643050032301 }, { "id": "687a7bf9a4f3539a202c7a7d", "type": "motion", "start": 1643049937902, "end": 1643049982911, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "73ae4f31d7df35867e73903b" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-687a7bf9a4f3539a202c7a7d", "heatmap": "e-687a7bf9a4f3539a202c7a7d", "modelKey": "event", "timestamp": 1643049953903 }, { "id": "f9558325b960881a8439048d", "type": "sensorMotion", "start": 1643049948867, "end": 1643049948867, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xrcooyt Tfgegbh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f9558325b960881a8439048d", "heatmap": "e-f9558325b960881a8439048d", "modelKey": "event", "timestamp": null }, { "id": "105e5188929e1eb4e41a6645", "type": "sensorMotion", "start": 1643049952858, "end": 1643049952858, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Crb Ogwym" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-105e5188929e1eb4e41a6645", "heatmap": "e-105e5188929e1eb4e41a6645", "modelKey": "event", "timestamp": null }, { "id": "eec4ccd210a96c4d50add14f", "type": "sensorMotion", "start": 1643049956850, "end": 1643049956850, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oye Kxna" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eec4ccd210a96c4d50add14f", "heatmap": "e-eec4ccd210a96c4d50add14f", "modelKey": "event", "timestamp": null }, { "id": "98a32c95ada534dc1955243f", "type": "motion", "start": 1643049959181, "end": 1643049985190, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-98a32c95ada534dc1955243f", "heatmap": "e-98a32c95ada534dc1955243f", "modelKey": "event", "timestamp": 1643049973514 }, { "id": "ff60f5f609e340f0db262217", "type": "sensorMotion", "start": 1643049960068, "end": 1643049960068, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iwftef Sploj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ff60f5f609e340f0db262217", "heatmap": "e-ff60f5f609e340f0db262217", "modelKey": "event", "timestamp": null }, { "id": "22ed37a605e9920cf1d502eb", "type": "sensorMotion", "start": 1643049964976, "end": 1643049964976, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vtx Wazv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-22ed37a605e9920cf1d502eb", "heatmap": "e-22ed37a605e9920cf1d502eb", "modelKey": "event", "timestamp": null }, { "id": "0445bfa9c0213342428d4724", "type": "sensorMotion", "start": 1643049969081, "end": 1643049969081, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Msvb Zeng" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0445bfa9c0213342428d4724", "heatmap": "e-0445bfa9c0213342428d4724", "modelKey": "event", "timestamp": null }, { "id": "e861a69e3258b5989fd1e5b1", "type": "sensorMotion", "start": 1643049971663, "end": 1643049971663, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Likghe Tzfaucg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e861a69e3258b5989fd1e5b1", "heatmap": "e-e861a69e3258b5989fd1e5b1", "modelKey": "event", "timestamp": null }, { "id": "ae77dd2f7d59d3f1e27d34e0", "type": "sensorMotion", "start": 1643049974231, "end": 1643049974231, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jjzdu Dfxdrf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ae77dd2f7d59d3f1e27d34e0", "heatmap": "e-ae77dd2f7d59d3f1e27d34e0", "modelKey": "event", "timestamp": null }, { "id": "358119aed7b73498a955f41b", "type": "sensorMotion", "start": 1643049976571, "end": 1643049976571, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Wdh Vprgc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-358119aed7b73498a955f41b", "heatmap": "e-358119aed7b73498a955f41b", "modelKey": "event", "timestamp": null }, { "id": "d146491b507d425024da0782", "type": "sensorMotion", "start": 1643049977079, "end": 1643049977079, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qimexk Jbgmfji" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d146491b507d425024da0782", "heatmap": "e-d146491b507d425024da0782", "modelKey": "event", "timestamp": null }, { "id": "d0b789cebf77818067e21764", "type": "sensorMotion", "start": 1643049983244, "end": 1643049983244, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kobx Jgdhmk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d0b789cebf77818067e21764", "heatmap": "e-d0b789cebf77818067e21764", "modelKey": "event", "timestamp": null }, { "id": "0afe89beb7727ac53c6705f8", "type": "sensorMotion", "start": 1643049989682, "end": 1643049989682, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ukyqmir Ofiqq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0afe89beb7727ac53c6705f8", "heatmap": "e-0afe89beb7727ac53c6705f8", "modelKey": "event", "timestamp": null }, { "id": "97d8d8063de25dc45aa2f37b", "type": "sensorMotion", "start": 1643049993551, "end": 1643049993551, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xjwn Cohmdcl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-97d8d8063de25dc45aa2f37b", "heatmap": "e-97d8d8063de25dc45aa2f37b", "modelKey": "event", "timestamp": null }, { "id": "de6aad9c35ebbd909b2b64ee", "type": "motion", "start": 1643049994930, "end": 1643050019430, "score": 34, "smartDetectTypes": [], "smartDetectEvents": [ "73ae4f31d7df35867e73903b" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-de6aad9c35ebbd909b2b64ee", "heatmap": "e-de6aad9c35ebbd909b2b64ee", "modelKey": "event", "timestamp": 1643049953903 }, { "id": "6aef7e6b3ebefcc091186817", "type": "sensorMotion", "start": 1643049997793, "end": 1643049997793, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iuetq Ktplq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6aef7e6b3ebefcc091186817", "heatmap": "e-6aef7e6b3ebefcc091186817", "modelKey": "event", "timestamp": null }, { "id": "5eba5bca1023fe1835112ecd", "type": "motion", "start": 1643049998829, "end": 1643050020497, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5eba5bca1023fe1835112ecd", "heatmap": "e-5eba5bca1023fe1835112ecd", "modelKey": "event", "timestamp": 1643050009993 }, { "id": "c299aff7f4ca4a8533583ca3", "type": "sensorMotion", "start": 1643050002686, "end": 1643050002686, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oujppj Yehq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c299aff7f4ca4a8533583ca3", "heatmap": "e-c299aff7f4ca4a8533583ca3", "modelKey": "event", "timestamp": null }, { "id": "9930c48e44632c39c56952a3", "type": "sensorMotion", "start": 1643050006806, "end": 1643050006806, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jujvxny Uqn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9930c48e44632c39c56952a3", "heatmap": "e-9930c48e44632c39c56952a3", "modelKey": "event", "timestamp": null }, { "id": "5d33b8f061b23569ee73b258", "type": "sensorMotion", "start": 1643050009896, "end": 1643050009896, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iciqtfs Dhnl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5d33b8f061b23569ee73b258", "heatmap": "e-5d33b8f061b23569ee73b258", "modelKey": "event", "timestamp": null }, { "id": "3e24246f3e58485f71bfc2cb", "type": "sensorMotion", "start": 1643050015046, "end": 1643050015046, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mlyutb Hzzlnpg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3e24246f3e58485f71bfc2cb", "heatmap": "e-3e24246f3e58485f71bfc2cb", "modelKey": "event", "timestamp": null }, { "id": "4ffff1947bbee65fb5b36a73", "type": "motion", "start": 1643050021263, "end": 1643050054932, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "73ae4f31d7df35867e73903b" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4ffff1947bbee65fb5b36a73", "heatmap": "e-4ffff1947bbee65fb5b36a73", "modelKey": "event", "timestamp": 1643050036436 }, { "id": "23a7b598124e9bc59ad0fe7f", "type": "sensorMotion", "start": 1643050022772, "end": 1643050022772, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Luafovc Fhx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-23a7b598124e9bc59ad0fe7f", "heatmap": "e-23a7b598124e9bc59ad0fe7f", "modelKey": "event", "timestamp": null }, { "id": "ad0cf4786e053ec5626d9a6f", "type": "sensorMotion", "start": 1643050027793, "end": 1643050027793, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Blaiv Upkhgdb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ad0cf4786e053ec5626d9a6f", "heatmap": "e-ad0cf4786e053ec5626d9a6f", "modelKey": "event", "timestamp": null }, { "id": "d160756d60de7daf017b998d", "type": "sensorMotion", "start": 1643050030755, "end": 1643050030755, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fabik Yqyes" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d160756d60de7daf017b998d", "heatmap": "e-d160756d60de7daf017b998d", "modelKey": "event", "timestamp": null }, { "id": "26d7535732794cacbfa18be3", "type": "sensorMotion", "start": 1643050035790, "end": 1643050035790, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Whztfn Hpg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-26d7535732794cacbfa18be3", "heatmap": "e-26d7535732794cacbfa18be3", "modelKey": "event", "timestamp": null }, { "id": "0476583f47bae21e0b6c088c", "type": "sensorMotion", "start": 1643050038866, "end": 1643050038866, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bwvx Rtrknv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0476583f47bae21e0b6c088c", "heatmap": "e-0476583f47bae21e0b6c088c", "modelKey": "event", "timestamp": null }, { "id": "88cfc451716bdc38f7eae4a8", "type": "smartDetectZone", "start": 1643050065508, "end": 1643050265938, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-88cfc451716bdc38f7eae4a8", "heatmap": "e-88cfc451716bdc38f7eae4a8", "modelKey": "event", "timestamp": 1643050076508 }, { "id": "b2fb7ec2ae74f80547810444", "type": "motion", "start": 1643050066754, "end": 1643050110755, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "88cfc451716bdc38f7eae4a8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b2fb7ec2ae74f80547810444", "heatmap": "e-b2fb7ec2ae74f80547810444", "modelKey": "event", "timestamp": 1643050082423 }, { "id": "1e302e724c888ac094888e7c", "type": "sensorMotion", "start": 1643050075947, "end": 1643050075947, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jluc Jjeu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1e302e724c888ac094888e7c", "heatmap": "e-1e302e724c888ac094888e7c", "modelKey": "event", "timestamp": null }, { "id": "efbbee2f6d0ef05545e663d4", "type": "sensorMotion", "start": 1643050081742, "end": 1643050081742, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Btlu Eupky" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-efbbee2f6d0ef05545e663d4", "heatmap": "e-efbbee2f6d0ef05545e663d4", "modelKey": "event", "timestamp": null }, { "id": "bf18a67a505675b30a45168f", "type": "sensorMotion", "start": 1643050085346, "end": 1643050085346, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ujltrr Hlyic" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bf18a67a505675b30a45168f", "heatmap": "e-bf18a67a505675b30a45168f", "modelKey": "event", "timestamp": null }, { "id": "f76edcf2ae31eb026c6fef3e", "type": "sensorMotion", "start": 1643050089339, "end": 1643050089339, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sbk Bsgo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f76edcf2ae31eb026c6fef3e", "heatmap": "e-f76edcf2ae31eb026c6fef3e", "modelKey": "event", "timestamp": null }, { "id": "c35189b61298e2697d14c0b2", "type": "sensorMotion", "start": 1643050093458, "end": 1643050093458, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ubuv Wgds" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c35189b61298e2697d14c0b2", "heatmap": "e-c35189b61298e2697d14c0b2", "modelKey": "event", "timestamp": null }, { "id": "bb6ec45db73e8ce4156e54e3", "type": "sensorMotion", "start": 1643050097593, "end": 1643050097593, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Armm Ixffdyt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bb6ec45db73e8ce4156e54e3", "heatmap": "e-bb6ec45db73e8ce4156e54e3", "modelKey": "event", "timestamp": null }, { "id": "a17e96572835c3b2b9080612", "type": "sensorMotion", "start": 1643050101585, "end": 1643050101585, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zljih Xvkbgok" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a17e96572835c3b2b9080612", "heatmap": "e-a17e96572835c3b2b9080612", "modelKey": "event", "timestamp": null }, { "id": "11d83d0e8ce509e46aa45b19", "type": "sensorMotion", "start": 1643050111355, "end": 1643050111355, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zsorlt Hpabi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-11d83d0e8ce509e46aa45b19", "heatmap": "e-11d83d0e8ce509e46aa45b19", "modelKey": "event", "timestamp": null }, { "id": "c39b526441cf92fdf9ec1b19", "type": "sensorMotion", "start": 1643050115475, "end": 1643050115475, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Eivziua Enccfan" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c39b526441cf92fdf9ec1b19", "heatmap": "e-c39b526441cf92fdf9ec1b19", "modelKey": "event", "timestamp": null }, { "id": "21d7126a0a6157a2d987f1a9", "type": "sensorMotion", "start": 1643050120624, "end": 1643050120624, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jyxtlia Iuilp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-21d7126a0a6157a2d987f1a9", "heatmap": "e-21d7126a0a6157a2d987f1a9", "modelKey": "event", "timestamp": null }, { "id": "a16d55ce9211c04f4a3909a0", "type": "sensorMotion", "start": 1643050132857, "end": 1643050132857, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ywt Wwun" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a16d55ce9211c04f4a3909a0", "heatmap": "e-a16d55ce9211c04f4a3909a0", "modelKey": "event", "timestamp": null }, { "id": "8ee33b9b106bd5f7f44e4c1a", "type": "sensorMotion", "start": 1643050135991, "end": 1643050135991, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mkgehu Giaf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8ee33b9b106bd5f7f44e4c1a", "heatmap": "e-8ee33b9b106bd5f7f44e4c1a", "modelKey": "event", "timestamp": null }, { "id": "b301eef6d0beefe5138317e5", "type": "sensorMotion", "start": 1643050140859, "end": 1643050140859, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ycbbv Janrdl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b301eef6d0beefe5138317e5", "heatmap": "e-b301eef6d0beefe5138317e5", "modelKey": "event", "timestamp": null }, { "id": "71fbce0cd5c10f8bdf6092cb", "type": "sensorMotion", "start": 1643050145346, "end": 1643050145346, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gqcnhny Xsrm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-71fbce0cd5c10f8bdf6092cb", "heatmap": "e-71fbce0cd5c10f8bdf6092cb", "modelKey": "event", "timestamp": null }, { "id": "871b72df703079f4f44edfe8", "type": "sensorMotion", "start": 1643050153241, "end": 1643050153241, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Okbn Edgd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-871b72df703079f4f44edfe8", "heatmap": "e-871b72df703079f4f44edfe8", "modelKey": "event", "timestamp": null }, { "id": "b16b8475e6c66724f9c2b5f0", "type": "sensorMotion", "start": 1643050157835, "end": 1643050157835, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Djetyl Kzskynb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b16b8475e6c66724f9c2b5f0", "heatmap": "e-b16b8475e6c66724f9c2b5f0", "modelKey": "event", "timestamp": null }, { "id": "bbef379af7b402ea6f580dda", "type": "sensorMotion", "start": 1643050163372, "end": 1643050163372, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uuucc Dgukgrf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bbef379af7b402ea6f580dda", "heatmap": "e-bbef379af7b402ea6f580dda", "modelKey": "event", "timestamp": null }, { "id": "a76a976fe2df881a26d4d537", "type": "sensorMotion", "start": 1643050166334, "end": 1643050166334, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mma Flfkmte" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a76a976fe2df881a26d4d537", "heatmap": "e-a76a976fe2df881a26d4d537", "modelKey": "event", "timestamp": null }, { "id": "a308b0d8525aee74d05d1b2c", "type": "sensorMotion", "start": 1643050171869, "end": 1643050171869, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tgtnva Ndtj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a308b0d8525aee74d05d1b2c", "heatmap": "e-a308b0d8525aee74d05d1b2c", "modelKey": "event", "timestamp": null }, { "id": "9e0796133cd8bc6a65e05b30", "type": "sensorMotion", "start": 1643050176648, "end": 1643050176648, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Upuxuqy Hbkh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9e0796133cd8bc6a65e05b30", "heatmap": "e-9e0796133cd8bc6a65e05b30", "modelKey": "event", "timestamp": null }, { "id": "cc4bcfa13108326a1df0aa6b", "type": "sensorMotion", "start": 1643050187449, "end": 1643050187449, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bjvkzep Lfwxbht" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cc4bcfa13108326a1df0aa6b", "heatmap": "e-cc4bcfa13108326a1df0aa6b", "modelKey": "event", "timestamp": null }, { "id": "1d51c4080682635bceff714f", "type": "sensorMotion", "start": 1643050200324, "end": 1643050200324, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iiwzfth Oarl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1d51c4080682635bceff714f", "heatmap": "e-1d51c4080682635bceff714f", "modelKey": "event", "timestamp": null }, { "id": "b582567e17f4a3dca8714d26", "type": "sensorMotion", "start": 1643050205346, "end": 1643050205346, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hekwj Obklrnw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b582567e17f4a3dca8714d26", "heatmap": "e-b582567e17f4a3dca8714d26", "modelKey": "event", "timestamp": null }, { "id": "fcca69ab8bc9c007e3e110ee", "type": "sensorMotion", "start": 1643050211912, "end": 1643050211912, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qrv Bfh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fcca69ab8bc9c007e3e110ee", "heatmap": "e-fcca69ab8bc9c007e3e110ee", "modelKey": "event", "timestamp": null }, { "id": "269e99e7fc3e0abbef8152b2", "type": "sensorMotion", "start": 1643050216331, "end": 1643050216331, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Geohwzf Vem" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-269e99e7fc3e0abbef8152b2", "heatmap": "e-269e99e7fc3e0abbef8152b2", "modelKey": "event", "timestamp": null }, { "id": "42f54dc1c5218562df5f81ec", "type": "sensorMotion", "start": 1643050221698, "end": 1643050221698, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qkbqjx Vuijm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-42f54dc1c5218562df5f81ec", "heatmap": "e-42f54dc1c5218562df5f81ec", "modelKey": "event", "timestamp": null }, { "id": "19c85a34298f7c306d9da2fb", "type": "sensorMotion", "start": 1643050226204, "end": 1643050226204, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xch Rfelb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-19c85a34298f7c306d9da2fb", "heatmap": "e-19c85a34298f7c306d9da2fb", "modelKey": "event", "timestamp": null }, { "id": "caa4c362b48517432c52789c", "type": "access", "start": 1643050229984, "end": 1643050604060, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "bc3dd633553907952a6fe20d", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-caa4c362b48517432c52789c", "heatmap": "e-caa4c362b48517432c52789c", "modelKey": "event", "timestamp": null }, { "id": "e2589f883d6320859a20f006", "type": "sensorMotion", "start": 1643050230710, "end": 1643050230710, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dssv Esqdte" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e2589f883d6320859a20f006", "heatmap": "e-e2589f883d6320859a20f006", "modelKey": "event", "timestamp": null }, { "id": "3adca0e42e748045ebf103d3", "type": "sensorMotion", "start": 1643050233815, "end": 1643050233815, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Osdibh Wogov" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3adca0e42e748045ebf103d3", "heatmap": "e-3adca0e42e748045ebf103d3", "modelKey": "event", "timestamp": null }, { "id": "a3f221f78cff201dd1e20185", "type": "sensorMotion", "start": 1643050239884, "end": 1643050239884, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hqagra Pymwq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a3f221f78cff201dd1e20185", "heatmap": "e-a3f221f78cff201dd1e20185", "modelKey": "event", "timestamp": null }, { "id": "782b2255145b002a53da469f", "type": "sensorMotion", "start": 1643050256204, "end": 1643050256204, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Krjk Skrcc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-782b2255145b002a53da469f", "heatmap": "e-782b2255145b002a53da469f", "modelKey": "event", "timestamp": null }, { "id": "75d703a32bc35af3246bea11", "type": "sensorMotion", "start": 1643050260338, "end": 1643050260338, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fuj Matgbls" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-75d703a32bc35af3246bea11", "heatmap": "e-75d703a32bc35af3246bea11", "modelKey": "event", "timestamp": null }, { "id": "7c916a87e9654f3f1b8fe894", "type": "sensorMotion", "start": 1643050264846, "end": 1643050264846, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ldtl Zao" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7c916a87e9654f3f1b8fe894", "heatmap": "e-7c916a87e9654f3f1b8fe894", "modelKey": "event", "timestamp": null }, { "id": "566f02efcb55fe5c66e826c8", "type": "sensorMotion", "start": 1643050268838, "end": 1643050268838, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ljf Wiesf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-566f02efcb55fe5c66e826c8", "heatmap": "e-566f02efcb55fe5c66e826c8", "modelKey": "event", "timestamp": null }, { "id": "5e3f080762b0168b5b32c22a", "type": "sensorMotion", "start": 1643050273717, "end": 1643050273717, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jjdh Rnyd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5e3f080762b0168b5b32c22a", "heatmap": "e-5e3f080762b0168b5b32c22a", "modelKey": "event", "timestamp": null }, { "id": "6e2e7b2c55500408de85adb8", "type": "sensorMotion", "start": 1643050277849, "end": 1643050277849, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ouu Vgtn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6e2e7b2c55500408de85adb8", "heatmap": "e-6e2e7b2c55500408de85adb8", "modelKey": "event", "timestamp": null }, { "id": "7cf439298f29f474f7625815", "type": "motion", "start": 1643050278700, "end": 1643050302034, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7cf439298f29f474f7625815", "heatmap": "e-7cf439298f29f474f7625815", "modelKey": "event", "timestamp": 1643050289867 }, { "id": "95388108ef1c9eaffed515aa", "type": "sensorMotion", "start": 1643050285302, "end": 1643050285302, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qibwk Afcnm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-95388108ef1c9eaffed515aa", "heatmap": "e-95388108ef1c9eaffed515aa", "modelKey": "event", "timestamp": null }, { "id": "fc949c629d91bd3b56571d7b", "type": "sensorMotion", "start": 1643050290326, "end": 1643050290326, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gmyi Rwa" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fc949c629d91bd3b56571d7b", "heatmap": "e-fc949c629d91bd3b56571d7b", "modelKey": "event", "timestamp": null }, { "id": "2f5f0e895611612659718226", "type": "sensorMotion", "start": 1643050298566, "end": 1643050298566, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tevue Seb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2f5f0e895611612659718226", "heatmap": "e-2f5f0e895611612659718226", "modelKey": "event", "timestamp": null }, { "id": "39f00bc4b8db931dbb45ead2", "type": "sensorMotion", "start": 1643050302942, "end": 1643050302942, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jjble Ugui" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-39f00bc4b8db931dbb45ead2", "heatmap": "e-39f00bc4b8db931dbb45ead2", "modelKey": "event", "timestamp": null }, { "id": "4e5b8a5f782ab70349254fad", "type": "sensorMotion", "start": 1643050311311, "end": 1643050311311, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gttfy Pze" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4e5b8a5f782ab70349254fad", "heatmap": "e-4e5b8a5f782ab70349254fad", "modelKey": "event", "timestamp": null }, { "id": "6998abd28da1ef3ae51076d9", "type": "sensorMotion", "start": 1643050319293, "end": 1643050319293, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oais Ctjgqcs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6998abd28da1ef3ae51076d9", "heatmap": "e-6998abd28da1ef3ae51076d9", "modelKey": "event", "timestamp": null }, { "id": "f5e38d96ae7d9ad1b4db6168", "type": "sensorMotion", "start": 1643050323285, "end": 1643050323285, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rdsvxni Kkqv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f5e38d96ae7d9ad1b4db6168", "heatmap": "e-f5e38d96ae7d9ad1b4db6168", "modelKey": "event", "timestamp": null }, { "id": "17fd6b913e8e458f80bbace9", "type": "sensorMotion", "start": 1643050329594, "end": 1643050329594, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tpoyjr Djrjtw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-17fd6b913e8e458f80bbace9", "heatmap": "e-17fd6b913e8e458f80bbace9", "modelKey": "event", "timestamp": null }, { "id": "e4b710c96afd7afa5e020e40", "type": "sensorMotion", "start": 1643050335902, "end": 1643050335902, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Urfjxcr Blo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e4b710c96afd7afa5e020e40", "heatmap": "e-e4b710c96afd7afa5e020e40", "modelKey": "event", "timestamp": null }, { "id": "3928bcb3d4fa74529885fb44", "type": "sensorMotion", "start": 1643050339856, "end": 1643050339856, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jgyip Ogp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3928bcb3d4fa74529885fb44", "heatmap": "e-3928bcb3d4fa74529885fb44", "modelKey": "event", "timestamp": null }, { "id": "b9cae957c1f122862dfc728f", "type": "sensorMotion", "start": 1643050345442, "end": 1643050345442, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iyhbz Yooxdne" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b9cae957c1f122862dfc728f", "heatmap": "e-b9cae957c1f122862dfc728f", "modelKey": "event", "timestamp": null }, { "id": "d759077dadf40d9eb84c18a9", "type": "sensorMotion", "start": 1643050351143, "end": 1643050351143, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xpag Oweqdnm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d759077dadf40d9eb84c18a9", "heatmap": "e-d759077dadf40d9eb84c18a9", "modelKey": "event", "timestamp": null }, { "id": "afaffa60e221769f473d62a2", "type": "sensorMotion", "start": 1643050362570, "end": 1643050362570, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jfwm Oclnxs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-afaffa60e221769f473d62a2", "heatmap": "e-afaffa60e221769f473d62a2", "modelKey": "event", "timestamp": null }, { "id": "22067c7e8b213b4b4d034036", "type": "sensorMotion", "start": 1643050372340, "end": 1643050372340, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yadliy Oizvcu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-22067c7e8b213b4b4d034036", "heatmap": "e-22067c7e8b213b4b4d034036", "modelKey": "event", "timestamp": null }, { "id": "ef476e535fe6df467231ceb3", "type": "sensorMotion", "start": 1643050379822, "end": 1643050379822, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jquybnn Paumeul" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ef476e535fe6df467231ceb3", "heatmap": "e-ef476e535fe6df467231ceb3", "modelKey": "event", "timestamp": null }, { "id": "52d04eab99c955ebf24a7070", "type": "sensorMotion", "start": 1643050389079, "end": 1643050389079, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Etmnqyj Mle" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-52d04eab99c955ebf24a7070", "heatmap": "e-52d04eab99c955ebf24a7070", "modelKey": "event", "timestamp": null }, { "id": "ef116f734e4f4d80aaad5318", "type": "sensorMotion", "start": 1643050393072, "end": 1643050393072, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wbdodi Ceyq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ef116f734e4f4d80aaad5318", "heatmap": "e-ef116f734e4f4d80aaad5318", "modelKey": "event", "timestamp": null }, { "id": "9b542a17d0a6fa0684bb55f0", "type": "sensorMotion", "start": 1643050399249, "end": 1643050399249, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rpyzzli Dxf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9b542a17d0a6fa0684bb55f0", "heatmap": "e-9b542a17d0a6fa0684bb55f0", "modelKey": "event", "timestamp": null }, { "id": "d26a58d6d83d1cb929a89828", "type": "sensorMotion", "start": 1643050413298, "end": 1643050413298, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jby Ppoj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d26a58d6d83d1cb929a89828", "heatmap": "e-d26a58d6d83d1cb929a89828", "modelKey": "event", "timestamp": null }, { "id": "16fb9a53dc55fa89e4585484", "type": "sensorMotion", "start": 1643050418820, "end": 1643050418820, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zmiy Eyje" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-16fb9a53dc55fa89e4585484", "heatmap": "e-16fb9a53dc55fa89e4585484", "modelKey": "event", "timestamp": null }, { "id": "2e6e0fcd3ac1c1f8925b444c", "type": "sensorMotion", "start": 1643050429121, "end": 1643050429121, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xmgh Rfxczim" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2e6e0fcd3ac1c1f8925b444c", "heatmap": "e-2e6e0fcd3ac1c1f8925b444c", "modelKey": "event", "timestamp": null }, { "id": "536ed217d80ca7b6273ec7de", "type": "smartDetectZone", "start": 1643050430140, "end": 1643050453138, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-536ed217d80ca7b6273ec7de", "heatmap": "e-536ed217d80ca7b6273ec7de", "modelKey": "event", "timestamp": 1643050441741 }, { "id": "5108cbfe4544b822945b4aba", "type": "motion", "start": 1643050431893, "end": 1643050455058, "score": 9, "smartDetectTypes": [], "smartDetectEvents": [ "536ed217d80ca7b6273ec7de" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5108cbfe4544b822945b4aba", "heatmap": "e-5108cbfe4544b822945b4aba", "modelKey": "event", "timestamp": 1643050082423 }, { "id": "5582e6a0c26b37bd534c5c8e", "type": "sensorMotion", "start": 1643050432096, "end": 1643050432096, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Exwmebg Waqn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5582e6a0c26b37bd534c5c8e", "heatmap": "e-5582e6a0c26b37bd534c5c8e", "modelKey": "event", "timestamp": null }, { "id": "d736bb33c08ead050676b9bf", "type": "sensorMotion", "start": 1643050436073, "end": 1643050436073, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yqgkm Wgfpk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d736bb33c08ead050676b9bf", "heatmap": "e-d736bb33c08ead050676b9bf", "modelKey": "event", "timestamp": null }, { "id": "0661f7ab4c004935e2486c1d", "type": "sensorMotion", "start": 1643050441352, "end": 1643050441352, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kvgie Fat" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0661f7ab4c004935e2486c1d", "heatmap": "e-0661f7ab4c004935e2486c1d", "modelKey": "event", "timestamp": null }, { "id": "8df618372d63c175049d18ac", "type": "sensorMotion", "start": 1643050445343, "end": 1643050445343, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yspfa Mittii" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8df618372d63c175049d18ac", "heatmap": "e-8df618372d63c175049d18ac", "modelKey": "event", "timestamp": null }, { "id": "b2c0021d25cc2d9f799b41c6", "type": "smartDetectZone", "start": 1643050445693, "end": 1643050477433, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b2c0021d25cc2d9f799b41c6", "heatmap": "e-b2c0021d25cc2d9f799b41c6", "modelKey": "event", "timestamp": 1643050456693 }, { "id": "532a7e8c90a5e60daecfa782", "type": "sensorMotion", "start": 1643050450107, "end": 1643050450107, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vyvfz Yldxvq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-532a7e8c90a5e60daecfa782", "heatmap": "e-532a7e8c90a5e60daecfa782", "modelKey": "event", "timestamp": null }, { "id": "e87471c5d14c8764bc322779", "type": "sensorMotion", "start": 1643050456674, "end": 1643050456674, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ant Qxy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e87471c5d14c8764bc322779", "heatmap": "e-e87471c5d14c8764bc322779", "modelKey": "event", "timestamp": null }, { "id": "9c9e99168f79da5b4b29c9b5", "type": "sensorMotion", "start": 1643050461051, "end": 1643050461051, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fxub Prik" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9c9e99168f79da5b4b29c9b5", "heatmap": "e-9c9e99168f79da5b4b29c9b5", "modelKey": "event", "timestamp": null }, { "id": "2e5b65115275db6f62c800a8", "type": "sensorMotion", "start": 1643050465686, "end": 1643050465686, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Aquepj Nrg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2e5b65115275db6f62c800a8", "heatmap": "e-2e5b65115275db6f62c800a8", "modelKey": "event", "timestamp": null }, { "id": "abd70e20ddc33f6327d3a8c2", "type": "sensorMotion", "start": 1643050469677, "end": 1643050469677, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dyrhmzn Ioiwrq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-abd70e20ddc33f6327d3a8c2", "heatmap": "e-abd70e20ddc33f6327d3a8c2", "modelKey": "event", "timestamp": null }, { "id": "b71f257e22cabdc48a7426bd", "type": "sensorMotion", "start": 1643050474956, "end": 1643050474956, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ropzmc Uepkn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b71f257e22cabdc48a7426bd", "heatmap": "e-b71f257e22cabdc48a7426bd", "modelKey": "event", "timestamp": null }, { "id": "e5cfe91582ea82c779a6ec5a", "type": "sensorMotion", "start": 1643050483582, "end": 1643050483582, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Obtmmt Hgdbix" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e5cfe91582ea82c779a6ec5a", "heatmap": "e-e5cfe91582ea82c779a6ec5a", "modelKey": "event", "timestamp": null }, { "id": "b11ced642e5243edb5f50d46", "type": "sensorMotion", "start": 1643050487588, "end": 1643050487588, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Freg Rodgtlv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b11ced642e5243edb5f50d46", "heatmap": "e-b11ced642e5243edb5f50d46", "modelKey": "event", "timestamp": null }, { "id": "23f529fefa74f48698703447", "type": "sensorMotion", "start": 1643050491580, "end": 1643050491580, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wqiaxgb Yghrf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-23f529fefa74f48698703447", "heatmap": "e-23f529fefa74f48698703447", "modelKey": "event", "timestamp": null }, { "id": "0aa03234505698a27d8b7618", "type": "sensorMotion", "start": 1643050495589, "end": 1643050495589, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Twysgt Xicxxe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0aa03234505698a27d8b7618", "heatmap": "e-0aa03234505698a27d8b7618", "modelKey": "event", "timestamp": null }, { "id": "84ac8b5f73f166e8b22e93a8", "type": "smartDetectZone", "start": 1643050496588, "end": 1643050697024, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-84ac8b5f73f166e8b22e93a8", "heatmap": "e-84ac8b5f73f166e8b22e93a8", "modelKey": "event", "timestamp": 1643050507885 }, { "id": "07c30c13550ae5844c64407b", "type": "motion", "start": 1643050499875, "end": 1643050534375, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "84ac8b5f73f166e8b22e93a8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-07c30c13550ae5844c64407b", "heatmap": "e-07c30c13550ae5844c64407b", "modelKey": "event", "timestamp": 1643050514207 }, { "id": "37657f86e3bdf24c91ab6c8c", "type": "sensorMotion", "start": 1643050501865, "end": 1643050501865, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nsfgd Ztk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-37657f86e3bdf24c91ab6c8c", "heatmap": "e-37657f86e3bdf24c91ab6c8c", "modelKey": "event", "timestamp": null }, { "id": "59180b687842fd4838ef8394", "type": "sensorMotion", "start": 1643050505866, "end": 1643050505866, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Joz Jhuxz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-59180b687842fd4838ef8394", "heatmap": "e-59180b687842fd4838ef8394", "modelKey": "event", "timestamp": null }, { "id": "e40819b946d3c50892559c53", "type": "sensorMotion", "start": 1643050508690, "end": 1643050508690, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xcgdoie Aelrb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e40819b946d3c50892559c53", "heatmap": "e-e40819b946d3c50892559c53", "modelKey": "event", "timestamp": null }, { "id": "d5ff719c83fc4f8effac1014", "type": "sensorMotion", "start": 1643050523509, "end": 1643050523509, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kfkc Rleptaj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d5ff719c83fc4f8effac1014", "heatmap": "e-d5ff719c83fc4f8effac1014", "modelKey": "event", "timestamp": null }, { "id": "9016fae7b9a2dbb2caeaee7d", "type": "sensorMotion", "start": 1643050548132, "end": 1643050548132, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rwawsy Ijml" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9016fae7b9a2dbb2caeaee7d", "heatmap": "e-9016fae7b9a2dbb2caeaee7d", "modelKey": "event", "timestamp": null }, { "id": "d50803bb14ad6ad4c0c07437", "type": "sensorMotion", "start": 1643050556973, "end": 1643050556973, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Grjv Ydhdzrz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d50803bb14ad6ad4c0c07437", "heatmap": "e-d50803bb14ad6ad4c0c07437", "modelKey": "event", "timestamp": null }, { "id": "fc8ca256f8eb34fa4e6b25fe", "type": "sensorMotion", "start": 1643050563152, "end": 1643050563152, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qxk Iiu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-fc8ca256f8eb34fa4e6b25fe", "heatmap": "e-fc8ca256f8eb34fa4e6b25fe", "modelKey": "event", "timestamp": null }, { "id": "3c8d4bcf9d62f1a9a4c8b723", "type": "sensorMotion", "start": 1643050574998, "end": 1643050574998, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sgzcy Cgdwg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3c8d4bcf9d62f1a9a4c8b723", "heatmap": "e-3c8d4bcf9d62f1a9a4c8b723", "modelKey": "event", "timestamp": null }, { "id": "b1400545e15f0c9a10abea7c", "type": "sensorMotion", "start": 1643050580534, "end": 1643050580534, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ytdtu Xzcnhos" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b1400545e15f0c9a10abea7c", "heatmap": "e-b1400545e15f0c9a10abea7c", "modelKey": "event", "timestamp": null }, { "id": "f4f569377b656dd3f0a84a7a", "type": "sensorMotion", "start": 1643050583897, "end": 1643050583897, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fvlhdw Qpvg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f4f569377b656dd3f0a84a7a", "heatmap": "e-f4f569377b656dd3f0a84a7a", "modelKey": "event", "timestamp": null }, { "id": "5829559c8e9f10291b388214", "type": "sensorMotion", "start": 1643050601136, "end": 1643050601136, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yetvzs Tedig" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5829559c8e9f10291b388214", "heatmap": "e-5829559c8e9f10291b388214", "modelKey": "event", "timestamp": null }, { "id": "d1344bdd296e98adc1968d9c", "type": "sensorMotion", "start": 1643050619418, "end": 1643050619418, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Eyr Gbfzx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d1344bdd296e98adc1968d9c", "heatmap": "e-d1344bdd296e98adc1968d9c", "modelKey": "event", "timestamp": null }, { "id": "8119aa04347b63e04e390c89", "type": "sensorMotion", "start": 1643050627530, "end": 1643050627530, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xfsi Lvp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8119aa04347b63e04e390c89", "heatmap": "e-8119aa04347b63e04e390c89", "modelKey": "event", "timestamp": null }, { "id": "c960d62f2aa872b13b09c87c", "type": "sensorMotion", "start": 1643050633195, "end": 1643050633195, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sbzbfcy Xtogd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c960d62f2aa872b13b09c87c", "heatmap": "e-c960d62f2aa872b13b09c87c", "modelKey": "event", "timestamp": null }, { "id": "256aaf4c00928faa1a417c31", "type": "motion", "start": 1643050640039, "end": 1643050664041, "score": 30, "smartDetectTypes": [], "smartDetectEvents": [ "84ac8b5f73f166e8b22e93a8" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-256aaf4c00928faa1a417c31", "heatmap": "e-256aaf4c00928faa1a417c31", "modelKey": "event", "timestamp": 1643050651539 }, { "id": "897672f84ae817e3a7e5a158", "type": "sensorMotion", "start": 1643050647615, "end": 1643050647615, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Btn Qhx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-897672f84ae817e3a7e5a158", "heatmap": "e-897672f84ae817e3a7e5a158", "modelKey": "event", "timestamp": null }, { "id": "5cb4579758e1bf254a8d3393", "type": "sensorMotion", "start": 1643050651993, "end": 1643050651993, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qayfj Mhskdbp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5cb4579758e1bf254a8d3393", "heatmap": "e-5cb4579758e1bf254a8d3393", "modelKey": "event", "timestamp": null }, { "id": "2284602f0bfbf90c4dd36ad7", "type": "sensorMotion", "start": 1643050657014, "end": 1643050657014, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uqfw Utrduo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2284602f0bfbf90c4dd36ad7", "heatmap": "e-2284602f0bfbf90c4dd36ad7", "modelKey": "event", "timestamp": null }, { "id": "77b71c653a25b7f61a7915a7", "type": "sensorMotion", "start": 1643050668474, "end": 1643050668474, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hkkop Qtj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-77b71c653a25b7f61a7915a7", "heatmap": "e-77b71c653a25b7f61a7915a7", "modelKey": "event", "timestamp": null }, { "id": "1224c78a8569cfeb30c9f1db", "type": "sensorMotion", "start": 1643050679201, "end": 1643050679201, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Sowmd Abpo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1224c78a8569cfeb30c9f1db", "heatmap": "e-1224c78a8569cfeb30c9f1db", "modelKey": "event", "timestamp": null }, { "id": "44d603976b19887675f53a68", "type": "sensorMotion", "start": 1643050687014, "end": 1643050687014, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Obb Mqy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-44d603976b19887675f53a68", "heatmap": "e-44d603976b19887675f53a68", "modelKey": "event", "timestamp": null }, { "id": "0c7b6d27d76deaddd58afa6f", "type": "sensorMotion", "start": 1643050692551, "end": 1643050692551, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ntsjtdc Kzwlxl" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-0c7b6d27d76deaddd58afa6f", "heatmap": "e-0c7b6d27d76deaddd58afa6f", "modelKey": "event", "timestamp": null }, { "id": "69197f96b2d753c63565a769", "type": "sensorMotion", "start": 1643050697572, "end": 1643050697572, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Xzxiks Dsz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-69197f96b2d753c63565a769", "heatmap": "e-69197f96b2d753c63565a769", "modelKey": "event", "timestamp": null }, { "id": "72c91b80ae8c8e7a56a3ccc9", "type": "sensorMotion", "start": 1643050704653, "end": 1643050704653, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jydc Oac" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-72c91b80ae8c8e7a56a3ccc9", "heatmap": "e-72c91b80ae8c8e7a56a3ccc9", "modelKey": "event", "timestamp": null }, { "id": "743d49925dbb7589ae226946", "type": "motion", "start": 1643050707353, "end": 1643050746017, "score": 54, "smartDetectTypes": [], "smartDetectEvents": [ "2d9155f200e0642ca338c8bd" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-743d49925dbb7589ae226946", "heatmap": "e-743d49925dbb7589ae226946", "modelKey": "event", "timestamp": 1643050718519 }, { "id": "83b82459678b3da16fc01385", "type": "sensorMotion", "start": 1643050707486, "end": 1643050707486, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Izso Wnta" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-83b82459678b3da16fc01385", "heatmap": "e-83b82459678b3da16fc01385", "modelKey": "event", "timestamp": null }, { "id": "2d9155f200e0642ca338c8bd", "type": "smartDetectZone", "start": 1643050710888, "end": 1643050911288, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2d9155f200e0642ca338c8bd", "heatmap": "e-2d9155f200e0642ca338c8bd", "modelKey": "event", "timestamp": 1643050722812 }, { "id": "4b20df7ec8ee5e3145057fae", "type": "sensorMotion", "start": 1643050711606, "end": 1643050711606, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gqmarj Fvnlep" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4b20df7ec8ee5e3145057fae", "heatmap": "e-4b20df7ec8ee5e3145057fae", "modelKey": "event", "timestamp": null }, { "id": "928295e3508ba15e15d4c267", "type": "sensorMotion", "start": 1643050715613, "end": 1643050715613, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fzqrlo Edlxbci" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-928295e3508ba15e15d4c267", "heatmap": "e-928295e3508ba15e15d4c267", "modelKey": "event", "timestamp": null }, { "id": "1af20130fc55ccdaacf0c843", "type": "sensorMotion", "start": 1643050720491, "end": 1643050720491, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Iudwcq Chxi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1af20130fc55ccdaacf0c843", "heatmap": "e-1af20130fc55ccdaacf0c843", "modelKey": "event", "timestamp": null }, { "id": "15c33f694e6222fe9dc0bd02", "type": "sensorMotion", "start": 1643050724496, "end": 1643050724496, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kuaztwb Jltu" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-15c33f694e6222fe9dc0bd02", "heatmap": "e-15c33f694e6222fe9dc0bd02", "modelKey": "event", "timestamp": null }, { "id": "890b2773349cc0753ada20f0", "type": "sensorMotion", "start": 1643050728631, "end": 1643050728631, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bgwh Fnjhciz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-890b2773349cc0753ada20f0", "heatmap": "e-890b2773349cc0753ada20f0", "modelKey": "event", "timestamp": null }, { "id": "7a239fa995e688c1a12fda31", "type": "access", "start": 1643050729825, "end": 1643052108134, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "android" }, "thumbnail": "e-7a239fa995e688c1a12fda31", "heatmap": "e-7a239fa995e688c1a12fda31", "modelKey": "event", "timestamp": null }, { "id": "38a8b865ddb07e84eaaa8f56", "type": "sensorMotion", "start": 1643050735569, "end": 1643050735569, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Whgystn Shbdq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-38a8b865ddb07e84eaaa8f56", "heatmap": "e-38a8b865ddb07e84eaaa8f56", "modelKey": "event", "timestamp": null }, { "id": "94dd9108f3214cab478abdad", "type": "sensorMotion", "start": 1643050739545, "end": 1643050739545, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Fkfpvyy Aovspyx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-94dd9108f3214cab478abdad", "heatmap": "e-94dd9108f3214cab478abdad", "modelKey": "event", "timestamp": null }, { "id": "0ad117f586b43157d9e6b0da", "type": "motion", "start": 1643050750303, "end": 1643050771302, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0ad117f586b43157d9e6b0da", "heatmap": "e-0ad117f586b43157d9e6b0da", "modelKey": "event", "timestamp": 1643050289867 }, { "id": "92b149f5d57f4e48736d97d2", "type": "sensorMotion", "start": 1643050765682, "end": 1643050765682, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hpqf Hpfw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-92b149f5d57f4e48736d97d2", "heatmap": "e-92b149f5d57f4e48736d97d2", "modelKey": "event", "timestamp": null }, { "id": "a70cfc002997d88aca93fa06", "type": "sensorMotion", "start": 1643050770575, "end": 1643050770575, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hthq Jyovvq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a70cfc002997d88aca93fa06", "heatmap": "e-a70cfc002997d88aca93fa06", "modelKey": "event", "timestamp": null }, { "id": "5e1ce41bdc4b887d50c7b90d", "type": "sensorMotion", "start": 1643050796583, "end": 1643050796583, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Wkfgwac Ahjw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5e1ce41bdc4b887d50c7b90d", "heatmap": "e-5e1ce41bdc4b887d50c7b90d", "modelKey": "event", "timestamp": null }, { "id": "4d54b2597521d93e3f3f96b3", "type": "sensorMotion", "start": 1643050799287, "end": 1643050799287, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Syp Wxhkkv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4d54b2597521d93e3f3f96b3", "heatmap": "e-4d54b2597521d93e3f3f96b3", "modelKey": "event", "timestamp": null }, { "id": "5a02ee3f1a3f7cb9e83e2c06", "type": "sensorMotion", "start": 1643050804052, "end": 1643050804052, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qoivii Xmdm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5a02ee3f1a3f7cb9e83e2c06", "heatmap": "e-5a02ee3f1a3f7cb9e83e2c06", "modelKey": "event", "timestamp": null }, { "id": "059ff047a8934be0ed4a742a", "type": "sensorMotion", "start": 1643050809761, "end": 1643050809761, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Oackqe Xfynr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-059ff047a8934be0ed4a742a", "heatmap": "e-059ff047a8934be0ed4a742a", "modelKey": "event", "timestamp": null }, { "id": "acd872b8a53c4d00e4600c74", "type": "sensorMotion", "start": 1643050814228, "end": 1643050814228, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Uxjc Lcahyfo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-acd872b8a53c4d00e4600c74", "heatmap": "e-acd872b8a53c4d00e4600c74", "modelKey": "event", "timestamp": null }, { "id": "66c1d40fa83219e7c0091743", "type": "sensorMotion", "start": 1643050821562, "end": 1643050821562, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dtickug Rkydt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-66c1d40fa83219e7c0091743", "heatmap": "e-66c1d40fa83219e7c0091743", "modelKey": "event", "timestamp": null }, { "id": "496dca8ef7f1f5b13cf74194", "type": "sensorMotion", "start": 1643050825583, "end": 1643050825583, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Kliekus Tgkas" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-496dca8ef7f1f5b13cf74194", "heatmap": "e-496dca8ef7f1f5b13cf74194", "modelKey": "event", "timestamp": null }, { "id": "4f298de64f9b33207ad292fc", "type": "sensorMotion", "start": 1643050831218, "end": 1643050831218, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Odaoh Ncf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4f298de64f9b33207ad292fc", "heatmap": "e-4f298de64f9b33207ad292fc", "modelKey": "event", "timestamp": null }, { "id": "b52045d8da92f1896ce85e56", "type": "sensorMotion", "start": 1643050834567, "end": 1643050834567, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Nvfvfev Thligci" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b52045d8da92f1896ce85e56", "heatmap": "e-b52045d8da92f1896ce85e56", "modelKey": "event", "timestamp": null }, { "id": "9d912c9a7d111a3639a9f182", "type": "sensorMotion", "start": 1643050839891, "end": 1643050839891, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Qtzczh Pvtjs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-9d912c9a7d111a3639a9f182", "heatmap": "e-9d912c9a7d111a3639a9f182", "modelKey": "event", "timestamp": null }, { "id": "eda6d847a9714a85e811c162", "type": "sensorMotion", "start": 1643050852220, "end": 1643050852220, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ziljw Gnyrdn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eda6d847a9714a85e811c162", "heatmap": "e-eda6d847a9714a85e811c162", "modelKey": "event", "timestamp": null }, { "id": "4906f6706e5337e32e88c916", "type": "sensorMotion", "start": 1643050856583, "end": 1643050856583, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ihnp Fetsg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4906f6706e5337e32e88c916", "heatmap": "e-4906f6706e5337e32e88c916", "modelKey": "event", "timestamp": null }, { "id": "95672e62180d58fc836ca1b7", "type": "sensorMotion", "start": 1643050879421, "end": 1643050879421, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ajbryen Wgnrju" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-95672e62180d58fc836ca1b7", "heatmap": "e-95672e62180d58fc836ca1b7", "modelKey": "event", "timestamp": null }, { "id": "1c03250f5dc28e42dc5e62b5", "type": "sensorMotion", "start": 1643050884909, "end": 1643050884909, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rznx Xnlc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1c03250f5dc28e42dc5e62b5", "heatmap": "e-1c03250f5dc28e42dc5e62b5", "modelKey": "event", "timestamp": null }, { "id": "cf093ab11848fe601293a99d", "type": "sensorMotion", "start": 1643050887627, "end": 1643050887627, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Hpp Mwkyxfz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-cf093ab11848fe601293a99d", "heatmap": "e-cf093ab11848fe601293a99d", "modelKey": "event", "timestamp": null }, { "id": "88a134d27b5acd4d55218d69", "type": "sensorMotion", "start": 1643050894709, "end": 1643050894709, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Yqevfm Bztfe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-88a134d27b5acd4d55218d69", "heatmap": "e-88a134d27b5acd4d55218d69", "modelKey": "event", "timestamp": null }, { "id": "e77303a371d3e7a77d313a3a", "type": "sensorMotion", "start": 1643050900102, "end": 1643050900102, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ovv Edlluhq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e77303a371d3e7a77d313a3a", "heatmap": "e-e77303a371d3e7a77d313a3a", "modelKey": "event", "timestamp": null }, { "id": "5e3637b0fd1ae5169898d247", "type": "sensorMotion", "start": 1643050902682, "end": 1643050902682, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mkgq Weyhc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5e3637b0fd1ae5169898d247", "heatmap": "e-5e3637b0fd1ae5169898d247", "modelKey": "event", "timestamp": null }, { "id": "a6f1b5811011ca1da699603f", "type": "sensorMotion", "start": 1643050910788, "end": 1643050910788, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pjcfjpp Iujsh" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a6f1b5811011ca1da699603f", "heatmap": "e-a6f1b5811011ca1da699603f", "modelKey": "event", "timestamp": null }, { "id": "bdab302950429540ec1bd9cd", "type": "sensorMotion", "start": 1643050915424, "end": 1643050915424, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cmbf Wzyr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bdab302950429540ec1bd9cd", "heatmap": "e-bdab302950429540ec1bd9cd", "modelKey": "event", "timestamp": null }, { "id": "bdb141e1a47edceddda271c5", "type": "sensorMotion", "start": 1643050919673, "end": 1643050919673, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Imjy Njb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bdb141e1a47edceddda271c5", "heatmap": "e-bdb141e1a47edceddda271c5", "modelKey": "event", "timestamp": null }, { "id": "e4329cc823c27dc37aa45a0d", "type": "sensorMotion", "start": 1643050923694, "end": 1643050923694, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Vpxkpwb Invk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e4329cc823c27dc37aa45a0d", "heatmap": "e-e4329cc823c27dc37aa45a0d", "modelKey": "event", "timestamp": null }, { "id": "b3100edd145e6d6d13d7c007", "type": "sensorMotion", "start": 1643050928814, "end": 1643050928814, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Tnei Blqn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b3100edd145e6d6d13d7c007", "heatmap": "e-b3100edd145e6d6d13d7c007", "modelKey": "event", "timestamp": null }, { "id": "07aef2985e55dc3fd7a92484", "type": "sensorMotion", "start": 1643050933192, "end": 1643050933192, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Msuu Rjqvfbj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-07aef2985e55dc3fd7a92484", "heatmap": "e-07aef2985e55dc3fd7a92484", "modelKey": "event", "timestamp": null }, { "id": "a8fe97eb5c2d67c713d2b0ee", "type": "sensorMotion", "start": 1643050935896, "end": 1643050935896, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ysnf Azdiml" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a8fe97eb5c2d67c713d2b0ee", "heatmap": "e-a8fe97eb5c2d67c713d2b0ee", "modelKey": "event", "timestamp": null }, { "id": "4fce6344c8d16998803e92db", "type": "sensorMotion", "start": 1643050940289, "end": 1643050940289, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mrgqrm Tgp" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4fce6344c8d16998803e92db", "heatmap": "e-4fce6344c8d16998803e92db", "modelKey": "event", "timestamp": null }, { "id": "4d560226a20cd77bb0b24099", "type": "sensorMotion", "start": 1643050944308, "end": 1643050944308, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gjpl Vxv" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4d560226a20cd77bb0b24099", "heatmap": "e-4d560226a20cd77bb0b24099", "modelKey": "event", "timestamp": null }, { "id": "6f032fcac2f0ba94d8bc23df", "type": "doorlockOpened", "start": 1643050947168, "end": 1643050947168, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-6f032fcac2f0ba94d8bc23df", "heatmap": "e-6f032fcac2f0ba94d8bc23df", "modelKey": "event", "timestamp": null }, { "id": "2e1486e5e0a812e60e130dfe", "type": "doorlockClosed", "start": 1643050954166, "end": 1643050954166, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-2e1486e5e0a812e60e130dfe", "heatmap": "e-2e1486e5e0a812e60e130dfe", "modelKey": "event", "timestamp": null }, { "id": "852988152f4d22576c364e14", "type": "doorlockOpened", "start": 1643050961304, "end": 1643050961304, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-852988152f4d22576c364e14", "heatmap": "e-852988152f4d22576c364e14", "modelKey": "event", "timestamp": null }, { "id": "8ec6c6880ef39712c22dc560", "type": "sensorMotion", "start": 1643050965895, "end": 1643050965895, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Refvbz Czqrd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8ec6c6880ef39712c22dc560", "heatmap": "e-8ec6c6880ef39712c22dc560", "modelKey": "event", "timestamp": null }, { "id": "892abca3b22f28f9fee7f0c9", "type": "sensorMotion", "start": 1643050968728, "end": 1643050968728, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Zpjfc Piq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-892abca3b22f28f9fee7f0c9", "heatmap": "e-892abca3b22f28f9fee7f0c9", "modelKey": "event", "timestamp": null }, { "id": "0f9ee11ce6847125e9d64706", "type": "doorlockClosed", "start": 1643050970912, "end": 1643050970912, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-0f9ee11ce6847125e9d64706", "heatmap": "e-0f9ee11ce6847125e9d64706", "modelKey": "event", "timestamp": null }, { "id": "3649b9888912bcaabe8dc7d9", "type": "doorlockOpened", "start": 1643050971287, "end": 1643050971287, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-3649b9888912bcaabe8dc7d9", "heatmap": "e-3649b9888912bcaabe8dc7d9", "modelKey": "event", "timestamp": null }, { "id": "5b642886f0b8a3f453aeeae6", "type": "sensorMotion", "start": 1643050972719, "end": 1643050972719, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cbhqtvt Datdfm" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5b642886f0b8a3f453aeeae6", "heatmap": "e-5b642886f0b8a3f453aeeae6", "modelKey": "event", "timestamp": null }, { "id": "bbe6f957034a6af369bae386", "type": "doorlockClosed", "start": 1643050976663, "end": 1643050976663, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-bbe6f957034a6af369bae386", "heatmap": "e-bbe6f957034a6af369bae386", "modelKey": "event", "timestamp": null }, { "id": "a279a99a4b77bbc5880bd498", "type": "sensorMotion", "start": 1643050976839, "end": 1643050976839, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Bew Hrt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a279a99a4b77bbc5880bd498", "heatmap": "e-a279a99a4b77bbc5880bd498", "modelKey": "event", "timestamp": null }, { "id": "7244b8f1007f21cf69818c1f", "type": "doorlockClosed", "start": 1643050989055, "end": 1643050989055, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-7244b8f1007f21cf69818c1f", "heatmap": "e-7244b8f1007f21cf69818c1f", "modelKey": "event", "timestamp": null }, { "id": "c5b6dead2f4ffa5877c26184", "type": "sensorMotion", "start": 1643050991648, "end": 1643050991648, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Jvucc Fdhch" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c5b6dead2f4ffa5877c26184", "heatmap": "e-c5b6dead2f4ffa5877c26184", "modelKey": "event", "timestamp": null }, { "id": "ad4d4d1a2840b5b9c6cb322b", "type": "doorlockOpened", "start": 1643050992288, "end": 1643050992288, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-ad4d4d1a2840b5b9c6cb322b", "heatmap": "e-ad4d4d1a2840b5b9c6cb322b", "modelKey": "event", "timestamp": null }, { "id": "0c1b026ef03f0d5b320b7755", "type": "motion", "start": 1643050992642, "end": 1643051014997, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0c1b026ef03f0d5b320b7755", "heatmap": "e-0c1b026ef03f0d5b320b7755", "modelKey": "event", "timestamp": 1643051003809 }, { "id": "b5eea6cfc703199b4c78c14b", "type": "doorlockClosed", "start": 1643050993413, "end": 1643050993413, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-b5eea6cfc703199b4c78c14b", "heatmap": "e-b5eea6cfc703199b4c78c14b", "modelKey": "event", "timestamp": null }, { "id": "6f3b8af9e74db5e77a0b4eb3", "type": "doorlockOpened", "start": 1643050993929, "end": 1643050993929, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-6f3b8af9e74db5e77a0b4eb3", "heatmap": "e-6f3b8af9e74db5e77a0b4eb3", "modelKey": "event", "timestamp": null }, { "id": "668ecf9192ea123996b745e6", "type": "doorlockClosed", "start": 1643050994571, "end": 1643050994571, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-668ecf9192ea123996b745e6", "heatmap": "e-668ecf9192ea123996b745e6", "modelKey": "event", "timestamp": null }, { "id": "f7a1bcd7c329562ec6f9fc98", "type": "doorlockOpened", "start": 1643050995696, "end": 1643050995696, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-f7a1bcd7c329562ec6f9fc98", "heatmap": "e-f7a1bcd7c329562ec6f9fc98", "modelKey": "event", "timestamp": null }, { "id": "93414955714fedcd1c20231a", "type": "doorlockClosed", "start": 1643050997929, "end": 1643050997929, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-93414955714fedcd1c20231a", "heatmap": "e-93414955714fedcd1c20231a", "modelKey": "event", "timestamp": null }, { "id": "dc6718a6dd54639bbd552f76", "type": "sensorMotion", "start": 1643050998599, "end": 1643050998599, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Gbtqw Vxo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dc6718a6dd54639bbd552f76", "heatmap": "e-dc6718a6dd54639bbd552f76", "modelKey": "event", "timestamp": null }, { "id": "8c23f88862fa14e491888c7d", "type": "smartDetectZone", "start": 1643051003635, "end": 1643051039747, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8c23f88862fa14e491888c7d", "heatmap": "e-8c23f88862fa14e491888c7d", "modelKey": "event", "timestamp": 1643051014635 }, { "id": "75657a497663837582c4b4a7", "type": "doorlockOpened", "start": 1643051005196, "end": 1643051005196, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-75657a497663837582c4b4a7", "heatmap": "e-75657a497663837582c4b4a7", "modelKey": "event", "timestamp": null }, { "id": "d641d2a03babd4ab428294ad", "type": "motion", "start": 1643051006311, "end": 1643051039818, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "8c23f88862fa14e491888c7d" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d641d2a03babd4ab428294ad", "heatmap": "e-d641d2a03babd4ab428294ad", "modelKey": "event", "timestamp": 1643051020812 }, { "id": "30f4fa2beff3e96767209f65", "type": "doorlockClosed", "start": 1643051012290, "end": 1643051012290, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-30f4fa2beff3e96767209f65", "heatmap": "e-30f4fa2beff3e96767209f65", "modelKey": "event", "timestamp": null }, { "id": "91d361a40390415512ca0879", "type": "sensorMotion", "start": 1643051013803, "end": 1643051013803, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Lxc Loakhxn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-91d361a40390415512ca0879", "heatmap": "e-91d361a40390415512ca0879", "modelKey": "event", "timestamp": null }, { "id": "8b7814a057ce666a7d4e1e10", "type": "sensorMotion", "start": 1643051017526, "end": 1643051017526, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Dubj Tgpea" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8b7814a057ce666a7d4e1e10", "heatmap": "e-8b7814a057ce666a7d4e1e10", "modelKey": "event", "timestamp": null }, { "id": "b37a1a2145aaefd3973299c5", "type": "doorlockOpened", "start": 1643051022541, "end": 1643051022541, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-b37a1a2145aaefd3973299c5", "heatmap": "e-b37a1a2145aaefd3973299c5", "modelKey": "event", "timestamp": null }, { "id": "3b13eef2dd1750658cbcbbbe", "type": "sensorMotion", "start": 1643051022676, "end": 1643051022676, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ugeist Skdklct" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3b13eef2dd1750658cbcbbbe", "heatmap": "e-3b13eef2dd1750658cbcbbbe", "modelKey": "event", "timestamp": null }, { "id": "c74cb5fc409e9247a2cb6162", "type": "sensorMotion", "start": 1643051027182, "end": 1643051027182, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Niuqjco Sdgxtr" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c74cb5fc409e9247a2cb6162", "heatmap": "e-c74cb5fc409e9247a2cb6162", "modelKey": "event", "timestamp": null }, { "id": "1590ee685cf2d63178878cef", "type": "doorlockClosed", "start": 1643051029572, "end": 1643051029572, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-1590ee685cf2d63178878cef", "heatmap": "e-1590ee685cf2d63178878cef", "modelKey": "event", "timestamp": null }, { "id": "362e855a9acdd341f7008a01", "type": "motion", "start": 1643051090846, "end": 1643051111845, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-362e855a9acdd341f7008a01", "heatmap": "e-362e855a9acdd341f7008a01", "modelKey": "event", "timestamp": 1643051003809 }, { "id": "c7451e5ced8dd8d511ac6bc5", "type": "access", "start": 1643051220245, "end": 1643051248534, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-c7451e5ced8dd8d511ac6bc5", "heatmap": "e-c7451e5ced8dd8d511ac6bc5", "modelKey": "event", "timestamp": null }, { "id": "bac57e01440d374ed29e6c42", "type": "motion", "start": 1643051224934, "end": 1643051246103, "score": 70, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bac57e01440d374ed29e6c42", "heatmap": "e-bac57e01440d374ed29e6c42", "modelKey": "event", "timestamp": 1643051236101 }, { "id": "4efab3ba3311922baac8732f", "type": "doorlockOpened", "start": 1643051232921, "end": 1643051232921, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-4efab3ba3311922baac8732f", "heatmap": "e-4efab3ba3311922baac8732f", "modelKey": "event", "timestamp": null }, { "id": "0bea7b4e4c7f25775e56ebdc", "type": "doorlockClosed", "start": 1643051245329, "end": 1643051245329, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-0bea7b4e4c7f25775e56ebdc", "heatmap": "e-0bea7b4e4c7f25775e56ebdc", "modelKey": "event", "timestamp": null }, { "id": "62302386a66c10b483664b3c", "type": "motion", "start": 1643051446018, "end": 1643051467018, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-62302386a66c10b483664b3c", "heatmap": "e-62302386a66c10b483664b3c", "modelKey": "event", "timestamp": 1643051236101 }, { "id": "f4c8b762a0050b2c84f4e25d", "type": "access", "start": 1643051726135, "end": 1643051754321, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-f4c8b762a0050b2c84f4e25d", "heatmap": "e-f4c8b762a0050b2c84f4e25d", "modelKey": "event", "timestamp": null }, { "id": "947e9acb2748c6b32e5eaa61", "type": "doorlockOpened", "start": 1643051737063, "end": 1643051737063, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-947e9acb2748c6b32e5eaa61", "heatmap": "e-947e9acb2748c6b32e5eaa61", "modelKey": "event", "timestamp": null }, { "id": "d72d56d2822625546319b811", "type": "access", "start": 1643051780121, "end": 1643051783884, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-d72d56d2822625546319b811", "heatmap": "e-d72d56d2822625546319b811", "modelKey": "event", "timestamp": null }, { "id": "131d2b0065fdcbac740b35ad", "type": "doorlockClosed", "start": 1643051792456, "end": 1643051792456, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-131d2b0065fdcbac740b35ad", "heatmap": "e-131d2b0065fdcbac740b35ad", "modelKey": "event", "timestamp": null }, { "id": "ed5cd7f162967d8a59be1ad2", "type": "doorlockOpened", "start": 1643051801855, "end": 1643051801855, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-ed5cd7f162967d8a59be1ad2", "heatmap": "e-ed5cd7f162967d8a59be1ad2", "modelKey": "event", "timestamp": null }, { "id": "11427f0ba17e684ff8537e2e", "type": "doorlockClosed", "start": 1643051822610, "end": 1643051822610, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-11427f0ba17e684ff8537e2e", "heatmap": "e-11427f0ba17e684ff8537e2e", "modelKey": "event", "timestamp": null }, { "id": "738360365585e537d12f81df", "type": "access", "start": 1643051831465, "end": 1643051859650, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-738360365585e537d12f81df", "heatmap": "e-738360365585e537d12f81df", "modelKey": "event", "timestamp": null }, { "id": "f99fd77ed0d11b541c8625bb", "type": "doorlockOpened", "start": 1643051843234, "end": 1643051843234, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-f99fd77ed0d11b541c8625bb", "heatmap": "e-f99fd77ed0d11b541c8625bb", "modelKey": "event", "timestamp": null }, { "id": "9bdd7e705f8d72c3b7e9ddfa", "type": "motion", "start": 1643051849405, "end": 1643051870571, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9bdd7e705f8d72c3b7e9ddfa", "heatmap": "e-9bdd7e705f8d72c3b7e9ddfa", "modelKey": "event", "timestamp": 1643049044771 }, { "id": "2069cd1e332a0c02d454aaa2", "type": "motion", "start": 1643051852654, "end": 1643051874156, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2069cd1e332a0c02d454aaa2", "heatmap": "e-2069cd1e332a0c02d454aaa2", "modelKey": "event", "timestamp": 1643051863820 }, { "id": "4406517a4fe3006248c644cd", "type": "doorlockClosed", "start": 1643051853487, "end": 1643051853487, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-4406517a4fe3006248c644cd", "heatmap": "e-4406517a4fe3006248c644cd", "modelKey": "event", "timestamp": null }, { "id": "a6de77b02fa60d941d2c8ac8", "type": "motion", "start": 1643051966296, "end": 1643051987966, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a6de77b02fa60d941d2c8ac8", "heatmap": "e-a6de77b02fa60d941d2c8ac8", "modelKey": "event", "timestamp": 1643051977811 }, { "id": "bfc9ecfff91587fc82061689", "type": "motion", "start": 1643052007703, "end": 1643052029375, "score": 68, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bfc9ecfff91587fc82061689", "heatmap": "e-bfc9ecfff91587fc82061689", "modelKey": "event", "timestamp": 1643052018873 }, { "id": "64c493ad9773569f48ea294d", "type": "motion", "start": 1643052019274, "end": 1643052040944, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-64c493ad9773569f48ea294d", "heatmap": "e-64c493ad9773569f48ea294d", "modelKey": "event", "timestamp": 1643052030941 }, { "id": "f58f732e73e8cbefee0f6ed9", "type": "motion", "start": 1643052055517, "end": 1643052079869, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f58f732e73e8cbefee0f6ed9", "heatmap": "e-f58f732e73e8cbefee0f6ed9", "modelKey": "event", "timestamp": 1643052066685 }, { "id": "f4fa9899bb32355821756da5", "type": "motion", "start": 1643052125174, "end": 1643052146492, "score": 77, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f4fa9899bb32355821756da5", "heatmap": "e-f4fa9899bb32355821756da5", "modelKey": "event", "timestamp": 1643052136322 }, { "id": "42d1dad4d44d5320c65d6439", "type": "motion", "start": 1643052128248, "end": 1643052149581, "score": 10, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-42d1dad4d44d5320c65d6439", "heatmap": "e-42d1dad4d44d5320c65d6439", "modelKey": "event", "timestamp": 1643049044771 }, { "id": "b8697707c3ad6b6b5a52470c", "type": "motion", "start": 1643052182963, "end": 1643052206270, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b8697707c3ad6b6b5a52470c", "heatmap": "e-b8697707c3ad6b6b5a52470c", "modelKey": "event", "timestamp": 1643052194104 }, { "id": "820f224d4d3720d9e61972b9", "type": "motion", "start": 1643052367921, "end": 1643052389259, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-820f224d4d3720d9e61972b9", "heatmap": "e-820f224d4d3720d9e61972b9", "modelKey": "event", "timestamp": 1643052379111 }, { "id": "1ea08ccfa9e0d5687cb33404", "type": "motion", "start": 1643052429874, "end": 1643052451560, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1ea08ccfa9e0d5687cb33404", "heatmap": "e-1ea08ccfa9e0d5687cb33404", "modelKey": "event", "timestamp": 1643052441544 }, { "id": "c44f2e8b9cde149d01ee2e42", "type": "motion", "start": 1643052439084, "end": 1643052461252, "score": 75, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c44f2e8b9cde149d01ee2e42", "heatmap": "e-c44f2e8b9cde149d01ee2e42", "modelKey": "event", "timestamp": 1643052451087 }, { "id": "815f5c8c18622d68c4892468", "type": "motion", "start": 1643052581656, "end": 1643052602826, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-815f5c8c18622d68c4892468", "heatmap": "e-815f5c8c18622d68c4892468", "modelKey": "event", "timestamp": 1643052592822 }, { "id": "2d8c0d40412d5d3bc8ce0990", "type": "motion", "start": 1643052607396, "end": 1643052628907, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-2d8c0d40412d5d3bc8ce0990", "heatmap": "e-2d8c0d40412d5d3bc8ce0990", "modelKey": "event", "timestamp": 1643052618897 }, { "id": "93bc0d7c525356dc3b1304af", "type": "motion", "start": 1643052886428, "end": 1643052908266, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-93bc0d7c525356dc3b1304af", "heatmap": "e-93bc0d7c525356dc3b1304af", "modelKey": "event", "timestamp": 1643052897596 }, { "id": "97686c9a8ac9d62b3ca99064", "type": "motion", "start": 1643052900462, "end": 1643052921963, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-97686c9a8ac9d62b3ca99064", "heatmap": "e-97686c9a8ac9d62b3ca99064", "modelKey": "event", "timestamp": 1643052911961 }, { "id": "35201f7b1d057bfde724d955", "type": "sensorMotion", "start": 1643052938897, "end": 1643052938897, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ebhix Yxrs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-35201f7b1d057bfde724d955", "heatmap": "e-35201f7b1d057bfde724d955", "modelKey": "event", "timestamp": null }, { "id": "79a7875bbe152d5723277fdf", "type": "motion", "start": 1643052939578, "end": 1643052960914, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-79a7875bbe152d5723277fdf", "heatmap": "e-79a7875bbe152d5723277fdf", "modelKey": "event", "timestamp": 1643052950912 }, { "id": "a5053650cf38fcc579aab665", "type": "motion", "start": 1643052945052, "end": 1643052967202, "score": 87, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a5053650cf38fcc579aab665", "heatmap": "e-a5053650cf38fcc579aab665", "modelKey": "event", "timestamp": 1643052956200 }, { "id": "08baf052cf4196976cf71132", "type": "motion", "start": 1643052947910, "end": 1643052969603, "score": 3, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-08baf052cf4196976cf71132", "heatmap": "e-08baf052cf4196976cf71132", "modelKey": "event", "timestamp": 1643052950912 }, { "id": "cd62e5d1ee4eb2d4510df9dd", "type": "motion", "start": 1643053070434, "end": 1643053092273, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cd62e5d1ee4eb2d4510df9dd", "heatmap": "e-cd62e5d1ee4eb2d4510df9dd", "modelKey": "event", "timestamp": 1643053082267 }, { "id": "f907cd94a88c081c752ffcad", "type": "motion", "start": 1643053082503, "end": 1643053106521, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-f907cd94a88c081c752ffcad", "heatmap": "e-f907cd94a88c081c752ffcad", "modelKey": "event", "timestamp": 1643053093669 }, { "id": "ff028ccf68dfabcf4b353ae6", "type": "motion", "start": 1643053086850, "end": 1643053108019, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ff028ccf68dfabcf4b353ae6", "heatmap": "e-ff028ccf68dfabcf4b353ae6", "modelKey": "event", "timestamp": 1643053098017 }, { "id": "920abbf0f89eb5ca91ff383e", "type": "motion", "start": 1643053087589, "end": 1643053109107, "score": 10, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-920abbf0f89eb5ca91ff383e", "heatmap": "e-920abbf0f89eb5ca91ff383e", "modelKey": "event", "timestamp": 1643052950912 }, { "id": "9b90fde17804ffe1cf79885e", "type": "motion", "start": 1643053276401, "end": 1643053298404, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9b90fde17804ffe1cf79885e", "heatmap": "e-9b90fde17804ffe1cf79885e", "modelKey": "event", "timestamp": 1643053287569 }, { "id": "1e1e8e1c973d920ee7843a3a", "type": "motion", "start": 1643053294807, "end": 1643053317333, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1e1e8e1c973d920ee7843a3a", "heatmap": "e-1e1e8e1c973d920ee7843a3a", "modelKey": "event", "timestamp": 1643053305975 }, { "id": "76b2b3cc8750a0a129d7819c", "type": "motion", "start": 1643053298450, "end": 1643053319969, "score": 8, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-76b2b3cc8750a0a129d7819c", "heatmap": "e-76b2b3cc8750a0a129d7819c", "modelKey": "event", "timestamp": 1643052950912 }, { "id": "6b798e2f404909399fd1fe9f", "type": "disconnect", "start": 1643053355358, "end": 1643053362720, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "4a333d993fe8e2e8472bc901", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-6b798e2f404909399fd1fe9f", "heatmap": "e-6b798e2f404909399fd1fe9f", "modelKey": "event", "timestamp": null }, { "id": "c1bf3ee329dad6aae2887831", "type": "disconnect", "start": 1643053355422, "end": 1643053363029, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "f0cd15b8bed9e38899286a8c", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-c1bf3ee329dad6aae2887831", "heatmap": "e-c1bf3ee329dad6aae2887831", "modelKey": "event", "timestamp": null }, { "id": "1a493b780595e6f80ebd1941", "type": "disconnect", "start": 1643053359378, "end": 1643053373345, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "ab3e27f2d55fad817dac7bb9", "partition": null, "user": null, "metadata": { "reason": "CONNECTION_LOSS" }, "thumbnail": "e-1a493b780595e6f80ebd1941", "heatmap": "e-1a493b780595e6f80ebd1941", "modelKey": "event", "timestamp": null }, { "id": "bc2b177ea45d42a0a38e847f", "type": "motion", "start": 1643053507966, "end": 1643053529139, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bc2b177ea45d42a0a38e847f", "heatmap": "e-bc2b177ea45d42a0a38e847f", "modelKey": "event", "timestamp": 1643053519133 }, { "id": "c5ab1bdee3240243d3f16cf4", "type": "motion", "start": 1643053553567, "end": 1643053575399, "score": 90, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c5ab1bdee3240243d3f16cf4", "heatmap": "e-c5ab1bdee3240243d3f16cf4", "modelKey": "event", "timestamp": 1643053565374 }, { "id": "ddd7747c18fa81be4cc632ed", "type": "motion", "start": 1643053563426, "end": 1643053584429, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ddd7747c18fa81be4cc632ed", "heatmap": "e-ddd7747c18fa81be4cc632ed", "modelKey": "event", "timestamp": 1643053565374 }, { "id": "7e36d50ee5c6404464339bf6", "type": "motion", "start": 1643053620678, "end": 1643053642181, "score": 84, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-7e36d50ee5c6404464339bf6", "heatmap": "e-7e36d50ee5c6404464339bf6", "modelKey": "event", "timestamp": 1643053632178 }, { "id": "8e4a7d35cc3f42b0451c6991", "type": "motion", "start": 1643053728845, "end": 1643053749845, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8e4a7d35cc3f42b0451c6991", "heatmap": "e-8e4a7d35cc3f42b0451c6991", "modelKey": "event", "timestamp": 1643053632178 }, { "id": "caec8a02af526ec31aa704a8", "type": "motion", "start": 1643053812976, "end": 1643053834310, "score": 87, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-caec8a02af526ec31aa704a8", "heatmap": "e-caec8a02af526ec31aa704a8", "modelKey": "event", "timestamp": 1643053824143 }, { "id": "079d552683bae554c5d8e6b6", "type": "motion", "start": 1643053879742, "end": 1643053903746, "score": 98, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-079d552683bae554c5d8e6b6", "heatmap": "e-079d552683bae554c5d8e6b6", "modelKey": "event", "timestamp": 1643053892577 }, { "id": "b465384309318dc68d41bb94", "type": "motion", "start": 1643053912880, "end": 1643053934559, "score": 92, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b465384309318dc68d41bb94", "heatmap": "e-b465384309318dc68d41bb94", "modelKey": "event", "timestamp": 1643053924223 }, { "id": "5958e375e9a7943e170bc7d6", "type": "motion", "start": 1643053917647, "end": 1643053940326, "score": 64, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5958e375e9a7943e170bc7d6", "heatmap": "e-5958e375e9a7943e170bc7d6", "modelKey": "event", "timestamp": 1643053929312 }, { "id": "9e7e01b904f1536af43633ab", "type": "motion", "start": 1643053961265, "end": 1643053989271, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9e7e01b904f1536af43633ab", "heatmap": "e-9e7e01b904f1536af43633ab", "modelKey": "event", "timestamp": 1643053975934 }, { "id": "3b46cac1f3afab7c7e5b7d85", "type": "motion", "start": 1643053962702, "end": 1643053985233, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-3b46cac1f3afab7c7e5b7d85", "heatmap": "e-3b46cac1f3afab7c7e5b7d85", "modelKey": "event", "timestamp": 1643053973870 }, { "id": "d7d3e1df973355a573cc5a88", "type": "motion", "start": 1643053965375, "end": 1643053989533, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d7d3e1df973355a573cc5a88", "heatmap": "e-d7d3e1df973355a573cc5a88", "modelKey": "event", "timestamp": 1643053976535 }, { "id": "81c40abf395a6d6008edd44c", "type": "motion", "start": 1643053993113, "end": 1643054014971, "score": 95, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-81c40abf395a6d6008edd44c", "heatmap": "e-81c40abf395a6d6008edd44c", "modelKey": "event", "timestamp": 1643054004301 }, { "id": "a06e149794fbe9a33cf8c3ed", "type": "motion", "start": 1643054293279, "end": 1643054316444, "score": 12, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a06e149794fbe9a33cf8c3ed", "heatmap": "e-a06e149794fbe9a33cf8c3ed", "modelKey": "event", "timestamp": 1643053976535 }, { "id": "bd080016644d6954508952b7", "type": "motion", "start": 1643054602210, "end": 1643054623210, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bd080016644d6954508952b7", "heatmap": "e-bd080016644d6954508952b7", "modelKey": "event", "timestamp": 1643054004301 }, { "id": "e32c7fd06ae4abf236f95ad0", "type": "motion", "start": 1643054612223, "end": 1643054633733, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e32c7fd06ae4abf236f95ad0", "heatmap": "e-e32c7fd06ae4abf236f95ad0", "modelKey": "event", "timestamp": 1643054623723 }, { "id": "5a604ea7861254a944bd63b3", "type": "sensorMotion", "start": 1643054627462, "end": 1643054627462, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qkxnsvp Lrotrn" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-5a604ea7861254a944bd63b3", "heatmap": "e-5a604ea7861254a944bd63b3", "modelKey": "event", "timestamp": null }, { "id": "0ce04347e042445c503e6f14", "type": "motion", "start": 1643054663150, "end": 1643054684320, "score": 91, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0ce04347e042445c503e6f14", "heatmap": "e-0ce04347e042445c503e6f14", "modelKey": "event", "timestamp": 1643054674317 }, { "id": "1bf0b115d5d7c0596601eb23", "type": "motion", "start": 1643054742302, "end": 1643054766147, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1bf0b115d5d7c0596601eb23", "heatmap": "e-1bf0b115d5d7c0596601eb23", "modelKey": "event", "timestamp": 1643054755639 }, { "id": "5088f5afd788114524a81887", "type": "motion", "start": 1643054849864, "end": 1643054870868, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-5088f5afd788114524a81887", "heatmap": "e-5088f5afd788114524a81887", "modelKey": "event", "timestamp": 1643054755639 }, { "id": "a6e02c235d3d47486f583618", "type": "motion", "start": 1643054851265, "end": 1643054880434, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a6e02c235d3d47486f583618", "heatmap": "e-a6e02c235d3d47486f583618", "modelKey": "event", "timestamp": 1643054865768 }, { "id": "141f87a3241c063a599f488e", "type": "sensorMotion", "start": 1643054863294, "end": 1643054863294, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Csa Jeamad" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-141f87a3241c063a599f488e", "heatmap": "e-141f87a3241c063a599f488e", "modelKey": "event", "timestamp": null }, { "id": "eca22cd738a7e97c8aeae8d1", "type": "sensorMotion", "start": 1643054867543, "end": 1643054867543, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fquw Disnd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-eca22cd738a7e97c8aeae8d1", "heatmap": "e-eca22cd738a7e97c8aeae8d1", "modelKey": "event", "timestamp": null }, { "id": "39a68c8c8c57dbef9560fcd2", "type": "motion", "start": 1643054873017, "end": 1643054896194, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-39a68c8c8c57dbef9560fcd2", "heatmap": "e-39a68c8c8c57dbef9560fcd2", "modelKey": "event", "timestamp": 1643054884185 }, { "id": "b4670d0a0400e717aa7b41ef", "type": "sensorMotion", "start": 1643054881963, "end": 1643054881963, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jwmrk Idi" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b4670d0a0400e717aa7b41ef", "heatmap": "e-b4670d0a0400e717aa7b41ef", "modelKey": "event", "timestamp": null }, { "id": "6afa39e93c7675715187d3a5", "type": "motion", "start": 1643055002332, "end": 1643055023998, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6afa39e93c7675715187d3a5", "heatmap": "e-6afa39e93c7675715187d3a5", "modelKey": "event", "timestamp": 1643054755639 }, { "id": "436a7bfb6de6a84e75878125", "type": "motion", "start": 1643055051296, "end": 1643055072965, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-436a7bfb6de6a84e75878125", "heatmap": "e-436a7bfb6de6a84e75878125", "modelKey": "event", "timestamp": 1643054755639 }, { "id": "299b8fc031e1f206e8b9c6c8", "type": "motion", "start": 1643055066163, "end": 1643055088511, "score": 78, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-299b8fc031e1f206e8b9c6c8", "heatmap": "e-299b8fc031e1f206e8b9c6c8", "modelKey": "event", "timestamp": 1643055077329 }, { "id": "aecb3f1eb4bb4e9b9dfa9500", "type": "motion", "start": 1643055069365, "end": 1643055091363, "score": 88, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-aecb3f1eb4bb4e9b9dfa9500", "heatmap": "e-aecb3f1eb4bb4e9b9dfa9500", "modelKey": "event", "timestamp": 1643055080532 }, { "id": "988b9f933bb8b0f88613cf78", "type": "access", "start": 1643055115307, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-988b9f933bb8b0f88613cf78", "heatmap": "e-988b9f933bb8b0f88613cf78", "modelKey": "event", "timestamp": null }, { "id": "b094781c1e975be1afaf4f9b", "type": "motion", "start": 1643055132162, "end": 1643055154664, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b094781c1e975be1afaf4f9b", "heatmap": "e-b094781c1e975be1afaf4f9b", "modelKey": "event", "timestamp": 1643055143329 }, { "id": "ca411e12910e5a207b9d25bd", "type": "motion", "start": 1643055205892, "end": 1643055235060, "score": 65, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ca411e12910e5a207b9d25bd", "heatmap": "e-ca411e12910e5a207b9d25bd", "modelKey": "event", "timestamp": 1643055218725 }, { "id": "2904f432b76761dc659024eb", "type": "sensorMotion", "start": 1643055213104, "end": 1643055213104, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Yab Koad" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-2904f432b76761dc659024eb", "heatmap": "e-2904f432b76761dc659024eb", "modelKey": "event", "timestamp": null }, { "id": "d9499661def27e2e22235a4c", "type": "sensorMotion", "start": 1643055218511, "end": 1643055218511, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mbtl Aozuk" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d9499661def27e2e22235a4c", "heatmap": "e-d9499661def27e2e22235a4c", "modelKey": "event", "timestamp": null }, { "id": "525146b492fcec280faba9f5", "type": "sensorMotion", "start": 1643055223661, "end": 1643055223661, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Mkf Zhuamg" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-525146b492fcec280faba9f5", "heatmap": "e-525146b492fcec280faba9f5", "modelKey": "event", "timestamp": null }, { "id": "96cf40d76e90d0b90b7d5b54", "type": "motion", "start": 1643055233458, "end": 1643055281958, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-96cf40d76e90d0b90b7d5b54", "heatmap": "e-96cf40d76e90d0b90b7d5b54", "modelKey": "event", "timestamp": 1643055253291 }, { "id": "47ca7df750f2db3f6411e8c2", "type": "sensorMotion", "start": 1643055244133, "end": 1643055244133, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Idi Vozqal" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-47ca7df750f2db3f6411e8c2", "heatmap": "e-47ca7df750f2db3f6411e8c2", "modelKey": "event", "timestamp": null }, { "id": "b2d82ccf0091e431544abb2e", "type": "sensorMotion", "start": 1643055249169, "end": 1643055249169, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qxecmf Gmgy" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-b2d82ccf0091e431544abb2e", "heatmap": "e-b2d82ccf0091e431544abb2e", "modelKey": "event", "timestamp": null }, { "id": "64e1016e325ad497ad734ec7", "type": "sensorMotion", "start": 1643055254304, "end": 1643055254304, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qtvm Jnkt" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-64e1016e325ad497ad734ec7", "heatmap": "e-64e1016e325ad497ad734ec7", "modelKey": "event", "timestamp": null }, { "id": "8b9bc5f044c4fa356aa7f36b", "type": "sensorMotion", "start": 1643055259197, "end": 1643055259197, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Cph Zyje" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8b9bc5f044c4fa356aa7f36b", "heatmap": "e-8b9bc5f044c4fa356aa7f36b", "modelKey": "event", "timestamp": null }, { "id": "7d8c66b17e274d199ab2a9b3", "type": "sensorMotion", "start": 1643055266793, "end": 1643055266793, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qjemjj Kvej" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-7d8c66b17e274d199ab2a9b3", "heatmap": "e-7d8c66b17e274d199ab2a9b3", "modelKey": "event", "timestamp": null }, { "id": "607e9ae058d9ca04999216c6", "type": "sensorMotion", "start": 1643055280200, "end": 1643055280200, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hxgrpa Dsddgw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-607e9ae058d9ca04999216c6", "heatmap": "e-607e9ae058d9ca04999216c6", "modelKey": "event", "timestamp": null }, { "id": "e97bb0b257b30babeb8ad025", "type": "sensorMotion", "start": 1643055308124, "end": 1643055308124, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Iwizxxk Oqzyd" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e97bb0b257b30babeb8ad025", "heatmap": "e-e97bb0b257b30babeb8ad025", "modelKey": "event", "timestamp": null }, { "id": "f6bb8676252bae9c818e3356", "type": "sensorMotion", "start": 1643055318168, "end": 1643055318168, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Scmjfji Plovq" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-f6bb8676252bae9c818e3356", "heatmap": "e-f6bb8676252bae9c818e3356", "modelKey": "event", "timestamp": null }, { "id": "dfc2f5934ce355ed2bce67c8", "type": "sensorMotion", "start": 1643055329768, "end": 1643055329768, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Bnqey Bffydzc" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-dfc2f5934ce355ed2bce67c8", "heatmap": "e-dfc2f5934ce355ed2bce67c8", "modelKey": "event", "timestamp": null }, { "id": "bb64b41a56f3576d0c76bbca", "type": "motion", "start": 1643055332076, "end": 1643055362906, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-bb64b41a56f3576d0c76bbca", "heatmap": "e-bb64b41a56f3576d0c76bbca", "modelKey": "event", "timestamp": 1643055351573 }, { "id": "f1e6540f70c74dc2b38ae8cc", "type": "sensorOpened", "start": 1643055342130, "end": 1643055342130, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Yopekmp Wmr" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-f1e6540f70c74dc2b38ae8cc", "heatmap": "e-f1e6540f70c74dc2b38ae8cc", "modelKey": "event", "timestamp": null }, { "id": "1a0ff6b39154c4630db1de1f", "type": "sensorMotion", "start": 1643055343144, "end": 1643055343144, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ldzva Mbbagj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1a0ff6b39154c4630db1de1f", "heatmap": "e-1a0ff6b39154c4630db1de1f", "modelKey": "event", "timestamp": null }, { "id": "ed344a69c0e14cc6b186470d", "type": "motion", "start": 1643055351675, "end": 1643055377338, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ed344a69c0e14cc6b186470d", "heatmap": "e-ed344a69c0e14cc6b186470d", "modelKey": "event", "timestamp": 1643055363008 }, { "id": "4e8aac11f406542b105cfa54", "type": "motion", "start": 1643055384498, "end": 1643055405831, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-4e8aac11f406542b105cfa54", "heatmap": "e-4e8aac11f406542b105cfa54", "modelKey": "event", "timestamp": 1643055395664 }, { "id": "6883f7f7381645ea50d62db8", "type": "sensorMotion", "start": 1643055390397, "end": 1643055390397, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nbfnis Lrnwhf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6883f7f7381645ea50d62db8", "heatmap": "e-6883f7f7381645ea50d62db8", "modelKey": "event", "timestamp": null }, { "id": "af1beccd369bbcdf85e5ce09", "type": "sensorClosed", "start": 1643055394532, "end": 1643055394532, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Lfy Zemzosr" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-af1beccd369bbcdf85e5ce09", "heatmap": "e-af1beccd369bbcdf85e5ce09", "modelKey": "event", "timestamp": null }, { "id": "155d410caaa8f4a8354b3c7a", "type": "sensorMotion", "start": 1643055395291, "end": 1643055395291, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Gfixk Blj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-155d410caaa8f4a8354b3c7a", "heatmap": "e-155d410caaa8f4a8354b3c7a", "modelKey": "event", "timestamp": null }, { "id": "c409d74eaa81ee320a5d6d7b", "type": "sensorMotion", "start": 1643055399296, "end": 1643055399296, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Amdwoc Ygw" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-c409d74eaa81ee320a5d6d7b", "heatmap": "e-c409d74eaa81ee320a5d6d7b", "modelKey": "event", "timestamp": null }, { "id": "d9728ec0394b2c362bd8d82d", "type": "motion", "start": 1643055422875, "end": 1643055445206, "score": 74, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-d9728ec0394b2c362bd8d82d", "heatmap": "e-d9728ec0394b2c362bd8d82d", "modelKey": "event", "timestamp": 1643055434042 }, { "id": "fd027ebb11edf1a5d4e02917", "type": "motion", "start": 1643055425997, "end": 1643055447656, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "c462c07dbd63ad805a7318c7", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-fd027ebb11edf1a5d4e02917", "heatmap": "e-fd027ebb11edf1a5d4e02917", "modelKey": "event", "timestamp": 1643055437156 }, { "id": "ebe0880538a65ae729465964", "type": "motion", "start": 1643055428814, "end": 1643055481335, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "cae56878dda06c28448931ef" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-ebe0880538a65ae729465964", "heatmap": "e-ebe0880538a65ae729465964", "modelKey": "event", "timestamp": 1643055471170 }, { "id": "c2257bbb62739a5aede83843", "type": "motion", "start": 1643055430117, "end": 1643055463780, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c2257bbb62739a5aede83843", "heatmap": "e-c2257bbb62739a5aede83843", "modelKey": "event", "timestamp": 1643055441623 }, { "id": "83678f9d98618da78f49c14a", "type": "motion", "start": 1643055433452, "end": 1643055473416, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "1f7ba6f81d4e5b3bce4f92ee" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-83678f9d98618da78f49c14a", "heatmap": "e-83678f9d98618da78f49c14a", "modelKey": "event", "timestamp": 1643055452223 }, { "id": "d8da1fbc5f116d25d25474dc", "type": "sensorMotion", "start": 1643055435332, "end": 1643055435332, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Tnotfga Qcdcvo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-d8da1fbc5f116d25d25474dc", "heatmap": "e-d8da1fbc5f116d25d25474dc", "modelKey": "event", "timestamp": null }, { "id": "1f7ba6f81d4e5b3bce4f92ee", "type": "smartDetectZone", "start": 1643055436123, "end": 1643055472745, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-1f7ba6f81d4e5b3bce4f92ee", "heatmap": "e-1f7ba6f81d4e5b3bce4f92ee", "modelKey": "event", "timestamp": 1643055452223 }, { "id": "18316f8b1857cba3bb51f24d", "type": "smartDetectZone", "start": 1643055436342, "end": 1643055469329, "score": 85, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-18316f8b1857cba3bb51f24d", "heatmap": "e-18316f8b1857cba3bb51f24d", "modelKey": "event", "timestamp": 1643055456706 }, { "id": "6a5d5803f2fc20b5c63b3b12", "type": "motion", "start": 1643055439053, "end": 1643055469070, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "18316f8b1857cba3bb51f24d" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6a5d5803f2fc20b5c63b3b12", "heatmap": "e-6a5d5803f2fc20b5c63b3b12", "modelKey": "event", "timestamp": 1643055451891 }, { "id": "2ca51eabafc9285919d3dbfd", "type": "sensorOpened", "start": 1643055439709, "end": 1643055439709, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Fwaa Xquqcs" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-2ca51eabafc9285919d3dbfd", "heatmap": "e-2ca51eabafc9285919d3dbfd", "modelKey": "event", "timestamp": null }, { "id": "e33bbe7801d3fb62b2a5ce87", "type": "sensorMotion", "start": 1643055439725, "end": 1643055439725, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Rrlhrj Mbe" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-e33bbe7801d3fb62b2a5ce87", "heatmap": "e-e33bbe7801d3fb62b2a5ce87", "modelKey": "event", "timestamp": null }, { "id": "1830c4fcfc98cb8679a7f5a7", "type": "sensorMotion", "start": 1643055439875, "end": 1643055439875, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Rochjgn Dbojwpo" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1830c4fcfc98cb8679a7f5a7", "heatmap": "e-1830c4fcfc98cb8679a7f5a7", "modelKey": "event", "timestamp": null }, { "id": "24032a6cd303b586ee2cdcf7", "type": "sensorOpened", "start": 1643055439992, "end": 1643055439992, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pos Kdweeq" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-24032a6cd303b586ee2cdcf7", "heatmap": "e-24032a6cd303b586ee2cdcf7", "modelKey": "event", "timestamp": null }, { "id": "69334ef9ad4c5d10e3c7f6a4", "type": "doorlockOpened", "start": 1643055440126, "end": 1643055440126, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-69334ef9ad4c5d10e3c7f6a4", "heatmap": "e-69334ef9ad4c5d10e3c7f6a4", "modelKey": "event", "timestamp": null }, { "id": "cae56878dda06c28448931ef", "type": "smartDetectZone", "start": 1643055443840, "end": 1643055499503, "score": 95, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-cae56878dda06c28448931ef", "heatmap": "e-cae56878dda06c28448931ef", "modelKey": "event", "timestamp": 1643055455296 }, { "id": "b4db9f07c10c6b10345f9e7f", "type": "ring", "start": 1643055447318, "end": 1643055448318, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b4db9f07c10c6b10345f9e7f", "heatmap": "e-b4db9f07c10c6b10345f9e7f", "modelKey": "event", "timestamp": null }, { "id": "45240b20222327acefef906d", "type": "sensorOpened", "start": 1643055448119, "end": 1643055448119, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Bsa Ickshd" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "thumbnail": "e-45240b20222327acefef906d", "heatmap": "e-45240b20222327acefef906d", "modelKey": "event", "timestamp": null }, { "id": "e6f18a416896510c4f6bba81", "type": "sensorClosed", "start": 1643055451596, "end": 1643055451596, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Skjw Yurgmz" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "thumbnail": "e-e6f18a416896510c4f6bba81", "heatmap": "e-e6f18a416896510c4f6bba81", "modelKey": "event", "timestamp": null }, { "id": "ca4420000dd74bb22e256436", "type": "sensorMotion", "start": 1643055459060, "end": 1643055459060, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cfzngh Rqqra" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-ca4420000dd74bb22e256436", "heatmap": "e-ca4420000dd74bb22e256436", "modelKey": "event", "timestamp": null }, { "id": "0f55d95abc0a3d18c0851204", "type": "lightMotion", "start": 1643055459357, "end": 1643055459357, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-0f55d95abc0a3d18c0851204", "heatmap": "e-0f55d95abc0a3d18c0851204", "modelKey": "event", "timestamp": null }, { "id": "6ca308338938954b596dec93", "type": "sensorMotion", "start": 1643055462604, "end": 1643055462604, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Soktlm Slxb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-6ca308338938954b596dec93", "heatmap": "e-6ca308338938954b596dec93", "modelKey": "event", "timestamp": null }, { "id": "179a89d5508b1da7884c0e06", "type": "doorlockClosed", "start": 1643055464144, "end": 1643055464144, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-179a89d5508b1da7884c0e06", "heatmap": "e-179a89d5508b1da7884c0e06", "modelKey": "event", "timestamp": null }, { "id": "468a5ac35556ad5f4e81cdda", "type": "motion", "start": 1643055472336, "end": 1643055499336, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "cae56878dda06c28448931ef" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-468a5ac35556ad5f4e81cdda", "heatmap": "e-468a5ac35556ad5f4e81cdda", "modelKey": "event", "timestamp": 1643055483669 }, { "id": "b1f6828f0778ee11793241f9", "type": "smartDetectZone", "start": 1643055475782, "end": 1643055507694, "score": 83, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b1f6828f0778ee11793241f9", "heatmap": "e-b1f6828f0778ee11793241f9", "modelKey": "event", "timestamp": 1643055497338 }, { "id": "6b38b014d17c6ef8bcc68fad", "type": "motion", "start": 1643055475795, "end": 1643055509171, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "b1f6828f0778ee11793241f9" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-6b38b014d17c6ef8bcc68fad", "heatmap": "e-6b38b014d17c6ef8bcc68fad", "modelKey": "event", "timestamp": 1643055491479 }, { "id": "e46822b9b3d12873255725a5", "type": "smartDetectZone", "start": 1643055476431, "end": 1643055508456, "score": 90, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e46822b9b3d12873255725a5", "heatmap": "e-e46822b9b3d12873255725a5", "modelKey": "event", "timestamp": 1643055493110 }, { "id": "c7f20c2b5645a5847a79d0fa", "type": "motion", "start": 1643055478772, "end": 1643055513295, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [ "e46822b9b3d12873255725a5" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-c7f20c2b5645a5847a79d0fa", "heatmap": "e-c7f20c2b5645a5847a79d0fa", "modelKey": "event", "timestamp": 1643055493110 }, { "id": "ba06e097e51a5ce729aa16e3", "type": "lightMotion", "start": 1643055484857, "end": 1643055484857, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-ba06e097e51a5ce729aa16e3", "heatmap": "e-ba06e097e51a5ce729aa16e3", "modelKey": "event", "timestamp": null }, { "id": "3192d319f3d19bf3d4404d23", "type": "sensorMotion", "start": 1643055486355, "end": 1643055486355, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Myo Seggb" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-3192d319f3d19bf3d4404d23", "heatmap": "e-3192d319f3d19bf3d4404d23", "modelKey": "event", "timestamp": null }, { "id": "0002da2371731c382439c36b", "type": "motion", "start": 1643055500866, "end": 1643055533866, "score": 76, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-0002da2371731c382439c36b", "heatmap": "e-0002da2371731c382439c36b", "modelKey": "event", "timestamp": 1643055518868 }, { "id": "be77a38fa3173618810fd085", "type": "motion", "start": 1643055506494, "end": 1643055537856, "score": 89, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-be77a38fa3173618810fd085", "heatmap": "e-be77a38fa3173618810fd085", "modelKey": "event", "timestamp": 1643055519494 }, { "id": "e613ac4cf59f8633a305ab59", "type": "motion", "start": 1643055520862, "end": 1643055542532, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e613ac4cf59f8633a305ab59", "heatmap": "e-e613ac4cf59f8633a305ab59", "modelKey": "event", "timestamp": 1643055532528 }, { "id": "91af7e8b1224684cad660d91", "type": "motion", "start": 1643055524709, "end": 1643055545876, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-91af7e8b1224684cad660d91", "heatmap": "e-91af7e8b1224684cad660d91", "modelKey": "event", "timestamp": 1643055519494 }, { "id": "a9ebb0891064af1c95a2df66", "type": "motion", "start": 1643055538147, "end": 1643055575971, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-a9ebb0891064af1c95a2df66", "heatmap": "e-a9ebb0891064af1c95a2df66", "modelKey": "event", "timestamp": 1643055556144 }, { "id": "1ba92829cd9f0f3ffe82d00a", "type": "sensorMotion", "start": 1643055545288, "end": 1643055545288, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Tjpdmqf Ucjs" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-1ba92829cd9f0f3ffe82d00a", "heatmap": "e-1ba92829cd9f0f3ffe82d00a", "modelKey": "event", "timestamp": null }, { "id": "22e772869c106dcb512c7226", "type": "sensorClosed", "start": 1643055549536, "end": 1643055549536, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Jrddx Urr" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-22e772869c106dcb512c7226", "heatmap": "e-22e772869c106dcb512c7226", "modelKey": "event", "timestamp": null }, { "id": "90f051ebf085214d331644a5", "type": "motion", "start": 1643055550167, "end": 1643055573501, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-90f051ebf085214d331644a5", "heatmap": "e-90f051ebf085214d331644a5", "modelKey": "event", "timestamp": 1643055561334 }, { "id": "bd5164750940a0864e08f87f", "type": "sensorMotion", "start": 1643055550567, "end": 1643055550567, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Twh Pvdivz" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-bd5164750940a0864e08f87f", "heatmap": "e-bd5164750940a0864e08f87f", "modelKey": "event", "timestamp": null }, { "id": "9299f143c03dd92af383bae6", "type": "sensorClosed", "start": 1643055553059, "end": 1643055553059, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ftc Trrp" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-9299f143c03dd92af383bae6", "heatmap": "e-9299f143c03dd92af383bae6", "modelKey": "event", "timestamp": null }, { "id": "25fe763b2b6b1d031e0eff17", "type": "sensorMotion", "start": 1643055555239, "end": 1643055555239, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Arq Xul" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-25fe763b2b6b1d031e0eff17", "heatmap": "e-25fe763b2b6b1d031e0eff17", "modelKey": "event", "timestamp": null }, { "id": "8d9d6bcd2deca4bae4a324b6", "type": "sensorMotion", "start": 1643055556505, "end": 1643055556505, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Hczjj Ahjbwgf" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-8d9d6bcd2deca4bae4a324b6", "heatmap": "e-8d9d6bcd2deca4bae4a324b6", "modelKey": "event", "timestamp": null }, { "id": "16670a85458b8d00d0a1af9c", "type": "access", "start": 1643055662539, "end": 1643055664550, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-16670a85458b8d00d0a1af9c", "heatmap": "e-16670a85458b8d00d0a1af9c", "modelKey": "event", "timestamp": null }, { "id": "265f46078ef81ee2798b01ff", "type": "access", "start": 1643055685331, "end": 1643055724820, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-265f46078ef81ee2798b01ff", "heatmap": "e-265f46078ef81ee2798b01ff", "modelKey": "event", "timestamp": null }, { "id": "d9bf979106283fe52998681c", "type": "access", "start": 1643055740252, "end": 1643055747997, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-d9bf979106283fe52998681c", "heatmap": "e-d9bf979106283fe52998681c", "modelKey": "event", "timestamp": null }, { "id": "8d9bae8359268f6048d92ed3", "type": "motion", "start": 1643055781132, "end": null, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8d9bae8359268f6048d92ed3", "heatmap": "e-8d9bae8359268f6048d92ed3", "modelKey": "event", "timestamp": 1643055793162 }, { "id": "e9114260a65e566d9667dfbb", "type": "motion", "start": 1643055782161, "end": null, "score": 100, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-e9114260a65e566d9667dfbb", "heatmap": "e-e9114260a65e566d9667dfbb", "modelKey": "event", "timestamp": 1643055798154 }, { "id": "00368a5908cad9e2afdcb583", "type": "access", "start": 1643055787954, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": "4c5f03a8c8bd48ad8e066285", "metadata": { "clientPlatform": "web" }, "thumbnail": "e-00368a5908cad9e2afdcb583", "heatmap": "e-00368a5908cad9e2afdcb583", "modelKey": "event", "timestamp": null }, { "id": "9a5112fdedbab7188edaaf8b", "type": "motion", "start": 1643055788720, "end": null, "score": 94, "smartDetectTypes": [], "smartDetectEvents": [ "b19e92fb1f4244a78e493757" ], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9a5112fdedbab7188edaaf8b", "heatmap": "e-9a5112fdedbab7188edaaf8b", "modelKey": "event", "timestamp": null }, { "id": "b19e92fb1f4244a78e493757", "type": "smartDetectZone", "start": 1643055790555, "end": null, "score": 52, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-b19e92fb1f4244a78e493757", "heatmap": "e-b19e92fb1f4244a78e493757", "modelKey": "event", "timestamp": null }, { "id": "9376e572cf89f17ef33a3fce", "type": "smartDetectZone", "start": 1643055792010, "end": null, "score": 78, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-9376e572cf89f17ef33a3fce", "heatmap": "e-9376e572cf89f17ef33a3fce", "modelKey": "event", "timestamp": null }, { "id": "27f23bc2fe5a0aa180efcc8b", "type": "sensorMotion", "start": 1643055793140, "end": 1643055793140, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ftfqoe Ccmeel" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-27f23bc2fe5a0aa180efcc8b", "heatmap": "e-27f23bc2fe5a0aa180efcc8b", "modelKey": "event", "timestamp": null }, { "id": "19c87e73d92c5e0dc83f9028", "type": "motion", "start": 1643055794261, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [ "9376e572cf89f17ef33a3fce" ], "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-19c87e73d92c5e0dc83f9028", "heatmap": "e-19c87e73d92c5e0dc83f9028", "modelKey": "event", "timestamp": null }, { "id": "576a8593de0b9d286d53d08a", "type": "sensorMotion", "start": 1643055794595, "end": 1643055794595, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Pdw Lbj" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-576a8593de0b9d286d53d08a", "heatmap": "e-576a8593de0b9d286d53d08a", "modelKey": "event", "timestamp": null }, { "id": "12a435bf95fb283c0df61f58", "type": "sensorOpened", "start": 1643055794712, "end": 1643055794712, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Upv Udusuv" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "thumbnail": "e-12a435bf95fb283c0df61f58", "heatmap": "e-12a435bf95fb283c0df61f58", "modelKey": "event", "timestamp": null }, { "id": "203d7d9c67917b453e6b51bb", "type": "sensorOpened", "start": 1643055798932, "end": 1643055798932, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Xccje Kfhkq" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "thumbnail": "e-203d7d9c67917b453e6b51bb", "heatmap": "e-203d7d9c67917b453e6b51bb", "modelKey": "event", "timestamp": null }, { "id": "8db016edca05e2632f3778ea", "type": "smartDetectZone", "start": 1643055799985, "end": null, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-8db016edca05e2632f3778ea", "heatmap": "e-8db016edca05e2632f3778ea", "modelKey": "event", "timestamp": null }, { "id": "a4002f8e0e0860ea3c7bc43a", "type": "sensorMotion", "start": 1643055800106, "end": 1643055800106, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Lkvz Gjmlce" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-a4002f8e0e0860ea3c7bc43a", "heatmap": "e-a4002f8e0e0860ea3c7bc43a", "modelKey": "event", "timestamp": null }, { "id": "dcaafd507e8a37badffb97de", "type": "motion", "start": 1643055801481, "end": null, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [ "8db016edca05e2632f3778ea" ], "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-dcaafd507e8a37badffb97de", "heatmap": "e-dcaafd507e8a37badffb97de", "modelKey": "event", "timestamp": null }, { "id": "0df472afadf13ef46fb8b606", "type": "doorlockOpened", "start": 1643055802893, "end": 1643055802893, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "thumbnail": "e-0df472afadf13ef46fb8b606", "heatmap": "e-0df472afadf13ef46fb8b606", "modelKey": "event", "timestamp": null }, { "id": "433e706f29ca680d4101d5ce", "type": "ring", "start": 1643055803577, "end": 1643055804577, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "metadata": {}, "thumbnail": "e-433e706f29ca680d4101d5ce", "heatmap": "e-433e706f29ca680d4101d5ce", "modelKey": "event", "timestamp": null }, { "id": "628609548952eee457c94647", "type": "sensorOpened", "start": 1643055805813, "end": 1643055805813, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Bbpqm Ezl" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "thumbnail": "e-628609548952eee457c94647", "heatmap": "e-628609548952eee457c94647", "modelKey": "event", "timestamp": null }, { "id": "1866ac25e630d3c37d0b57b2", "type": "sensorClosed", "start": 1643055811080, "end": 1643055811080, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Ijylcci Gnueel" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "thumbnail": "e-1866ac25e630d3c37d0b57b2", "heatmap": "e-1866ac25e630d3c37d0b57b2", "modelKey": "event", "timestamp": null }, { "id": "4247f5ad2e1aa1327ecff28d", "type": "sensorMotion", "start": 1643055813651, "end": 1643055813651, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ylct Wvmvx" }, "type": { "text": "UFP-SENSE" } }, "thumbnail": "e-4247f5ad2e1aa1327ecff28d", "heatmap": "e-4247f5ad2e1aa1327ecff28d", "modelKey": "event", "timestamp": null }, { "id": "df90543f94ff894427b79827", "type": "lightMotion", "start": 1643055813957, "end": 1643055813957, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": null, "partition": null, "user": null, "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "thumbnail": "e-df90543f94ff894427b79827", "heatmap": "e-df90543f94ff894427b79827", "modelKey": "event", "timestamp": null } ] uiprotect-6.1.0/tests/sample_data/sample_sensor.json000066400000000000000000000043331467310220200227150ustar00rootroot00000000000000{ "mac": "4191A8E35F39", "host": null, "connectionHost": "192.168.102.63", "type": "UFP-SENSE", "name": "Vyiu Ccxeqw", "upSince": 1642991171327, "uptime": null, "lastSeen": 1643054753862, "connectedSince": 1643054778327, "state": "CONNECTED", "hardwareRevision": 6, "firmwareVersion": "1.0.2", "latestFirmwareVersion": "1.0.2", "firmwareBuild": null, "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "isMotionDetected": false, "mountType": "door", "leakDetectedAt": null, "tamperingDetectedAt": null, "isOpened": true, "openStatusChangedAt": 1643055798932, "alarmTriggeredAt": null, "motionDetectedAt": 1643055801008, "wiredConnectionState": { "phyRate": null }, "stats": { "light": { "value": 20, "status": "neutral" }, "humidity": { "value": 29, "status": "neutral" }, "temperature": { "value": 16.95, "status": "neutral" } }, "bluetoothConnectionState": { "signalQuality": 45, "signalStrength": -72 }, "batteryStatus": { "percentage": 80, "isLow": false }, "alarmSettings": { "isEnabled": false }, "lightSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 10 }, "motionSettings": { "isEnabled": true, "sensitivity": 100 }, "temperatureSettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 0.1 }, "humiditySettings": { "isEnabled": true, "lowThreshold": null, "highThreshold": null, "margin": 1 }, "ledSettings": { "isEnabled": true }, "bridge": "61b3f5c90054a703e700042b", "camera": null, "bridgeCandidates": [], "id": "02ee9b99f17d69346e0c8c00", "isConnected": true, "marketName": "UP Sense", "modelKey": "sensor" } uiprotect-6.1.0/tests/sample_data/sample_viewport.json000066400000000000000000000017161467310220200232650ustar00rootroot00000000000000{ "mac": "85EEA7DF1601", "host": "192.168.27.69", "connectionHost": "192.168.102.63", "type": "UP Viewport", "name": "Uknkgc Jpyuso", "upSince": 1642661973905, "uptime": 393786, "lastSeen": 1643055759905, "connectedSince": 1642672730441, "state": "CONNECTED", "hardwareRevision": null, "firmwareVersion": "1.2.54", "latestFirmwareVersion": "1.2.54", "firmwareBuild": "dcfb16f3.210907.625", "isUpdating": false, "isAdopting": false, "isAdopted": true, "isAdoptedByOther": false, "isProvisioned": false, "isRebooting": false, "isSshEnabled": false, "canAdopt": false, "isAttemptingToConnect": false, "streamLimit": 16, "softwareVersion": "1.2.54", "wiredConnectionState": { "phyRate": 1000 }, "liveview": "bf41f6b5ba0ddd046eeb1c98", "id": "081c58d13ad7e198d3dddffa", "isConnected": true, "marketName": "UP ViewPort", "modelKey": "viewer" } uiprotect-6.1.0/tests/sample_data/sample_ws_messages.json000066400000000000000000004701701467310220200237320ustar00rootroot00000000000000{ "2.7892892999807373": { "raw": "AQEBAAAAAHR4nB2MWwqDMBBFtyLz3YAmkzDpDorfLmCSGUFotfigFHHvknyee7jnBM77tMzwbOD4Cu8KjwZm/Q0VXlJEsIqx88mQxWDQ52jIiRgZO6eOOSKncvssou9e/zW26Vq2qRYw+7F1TJmSILGQtiFY8nDdlRMkmAIBAQAAAAEkeJyFkc1uwjAQhF8FRT2hHOz4Jz8P0HMv7aXisLbXwcJJkB1AqOq7NwSoCm3xxbJG34x3xx9ZG4bdNmbN4j2TVDErNCe0FlAShiUh06mzVb7IwPsXDJ2L0Q39mfduj3uHh2bZGCmU4lRTbiRAXSgLHKSh2eT8gfFaKSUIEbzgQAwTVBSypHCHqaLSrLK0KIiVijPLakaMtfdYXU0jC8agKhEkq6dHDWfVCev3oQkIplnemPSkjXiSdhHPRH4IbsTcoMcRm6er+UadQ+amLgn5b+fyO/UxEvUazc5jAvPYgj6+Pr/9MeQMqOBMm0rR0GGAf6FZ6dA4uAjz/dpZux5Tu2Afh9TCZhiCH/QmgZ3+J1mecXHr4Zjaeu26h82sPr8AeQX7wQ==", "action": { "action": "update", "newUpdateId": "62e4915b-8246-45c9-83dd-df13e3aa94ab", "modelKey": "user", "id": "4c5f03a8c8bd48ad8e066285" }, "data": { "groups": [ "61b3f5c40195a703e70003ec" ], "allPermissions": [ "liveview:*:d65bb41c14d6aa92bfa4a6d1", "liveview:*:49bbb5005424a0d35152671a", "liveview:*:b28c38f1220f6b43f3930dff", "liveview:*:b9861b533a87ea639fa4d438", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "2.79086060001282": { "raw": "AQEBAAAAAHR4nB2MWwrCQAxFt1Ly7UDTebXuQPx2AckkgYK2ohUR6d7LzOe5h3v+QGWb1wXOHXyeQpvCqYNFv7cGF6nCRI0xJJdKRhdiZDcReVfYevQYUYah3h6r6P2qvxZ766tucysQWwqZVHyKPeURizKhN9gPtb8lMQIBAQAAAACneJxVjkEKgzAURK8ioaviIsW2Sk7RfXERk6l+mhhJolJK714lUNLNwJ95n5k3672bp8BEcWfXU1c9LurMq6aRNa9Qc76pZm1ZMGnMDd5SCOTGxI+LFx5SiyPbAEMLFsIq1OZF7NYckIhy9RRRahhEiMOedZ50j+xfSQsvE76LhSaZIu2cN049/9r6IWZ3wBhcPmefgtzQFCYjX3njQPa3oP18AYpfVU8=", "action": { "action": "update", "newUpdateId": "fdefb146-6c71-455b-9aa3-cbf013151d22", "modelKey": "user", "id": "abf647aed3650a781ceba13f" }, "data": { "groups": [ "61b3f5c40388a703e70003ed" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ] } }, "2.792094599979464": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbWBqkmnjDaRrDzCTmUBBW7GVIuLdJVm+//jvC5z3eV3g0sH7qbwbnDpY7Lg1uGoVUQfyUrITHXsXPKJLUkaHKL1y4cSa6+2xqt0n+7TYZq+6za3Aajl6H4RSiVSIKYchnhF+f82IJUMCAQEAAAABBnicpVKxbsMgFPyVCHWyPDhy06R8QOcu7VJlIHCxUbGJwHYUVf33GoMSoiR46ILE3b1773H8kMro/mAJXXyRl+Wu3K/4c1FuNmxdlFgXxXgKki8u3PJ1FXGcbEeSKfUO00hrpW69VzsYasAEzVy1kgMGiSPlI9bBQb2FV+RHIzvkAgod6JPjdkaKClE9Zw0M83J3NBCSeUpobZTm31fdqrqL7hat1fE4bhTEgJD2oNgp7ljLJp7gX/u44it0MplePjjkt5XZ2TUtsbyG6BVmZAoV46ePt887Q2bRo6ddQhCPRJdwAhAF5VOZ2cUnlRadE0/LQsgzXiH4ma2nz5DQbH//AI70GPY=", "action": { "action": "update", "newUpdateId": "5d763bfc-bd81-4300-9bf8-00b1dafa9adc", "modelKey": "user", "id": "adec5334b69f56f6a6c47520" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "2.7935844999738038": { "raw": "AQEBAAAAAHR4nB2MQQqDQAxFryJZd2Aak3GmN5CuPUA0EYRWS1WKiHcvM8v3H/+dIMM2LTM8Ktg/KpvBrYLZfl2BVrPwCUdvlpwYoSMN6JIO6hCJme+Etdd8ey9qr6cdJbbaN29TKUROdeBGkPsmYhDCGPsgI1x/dysjpQIBAQAAAAEGeJylUrFuwyAU/JUIdbI8OHLTpHxA5y7tUmUgcLFRsYnAdhRV/fcagxKiJHjogsTdvXvvcfyQyuj+YAldfJGX5a7cr/hzUW42bF2UWBfFeAqSLy7c8nUVcZxsR5Ip9Q7TSGulbr1XOxhqwATNXLWSAwaJI+Uj1sFBvYVX5EcjO+QCCh3ok+N2RooKUT1nDQzzcnc0EJJ5SmhtlObfV92quovuFq3V8ThuFMSAkPag2CnuWMsmnuBf+7jiK3QymV4+OOS3ldnZNS2xvIboFWZkChXjp4+3zztDZtGjp11CEI9El3ACEAXlU5nZxSeVFp0TT8tCyDNeIfiZrafPkNBsf/8AjvQY9g==", "action": { "action": "update", "newUpdateId": "092f0ee9-ae42-4d62-9dcd-22455514230d", "modelKey": "user", "id": "8593657a25b7826a4288b6af" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "2.795322699996177": { "raw": "AQEBAAAAAHR4nB2MWwqDMBBFtyLz3UBoHmbcQfHbBUxmJiC0Kq0iUrr3knyee7jnC8T7vC4wdHBsQrvCrYNFz6nBQ6ooaK2g86b0TMYHfzeYYjHoMlEOzJhcvb1W0eeoV4t99F23uRWESQtyTqRiA/eSY0jkI/z+sX8lkQIBAQAAAAEGeJylUrFuwyAU/JUIdbI8OHLTpHxA5y7tUmUgcLFRsYnAdhRV/fcagxKiJHjogsTdvXvvcfyQyuj+YAldfJGX5a7cr/hzUW42bF2UWBfFeAqSLy7c8nUVcZxsR5Ip9Q7TSGulbr1XOxhqwATNXLWSAwaJI+Uj1sFBvYVX5EcjO+QCCh3ok+N2RooKUT1nDQzzcnc0EJJ5SmhtlObfV92quovuFq3V8ThuFMSAkPag2CnuWMsmnuBf+7jiK3QymV4+OOS3ldnZNS2xvIboFWZkChXjp4+3zztDZtGjp11CEI9El3ACEAXlU5nZxSeVFp0TT8tCyDNeIfiZrafPkNBsf/8AjvQY9g==", "action": { "action": "update", "newUpdateId": "f900d934-f7ca-4542-986f-93baab5cc983", "modelKey": "user", "id": "dcaef9cb8aed05c7db658a46" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "2.797002999985125": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbaDOT9LEG0jXHiDNTKCgrdhKEfHukizff/z3pZT3eV3o0tH7KWlXOnW06HFrcJUqMqNYCcWALRvLZ2ciT9EEADGzHeBDvT1W0fuonxbb9FW3uRWmDBEPOIfYD9Fx8kW5F/r9AU/zI0cCAQEAAAAAp3icVY5BCoMwFESvIqGr4iLFtkpO0X1xEZOpfpoYSaJSSu9eJVDSzcCfeZ+ZN+u9m6fARHFn11NXPS7qzKumkTWvUHO+qWZtWTBpzA3eUgjkxsSPixceUosj2wBDCxbCKtTmRezWHJCIcvUUUWoYRIjDnnWedI/sX0kLLxO+i4UmmSLtnDdOPf/a+iFmd8AYXD5nn4Lc0BQmI19540D2t6D9fAGKX1VP", "action": { "action": "update", "newUpdateId": "c23f4d8f-3242-4215-92b9-83339c247368", "modelKey": "user", "id": "bc3dd633553907952a6fe20d" }, "data": { "groups": [ "61b3f5c40388a703e70003ed" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ] } }, "3.4176020000013523": { "raw": "AQEBAAAAAHd4nB2MQQoCMQxFrzJkbcEY20m9gbj2AGmawOA4FRkREe8uneV/j/e/ILpObYHTAK9HldVgN8Bi7+s2zrULRpQxYwmYJYYjuoeS1IMSuTOVNJL27N6qzRf79Ka29pyb3jqfthdUxoPx3mvKJCVipFiMGH5/TaUmLQIBAQAAAAAheJyrVsrJT84OLkksKS1WslJQ8g9w9fP0c1eqBQBwiwgY", "action": { "action": "update", "newUpdateId": "811a791b-19a5-41ff-b6cf-c33ff83b673c", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "lockStatus": "OPENING" } }, "4.266343800001778": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBA0auUWRtIk5k48QbStQeYZqZQ0NRFqIj07hq3D/7/gJS2bhUuA4gqnAao9ro9VZpdtWtYCsWU0Y1nJodjiI6peOez0syEjCI9e2xq98nevbHdauu4/heseRbjSDkkXpLHHwTTCMcX0pMilAIBAQAAAACveJxVjrsKwzAMRX8leO7gRx5253bq2q10cGIZAnESHKUQQv69UoeWbjpHl8vdBW4ziHMh0oT9NIpTIRb0GUmpujSyqhqrlNHsuylzVPKdKHMBhA7vVLCQfjz//fUFI34fCdAHj554P4g7nyAzCQVgrKugLSFq1UhZ1tFF3fGUmdr6z65zMa7DQGpdIP+oD1xhg2s9WFM5XdtYy5KEhmC4Ik0BhhtsnAOeJI43XpNJIA==", "action": { "action": "add", "newUpdateId": "2fc53694-1785-4123-85c0-09d5b85484aa", "modelKey": "event", "id": "8d9bae8359268f6048d92ed3" }, "data": { "type": "motion", "start": 1643055781132, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "1ee3895eb4ef2170046f9f2c", "partition": null, "user": null, "id": "8d9bae8359268f6048d92ed3", "modelKey": "event" } }, "4.361886799975764": { "raw": "AQEBAAAAAHZ4nB2MWwrCMBBFt1Lm20CazOThDsRvFzBNbqBgW5GKiLh3ST/vOZz7JS37vK10Huj1qLqDTgOteN+OcaldxOiZhdlwxmTYsRotoRkrNXGqIvChZ8tWcb/i05uiC57a6Xx8jIBPWTAxmhujtRxabq7Q7w++JSS6AgEBAAAAAMV4nF1QTQuDMAz9K6VnD61aP3beYZdd5nF4KBqcoBZqFET874vtRBwkIS95eX105Z0e8WmwNQO/MZnEkVAqzaSMwoDxdnxBZWzdDg1t0U7ghp5/B4QKoT43MMOABWocabby/pBdOZpaL9TFMdH0DFY3QFBJgruDu172m3eqAhbllEnA4shnLgirkpgWKnrgYSbryJIWFFnAQldJLHHVz39NudHl2GuLFyfiYkT8+RCHyBnltnkL/j+K6gP11IGjl9sXRVpcKw==", "action": { "action": "update", "newUpdateId": "77344544-49eb-424a-ac6f-05d848d55e36", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "lastMotion": 1643055781132, "isRecording": true, "isMotionDetected": true, "eventStats": { "motion": { "today": 44, "average": 51, "lastDays": [ 75, 39, 36, 43, 43, 90, 35 ], "recentHours": [ 10, 0, 8, 2, 8, 1, 6, 1, 0, 0, 1, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "recordingSchedules": [] } }, "5.005999299988616": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBA0auUWRvIxIxJvYG47gEmMxMoaOoiVES8u6TbD+9/gaWvW4PrBKwKpwmavZeXcrebjhqTzOUS1amRuUgY3YzBu1wJhQQ1cxrsuak97vYZxnZrfcT1WIRUw7lIqEbsmTF7qyK5wO8PFmskHQIBAQAAAADneJxtUE1rwzAM/StF5w6c73jnZTDGQqHbaezg2AqEJXax1ZFS8t8nh66lsJvehx5POgOdDgiPGwhog/NvjgZnYcuYlCcWkjLPRFFUMktywTxa8w8btPMxZp0n3nxCQk3vHB6Y/vy655sftHQVJiRlFCnG50uPF7MCwjl2AJEiyk7KPqlMKbO8RKFrLQQs27/mrZrwbqedv2e+YbPTbl59l0tvjo/n3cO+afcNLFHXnOBjCXscR8YHrjus77hSx4D+hoZYEtKqT7NOpz0WSiiV1AJ7resuPnFyBsdXPEUfxpth+QVxCHAi", "action": { "action": "add", "newUpdateId": "47c9b64d-de5e-4514-9120-8f51c5c1d8a7", "modelKey": "event", "id": "27f23bc2fe5a0aa180efcc8b" }, "data": { "type": "sensorMotion", "start": 1643055793140, "end": 1643055793140, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Nxkxtar Pcox" }, "type": { "text": "UFP-SENSE" } }, "camera": null, "partition": null, "user": null, "id": "27f23bc2fe5a0aa180efcc8b", "modelKey": "event" } }, "5.011190899997018": { "raw": "AQEBAAAAAHd4nB2MQQrCMBBFr1JmbWDSadKMNxDXHiCZGaGgidiKiHh3SZf/Pd7/QpZtaRWOA7wemjeDwwDV3pd9nLQLTcWP5LNT4dlNGMgxUXRUImaV4JOFnt2b2u1sn96sVtf27HTZP3A048J89bNGpikaShJE+P0B0mgk2AIBAQAAAAA5eJyrVsos9s0vyczPc0ktSU0uSU1RslIoKSpN1VFQykURdywByhiamRgbmJqaWxobmhjUAgCbTBPC", "action": { "action": "update", "newUpdateId": "d8b1231a-dc97-4053-9336-3b60adc518e5", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "isMotionDetected": true, "motionDetectedAt": 1643055793140 } }, "5.320390500011854": { "raw": "AQEBAAAAAHR4nB3MwQrCMAyA4VcZOVtYY5OtvoF43gMkTYSBdh7KRMR3l+76w/d/QUpbtwqXAcQMTgNUfy8vk+ZX6xXLRFjiOZBkDWkmDTkZBZoLEjpH1rGz52b+uPmnG9+9th7XY+E5xoQ8CpMTs2Xmye6q8PsD6gUi4AIBAQAAAACveJxVjj0LgzAURf+KZO5gYvKizu3UtVvpkI8nCEYlPgsi/vcmDi3d3jnvcrk7o21G1hYsTNRPI7sUbCETKSkOsiqV0rXgwLN3U8zRMt8hZa5I6OiRCpakn69/f3vjSN9HQDLekEm8H4mdCRgzMVWDsdoJa0GKylWd91Jjo/KUObX15662GNdhSGpdMP6o97kCG86lgNKAQgXgGwDtO2tzRZg8DnfczlyexI4PaolJRQ==", "action": { "action": "add", "newUpdateId": "2c752c13-5a9b-485b-94d5-58c252e616b0", "modelKey": "event", "id": "e9114260a65e566d9667dfbb" }, "data": { "type": "motion", "start": 1643055782161, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "id": "e9114260a65e566d9667dfbb", "modelKey": "event" } }, "5.393986199982464": { "raw": "AQEBAAAAAHZ4nB2MWwrCQAxFt1Ly7UA7yTzqDsRvF5BJMlCwrUhFRNy7zHzeczj3CyzHsm9wHuD1UD4MTgNs9r71cdEmMCcbx2iuxhodIQeXJ6xOJl/yzEpC3LJ1V7tf7dMa4dWenS79I+TIJYkvJZJHwapKyeYAvz/nWiVuAgEBAAAAAL54nF1QsQqDMBD9lZA5Q6LRSucOXbrUsTgEc1hBDSSnIOK/94x1UHgcubx37x638M4EfDls3cDvTOU6lVl2KxKVK8F4G3bqAQg1giUJ+hGIgQkGLNFgoL+F94fDwtFZM9MrSUlmJvCmgc26oHZb9jDzNvNJc8EIUrB0BzVaV6TyUJP5040+CrVglEUlUatizWI9oVppMvTG4ymFPIWQlwxXk+iz7hGct+3QlPUX7NhBlFfxJO+D+19j/QF9ultG", "action": { "action": "update", "newUpdateId": "387e006e-f6f6-43a5-813f-c12b89ad4c4a", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055782161, "isMotionDetected": true, "eventStats": { "motion": { "today": 23, "average": 18, "lastDays": [ 36, 6, 0, 3, 3, 36, 44 ], "recentHours": [ 4, 1, 12, 0, 1, 0, 5, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "recordingSchedules": [], "isRecording": true } }, "5.838011400017422": { "raw": "AQEBAAAAAHh4nB2MQQrCMBBFr1JmbWCSTNqJNxDXHiDJjFCsHZGKiHh3SZf/Pd7/QmnbbCscB3g9pGwKhwFWfV/2cZIuqHjMCdHpSOIIK7uSw+QkUCPGSTFzz+4mupz10xsxey7Wbp3P+4tv7IMyXmXMsdTkU0xVI8PvDwd7JTYCAQEAAAAASnicq1ZKyilNLcnPL8lwzs/LS00uyczPCy5JLElVslKoVirOTM9LzAksTczJLKkEipiZ6yhABYNLilLz0ksygKK6Zsa1tQAD1Bo1", "action": { "action": "update", "newUpdateId": "4a109500-e64d-40b8-a927-d24c4807e098", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "bluetoothConnectionState": { "signalQuality": 67, "signalStrength": -63 } } }, "5.846109299978707": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbWDSppp4A3HtATKd+VDQRGxFRLy7psv/Hu9/KE/rXAsdO3reNa9Gu46KvS7bOGkTowWdkNjBD8mFCHaCABe9IiuG+Jctu1W169nerVmsLPXR6Lx99Cze+igpjxCF5z0LDhrp+wMwASaUAgEBAAAAAEp4nKtWSsopTS3Jzy/JcM7Py0tNLsnMzwsuSSxJVbJSqFYqzkzPS8wJLE3MySypBIqYGugoQAWDS4pS89JLMoCiuuYGtbUAAv4aKw==", "action": { "action": "update", "newUpdateId": "5e4dcf90-f139-48f0-bf4f-81dfadf38cf9", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "bluetoothConnectionState": { "signalQuality": 50, "signalStrength": -70 } } }, "5.960623399994802": { "raw": "AQEBAAAAAHV4nB2MWwrCQAxFt1Ly7UDmmdEdSL9dQCaJULCtSEWkuHeZft5zOHcHlm1aF7gM8H4qbwanARb73I5x1S6SaEBDdJkCutQ0OPb34szXkFrkdKbcs3lVe4z27Y3wbC/udDo+JJUgSNq0RNaKmSn6KgS/P8rlJL0CAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "4cd20e00-5720-4bd2-a1f6-e1824b3a4975", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [] } }, "5.982410800002981": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm20Aeg824A/HbBUwyN1CwrUiLiLh3ST/vOZz7Ja3btC50GWh/mm6g00AL3vdjXK0LtBJG9d6dDeo4cHJSuTgFjKPkqLn1bF4Njxs+vak646WdTscHa0rJRFJDRkTmMZYqPtDvDxMwJZkCAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "efb17a00-6dea-4143-9c4b-aeed42982a8f", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [] } }, "5.984701499983203": { "raw": "AQEBAAAAAHR4nB2MSwrCQBAFrxJ67YDTcewebyCuPUD/AgGTiCSIiHeXyfJVUe8LYuu4zHDpYHu6rAGHDuZ43/dx9SbcNaOxJeSjpRNlSixUEtUchjV6PXPLpsXjcYtPa0ymeEmj4/4h2gfSgF7KIM6ZXIxUK/z+7fAl0QIBAQAAAAAieJyrVipKTc4vSsnMSw9OzkhNKc1JLVayUoiOrQUAhrYJrA==", "action": { "action": "update", "newUpdateId": "ddb12c8c-280c-4717-8a75-791ec29e3b68", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [] } }, "5.985874200006947": { "raw": "AQEBAAAAAHV4nB2MWwrCQAxFt1Ly7cA8nDFxB9JvF5BJUijYVqQiUty7zHzeczj3AJZ93la4DvB+Ku8GpwFW+9z7uGkTYolSpuBiDOrOpaDjGi+ONQrShBm9b9myqT1G+/aGF3txo3P/mLxoyBWrKVlCJIpYGAV+f8kmJPQCAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "ce393591-221d-4668-ab27-ad2c89f85800", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [] } }, "5.988382300012745": { "raw": "AQEBAAAAAHV4nB2MSwrCQBAFrxJ67UA76YlpbyCuPUD/BgImEYmIiHeXyfJVUe8LYtu0LnDu4PVw2QIOHSzxvu3j4k04c0U1TKgFE41WEmeyJH02KQPZqNqyefW4X+PTGpM5ntLotH8cjSW7ktchqgudpC/IGvD7A/7lJhgCAQEAAAAAjnicTYzBCsIwEER/JeSs0CpR8NqzB+1RPIRkmyzETU22SCn9d1e9eJt585hFF3C5eKTQuwh+SlD1Sd3uG6VfOGCXicAxZurZMsi0aBetwCS5NXvxhgLPCcjNQszxYASNcb7+9F3TSK8YyKbLZBPyR2v/aM8FKHAUvP3+1Ypeij7nwlhVZx9QrF7XNyPiN0A=", "action": { "action": "update", "newUpdateId": "d99f0bc0-0b50-48c5-924c-a32ca564c8bb", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "recordingSchedules": [], "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -53, "ssid": "Mortis Camera" } } }, "5.9925161999999546": { "raw": "AQEBAAAAAHV4nB2MQQrCQAxFr1KydqATm9jxBuLaAySTBAq2FamIiHeX6fK/x/tfkLpN6wLnDl4Pk83h0MHi79s+LtaEqZaKJ0lEA6Uhs6axRKTAoJz1aJS5ZfNqfr/6pzVVZn9Ko9P+4RjRizmr94GC6sp55AK/PwCGJfUCAQEAAAAAknicXY3BCsIwEER/peSsUKtW8NqzB81RPIRkmyzETU22SCn9d7cIHrzNvJmdndUQppthUOfqeNpUKoNN2SF5bQO4MUKR5P6Q5I09dokILGMizd+jWdlgBEbRrbT6DK8RyE7im8N+nfz7UNCTidfRROS1tavrH9WcgTwHwdu2WXFBJ0ZdUmYsVWeekI1alg+QOzrj", "action": { "action": "update", "newUpdateId": "dbb9c27a-5545-416b-89ff-f2f511b3d516", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "phyRate": 57, "recordingSchedules": [], "wifiConnectionState": { "channel": 6, "frequency": 2437, "phyRate": 57, "signalQuality": 100, "signalStrength": -62, "ssid": "Mortis Camera" } } }, "5.993778900010511": { "raw": "AQEBAAAAAHZ4nB2MWwrCMBBFt1Lm20DbTKaJOxC/XcC8AgXbilRExL1L+nnP4dwvsO7ztsK5g9fDeHc4dbD6+3aMizVh2VLkvgbjMgTEiqFYpkAkaZAce1Ft2bKZ36/+aY3y4k9udD4+UiaWSUcRwjFqrGY4eUnw+wP9JiWfAgEBAAAAAD54nKtWykksLvHNL8nMz1OyUjA0MzE2MDU1tzQ2tjTSUVAqSk3OL0rJzEsPTs5ITSnNSS0GKoqOrQUA3EsRaQ==", "action": { "action": "update", "newUpdateId": "d8d53a0f-da91-44f4-9d86-66b51b830bcc", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055793392, "recordingSchedules": [] } }, "5.994852499978151": { "raw": "AQEBAAAAAHZ4nB2MQQqDMBBFryKzbiCJEzP2BqXrHiCZ/IBQtRSLSOndS1z+93j/S0m3aV3o2tHnVdIGunS0YH+c41aaGDz7kFmM48iGVWBytGqEawlVtM9BWzavBc87jtZomvFOjU7nhwN6GQMyo3oXreWhjtUr/f7TGyUWAgEBAAAAAD54nKtWykksLvHNL8nMz1OyUjA0MzE2MDU1tzQyNjbQUVAqSk3OL0rJzEsPTs5ITSnNSS0GKoqOrQUA20YRYA==", "action": { "action": "update", "newUpdateId": "62425b48-1474-4c8e-b70c-84fd5f8c3b5c", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "lastMotion": 1643055792330, "recordingSchedules": [] } }, "6.025717800017446": { "raw": "AQEBAAAAAHd4nB2MSwoCMRAFrzL02kDnM0naG4hrD5B0tzCgiTgjIuLdJbN8VdT7QuFt6Q2OE7weUjaFwwRN35d9nGSIwlkqp2RmJ2pCRWeozMEEb7Mlzd7ZOLJ7F72d9TOaVdvan4Mu+wc6VapEV5skkg9RkTMjwu8P4qEk4AIBAQAAAAA6eJyrVsos9s0vyczPc0ktSU0uSU1RslJIS8wpTtVRUMpFkXAsAUoZmpkYG5iamluaGJpZ1gIArx8UGQ==", "action": { "action": "update", "newUpdateId": "ac8dbc77-52de-4b02-9a54-431819e83216", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "isMotionDetected": false, "motionDetectedAt": 1643055794169 } }, "6.252706800005399": { "raw": "AQEBAAAAAHd4nB2MSwrDMAxErxK0rsGyHX96g9J1DyBZKgSaDyGllNK7J85uZh5vfkB1G+YJrh28F6FN4dLBpJ/HWW7SgEVb2KszDiOaYD0bKpINoUrMfOSETRtn0dddv82pNOpKbR3Ojz5H4lQdcwzOV/8UCUlLD/8d0DwlKwIBAQAAAAFFeJxtUsFOhDAU/JUNZ2MehdLWo8abHjYcjYcuvIXGWrAt6mbDv9uWuMvq3mBm3ryZlx4zi81gW2W6uumxnTS67G7z8nqzyZyXPv4cM/t9f/CJIDkRFAShZREU/oTnAIQJBlwwFpkvtVdptOmlMajDt5m0Dsze4seEpjmcIa3MWz0its+70Z1hpzoj9XaSWvmopnACa2/RdL4PKMwB3Unv0R7SxhFtg8bLDs9Wyj300nahZcD2UjuMVhpxrEPJKMxa5ZohRG08tln0/FQtDkv904m8tD62rUognLOy4FUepCfBo2kXugBKGaMcyJpO80/btUNZFPyPw68geQhgaYVX76jl6PBKBsrXgosMJQcO8G/+MsMVh3UGECEGm9OLGOxy12M2OUx7RDAQVZ5XhJSxyHLO8BJuSZELAjkvqBDzPP8Av5u4OQ==", "action": { "action": "update", "newUpdateId": "0109b3e2-2161-403b-a9d8-a1ed68ba9d71", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 21295092543, "txBytes": 1002797089773, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640288743861, "recordingEnd": 1643055775802, "recordingStartLQ": 1640288744338, "recordingEndLQ": 1643055790761, "timelapseStart": 1640288743858, "timelapseEnd": 1643055480800, "timelapseStartLQ": 1640288743858, "timelapseEndLQ": 1643055095797 }, "storage": { "used": 1987496116224, "rate": 708.231920183599 } } } }, "6.293135799991433": { "raw": "AQEBAAAAAHZ4nB2MSwrDMAwFrxK0rsF25MjpDUrWPYBsyRDIp5SUUkLvHuzlm2HeCZyPed/g3sHnJXwo3DrY9Pts4yFV9JGtJ5+Mw4EMilgzYrQmY3FBB0GKLVt30WXSX20yr/rmSuf2walXT8VLCIUlOhLOlNII/wvFUiVrAgEBAAAAAUJ4nG1STU+EMBT8K6ZnY1roFx413vSw4Wg8dOlbaKwF26JuNvx3WzDslzc6M2/evAkH5KHpvTaurZsO9GghoPub17fbGxSiivlxQP7nYR9ngghKC4mZ4Cwp4opzVhaswqQkFCfi2+zMPNl0yjmw6duN1iZm5+FzBNfsj5A17r0eAPTLdghHOJjWKbsZlTUxqxlewTp6cG3sEoqnhG5VjOD388YBfAMuqhaOViY8dsq36ciE7ZQNkK0swFCnG7MQaROaPkVtImiUPb+Mhn65fm0oKh9zCbysiCxlulnmUKvgyemZpiVmTAiOBTul5/nnzbkDkxcOf4LFoyKYV7lp8wFWDQH+ySDOBGcZOCEEk6v5ywxXDqcZCOGYyWn+IXq/9HpAY4B5DxUEF7yopFyqWOosS3ZXcCJFRXlaQqdp+gUkhLfw", "action": { "action": "update", "newUpdateId": "38a0272b-1467-4dd0-9480-c4f15e6d478e", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 17442805765, "txBytes": 653259013140, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183853280, "recordingEnd": 1643055776075, "recordingStartLQ": 1639183853258, "recordingEndLQ": 1643055791069, "timelapseStart": 1639183853279, "timelapseEnd": 1643055611101, "timelapseStartLQ": 1639183853279, "timelapseEndLQ": 1643055116058 }, "storage": { "used": 1471026298880, "rate": 335.261879469184 } } } }, "6.3141223000129685": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbSCNyZh6A3HtAWaSP1CwrUhFRLy7JMv/Hu9/Sco+byudB3o9quygw0Ar3rc+LrWJYzilaQQ7ZvUuSlInVkaXc5RqSBqtZ8tWcb/i05oiC57S6Nw/EMy8VLDCW5CgUB4zT/T7AwmyJlUCAQEAAAABSnicbVLLTsMwEPyVymdAfifmCOIGhypHxMFNtomFcYLtAFWVf8dORCgtN3t2dnZ27CPyUPe+Ma6t6g6a0UJAt5vnl6sNClHHfDki/3V3iHOBc6VYKRnjiRBXmHAhFC4IV7hMhU+zN3Nj3WnnwKazTPDew/sIrj6kO+WsSJA17rUaAJqn3ZCF3GhtHm1ap+121NbEzCYYr2gVPbg2dgm+lnRK+E7HCP4wTxzA1+CibuFXzYT7Tvs27ZiwvbYBspgFGKq0YiaixoS6T1brCA3Kmh+mgX5Zfg0oah+zGckxw2XJsZB5h5Xw4JqlzLAQRSExVafluf9xe6bAzhR+CLOGIrigOWnzBlYPAf7xwNgp4Y8HQVNw4qL/3MOFwqkHwiVm5TT/h94vuR7RGCDPKXjB0ktiJXD+EH5Jk3F6QygtGSkpFpxO0/QNYZS1YA==", "action": { "action": "update", "newUpdateId": "327591e6-66b0-4a5b-afc1-884adfe5b4fe", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 4499386334, "txBytes": 145590714908, "wifi": { "channel": 6, "frequency": 2437, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -62 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640308840567, "recordingEnd": 1643055776029, "recordingStartLQ": 1640308840563, "recordingEndLQ": 1643055791072, "timelapseStart": 1640308840533, "timelapseEnd": 1643055521005, "timelapseStartLQ": 1640308840533, "timelapseEndLQ": 1643055146038 }, "storage": { "used": 747324309504, "rate": 342.122831820542 } } } }, "6.316282199986745": { "raw": "AQEBAAAAAHZ4nB2MWwrCMBBFt1Lm20CcPJq4A/HbBdxkJlCwrUiLiLh3ST/vOZz7JdRtWhe6DLQ/BZvSaaBF3/djXKULl9lWQTI2OW98C2wKQjLKOtoWS4yOezavoo+bfnpTMesLnU7Hx7lmsBQvLWoT+BEu2FyUfn/YTCWSAgEBAAAAAUl4nG1SwU7EIBT8lQ1nNUChFI8ab3rY9Gg8sPRtS0RagaqbTf9daGPdXb2VmXnz5k16RB507xvj2lp30IwWArrdPL9cbVCIKubHEfmvu0OcCSFJKcpKcJkEcYWpwIySqsCEsUR8mr2ZB3WnnAObvgkvErH38D6C04eEcFHyBFnjXusBoHnaDdnKjdbm5aZ1ym5HZU3MaoLxitbRg2tjl+BrXkwJ36kYwR/mnQN4DS6qFn7dTLjvlG/TlQnbKxsgm1mAoU5HZiFqTNB9CqsjNCh7fpgG+uX8taKofMxhSkYkJZhJLnMRq+DBNQtdYM6F4IKJU3qef9yeOwh24fAjmD0kFpTmrs0bWDUE+C8DOxWcZWAlFoT/mb/M8MfhNAOnKQOf5j+i90uvRzQGyHsYFQWTlHHJc0y/tFkyelNgWlWkSjvINE3f2x+12A==", "action": { "action": "update", "newUpdateId": "3920cda8-0834-4f52-ba58-e2e70f6b6632", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 7916768759, "txBytes": 270421830144, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -53 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1641921049599, "recordingEnd": 1643055775747, "recordingStartLQ": 1641921049574, "recordingEndLQ": 1643055790722, "timelapseStart": 1641921049594, "timelapseEnd": 1643055460715, "timelapseStartLQ": 1641921049594, "timelapseEndLQ": 1643055520725 }, "storage": { "used": 427349245952, "rate": 642.30288184191 } } } }, "6.318183100025635": { "raw": "AQEBAAAAAHR4nB2MQQrCMBBFr1JmbcBkBjvxBuK6B5gkP1CwrUhFpHh3SZb/Pd4/yPI+bytdB3o/i+2g00ArPlMft9IEIJfKFpwIqhPl4JQ1uuzZj6nEpKlny1bwuOPbmmwLXtbo3D/EmLnEyBWKAJUxpBzPnn5/65QlHQIBAQAAAAFGeJxtUsFOxCAU/JVNz8Y8oFDqUeNND5sejQe2fdsSkVag6mbTfxdas9vVvcHMvGHmhWPmsO5do21b1R02o0Gf3W1eXm82mQ8qpMsxc9/3hzATtOCUEiEY41ERTjjJy6KglHEmCYnMl97rebTulLVo4tmOxkRm7/BjRFsfzpDR9q0aEJvn3eDPsNetVWY7KqNDUnM4gVVwaNvQRRSmiO5UCOgO84sDuhptUC2erbR/6JRrY8uI7ZXxmKwM4lDFkkmYNdrXfYxaB2yy5PmpG+yX+qcVBeVCaitYSSSTnJaljNKT4NE2M50z4LwQQAms6Xn+abt2YAD5H4dfweJRAgiWVq3f0ajB47UMfC24yCAgZsj/zV9muOKwzkAAGBfT/CN6t+z1mI0e0zsxP8+BMcFZIVKRZZ2S0dscCklKCaIo5TRNP6O4uBI=", "action": { "action": "update", "newUpdateId": "ee46f3a2-44ef-4832-8389-c1317bd9b8be", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 27522166335, "txBytes": 1497722353811, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852998, "recordingEnd": 1643055760210, "recordingStartLQ": 1639183853004, "recordingEndLQ": 1643055790063, "timelapseStart": 1639183852995, "timelapseEnd": 1643055600214, "timelapseStartLQ": 1639183852995, "timelapseEndLQ": 1643055100356 }, "storage": { "used": 3005403365376, "rate": 832.407819806798 } } } }, "6.3199059999897145": { "raw": "AQEBAAAAAHZ4nB2MWwrCMBBFt1Lm20CSjnm4A/HbBSSZO1CwrUiLiLh3ST/vOZz7pdK2aV3oMtD+lLKBTgMteN+PcZUuAqv1uYppKTrDgmhSttWEsUJUM7caejavgscNn960MuNVOp2ODweMKZ9RGepdtJaDZvWNfn8GDiWvAgEBAAAAAUp4nG1SQU7DMBD8SpUzINtrx3GPIG5wqHJEHNxkm1gYJzgOUFX5O3ZahRZ6i2dmZ2dWOWQeq87XxjVl1WI9Whyy9erl9WaVDUGH9Dhk/vt+H2aioEIoludMRUFYYOC5pERKVfA8El9mZ+bBqtXOoY3fVEAkdh4/RnTVPiJC5iJC1ri3skesn7d9snKjtWm5aZy2m1FbE5KaErKgZfDomtBG+JaLKeJbHQL6/byzR1+hC7rBXzczPLTaN7FlxHbaDpjMLGJfxpJJmNVmqLoYtgpYZ8nz09TYHesvJwrahxQmB8WoYgUnMh1iETy6eqY5ECGEBMGKc3qef9qcO4Aq5B+Hk+DkwQVJHsG8o9X9gFcyADkXXGagIHLyb/4ywxWHiwyQekzzH9H5410P2Thg2gNKRgMueTE3PR6Twh0QYACMU1IwNU3TD/xSteE=", "action": { "action": "update", "newUpdateId": "64f029bd-c871-4de7-890b-63bedff94cb6", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 8155926629, "txBytes": 346710779846, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -45 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639219284079, "recordingEnd": 1643055573528, "recordingStartLQ": 1639219283987, "recordingEndLQ": 1643055574508, "timelapseStart": 1639219284030, "timelapseEnd": 1643055513560, "timelapseStartLQ": 1639219284030, "timelapseEndLQ": 1643055533528 }, "storage": { "used": 39728447488, "rate": 13.3032332410829 } } } }, "6.321674300008453": { "raw": "AQEBAAAAAHZ4nB2MQQrCQAxFr1KydsBpxjHxBtK1B8gkGSjYVqQiUry7Tpf/fd7bQHQdlxkuHbweJqvDoYPZ37d9XK0deBYhjRhy9RRS5T4UlxwU2WPCokq5adNifh/80xyVyZ/S6Lg36lEtngoVN3YkYu4p/6vw/QEOQiXgAgEBAAAAAUJ4nG1STU/EIBT8K6ZnY6BAAY8ab3rY9Gg8sO3bloi0AlU3m/53gZrulzeYmTfMm3AoHDSDa7Xt6qaHdjLgi/ub17fbm8IHFdLlULifh33IBBa8IpQKLEVUhBXnCCHOaVnyEkXiW+90nmx6ZS2YeLaTMZHZOficwDb7I2S0fa9HgPZlO/oj7HVnldlMyuiQ1AytYB0c2C70EUVzRLcqBHD7/OIIrgEbVAdHK+0fe+W6uGTEdsp4SFYGYKzjjklYtNo3Q4zaBGiL5PmlWxiW7deGgnIhlVARiQURrJScR+kqeLJtpilBjMU6ZCVO6Tz/vDl3kOWFw59g8RBSVkkQ9AcYNXr4JwPFp4KzDExKKcTV/GWGK4fTDBgxhMScP8Tgll4PxeRheQcRJCtGSUnyIkudFLM7jASXFBEuMZ3n+RdltLgX", "action": { "action": "update", "newUpdateId": "37aa8c13-6fe4-4f92-bea6-c39e143bcc86", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 18763448198, "txBytes": 700077422720, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852977, "recordingEnd": 1643055774968, "recordingStartLQ": 1639183852992, "recordingEndLQ": 1643055789962, "timelapseStart": 1639183852941, "timelapseEnd": 1643055599988, "timelapseStartLQ": 1639183852941, "timelapseEndLQ": 1643055105008 }, "storage": { "used": 1603096543232, "rate": 415.108794037914 } } } }, "6.323465799971018": { "raw": "AQEBAAAAAHZ4nB2MQQrCQAxFr1KydmCcSZPRG0jXHiCTpFCwrUhFpHh3mS7/e7y/g+g2rQtcO3g/TTaHUweLf+7HuFkTxpiKcw6YxxKQDEO99BaqcB0TpShGLZtX88fg39aozP6SRqfjQ5GSRrZqlMVK7IXzuSjD7w/piSU6AgEBAAAAAUN4nGVSTU/EIBT8KxvOxpTPUo8ab3rY9Gg8sOVtS0RagaqbTf+70Jru163MzBtmHj0iD03vtXFt3XSgRwsBPWze3u82KEQV8+GI/O/jIc4EkYwJyXjFq6SIK465pFKSQpYUl4n5MXszjzadcg5s+najtYnZe/gawTWHE2SN+6gHAP26G8IJDqZ1ym5HZU3Mal6sYB09uDZ2CS2mhO5UjOAP840D+AZcVC2crEx46pRvU8uE7ZUNkK0swFCnklmItAlNn6I2ETTKnt9GQ7/UX1cUlY+5raAVToU5qQqRpKvg2emZZrTgvOQcF8U5Pc+/bC8dML5y+BcsHpJVQuZVm0+waghwm0HKC8FFhhQCC34zf5Xh1uE8A0mKspzmP6L3y16PaAyg50fBhBAhKWOU5CLLOnHJ6T0WggieCMqmafoD+iK4SQ==", "action": { "action": "update", "newUpdateId": "d7428e73-43f8-46d4-b95d-ba7bf2620ad6", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 28446845959, "txBytes": 1583882087317, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852906, "recordingEnd": 1643055755100, "recordingStartLQ": 1639183852911, "recordingEndLQ": 1643055784968, "timelapseStart": 1639183852888, "timelapseEnd": 1643055575165, "timelapseStartLQ": 1639183852888, "timelapseEndLQ": 1643055285277 }, "storage": { "used": 5012226834432, "rate": 1753.16626544334 } } } }, "6.458593200019095": { "raw": "AQEBAAAAAHN4nB3MSwoCMRBF0a00NTYQOh8r7kAcu4BKXhU0aNpBUETcu8TphXM/JG1se6fTQgLQYaGur+sDMvSMWWNQ9sbNtaTmolR2JRucxGpstlqKfrL7Dr1d9D2NPrWPGbf/Ih2zcCoB6mvByhkpwLPQ9wdUNCRCAgEBAAAAAOl4nG1QTWvDMAz9K8XnFpw6TuJdtwzKaBl0O40dXFuDQGwHWx3NSv775NC1FHbT+9DjSWeG4wDsYcES+BTiNmAXPFsSRh2RhKIqBZeyVqVUknjw9h82mRBzDM+zo80nQDD4RuGJ6I/Pe779Bo9XwQFqq1ETPl96bOwMEE65A5OmOBRFZdYKSpBS1/xLqFpUbFr+Nd9pB3c7j6M9ucUWhp9Bz77LpTfH+/Prat/u9i2bsm4oIeYS/tj3hAeq283vuFLHBPGGOjt3qyvdSCUs8IOy66ayUlje6PxEFyz0LzBmH+Sb2fQLQv9vHA==", "action": { "action": "add", "newUpdateId": "43e80f8c-c5ef-4ab8-96fd-a4bf8ff2f540", "modelKey": "event", "id": "576a8593de0b9d286d53d08a" }, "data": { "type": "sensorMotion", "start": 1643055794595, "end": 1643055794595, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Cydxm Mepzpa" }, "type": { "text": "UFP-SENSE" } }, "camera": null, "partition": null, "user": null, "id": "576a8593de0b9d286d53d08a", "modelKey": "event" } }, "6.4596403000177816": { "raw": "AQEBAAAAAHd4nB2MWwrCMBBFt1Lm20Aek5c7KP12AdPJFAqaiK1IEfcu6ec9h3O/QLyvrcJ1gPez0C5wGaDK53aOsXThEL0uwaskyApjsCoRoUpzsA550TpTzx6tyH2Sozeb1K29Ol3PD89mNiawzYLiPUW9uBxdgN8fvkMkeAIBAQAAAAA5eJyrVsos9s0vyczPc0ktSU0uSU1RslIoKSpN1VFQykURdywByhiamRgbmJqaW5qYWprWAgCbehPR", "action": { "action": "update", "newUpdateId": "34450d65-8e4c-4762-8aa4-8b6234cf009a", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "isMotionDetected": true, "motionDetectedAt": 1643055794595 } }, "6.532936399977189": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbWDSTGrjDaRrDzCZmUBBU5HWIuLdJV3+93j/CyzrvFS4dLA9lVeDUwfV9tsxrtpEkR55GAeH4rMjSexSoeiUNRCX6EeTlj0Wtftkn9bU96uh+TjoKcRSIueAqN4nPSPlbAF+f5IuJJwCAQEAAAABRHiczVK7agMxEPyVoDrm9H6kTxFwFYc0IYVyt2cL7iEkneFi/O9Z2QEH/ANpJDSzmt0Z6USWWMII5OlBW2aspZQ+PpDB57IDmBBmWgqqlHFSK45UXnOB8WXqZyRPpI3LZfdHSH4P29l3VQsLsSoiVpZ0VT8jNsI4p/X3gg+D/xoupLAW+2BBn6ACjHJObRWZix8QQFpQLqtGLnPtdCfChJXaKWOUlqwOEPIrtGs7hGmPdO+HDPV6+K7VglmlnHVScMGr5bLGipPkQ0fwvGTorvapM84IJaSp/js4hhYyUh8nMs4d1PHI7o1pjO79GZcN33JGBbk1u3COUq0Zl9XWAfxQDjWIkhZAV/9R6rOmXcbY57usnRba6T/vw6i0yuhbblbiX8Jj9OVQR2mOPjVzLM0yhT5sYpoLtKVBeXI+n38AND2zgA==", "action": { "action": "update", "newUpdateId": "fc20a686-0c1b-4c9a-9f45-dad34af518ec", "modelKey": "nvr", "id": "2435ff5ab300d119d704bbe3" }, "data": { "uptime": 681788000, "lastSeen": 1643055794652, "systemInfo": { "cpu": { "averageLoad": 6, "temperature": 68 }, "memory": { "available": 6388164, "free": 102208, "total": 8163024 }, "storage": { "available": 13846957756416, "isRecycling": false, "size": 31855989432320, "type": "raid", "used": 16409797353472, "devices": [ { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true } ] }, "tmpfs": { "available": 963696, "total": 1048576, "used": 84880, "path": "/var/opt/unifi-protect/tmp" } } } }, "6.581093100016005": { "raw": "AQEBAAAAAHR4nB3MQQrDIBBA0auEWVeIZnRMb1C67gHUmYFAa7KQllJy99RsP7z/g1Tasla4DpCY4TJAlc9j49Tkxr0SIXuNaMghGSS2JjOikYI5aLBjcCd7rSzPu3y7kbfU1uNyLqxLOPmss9fs4lRG/jv1EfYD/XAjQQIBAQAAAAD5eJxtUF1LwzAU/Ssjzxs0TdOuPlthKFOYgiA+pMnNKLZpTVKxjP53b8K0FHzL+bj3npML8dMA5GZDHBjX28cBDCiyReyF9SjQPGMJ50WZFTRFHoz6h3Wyt2FNEt4dTt6CB+mfcblD+u19zVdfYPyf0IEXSniB+HLNcVARePgOGQiXtKY0l2kJGXAuikSzsmA5mbe/yY+ig9XMw6gmOX1uXmvz0TbRee26eF7unnan6niqotz1o4mRV56zsOIMZA4OiUdsyGnGtkU8YKPGN71ZqNGBXVATehCaiozxWpdc1+meyUTpnGq+J/GogvYepuCD8C1k/gGO+Hq6", "action": { "action": "add", "newUpdateId": "774d5f84-7247-47d1-bd44-ec4b6f61062d", "modelKey": "event", "id": "12a435bf95fb283c0df61f58" }, "data": { "type": "sensorOpened", "start": 1643055794712, "end": 1643055794712, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Ludycyq Xbnkli" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "garage" } }, "camera": null, "partition": null, "user": null, "id": "12a435bf95fb283c0df61f58", "modelKey": "event" } }, "6.583303999970667": { "raw": "AQEBAAAAAHV4nB2MWwrCMBQFt1Lut4G8H+5A+u0CYu4JFDQRWyki7l3SzzPDnC/lsi290Xmi95PzBjpN1LBfj3HhITimHCGzQJRWWO2MiKhS6GrA7IN00Yzs0Rn3GZ/RrGhrfw26HB+uqJtSvugEC+dykNWkYDz9/udHJOMCAQEAAAAAPXicq1bKLPYvSM1LTVGyUigpKk3VUVDKB/KDSxJLSoudMxLz0lNTHEuAkoZmJsYGpqbmlibmhka1AB9uEbM=", "action": { "action": "update", "newUpdateId": "d89a8e0a-e804-4253-8ef0-2f3edd670583", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "isOpened": true, "openStatusChangedAt": 1643055794712 } }, "9.09117349999724": { "raw": "AQEBAAAAAHR4nB2MWwrCQAxFt1Ly7cA4j5C4A/G7C8hMUihoK9oiRdy7zHyee7jnC1K3eV3gMsD+VNkMTgMs9hk7XLWJMlnhgsFRlOJSCt6xndkpiqpGzoyx3R6r2v1mR4+97dW2uRdSzZOPQpWKJhIl84iBMvz+txUkzgIBAQAAAAEkeJyFkc1uwjAQhF8FRT2hHOz4Jz8P0HMv7aXisLbXwcJJkB1AqOq7NwSoCm3xxbJG34x3xx9ZG4bdNmbN4j2TVDErNCe0FlAShiUh06mzVb7IwPsXDJ2L0Q39mfduj3uHh2bZGCmU4lRTbiRAXSgLHKSh2eT8gfFaKSUIEbzgQAwTVBSypHCHqaLSrLK0KIiVijPLakaMtfdYXU0jC8agKhEkq6dHDWfVCev3oQkIplnemPSkjXiSdhHPRH4IbsTcoMcRm6er+UadQ+amLgn5b+fyO/UxEvUazc5jAvPYgj6+Pr/9MeQMqOBMm0rR0GGAf6FZ6dA4uAjz/dpZux5Tu2Afh9TCZhiCH/QmgZ3+J1mecXHr4Zjaeu26h82sPr8AeQX7wQ==", "action": { "action": "update", "newUpdateId": "bfeb9b62-83ab-4420-9e19-d6addd395963", "modelKey": "user", "id": "4c5f03a8c8bd48ad8e066285" }, "data": { "groups": [ "61b3f5c40195a703e70003ec" ], "allPermissions": [ "liveview:*:d65bb41c14d6aa92bfa4a6d1", "liveview:*:49bbb5005424a0d35152671a", "liveview:*:b28c38f1220f6b43f3930dff", "liveview:*:b9861b533a87ea639fa4d438", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "9.091784799995366": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm28A0SZPWHUi/XcC8AgVtRStSxL1L8nnu4Z4vkOzLtsK5g/dDaTc4dbDa59rgolWwiDc/oUtYoosswfE0RqfqCyIPMSept/umdpvtaLGXPeu2tAJxSTGTaUgDUh57MaY+FPj9AbKXJToCAQEAAAAAp3icVY5BCoMwFESvIqGr4iLFtkpO0X1xEZOpfpoYSaJSSu9eJVDSzcCfeZ+ZN+u9m6fARHFn11NXPS7qzKumkTWvUHO+qWZtWTBpzA3eUgjkxsSPixceUosj2wBDCxbCKtTmRezWHJCIcvUUUWoYRIjDnnWedI/sX0kLLxO+i4UmmSLtnDdOPf/a+iFmd8AYXD5nn4Lc0BQmI19540D2t6D9fAGKX1VP", "action": { "action": "update", "newUpdateId": "bcc2e290-60f4-4bc3-b984-dd2f00b5476c", "modelKey": "user", "id": "abf647aed3650a781ceba13f" }, "data": { "groups": [ "61b3f5c40388a703e70003ed" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ] } }, "9.092388899996877": { "raw": "AQEBAAAAAHR4nB2MQQrCMBBFr1JmbSBMZqbGG4jrHmCSTKCgrWiLFPHukizff/z3Bc3bvC5wGWB/Ft0MTgMs9pk6XEsTFDyPZOhSzNERMzpljc6fBYMEsoqp3R5rsfvNjh5726ttcy9oscwhUJJYWaqoZBoZPfz+Zbwj1AIBAQAAAAEGeJylUrFuwyAU/JUIdbI8OHLTpHxA5y7tUmUgcLFRsYnAdhRV/fcagxKiJHjogsTdvXvvcfyQyuj+YAldfJGX5a7cr/hzUW42bF2UWBfFeAqSLy7c8nUVcZxsR5Ip9Q7TSGulbr1XOxhqwATNXLWSAwaJI+Uj1sFBvYVX5EcjO+QCCh3ok+N2RooKUT1nDQzzcnc0EJJ5SmhtlObfV92quovuFq3V8ThuFMSAkPag2CnuWMsmnuBf+7jiK3QymV4+OOS3ldnZNS2xvIboFWZkChXjp4+3zztDZtGjp11CEI9El3ACEAXlU5nZxSeVFp0TT8tCyDNeIfiZrafPkNBsf/8AjvQY9g==", "action": { "action": "update", "newUpdateId": "430574e2-b9c9-4552-a5a9-08623634ef2b", "modelKey": "user", "id": "adec5334b69f56f6a6c47520" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "9.093002400011756": { "raw": "AQEBAAAAAHR4nB2MQQoCMQxFrzJkbTdp06beQFx7gKRNYUBnREdEhrm7tMv3H//tIGWb1wXOE3yeVTaD0wSLfW8DLrULbBpCJHO1kLqgWR1TEeeTJI+5pVhLvz3Waver/Ubsba++zaPAlH2kJEiaGKMEZNYoDY4/rqgkeAIBAQAAAAEGeJylUrFuwyAU/JUIdbI8OHLTpHxA5y7tUmUgcLFRsYnAdhRV/fcagxKiJHjogsTdvXvvcfyQyuj+YAldfJGX5a7cr/hzUW42bF2UWBfFeAqSLy7c8nUVcZxsR5Ip9Q7TSGulbr1XOxhqwATNXLWSAwaJI+Uj1sFBvYVX5EcjO+QCCh3ok+N2RooKUT1nDQzzcnc0EJJ5SmhtlObfV92quovuFq3V8ThuFMSAkPag2CnuWMsmnuBf+7jiK3QymV4+OOS3ldnZNS2xvIboFWZkChXjp4+3zztDZtGjp11CEI9El3ACEAXlU5nZxSeVFp0TT8tCyDNeIfiZrafPkNBsf/8AjvQY9g==", "action": { "action": "update", "newUpdateId": "2fb4465e-dc5b-4b9b-85ca-37a7329f76dc", "modelKey": "user", "id": "8593657a25b7826a4288b6af" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "9.093608300026972": { "raw": "AQEBAAAAAHV4nB2MWwqDMBBFtyLz3UCseUy6A+m3CxhnJiC0WqpSRNx7ST7PPdxzAvE2LTM8Gtg/QpvCrYFZf0OFXorAiJHojqbtSI1zWQxaZZO67FubEga05fZeRF9PPWps1W/ZploQJs2JRyQV6znKGDySC3D9AZEoJQYCAQEAAAABBnicpVKxbsMgFPyVCHWyPDhy06R8QOcu7VJlIHCxUbGJwHYUVf33GoMSoiR46ILE3b1773H8kMro/mAJXXyRl+Wu3K/4c1FuNmxdlFgXxXgKki8u3PJ1FXGcbEeSKfUO00hrpW69VzsYasAEzVy1kgMGiSPlI9bBQb2FV+RHIzvkAgod6JPjdkaKClE9Zw0M83J3NBCSeUpobZTm31fdqrqL7hat1fE4bhTEgJD2oNgp7ljLJp7gX/u44it0MplePjjkt5XZ2TUtsbyG6BVmZAoV46ePt887Q2bRo6ddQhCPRJdwAhAF5VOZ2cUnlRadE0/LQsgzXiH4ma2nz5DQbH//AI70GPY=", "action": { "action": "update", "newUpdateId": "8787aa28-13ae-44fd-80ec-93f510998680", "modelKey": "user", "id": "dcaef9cb8aed05c7db658a46" }, "data": { "groups": [ "61b3f5c40388a703e70003ed", "61b3f5c40195a703e70003ec" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*", "nvr:read:*", "liveview:create", "user:read,write,delete:$", "nvr:write,delete:*", "group:create,read,write,delete:*", "user:create,read,write,delete:*", "schedule:create,read,write,delete:*", "legacyUFV:read,write,delete:*", "bridge:create,read,write,delete:*", "camera:create,read,write,delete,readmedia,deletemedia:*", "light:create,read,write,delete:*", "sensor:create,read,write,delete:*", "doorlock:create,read,write,delete:*", "viewer:create,read,write,delete:*", "display:create,read,write,delete:*", "chime:create,read,write,delete:*" ] } }, "9.094171800010372": { "raw": "AQEBAAAAAHR4nB2MQQoCMQxFrzJkbSFt2tR6A3HtAWKTwoDOiI6IiHeXdvn+478vSN3mdYHDBK+7ymawm2Cx93nAUbtgTiHGyC4H8S763JxgJid1X1Cbqs/Yb7dV7Xqyz4g97dG3eRQulVSZKCUqmEsKws0CKvz+Y2QjxwIBAQAAAACneJxVjkEKgzAURK8ioaviIsW2Sk7RfXERk6l+mhhJolJK714lUNLNwJ95n5k3672bp8BEcWfXU1c9LurMq6aRNa9Qc76pZm1ZMGnMDd5SCOTGxI+LFx5SiyPbAEMLFsIq1OZF7NYckIhy9RRRahhEiMOedZ50j+xfSQsvE76LhSaZIu2cN049/9r6IWZ3wBhcPmefgtzQFCYjX3njQPa3oP18AYpfVU8=", "action": { "action": "update", "newUpdateId": "66524446-72a1-417f-a073-ac890dfdd170", "modelKey": "user", "id": "bc3dd633553907952a6fe20d" }, "data": { "groups": [ "61b3f5c40388a703e70003ed" ], "allPermissions": [ "nvr:read:*", "liveview:create", "user:read,write,delete:$", "bridge:read:*", "camera:read,readmedia:*", "doorlock:read:*", "light:read:*", "sensor:read:*", "viewer:read:*", "display:read:*", "chime:read:*" ] } }, "10.814676899986807": { "raw": "AQEBAAAAAHR4nB3MwQrCMAyA4VcZOVvY0qUxvoHs7AM0TQYD7TwURYbvLvX6w/cfkEvb9gqXAbIZnAao/r49LTe/Wq+ROBXxGERlDXMiD2fXMSCuWTURIktnj938vvinG395bT1u/wWO0dikJJaJdaboSWlShe8PDQUjSgIBAQAAAADyeJxtUMtKxDAU/ZUh6xlIX+nErXZAlFEYXYmLPI5YaJOSJuIw9N9NymgpuMt53HvPyYX48wBysyEjzGjd0wADTbYRe+F8FDJWFrSqar7nRR55GP0POyrr0hqa3n2cvIOH8i9x+Rjpt/c133zB+D+hhxdaeBHx5ZrjXs/A4ztlIDQHuOT8I6s140XJQNVeUUqm7W/yo+ixmrlVnwibxyBgZtu16GJ4PTzvTs3x1Mxyb4OZ86482lpHpqSruN+liCZ0XcRDLNP61pqFCiPcgtpUgeS00LXmitU8q2VZFWCyyqQk80mN7gHn5EP6ETL9AP7zeKk=", "action": { "action": "add", "newUpdateId": "3576c9e3-9b9f-465e-8eb0-22fabb652279", "modelKey": "event", "id": "203d7d9c67917b453e6b51bb" }, "data": { "type": "sensorOpened", "start": 1643055798932, "end": 1643055798932, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Ccheu Luaen" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "door" } }, "camera": null, "partition": null, "user": null, "id": "203d7d9c67917b453e6b51bb", "modelKey": "event" } }, "10.815824599994812": { "raw": "AQEBAAAAAHd4nB2MUQrDIBQErxLedwU1ifp6g5DvHkCfGwi0WpqUEkrvXsznzjD7pSj7WgtdO3o/c9xBl44KPrdzTLmJlJBFj1BmMVCDt0GF4ESJ7W30bjRJuGWPmnGfcbRmQ9nqq9H1/NAW4MS8GJ8d94ODliBa0+8PCGslcAIBAQAAAAA9eJyrVsos9i9IzUtNUbJSKCkqTdVRUMoH8oNLEktKi50zEvPSU1McS4CShmYmxgampuaWFpbGRrUAH5ARuw==", "action": { "action": "update", "newUpdateId": "bbedc05e-1f1e-4728-886c-c232a7651bc9", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "isOpened": true, "openStatusChangedAt": 1643055798932 } }, "11.673517499992158": { "raw": "AQEBAAAAAHN4nB3MQQrCMBBG4auUWRvIlCZNvIG47gFm8k+goKmLoIh4d4nbB9/7kJS+H43OEwlAp4mavbYHpNsFo/oCwVrhlEN0i4/FKfzsQpIqIfsUMw92P2C3q72Hsae1PuL+X2QJzHOFQUVXTskgUpPS9wc7siSsAgEBAAAAAK54nFWOuw6DMAxFfwVl7kB4JTC3U9duVQcndiQkQhCYSgjx700YWnXzOb66urvgbSLRZcIH7sMoLplYGGaOSjZVmde10loVefI2zCl63j5mrsRk+RELlqifr39/e9PI34cnBgSGyPsR2YKnOZGQtoUCTYWuIYdQKSjrvDWUpkyxrT93ddm4DkNU60Lzj3pMFS3UUhYOCQ0YJbUmBHDapAofkIY7bSlHaZI4Ps8RSuU=", "action": { "action": "add", "newUpdateId": "0cdad7fd-b156-406c-bd02-58afa5908691", "modelKey": "event", "id": "9a5112fdedbab7188edaaf8b" }, "data": { "type": "motion", "start": 1643055788720, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "id": "9a5112fdedbab7188edaaf8b", "modelKey": "event" } }, "11.772577999974601": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm20Ca5ukOxG8XcCczhYJtRSoi4t4l+bzncO6XUI9l3+g80OshOJROA236vvVxkSY4l4wQnQlWkvEIbIpOznAeY47Jw9qerbvo/aqf1lSs+kSjS/8Ya4ET9jJHnQU+YQq2sNLvD83qJWoCAQEAAAAAx3icXVDLCoMwEPyVkLOH+Ei0PffQSy/1WDwEXVqhNRBXQcR/7zRpDxY2MLszkx12lU878sVx7wZ5FKkpcqV1WVVlphIh+zFSJ2JqmTpI2E8EhmYauGbLI2arfP1+WCW7zi5ARQGZncnbO6E1B7SfZSe7fDw3XSZCY5hmJgKj8WDSeQOppxYbzm7yUZ2IDDRYOBKhAi4C1qEwyZsNxvFlPe+SqF0Q9ZdDBe+umm2LCZzv+uFetw/qpicFeRPOcv1x34tsb+ubXFs=", "action": { "action": "update", "newUpdateId": "b898a562-50d7-4a5b-9e32-b8168674a00e", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055788720, "isMotionDetected": true, "eventStats": { "motion": { "today": 44, "average": 69, "lastDays": [ 57, 59, 126, 59, 65, 64, 53 ], "recentHours": [ 5, 2, 6, 5, 1, 0, 6, 4, 1, 5, 5, 0, 3 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "recordingSchedules": [], "isRecording": true } }, "11.967715699982364": { "raw": "AQEBAAAAAHR4nB3MTQrCMBCG4auUWRuY5qdJvYG49gCTzBcoaOoiVES8u8TtC8/7ISl92xudJxJVOk3U8Lo9VTouOmr0bl5nH0zlXI23KiatLpgcYoZFXBB4sMeuuF/xHgYHWh9x+y/EM9uawOC0MMSVmIt3Qt8f+e8jVwIBAQAAAADoeJxtkE9rwzAMxb9K8bkDpUndZOdlMMbKRtvT2MGxFQiL7WCrZaXku1cO/UNhN733JPGTToKOA4rnmYjoog8fnjrvxJw1qUAcZLLIYbksATKQ7KMz/7hR+5DWQKotT74goaYtL49sf/88+vUBHd0Ci6SMIsX6dOF4M5Mg/EsMAhaIVVNVbbYyssoLiaBLDSDG+ZV8rSw+zHw1v7P60NpmmLoud97z3evn06Zeb2oxplzzfEgIbt/3rAeG7aZn3Kx9xHBXXUIUqgBYtCUCQikBVa5XjS5ylV5ovcH+HY+pD9PFYjwDJ0xubw==", "action": { "action": "add", "newUpdateId": "74319145-f0bf-42da-8935-b57be2e76e50", "modelKey": "event", "id": "a4002f8e0e0860ea3c7bc43a" }, "data": { "type": "sensorMotion", "start": 1643055800106, "end": 1643055800106, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "02ee9b99f17d69346e0c8c00" }, "sensorName": { "text": "Qbk Evfmbp" }, "type": { "text": "UFP-SENSE" } }, "camera": null, "partition": null, "user": null, "id": "a4002f8e0e0860ea3c7bc43a", "modelKey": "event" } }, "11.968772899999749": { "raw": "AQEBAAAAAHZ4nCWMQQrCMBBFr1JmbWCapNOONxDXHiBmfqGgidiKiHh3iS7/e7z/ppS3pRbad/S4WdpAu44KnqffOFgTHLwM0SfnJ1UXZ8BpkN6lKCZjHjwCt+xaDZcjXq1ZUdZ6b3T5f3hAz6pzP5poiALOU2amzxe02SSHAgEBAAAAADl4nKtWyiz2zS/JzM9zSS1JTS5JTVGyUigpKk3VUVDKRRF3LAHKGJqZGBuYmloYGBgamNUCAJsOE7k=", "action": { "action": "update", "newUpdateId": "0326542a-2899-4fee-9361-a46d67c52e30", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "isMotionDetected": true, "motionDetectedAt": 1643055800106 } }, "12.870153600000776": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbWBq05rxBuK6B8hMfqGgidiKiPTumi7/+7z3pWjrXDKdG3o9UlxBh4Yy3uM+LqkeoYuJg5izYN55teA0TuZaY+l7DQC0aveScLviU50FeSnPSue9wUdAVGRqT2mQzg/gf42Zth8zyCY0AgEBAAAAADp4nKtWyiz2zS/JzM9zSS1JTS5JTVGyUkhLzClO1VFQykWRcCwBShmamRgbmJpaGBgaGFjUAgCuyRQG", "action": { "action": "update", "newUpdateId": "83ad089c-c8c4-4bc8-bafc-1c0955b8eeeb", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "isMotionDetected": false, "motionDetectedAt": 1643055801008 } }, "12.894433700013906": { "raw": "AQEBAAAAAHR4nB2MWwqDMBBFtyLz3UAmxqR2B6XfXcC8BKE+EKUUce8l+bzncO4JJPu4zPBo4FiVdoNbA7N933U8tQhBTVnuwbWI4iJldKzeOxbT3LGEQKlk06L2edmvNjTZRoWO9QOlp6AcdUg2KMVMbed7Nrj+BtgmOQIBAQAAAABMeJyrVspJLC7xzS/JzM9TslIwNDMxNjA1Nbe0tLAw01FQKsvPKUlMTwXKGJnrGQMFilKT84tSMvPSg5MzUlNKc1KLgXLRsbUAD70WHQ==", "action": { "action": "update", "newUpdateId": "c1d67c82-311c-4a71-bd00-bced75bc22a6", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055799886, "voltage": 27.3, "recordingSchedules": [] } }, "12.914624699973501": { "raw": "AQEBAAAAAHV4nB2MQQqDMBBFryKzNqCDSSbeQLruASYzExCqFrGUUnr3kiz/e7z/BZZrPXaYO3g9lS+DvoPd3vc2Fq1CNJMXVOdTYjcFjo58ITeyEEaZIkWp2XaoPW72aQ1vdnKla/swLGVgtZBtKMiYLYeRQoLfHwdfJgoCAQEAAAAAMnicq1Yqy88pSUxPVbJSMDLTM9FRUCpKTc4vSsnMSw9OzkhNKc1JLQbKRcfWAgAz+Q5S", "action": { "action": "update", "newUpdateId": "cdb85c2d-599a-46a7-85f8-1ac827c4787c", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "voltage": 26.4, "recordingSchedules": [] } }, "13.97603199997684": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBG4auUWRtoMhPTeAPp2gMknT9Q0NRFqIj07hq3D773obS0dat0GSip0mmgitftqanhqr0KR14m680ZnI1AYbIb2Xi1TsKE0aJ09tgU9xnvbrCjth7X/yLbiOhKtkWcSPohiRx8oOMLB7MjEQIBAQAAAAC3eJxVjrEKgzAQhl9FMncwmpjGuZ26dmrpcCYnCGokngUR370Xl+Jy8H//3cdtgtYJRZ2JeYBINyR09AojigsjYsSdrFSZa20sD524CzHd6OJyunuyamb+FhPGOYzic+7vXxzpWEjFgAQeCDhvO2cHA8aUhHQWCt8o31bYelAGSp3b5vhpYltHHcvrbFz6ntEyY/ynzidFIy3aom1kqwqlwFxR2dJokxRD8Ng/cE17mF4S+w8R2VA/", "action": { "action": "add", "newUpdateId": "4393c815-6e3b-4ede-b203-5d12478e01ef", "modelKey": "event", "id": "b19e92fb1f4244a78e493757" }, "data": { "type": "smartDetectZone", "start": 1643055790555, "score": 52, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "metadata": {}, "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "id": "b19e92fb1f4244a78e493757", "modelKey": "event" } }, "13.976735699980054": { "raw": "AQEBAAAAAHR4nB2MQQoCMQxFrzJkbaEtKU29gbj2AMkkhQHtuKiKyNx97Cz/e7z/A577sjY4T/B6KneD0wTNPrdjXHSIVD0W0ugkkjicDZ34hA6zN8P8N4VG9ljV7lf7jsbe1vqAy3FROIUQq5oKSw5EpsyVBLYdtysliQIBAQAAAABHeJyrVipOzi9KVbJSsDTRUVAqzk0sKnFJLUlNLnEtS80rKQZKRCslGVqmWhqlJRmmmRiZmCSaW6SaWBqbm5orxdYCAI6mEzw=", "action": { "action": "update", "newUpdateId": "5f0498d2-b28b-4ce4-b054-470ee478d298", "modelKey": "event", "id": "9a5112fdedbab7188edaaf8b" }, "data": { "score": 94, "smartDetectEvents": [ "b19e92fb1f4244a78e493757" ] } }, "14.750952099973802": { "raw": "AQEBAAAAAHh4nB3MUQrCQAwE0KuUfLvQdTc18QbitwfYTVIo1kakIiLeXbefM8ObDxRZJ1/g2MHzrmU12HWw2OuyhZO2oTfJJBxD5MwhJ8LAWIdg48FyKpUVS2M3V5vP9m5G3R+zy7X10/YSheLeqB914D/CiAmrJYLvDzRdJd4CAQEAAAAAHnicq1bKyU/ODi5JLCktVrJSUPIPcPVTqgUAWUAHOg==", "action": { "action": "update", "newUpdateId": "0ec48c91-1949-4385-95b6-ef7e43ab9d5a", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "lockStatus": "OPEN" } }, "14.751857800001744": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBA0auUWRtIkzGZegPp2gNMMjNQ0NRFUKR4d4nbD+8fwLVve4PLBCwCpwmavm9P4a5XGTXHWnmZxRFZdYg+uuJLcEqYMsXzEpIO9thF76t+htGXtj7i9l94McyBjcXmqIbJCpXkE3x/E8Ej0AIBAQAAAADheJxtUMFOwzAM/ZXI5x3Sps06zgOEQHDhhjhkiTNVtEmVeohp6r9jc1g1CZ/ynt+zX3wBOk8IdwpCzmXI/uttwoQBNgpmcoW4VdnG6LbtdN3tDPOYwj/s7HORQVreIzv3SOjpncfPTH983vL335jo2hiRXHDkGF+uSZ7CHyT8kRRgK5SK2gTcBW1wq7VvTQ3LZk3/6ka8cT264o6oHkpOpPasUi8sg0VMnsVFdqbTMDCeOF1PfU4rdZqxrKiXRKBDbLa1iy7EymBsbDx0B6ut3GzMAYdnPIsO5Yuw/AL2z2z9", "action": { "action": "add", "newUpdateId": "73cca91d-88fc-4403-b0b2-e8467835926e", "modelKey": "event", "id": "0df472afadf13ef46fb8b606" }, "data": { "type": "doorlockOpened", "start": 1643055802893, "end": 1643055802893, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "doorlockId": { "text": "61eeeeef03de9d03e700c532" }, "doorlockName": { "text": "Garage Front Door Lock" } }, "camera": null, "partition": null, "user": null, "id": "0df472afadf13ef46fb8b606", "modelKey": "event" } }, "15.305036499979906": { "raw": "AQEBAAAAAHR4nB2MXQrCMBAGr1L22UDNT5P1BtJnD5Bmv4WCpj6EikjvrvFtGJj5UC5t3SpdBsoidBqo4nV7Sm64SrdJvViri8GowXiZgkk/NEt0zIxQPI89e2yC+4x3b7Cjti7X/4JdnBCiLZpYzxHqXHZaQMcXJh0j8wIBAQAAAAC4eJxVjzEPgjAQhf+K6exQWmnBWSdXJ41DKa8JCVBSDhNC/O9eWYzj+97du3eboHWCOB/EPLhEFxA8PeIIcWREjNgrzEnLsrS1koXM3MeUd2x1/Nu7c9TM/CkmpDmO4vXvX98YaR/IxgByrSPHevuw9m5AykpAhSBdC9NABuVUg8YUlalzp4nTOuo4/HwYl75ntMxIP9W1OaLW1qC0yoeqDoVF0Nrp4Pe3htiiv2HdT+VK4vMFHINQzw==", "action": { "action": "add", "newUpdateId": "8f4d22fb-e0f5-4d65-8e0f-b73999e5c490", "modelKey": "event", "id": "9376e572cf89f17ef33a3fce" }, "data": { "type": "smartDetectZone", "start": 1643055792010, "score": 78, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "metadata": {}, "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "id": "9376e572cf89f17ef33a3fce", "modelKey": "event" } }, "15.53730899997754": { "raw": "AQEBAAAAAHR4nB3MQQoCMQxA0asMWVto2pi03kBce4C2ycCAdlwURcS7S91+eP8DpY1t73BaoKjCYYFur+tDy7CzzhqLZKZILnBKjmoVV7OIQ5RVFZEp8GT3Xe12sfc09rQ+Ztz+C4rRxPMaciucvBJ61GMz+P4A0RsifQIBAQAAAACveJxVjj0PgjAQhv+K6exQ6AfgrJOrm3E42sM0oUDKYUII/92rg8btnufee3OboHVCcTqIFIanOB7ETJCIRWG1ksbUUpmqyt6NKQdlniNnzkjo6MbnM+v7499fXjjQdxGRwAMB87YzO4iYMonCNVD6VvvOYudBV6CMbFrMr0zcFiiMAweHpe9ZLTOmHwWfK7RSWEnblY0DW0uvC1l44z4VcfTYX3HNOcwvif0NzQVI2Q==", "action": { "action": "add", "newUpdateId": "3a796434-2688-4bb7-b977-117fdd116426", "modelKey": "event", "id": "433e706f29ca680d4101d5ce" }, "data": { "type": "ring", "start": 1643055803577, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "1c9a2db4df6efda47a3509be", "partition": null, "user": null, "id": "433e706f29ca680d4101d5ce", "modelKey": "event" } }, "15.538912400021218": { "raw": "AQEBAAAAAHZ4nB2MWwrCMBBFt1Lm20DSzMTGHYjfLmA6DyjYVqRFRNy7pJ/3HM79Ass2rQtcOtifypvBqYPF3vdjXLWJKBx7Iw80lBQwK4VaMgXEmLzkIQl7y+ZV7XGzT2uEZ3txo9PxkaRyryOqF3NlPHOmWEeD3x/IsSVeAgEBAAAAAGl4nKtWykksLvHNL8nMz1OyUjA0MzE2MDW1MDAyN7LQUVDKLA7OTSwqcUktSU0uSU0BqigpKk0FSoB0BWXmpSPrMTY1NwdKFaUm5xelAOWCkzNSU0pzUouBiqJjwaYFweSgJtUCANG0KAk=", "action": { "action": "update", "newUpdateId": "0ca02e5f-5861-43d5-9635-4401f6381caf", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055802728, "isSmartDetected": true, "lastRing": 1643055803577, "recordingSchedules": [], "isRecording": true } }, "15.573490199982189": { "raw": "AQEBAAAAAHV4nB2MQQ4CIQxFrzLpWpIitAzewMzaA2ApySTKuEDNxHh3heV/P+99IElbtwqnCZ6PnJrCYYKq78sY59wPRXKIpGaWKxmfXDBRIxsSz/aPApfStfuW9bboPpyX1tbhOhLeOQ3I5Rgl8YzZW7SZROH7A63zJJICAQEAAAAAHnicq1ZKzUtRslIwNDMxNjA1tTAwMTU3rwUAQcwFdQ==", "action": { "action": "update", "newUpdateId": "e053005e-8cb5-4a37-9e96-5c461cb576ff", "modelKey": "event", "id": "433e706f29ca680d4101d5ce" }, "data": { "end": 1643055804577 } }, "15.84417029999895": { "raw": "AQEBAAAAAHh4nB2MWwrCMBBFt1Lm20AzebsD6bcLyGQmUKyNlEoRce+Sft5zOPcLuexzW+E6wPvFeRe4DLDKcT/Hjbsg4mpsMQo9ZmUdkko2VIVxxCCYAiXfs2djWSb59IZb25ZWHp3P54suUaPEsbJPJpPTzjgSE+H3BzE7JaACAQEAAAAASnicq1ZKyilNLcnPL8lwzs/LS00uyczPCy5JLElVslKoVirOTM9LzAksTczJLKkEipia6ihABYNLilLz0ksygKK6Zha1tQADlBo3", "action": { "action": "update", "newUpdateId": "bbdf34c3-262a-452b-947f-28027e297b96", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "bluetoothConnectionState": { "signalQuality": 55, "signalStrength": -68 } } }, "15.85507009999128": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSBpatJ4A3HtASYzP1DQRGxFRLy7pMv/Hu9/iWVbWqXTQK+H8gY6DFTxvu7jrF2ELEGFnYkZRzOVmU3m6Ix4DQXRMkbfs3tT3C749GZFXduz02X/sCOQckrFRQ3JTwFWZrGWfn85uiYnAgEBAAAAAEp4nKtWSsopTS3Jzy/JcM7Py0tNLsnMzwsuSSxJVbJSqFYqzkzPS8wJLE3MySypBIqYGOkoQAWDS4pS89JLMoCiuubGtbUAAyAaLw==", "action": { "action": "update", "newUpdateId": "6bc6dca1-7be5-4f8a-ba71-c3d6fe70ae23", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "bluetoothConnectionState": { "signalQuality": 42, "signalStrength": -73 } } }, "15.856947600026615": { "raw": "AQEBAAAAAHd4nB2MQQrCMBBFr1JmbWCmSZPWG0jXHmDGmUBBE7GVIuLdJV3+93j/C3zbllrg3MH7qbwZnDootl+PcdEmMAyRQk4uKSYXKLITjd7xhD57NjTClj2q2n22T2tWK2t9NbocHz0KWT/KxEMWzYQRJScd4fcHzHklTgIBAQAAAABKeJyrVkrKKU0tyc8vyXDOz8tLTS7JzM8LLkksSVWyUqhWKs5Mz0vMCSxNzMksqQSKmJjrKEAFg0uKUvPSSzKAorrmhrW1AAOcGjI=", "action": { "action": "update", "newUpdateId": "045614f7-7d07-416a-bd63-a903f3ae0e10", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "bluetoothConnectionState": { "signalQuality": 47, "signalStrength": -71 } } }, "15.857984500005841": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbWCmmSSMNxDXPcCYTKGgidiKSPHuki7/e7y/g+ZtaRXOA7yfRTeD0wDVPtMxLqULJAqBxbvIKI7Rk1NM7FDmkVhL9Cw9e7Ri96t9e7NaXdur0+X4CJluRDGPYmwhaMLZS/IRfn9xMyN8AgEBAAAAAEp4nKtWSsopTS3Jzy/JcM7Py0tNLsnMzwsuSSxJVbJSqFYqzkzPS8wJLE3MySypBIqYmeooQAWDS4pS89JLMoCiumYmtbUAA6MaNA==", "action": { "action": "update", "newUpdateId": "01155493-6409-4031-a074-09f214ad6349", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "bluetoothConnectionState": { "signalQuality": 65, "signalStrength": -64 } } }, "15.959116899990477": { "raw": "AQEBAAAAAHV4nB2MQQrCQAxFr1KydmCcSSfRG0jXHiCTRCjYVqRFinh3mS7/e7z/BdF1XGa4drC9TFaHUwezf+7HuFkTTmT2QA5OlQMipXCpUUPuMyW2WNSwZdNi/hx8b43K5G9pdDw+FEvSSFatZDGOvVA+sxL8/uhLJRMCAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "e77ddf48-e7b8-4472-9b0c-353728d06cd4", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [] } }, "15.993363299989142": { "raw": "AQEBAAAAAHV4nB2MQQrDMAwEvxJ0rg9JrEjuD0rPeYAsyRBoklJSSgn9e7GPO8PsCaLHsm9w7eD9NDkcLh1s/pnbuFkVLL17RAxEWEKUUQJPEUOZWBOjJy1Ss3U3f9z9WxuV1V+NLu1D8ugDlcEQixj3ZKKUc4LfH/jKJhUCAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "8a1ee455-775f-4a3a-8645-f68c985e9cfa", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [] } }, "15.99451470002532": { "raw": "AQEBAAAAAHZ4nB2MSwrDMAwFrxK0riGKHVfuDUrXPYBkyRBoPoSUUkrvHuzlm2HeDzgf07rArYP3pnwYXDpY7PNs465VRNTBOESHgugCeXR8leBE+uBLwuKZazavaq+HfWuTeba90al9lD4rjkJimswTpTRQZMrwPwHkUyVhAgEBAAAAACJ4nKtWKkpNzi9KycxLD07OSE0pzUktVrJSiI6tBQCGtgms", "action": { "action": "update", "newUpdateId": "61d2ea46-1b11-4831-a7b4-bb043f91f3aa", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [] } }, "15.99775859998772": { "raw": "AQEBAAAAAHd4nB2MUQrCMBBEr1L224Am2SbrDaTfHmCzu4GCbUVapIh31/RnYObx5gMs67jMcO1geyqvBqcOZnvfj3LTBnzSJBjUVcLqovTFZf4HZgqBSbI3bNq0qD0G25sjPNmL2zoeHxch9lqi1t6qckwc8EzF4PsD/zEmDAIBAQAAAACheJxNjE0PgjAQRP8K6VmTIhQ/rpw5KEfjoSkL3aS22i4xhPDf3caLx3nzZlbhdKIuEAYvLkXZ1JVU6iSrszzvChHBhDign3pjYZgdJJbuD24+OGIbvAeTpz1pAq5WYaxm6PKXqtgbI7xn8GZhoo6NYvSyy+2nH6TknHDy2l1n7ZCyVv7RniL4iSzjvaozTjhwEF2IhKlo9ROiFtv2BeDFPvo=", "action": { "action": "update", "newUpdateId": "27d7c53d-f95f-4c6b-8a6b-58933a9c82e5", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055803909, "recordingSchedules": [], "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -54, "ssid": "Mortis Camera" } } }, "16.000480599992443": { "raw": "AQEBAAAAAHZ4nB2M0QrCMAxFf2Xk2UKb1tj5B+KzH5A0CQzcJjIREf9dusd7Dud+gds2rQucB3g9lDeDwwCLvW/7uGgX2JwdKYZapYQSWwoSs4dqacx0OqqU3LN5Vbtf7dObxrM9udNp/zB0j6xGYtGRUUwoVRrh9wfu+yW8AgEBAAAAALp4nGWPOw/CMAyE/0qVGaSWNLxWWBmgI2KIUrexlCaQOEII8d9xeS1s9nd38vkunE60C4TBi3VRzWtZKrUspVyoSSEwNYOOtAUCQ9Cyg2IGFs72dtAEDGrJawQTYou+b4yFNjtIrBxPrFyxw03wnvN8oqF36C6M1Qwdz3N2dREuGby58T6r5eL/QsLea7fP2iGNrqosf7ShCL4ny3iqViNOOHYVuxAJU7HRA0QtHq+HDt+qn2ceT4+PUmc=", "action": { "action": "update", "newUpdateId": "2cfaf260-88b4-40c1-b03f-8e193675db43", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "lastMotion": 1643055803375, "isSmartDetected": true, "phyRate": 43, "recordingSchedules": [], "wifiConnectionState": { "channel": 6, "frequency": 2437, "phyRate": 43, "signalQuality": 100, "signalStrength": -59, "ssid": "Mortis Camera" }, "isRecording": true } }, "16.00195479998365": { "raw": "AQEBAAAAAHZ4nCWMQQrCMBBFr1JmbaDJJM2MNxDXHmCSmUDBtiIVEfHuEl3+93j/DVL3eVvhOMDjprIbHAZY7Xn5jZN2EXVKbWrVJZLsovfoiqXiLOaRORBSaD1bNrXr2V69qbLYXTqd/x+CiMqMzciCUcyhVB49fL7WXyTBAgEBAAAAACJ4nKtWKkpNzi9KycxLD07OSE0pzUktVrJSiI6tBQCGtgms", "action": { "action": "update", "newUpdateId": "4d65f6fc-58a7-4113-be5b-e4709928382f", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [] } }, "16.00301149999723": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm20CYZJLWHYjfLmBegYJtRSoi4t4l/bzncO4XWPd5W+E8wOthvDucBlj9fTvGxbqIEjEhcSiRJWRSClNTDiwtVh1VMUrPls38fvVPb5QXf3Kn8/FBY2GpiiIlY9LUzHL1ieD3BwT0JdwCAQEAAAAAPnicq1bKSSwu8c0vyczPU7JSMDQzMTYwNbUwMLa0MNNRUCpKTc4vSsnMSw9OzkhNKc1JLQYqio6tBQDcUxFq", "action": { "action": "update", "newUpdateId": "0b02325a-60ab-45c5-9fca-abf07c8cc20b", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055803986, "recordingSchedules": [] } }, "16.00560029997723": { "raw": "AQEBAAAAAHV4nB2MWwoCMQxFtzLk20KT1j7cgfjtAmKawIAzIzIiIu5d2s97Dud+gWWftxVOE7wejXeFwwSrvq9jnFsXYiEKVnGkXFzk4h2nQI6RrGJuqabcs2Vrer/oZzS86JM7nccHqoZSj3qLaoTZ+5isGgn8/uVRJRACAQEAAAAAoXicTYy7DsIwEAR/JXINkvNwgmipU0BKRGE5l/gkY4N9EYqi/Dtn0VDu7OxuwulEfSAMXpyLsm1qqdRJVl1bHQoRwYQ4op8HY2FcHCSW7g9uPjjhJXgPJk8H0gRcbcJYzdDlL1WzN0V4L+DNykR1rWL0suvtp1dSck44e+2ui3ZIWSv/6EAR/EyW8bHpMk44chB9iISpuOgnRC32/QveQz74", "action": { "action": "update", "newUpdateId": "cf34c19c-2ea8-4a80-a632-a12f917d6967", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "lastMotion": 1643055802762, "recordingSchedules": [], "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -47, "ssid": "Mortis Camera" } } }, "16.108367700013332": { "raw": "AQEBAAAAAHd4nCWMQQoCMQxFrzJkbaFpm9Z6g8G1B0ibCAPaijMiIt5dqsv/Hu+/geu29AaHCR434U1hN0HT5+k3ZhmCXInFRjQkqCZUtzccvDXCvhQSdSnkkV276OWor9Gs2tZ+H3T5f1QsiLG6rEGJONmzz8lH+HwB5jsk+AIBAQAAAAA6eJyrVsos9s0vyczPc0ktSU0uSU1RslJIS8wpTtVRUMpFkXAsAUoZmpkYG5iaWhiYGJka1QIAruMUCg==", "action": { "action": "update", "newUpdateId": "52b6b061-5d1e-4c28-a430-da3bb5de2749", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "isMotionDetected": false, "motionDetectedAt": 1643055804252 } }, "16.214602899970487": { "raw": "AQEBAAAAAHV4nB2MWwqDMBBFtyLz3YAmmky6A+l3FzDmTkCoD0QppXTvJfm853DulySd87bSvaFrh5xKt4ZWfT/rGFEEB0buWm8wsJjewhrRAONi70KGtyo1Wzbo66Gf0iRZ9JBC5/qR24RumHhSRHXMMVr2wol+f/1eJbkCAQEAAAABQXicbVJNT4QwFPwrpmdjWvrtUeNNDxuOxkMX3kJjLdgWdbPhv1vAsF/e6My86bwpBxSg6kJtfVNWLdSDg4jub17fbm9QTCZNhwMKPw/7NBNESUEFE1LqrEgrLjHGilOOhSgy8W13dp6sWuM9uPztB+cyswvwOYCv9kfIWf9e9gD1y7aPRzjaxhu3GYyzaVJzvIJlCuCb1GYUjxndmpQg7OcbewgV+GQaOFrZ+Nia0OQlM7YzLsJk5QD6Mu84CVFtY9XlqFWCGk2eX7aGbtl+bSiZkKYSBNVEUcULLWWWroInX880o5hzqZhW4pSe55835w66uHD4EyweWuulafsBzvQR/snAyKngLAPP80pdzV9muHI4zUAwz287zj9EF5ZeD2iIsNyDKdaCM1rQeZGlTkb4HcFKaoap1ISN4/gLgRe4Kg==", "action": { "action": "update", "newUpdateId": "878df106-d58a-42d2-ae7d-39437fd62eae", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 18763646779, "txBytes": 700085350662, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852977, "recordingEnd": 1643055784986, "recordingStartLQ": 1639183852992, "recordingEndLQ": 1643055799979, "timelapseStart": 1639183852941, "timelapseEnd": 1643055599988, "timelapseStartLQ": 1639183852941, "timelapseEndLQ": 1643055105008 }, "storage": { "used": 1603096543232, "rate": 415.108794037914 } } } }, "16.288119599979836": { "raw": "AQEBAAAAAHZ4nB2MXQrCQAyEr1Ly7EKa7q83EJ89QDZJoWBbkRYR8e66fZyZ75sPsGzTusC5g/2hvBmcOljsdTvCRduQECmqBid9KM5jqS4XRkc5Mgrl3I+pafOqdr/auznCsz25tdPxEf5sTUK1Rk+DDKOqT1YCfH+ydySuAgEBAAAAAUF4nG1SwU7EIBT8FdOzMUBLAY8ab3rY9Gg8sPC2JSKtQNXNpv8utNrd1b3BzLxh5oVD4UH1XhvXNqoDPVoIxe3V88v1VRGijPlyKPzX3T7OBMFEUCJqwlhSxBXHCBGOao4rzDPzaXZmHlWddA5sOrvR2sTsPLyP4NT+CFnjXpsBQD9th3CEg2mdtJtRWhOzmqIVbKIH18YuoWhK6FbGCH4/vziAV+CibOFoZcJ9J32bWiZsJ22AbGUBhiaVzMJCm6D6FFVF0EX2/DAa+qX+uqIofcxt6yrV5awqeY2TdBU8OL3QJaKUI8TRGT3PP25OHaqy5H8cfgU/HmxZtXkDK4cAFzJQfio4y1DxlAH9mz/PcMHhNANllAk+zT+i98teD8UYYH5HJANRY1wTUuUiyzoZ4jekxIIgzEsqxDRN36uiuCw=", "action": { "action": "update", "newUpdateId": "70026dd5-c159-409b-89a0-286a0c2881f7", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 21295296277, "txBytes": 1002806814187, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640288743861, "recordingEnd": 1643055800801, "recordingStartLQ": 1640288744338, "recordingEndLQ": 1643055800777, "timelapseStart": 1640288743858, "timelapseEnd": 1643055480800, "timelapseStartLQ": 1640288743858, "timelapseEndLQ": 1643055575798 }, "storage": { "used": 1987496116224, "rate": 708.231920183599 } } } }, "16.302973599988036": { "raw": "AQEBAAAAAHV4nB2MQQqDQAxFryJZdyAzajL2BsV1D5AmGRCqlqIUKb17GZf/Pd7/gug2rQtcG9hfJpvDpYHFP/dz3KwKHooRpRhEGUOHGYOwl0BDn4w1umip2byaP0c/aqMy+1sqnc4P7Sgpsj2MWrGMvXAbszL8/vNdJWcCAQEAAAABRHicZVLBUsMgFPyVTs6OEyBA8FjHmx46OToeaHhNGJFEIGqnk38XiJO29hZ29y27j5wKB+3glLZd0/agJgO+eNi8vt1tCh9kSIdT4X62x5AJXFcVLxnHJYuKsOKI1qQWhCGBhIjMtz7oPNr20low8dtOxkTm4OBzAtsez5DR9r0ZAdTLfvRn2OvOSrObpNEhqWm5gk1wYLvQR7ScI7qXIYA75htHcC3YIDs4W2n/2EvXxZYRO0jjIVkZgLGJJZOwUNq3Q4zaBlBF8vzSCoal/rqiIF1IbRkRKBamWOQ9rIInqzJdkZJSzihC6JLO88+7a4drSXT4EyweohKcp1XrDzBy9HCboa7rS8FVBsopYvRm/l+GW4fLDDgqOJ/zHzG4Za+nYvKg8qMgjDGrSVURnIos60ScknvEGGY0EqSa5/kX7Y24Rg==", "action": { "action": "update", "newUpdateId": "79fd6621-ac70-4080-a7ef-6952d7c1eacf", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 28447067206, "txBytes": 1583893619199, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852906, "recordingEnd": 1643055765111, "recordingStartLQ": 1639183852911, "recordingEndLQ": 1643055794977, "timelapseStart": 1639183852888, "timelapseEnd": 1643055575165, "timelapseStartLQ": 1639183852888, "timelapseEndLQ": 1643055285277 }, "storage": { "used": 5012226834432, "rate": 1753.16626544334 } } } }, "16.306021399970632": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbSDpBJPxBuLaA0wyP1CwrUiLiHh3SZf/Pd7/ktZtWhe6DLQ/TTfQaaAF7/sxrtZFaaF689UVhXcxC5xkia5ZgSKcmVPq2bwaHjd8elN1xks7nY6PqMxsItyQMSLHNJYqPtDvDxu2JaMCAQEAAAABSHicbVLLTsMwEPyVKmeENn7EDkcQNzhUOSIObrJNLIwTbAeoqvw7dorSFHqzZ2bHMysfM4d17xpt26rusBkN+uxu8/J6s8l8UCFdjpn7vj+EmSCCE8KglIxHRVjwnJVCUMJEQXIRmS+91/No3Slr0cSzHY2JzN7hx4i2Ppwho+1bNSA2z7vBn2GvW6vMdlRGh6TmsIBVcGjb0EUUpojuVAjoDvOLA7oabVAtnq20f+iUa2PLiO2V8ZisDOJQxZJJmDXa132MWgdssuT5qRvsT/WXFQXlQmpb0DKXVHJSljJKF8GjbWaaUeBcCCBQrul5/mm7dqAA7I/Dr2D2kAC5zNOq9TsaNXi8loGvBRcZCgCSs3/zlxmuOKwz5ACUF9P8I3p32usxGz2md2J+zoDSglNRpCKndUpKbhkImZcSClHKaZp+AMNzuCE=", "action": { "action": "update", "newUpdateId": "bf1c0d0c-bae0-489e-9894-fdbeae163377", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 27522409845, "txBytes": 1497732476217, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852998, "recordingEnd": 1643055770209, "recordingStartLQ": 1639183853004, "recordingEndLQ": 1643055800181, "timelapseStart": 1639183852995, "timelapseEnd": 1643055600214, "timelapseStartLQ": 1639183852995, "timelapseEndLQ": 1643055100356 }, "storage": { "used": 3005403365376, "rate": 832.407819806798 } } } }, "16.308143400005065": { "raw": "AQEBAAAAAHR4nB2MQQrCQAxFr1KydqBTZ0j0BuLaAyT5KRRsK1IREe8u0+V/j/e/pL5N60Lnjl4P6BZ06GiJ920fFzQhghJFkAwWqWQtydw5RQ+WKsy95ZbNK+J+jU9rXOd4aqPT/qF2jIHHAbWOCskMdTY70e8PGi4mXgIBAQAAAAFDeJxtUk1PxCAU/CumZ2OgBQoeNd70sOnReGDp25aItAJVN5v+d4Ga7pe3MjNv3rxJD4UDNbhW265RPbSTAV/c37y+3d4UPsiQHofC/TzsQyZwTUgpCOE1joqw4oxWJasYZSVlkfjWO50nVS+tBRO/7WRMZHYOPiewan+EjLbvzQjQvmxHf4S97qw0m0kaHZKaohVsggPbhT6iaI7oVoYAbp83juAU2CA7OFpp/9hL18UjI7aTxkOyMgBjE29MwqLVXg0xqgrQFsnzS7cwLNevDQXpQiqBVQLzisebeQq1Cp5sm2lSIUprzhAXp3Sef96cO1B+4fAnyB4cYSTq1LT+ACNHD/9kqMWp4CwDwxgjfDV/meHK4TQDxgxRPucfYnBLr4di8pD3kBqjkpWC86WKpc6qonclw7wWhMUlZJ7nX0tBuAE=", "action": { "action": "update", "newUpdateId": "88d4e48d-bdbe-41a4-bcc7-e0d7858770b1", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 17442944871, "txBytes": 653263656256, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183853280, "recordingEnd": 1643055786089, "recordingStartLQ": 1639183853258, "recordingEndLQ": 1643055801097, "timelapseStart": 1639183853279, "timelapseEnd": 1643055611101, "timelapseStartLQ": 1639183853279, "timelapseEndLQ": 1643055116058 }, "storage": { "used": 1471026298880, "rate": 335.261879469184 } } } }, "16.310351099993568": { "raw": "AQEBAAAAAHZ4nB2MQQrCQAxFr1KydmCaGTNTbyCuPUDSJFCwrUiLiHh3mS7/e7z/BR63aV3g0sH+VN4MTh0s9r4f46pNsConSjFIzUPIftYg2NdQSsqFohdVbNm8qj1u9mnNyLO9uNHp+DB0j6xGYtGRUUyorzTA7w/yfiXGAgEBAAAAAVZ4nG1STU+EMBD9K4azmn5C8ajx5h42HI2HLsxCYy3YDupmw3+3hbifhkv75s2bN6/sM6sDrno0vcsebmguOJFSEV4wcnuTeah73xjXVnUHzWghRNLrW6wE1Jgu+8z/PO5wLghRlkLGj0cCHmAqpCyZUILKVPg2WzM31p12Dmw85xHeevgcwdW7eGeCFxGyxr1XA0Cz2gxJyI3WptGmddquR20NJjYl5IBW6MG12EX4TpZTxDcaEfxunjiAr8GhbuGoZsJTp30bd4zYVtsAScwCDFVcMRGzxoS6j1ZrhCZLml+mgX5Z/hAQao9LgoQTpQSReXGa4LNrjgEXKifirDz3v6wvFPiFwh9heSRKVNJA8wFWDwH+8cD5KeHMg2QxOHnVf+nhSuHUAxU54Wqa/4feL7nuszFAmlOIgseXJKUkIu2xpMkFu6eMKU4VI1KwaZp+AW8fvRE=", "action": { "action": "update", "newUpdateId": "adda3630-b849-4f5d-b218-7734760f7dd2", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "lastMotion": 1643055803720, "recordingSchedules": [], "stats": { "rxBytes": 4499454543, "txBytes": 145592484153, "wifi": { "channel": 6, "frequency": 2437, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -59 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640308840567, "recordingEnd": 1643055786047, "recordingStartLQ": 1640308840563, "recordingEndLQ": 1643055801087, "timelapseStart": 1640308840533, "timelapseEnd": 1643055521005, "timelapseStartLQ": 1640308840533, "timelapseEndLQ": 1643055146038 }, "storage": { "used": 747324309504, "rate": 342.122831820542 } } } }, "16.312379500013776": { "raw": "AQEBAAAAAHV4nB2MSwrCQBAFrxJ67cA4v9jeQFx7gDfdPRAwiUhERLy7TJavinpfgmzTutB5oNdDsRkdBlrsfdvHRbuoCS1wFJdLhEtFssOJm4vsVaoftQTt2byq3a/26Y1gtic6nfaPozCC1qStWFOkETF7rka/PwRcJiUCAQEAAAABVnicbVJNU4MwFPwrHc7qJCEfxKOON3vocHQ8pPAKGWPAJKidDv/dBEakrQcO7Nu3u2/hlBnlw7YLurPZ/QZzmiPGCpQLIm42mYOqc7W2TVm1UA8GfCS9vMaJDyqkl1Pmvh+OYRoIiblEnCMZCWGBiUA0PjkvSBEHX/qgp8WqVdaCSbYsj4ODg48BbHWMCBOcRcho+1b2APV23ycpOxiTzHVjldkNyuiQ2BihBS2DA9uENsK3jI4R36sQwB0nzx5cBTaoBv7UtH9slWvilRE7KOMhiRmAvoxHJmJWa191MWwVoM6S5qeuoZvPXyoKyoW5QywJRlQyKdcdPtl6XTEStDirOO0/784VBL1Q+CUsGunyoN/BqN7DfxnomnCWgXIkMLvav8xwpbDOwAgShI3TH9G5uddTNnhIPvGz51QSyiQj6Y65TU7JXY5IUeAieuBxHH8A8lO9fw==", "action": { "action": "update", "newUpdateId": "b4af293c-563a-46c5-a89f-390dcb07d62d", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055803727, "recordingSchedules": [], "stats": { "rxBytes": 7916906609, "txBytes": 270427036828, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -54 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1641921049599, "recordingEnd": 1643055800748, "recordingStartLQ": 1641921049574, "recordingEndLQ": 1643055800740, "timelapseStart": 1641921049594, "timelapseEnd": 1643055460715, "timelapseStartLQ": 1641921049594, "timelapseEndLQ": 1643055520725 }, "storage": { "used": 427349245952, "rate": 642.30288184191 } } } }, "16.345541200018488": { "raw": "AQEBAAAAAHZ4nB2MSwrCQBBErxJ67UBnPm3GG0jWHmA+1RAwiUhEJHh3nWwKqh6vdkplm9aFLh29HjVtoFNHC963o1xrA5mhNetghAXGK4KJBe4f2SURKZZD0+a14j7i05ySZjxTW6fjowfcEAOyh9r+zOxFo9pC3x8hsCXUAgEBAAAAAU14nG1SwU7EIBD9FdOzGuhAAY8ab3rY9Gg8sO1sS0RaKVU3m/670N3Uru4N3nvz5s3AIfNYdb42rimrFuvR4pDdXb28Xl9lQ9AhXQ6Z/77fh5mQlBeUAM9JFIQFBlYIqggwKEQkvszOzIVVq51DG8+UQyR2Hj9GdNU+IlwUPELWuLeyR6yft32ycqO1qblpnLabUVsTkpoSsqBl8Oia0Eb4hokp4lsdAvr93LNHX6ELusFfNzM8tNo3ccqI7bQdMJlZxL6MQyZhVpuh6mLYKmCdJc9PU2N3HH9ZUdA+pDAFqJyqXDIiVJQugkdXzzQDwrlQkguypuf6p83aAZQUfxxOgpOHYjJPuzbvaHU/4IUMQNaCswycAi/Iv/rzDBcc1hk4xPeW0/wjOn/c6yEbB0x9QIlowASTMo1xXCaFWyCQA+SMEpmraZp+AMqNtdQ=", "action": { "action": "update", "newUpdateId": "b0efdbf8-606e-4fe5-9ce3-9cb3a666c205", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 8156103520, "txBytes": 346719034367, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -47 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639219284079, "recordingEnd": 1643055798570, "recordingStartLQ": 1639219283987, "recordingEndLQ": 1643055799482, "timelapseStart": 1639219284030, "timelapseEnd": 1643055513560, "timelapseStartLQ": 1639219284030, "timelapseEndLQ": 1643055533528 }, "storage": { "used": 39728447488, "rate": 13.3032332410829 } } } }, "16.526685399992857": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm20Di5NG4A+m3C5jJTKCgqUhrEXHvkn7eczj3C1TWeWlwGWB7Cq0KpwGa7rdjXKULYs+ZIhouthgfcTScQzUxaRqZgpCjnj0W0fukn96096uj+Tg4ewy1BmK0VpzLkqxnVoTfH5YpJJ8CAQEAAAABRXiczVHLTsMwEPyVymdK/H5w54DUE0VcEAeTbFpLeVixUylU+XfWAalI/AAXWzszO7seX8kcc+iBPOy0ZcZZSundjnQ+5SPAgDDTUlClLJVacaTSkjL0T0M7InkldZy3219g8ic4jL7BWt4bhVoURoTzPG0DzIpYD/04LT89PnT+o9tIYYWxEgXtBAWwxuqySh6z70rNtKBcFouUxzLrjwcTVmqnjFFaMo3CkJ6hXuouDCekW98lKO3hs6gFs0o566Tggm+TllhwMvnQEKznBM13ANQZZ4QS0pQEGriEGhJSb1fSjw2U9cjxhWkM7/URjz0/cEYFuQ3bOEep1oxLi/gZfJfPJYc8zYCv+o9W7yXt3Mc2/cnaWS61/PU/jEqrjL7lhqasxBV9PpdVqoufqjHmah5CG/ZxGjPUuUJ7sq7rF1Jms+E=", "action": { "action": "update", "newUpdateId": "ab4b9a63-bc0c-4638-b95f-67e78ba5da1a", "modelKey": "nvr", "id": "2435ff5ab300d119d704bbe3" }, "data": { "uptime": 681798000, "lastSeen": 1643055804652, "systemInfo": { "cpu": { "averageLoad": 4.75, "temperature": 67 }, "memory": { "available": 6383784, "free": 87860, "total": 8163024 }, "storage": { "available": 13846957756416, "isRecycling": false, "size": 31855989432320, "type": "raid", "used": 16409797353472, "devices": [ { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true } ] }, "tmpfs": { "available": 982464, "total": 1048576, "used": 66112, "path": "/var/opt/unifi-protect/tmp" } } } }, "17.2274890000117": { "raw": "AQEBAAAAAHN4nB3MTQrCMBCG4auUWRvIb5t4A+naA4wzX6CgqYugSPHuErcvPO9BLH3bG50nYlU6TdTwvj6VOy46aqxhDqxsIleYGJHMLYgzqeZZkoNn6wZ77Ir7is8weKH1Ebf/whXJC5agxUuCVcmhFuszfX828iPHAgEBAAAAAK94nFWOsQ6DIBCGX8UwdwBUBOd26tqt6YBwJCaiBs8mxvjuPRzadLv/u8t3/85wm4G1BYsT9tPILgVb0CYkJFRV8rpuTCWVyNxNKZ/yPEe6uQKCwwcJFsLP1z+/vWHE7yICWm/RUt4Pys5GSDkxkCFw60F1wIO0soNOCa1MrjKTrT97tcW4DgOhdYH0S73PCmGcbqApvZGuBu6dLoPhUmdFnDwMd9jOV7kSOz6v80nU", "action": { "action": "add", "newUpdateId": "4f363ada-4afe-44e5-b3c1-5f86c51e2a01", "modelKey": "event", "id": "19c87e73d92c5e0dc83f9028" }, "data": { "type": "motion", "start": 1643055794261, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "e2ff0ade6be0f2a2beb61869", "partition": null, "user": null, "id": "19c87e73d92c5e0dc83f9028", "modelKey": "event" } }, "17.288530400022864": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm20Ae7TRxB+K3C5hkbqBgW5GKiLh3ST7vOZz7JSnHsm90Huj1UDlAp4E2vG99XLSJSVUQJBkONppRUjLRx9GUMjtMLszspGXrrrhf8WlNkRXPTpf+AV+rFQVn2OrFZ2R2kRP9/uaTJZkCAQEAAAAA0HicXVC7DoMwDPwVlJkhCY+Uzh26dGnHiiECq0UCIgVTqUL8ey9pGagUO7747mxlEb2d+OK4c6M4JqrMM1kUpsp1qdJEdNO3dSKmhqkFhf1M6NCLRr6x5Qlvixg2h0Wwa+07ehnw7Iu8fRBwVmbAYdzJvoPqro1Ok6wCLTcVqkIi6RJQFaikqSHw1GDS2c0+alQeOGkSbnVAgAkDjVCIII45ONcr9NNgPe8Wk7u15N9SwP+nXtfvIs633fi4NU9q554ivY6/dN16vw9aP6fKX1Y=", "action": { "action": "update", "newUpdateId": "5ddae3a9-6308-4a99-8284-cc71e513761a", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "lastMotion": 1643055794261, "isMotionDetected": true, "eventStats": { "motion": { "today": 167, "average": 363, "lastDays": [ 272, 397, 479, 350, 326, 415, 307 ], "recentHours": [ 14, 32, 14, 18, 15, 7, 27, 17, 6, 4, 6, 2, 3 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "recordingSchedules": [], "isRecording": true } }, "17.350639200012665": { "raw": "AQEBAAAAAHV4nB2MQQoCMQxFrzJkbWHamjb1BuLaA8QkAwPacdFRRLy7tMv/Hu9/gaWtW4XTBPtTuRkcJqj2vo5x1i6SMMuC6kJBcccbRkfes/OSjFLggHlkj03tfrFPb+xltXW4jgtfhLLlqCUI2qxCcSlzIPj9AdepJPsCAQEAAAAAO3icq1Yqzk0sKnFJLUlNLnEtS80rKVayUohWsjQ2N0s1NTdKTrOwTDM0T00zNk40TktOVYqtBQC/HxBf", "action": { "action": "update", "newUpdateId": "6caacf5d-295c-4b53-811a-1c6e862a257e", "modelKey": "event", "id": "19c87e73d92c5e0dc83f9028" }, "data": { "smartDetectEvents": [ "9376e572cf89f17ef33a3fce" ] } }, "17.549540999985766": { "raw": "AQEBAAAAAHZ4nB2MUQrCMBBEr1L220C72XRTbyB+e4AkuwsF24q0iIh31+RvZh5vPpDKPm8rnDs4HpJ2hVMHq75urVykAowSMEdyWSg4Yvwn4+I8mxH6YCOnqi2b6P2q7+qUtOizrXP7GFR9nIJmUsOB+55GmwwLfH/dtSUZAgEBAAAAAFd4nKtWykksLvHNL8nMz1OyUjA0MzE2MDW1MDCxNLLQUVDKLA5KTc4vSsnMSwfKpiXmFKeCRSEaXFJLUpNLUlOQpIpgyoOTM1JTSnNSi4GS0bG1AL++Ij4=", "action": { "action": "update", "newUpdateId": "28d52b84-bd45-4724-bf7c-37ff4235f67a", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "lastMotion": 1643055804928, "isRecording": false, "isMotionDetected": false, "recordingSchedules": [] } }, "17.576620700012427": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSBp0rTjDcS1B5hk/kDBtiIVEfHuki7/e7z/Jan7vK107uj1UNlBp45WvG/HuGgTzIOPXNlNRcylOmZXYhCnKlkNJXC0li2b4n7FpzVVFjyl0fn4CECceEBJsD6M3qdsbH2l3x8P3SXaAgEBAAAAAD54nKtWykksLvHNL8nMz1OyUjA0MzE2MDW1MDQyMbLQUVAqSk3OL0rJzEsPTs5ITSnNSS0GKoqOrQUA20gRYQ==", "action": { "action": "update", "newUpdateId": "995039c9-8baf-4c76-b31a-dda6dfeb193f", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "lastMotion": 1643055812428, "recordingSchedules": [] } }, "17.59743119997438": { "raw": "AQEBAAAAAHV4nCWMQQrCMBBFr1JmbWBM0iHxBtK1B5jOTKHQpi5ii4h31+jyv897L2Cp81bg0sHjrlwNTh0UO26/cdV2JEGRpJM7c/YukqkbMarDINwLeSXMTVs3tWWwZ3Nst1IbnP8JzSNbCn32lCbC+AXeNMD7A+M6JS0CAQEAAAAAKnicq1ZKzUtRslIwNDMxNjA1tTA0MjGy0FFQKk7OL0oFiRsY1AIApsQJBg==", "action": { "action": "update", "newUpdateId": "8c0cc8df-1a92-46ed-b04d-03ca5c62d609", "modelKey": "event", "id": "8d9bae8359268f6048d92ed3" }, "data": { "end": 1643055812428, "score": 100 } }, "17.677909999969415": { "raw": "AQEBAAAAAHN4nB3MQQrCMBBG4auUWRtoJ5NM4g2kaw+Qdv5FQVMXQRHp3SXdPvjej8ratr3SdaBiRpeBKj73l5WGm/U6Jq9lEXWBJ3MCqCvK3vGkPrNlXsLJnrvhMePbDd6orcftXEROccxBUg4MQIKuWaIoHX/gqSIzAgEBAAAAAPR4nG1QTUvEMBD9K0vOK6TdJE09W1GEVVg9iHhImykW2qQmU9dl6X83Caul4C3vY2bey5ngaQRyvSEejLfucQQDmmwDRuUwCJlgO8q5pFxmu8CD0f+wvrEurqHxPYTJG0Bo8Dks94F+e1/z1RcY/BMGQKUVqoDPlxz3OgGE75iB5LTOIJd1qXhb6zajgtZtoSWZt7/J92qA1cyd+/zYvFqFyXSpucgvt09Xh2p/qJI82MmktCvPsTPaHskcHU3Y72JEM/V9wGMo02FnzUJNHtyCuliBiFwKWnImS54DAONFUzLBCpKOaugf4BR9EH+EzD9E+3ii", "action": { "action": "add", "newUpdateId": "0837ab47-521d-4ee7-a723-217392d92b5d", "modelKey": "event", "id": "628609548952eee457c94647" }, "data": { "type": "sensorOpened", "start": 1643055805813, "end": 1643055805813, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Hrqh Yoat" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "camera": null, "partition": null, "user": null, "id": "628609548952eee457c94647", "modelKey": "event" } }, "17.679753199976403": { "raw": "AQEBAAAAAHZ4nC2MWwrCMBBFt1Lm28BkqE3SHUi/XcCMM4GCJtIHRcS9S8TPew7nvoFv21wLjB3sT+XN4NRBseP6GxdtgpAlJDFHIaHrQ0YXI5ETHtg89srGLXtUtftkr9asVta6NDr/P8QbRUl8zqLZ44CSg0b4fAH3lyXgAgEBAAAAAD14nKtWyiz2L0jNS01RslIoKSpN1VFQygfyg0sSS0qLnTMS89JTUxxLgJKGZibGBqamFgamFobGtQAfShGu", "action": { "action": "update", "newUpdateId": "20ab79be-2790-47f0-8822-ba6ae104daea", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "isOpened": true, "openStatusChangedAt": 1643055805813 } }, "19.00935790000949": { "raw": "AQEBAAAAAHh4nB2MUQrCMBBEr1L220CySTTxBtJvD5DNbqFYuyIVKeLdNfkaZh5vPlDqNusK5wFeDy6bwGGAVd7XXi7cQMiWiDEY/KcJHMUQ2mymVHw4WUHkrt2VZRllbw6rPhett7bP/cXV5FCSnfiYfaHooo8kPsH3B0ufJhcCAQEAAAAAIXicq1bKyU/ODi5JLCktVrJSUHL28Q/29HNXqgUAcGMIFw==", "action": { "action": "update", "newUpdateId": "490bbd24-20bb-4d5e-b209-f8a3470e22de", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "lockStatus": "CLOSING" } }, "20.851950599986594": { "raw": "AQEBAAAAAHV4nB2MSwrCQBAFrxJ67cA46fl5A3HtAfo3EDCJSIKIeHeZLF8V9b5Ask3rApcB9qfSZnAaYLH3/RhX7aJaixY4OPVcHJpHRzF5V6pgi3TOqNKzeVV73OzTG6HZXtTpdHzEkoizBOaEYZSxqWK2GuH3B/2BJZ0CAQEAAAAAXXicq1bKSSwu8c0vyczPU7JSMDQzMTYwNbUwsDA0NdJRUMoshki5pJakJpekpgCVpCXmFKcCpYpSk/OLUjLz0oOTM1JTSnNSi4GS0bFgTUEwOaBQSVFpai0ApzYh7A==", "action": { "action": "update", "newUpdateId": "9ef5e2b2-d0b8-4e04-a560-89c4f5a174dc", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055808152, "isMotionDetected": false, "recordingSchedules": [], "isRecording": true } }, "20.885154700023122": { "raw": "AQEBAAAAAHR4nB2MSwqDQBAFryK9zoD2/HOD4DoH6J8gRA3BEILk7mFcvirqHUCyz9sK1w7eT6Xd4NLBap/7OW7aRGWkXgmdGPUu+KG6wj46oWhp4Iplyi1bNrXHaN/WCC32okbn8yOWRJwFmVNAL35SDdlqhN8f++olkwIBAQAAAAA8eJyrVspJLC7xzS/JzM9TslIwNDMxNjA1tTAEIksdBaWi1OT8opTMvPTg5IzUlNKc1GKgoujYWgDcHBFo", "action": { "action": "update", "newUpdateId": "9b2a0da2-cea0-4319-8b35-ca5e61b928f7", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055815819, "recordingSchedules": [] } }, "20.897355899971444": { "raw": "AQEBAAAAAHR4nB2MQQoCMQxFrzJkbcHUJmm8gbj2AO0kwoB2XFRFxLtLZ/nf4/0vlLkva4PjBM+Hle6wm6D5+7KNkw0hKhUVKeQsGJJFDppyDvGAWoRI3eeR3Vfz29k/o/GXtz7gsl24IqbI+8LkxGzKLHatFX5/az8jzgIBAQAAAAAoeJyrVkrNS1GyUjA0MzE2MDW1MAQiSx0FpeLk/KJUkLiBQS0Ap0QJDQ==", "action": { "action": "update", "newUpdateId": "797b1915-8871-4d26-9488-2319a7559eec", "modelKey": "event", "id": "e9114260a65e566d9667dfbb" }, "data": { "end": 1643055815819, "score": 100 } }, "20.973723599978257": { "raw": "AQEBAAAAAHZ4nB2MQQoCMQxFrzJkbaGdTLT1BuLaA6RJhMFxIjIiIt5d2uV/j/e/wLLNvsJxgNdDeTPYDbDa+9LHSZvQKJGlaOBIGCbmFDKphInQSqVDZRpbdne15Wyf3rg/F5db43N/SZLTaDledV+QKyVCqoYZfn9fgCZGAgEBAAAAACB4nKtWSirKTElPdU7MS8lMSSxJLVayUoiOrQUAbxcIrA==", "action": { "action": "update", "newUpdateId": "d0c0ac9d-a053-4aa1-85dc-453e9b57ba52", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "bridgeCandidates": [] } }, "21.156368599971756": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm28Bo05hxB+J3F5B5BAqaiK2ISPcuyec9h3N/kGRbaoHLAO+nps3gMECxz9zHVZsQEkHF6EQDO59ZHPkxuimzMamXNPXsUdXuN/u2ZrWy1lejS//AkxkxUT6eNdDog6FEQYT9D0dUJmYCAQEAAAAAIHicq1ZKKspMSU91TsxLyUxJLEktVrJSiI6tBQBvFwis", "action": { "action": "update", "newUpdateId": "c9cc0d08-cd6b-4fbc-9438-5fbeb9d4ca5e", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "bridgeCandidates": [] } }, "21.15968659997452": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbaCZ6EzjDaRrD5DMTKGgidhKEfHumi7/+7z3gSTrXAucO3g9NK0Ghw6Kbdd9XLQdPeYoE3knhOaO00nc0Ht2KWZNgnHAwE27V7XbaO/mLFaW+mx03hsaRLMPypkosxIz/QkafH/72yXMAgEBAAAAACB4nKtWSirKTElPdU7MS8lMSSxJLVayUoiOrQUAbxcIrA==", "action": { "action": "update", "newUpdateId": "02b9cf61-c62e-4f5c-8017-a9bdac298237", "modelKey": "sensor", "id": "d3cdb13d7b66b7d67763cd2e" }, "data": { "bridgeCandidates": [] } }, "21.166233900003135": { "raw": "AQEBAAAAAHd4nB2MWwrCMBBFt1Lm28AkzeThDorfLiDJTKCgidiKiLh3ST/vOZz7hVT2tTc4T/B6cNoFThM0eV+PsfAQmhLzjKjY6qAsEavoxarZkyvWFB1cHNm9s9wu8hnNJm3rz0HX48Ng1mJCjolq5qrRYa6eA/z+wVclBQIBAQAAAAAgeJyrVkoqykxJT3VOzEvJTEksSS1WslKIjq0FAG8XCKw=", "action": { "action": "update", "newUpdateId": "15add300-d418-455d-97e4-3756c42c1869", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "bridgeCandidates": [] } }, "21.173563799995463": { "raw": "AQEBAAAAAHd4nB3MUQrCMBAE0KuU/TbgNk3S9QbFbw+QbKZQ0ERsRUS8u6afM8ObD0Xdllro1NHznuMGOnRU8LrsYcptgPSzOE1G7ahmSGqNaC9GoD5HDpExNnarGdcz3s2sKGt9tHbZP5xyYvZ/hgHOxXCcrQTr6fsDHyYlqAIBAQAAAAAgeJyrVkoqykxJT3VOzEvJTEksSS1WslKIjq0FAG8XCKw=", "action": { "action": "update", "newUpdateId": "e92f95cb-c38c-4bc3-9c29-9ec6da17a1e8", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "bridgeCandidates": [] } }, "22.208892999973614": { "raw": "AQEBAAAAAHN4nB2MQQqDQAxFryJZd2A0kXF6g9K1B0hMBgQdpahFSu8u4/K/x/s/4GEblwzPCvZVeTN4VJDt29/jpUUoNxpsQEfC0RF15oSQHRIGkehTZ1SyeVGb3naWJh+fgsb7oCFsU2pZ0Hut66jBk4gh/C+OqCRxAgEBAAAAAS94nM1RTU/DMAz9K1POjOaracqdA9JOgLggDqF110jtUiXppDL1v2NXkwDtD5BDIr9nP9svF5aWlGF8OnWBPewurJnm7XVniO4Ih+BajKt7/ueIux3Dqglz8hwBM4xdERthDHG5Cjg/uM9hIzXn1nJM6CIQYLmoDGmE7AaKhVFcapJIOVDjGw2hrDZ1WVWl0YJqfXqGZmkGfzoi3bkhAZX7L8pWwpZlbWutpJLUOC8T4Sw63zKM5wS0mMDR6qquVKl0JRFv4ewbSEi9X9gYWqDx2MurMLj22yNee3mQgiv202zjas6NEVJbxHtwQ+7JhxxnwK3+o9QHuZ3HqUs3XtdWaqN//Y/g2pbbj119Q1FBdk0u9zRKcXaxCFMu5pPv/H6KIUOTC5Rn67p+A289qhc=", "action": { "action": "update", "newUpdateId": "da2d7ec3-4ba9-448e-b43a-3437bb90f8e4", "modelKey": "nvr", "id": "2435ff5ab300d119d704bbe3" }, "data": { "systemInfo": { "cpu": { "averageLoad": 7.000000000000001, "temperature": 68 }, "memory": { "available": 6400880, "free": 80176, "total": 8163024 }, "storage": { "available": 13846957756416, "isRecycling": false, "size": 31855989432320, "type": "raid", "used": 16409797353472, "devices": [ { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true }, { "model": "ST16000VE000-2L2103", "size": 16000900661248, "healthy": true } ] }, "tmpfs": { "available": 982464, "total": 1048576, "used": 66112, "path": "/var/opt/unifi-protect/tmp" } } } }, "22.94245959998807": { "raw": "AQEBAAAAAHV4nB3MTQoCMQxA4asMWVvopE1/vIG49gBpk8KAdlyUERHvLnX74Hsf4Dq2vcN5ARaB0wJdX7en8NCLzNpyQe+5meoJjU+YTQo5mqZILhFiXO1kj130ftX3NHpoHzNu/8WaQuCKpMFZcdVFsYViQfj+AN8jIn4CAQEAAAAA+HicbVBdS8MwFP0rI88TknZJq2/DdSDCEKZP4kM+brHQJqO53ayj/90kTEvBt5yPe+85uRIcT0AeVsSD9a5/bJ0HQ9YBo+wxCExscsp5yRgtaeDBmn9Yr10f16R3FyZ3gKDxNSz3gX7/WPLVGSz+CR2gNBJlwNdbjieTAMJXzEAyqhhkpbqXvFamZlRQVRemJNP6N/lBdrCY2cszgv9ebdvxE51O1lvZ2fS2f7k7VodjleTODTZlXngujTXuQqbo0OFKH4PaoW0DPoVKDTbOztTgoZ9RE4sQVgohdcZB5NTkOi8MVbxQGUlHDbTPMEYfxH8h0w+gfHuy", "action": { "action": "add", "newUpdateId": "f9b244af-c452-4829-8697-fe2538522710", "modelKey": "event", "id": "1866ac25e630d3c37d0b57b2" }, "data": { "type": "sensorClosed", "start": 1643055811080, "end": 1643055811080, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "20b1e28b9a5fbdf1060bf7d8" }, "sensorName": { "text": "Favtesz Alyhtoc" }, "type": { "text": "UFP-SENSE" }, "mountType": { "text": "window" } }, "camera": null, "partition": null, "user": null, "id": "1866ac25e630d3c37d0b57b2", "modelKey": "event" } }, "22.94359879998956": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSBNm3TqDYprDzDTmYGCJmIrIuLdJVn+93j/C7QeW8lw7uD1EDoUTh1kfV/bWKSKNVmUJMmFeRQ3+hgcI5OTwZDQBvJTy+5F9HbRT212zXt5Vrq1j+C514A8UzQW633ybJMg/P4RbyYlAgEBAAAAAD54nKtWyiz2L0jNS01RslJIS8wpTtVRUMoHCgSXJJaUFjtnJOalp6Y4lgBlDc1MjA1MTS0MDQ0sDGoBMIUR8g==", "action": { "action": "update", "newUpdateId": "c6f5d6d6-294d-4052-b8ba-d3f8a8f3a07e", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "isOpened": false, "openStatusChangedAt": 1643055811080 } }, "23.047045100014657": { "raw": "AQEBAAAAAHR4nB3MTQoCMQxA4asMWVvoj21abyCz9gBpk8CAdlwURQbvLnX74HsHUBvb3uGyADHDaYEu79uTaciVZ9UYcy3aDGIs5oyhmKLeGXWJKiXCYGWyx85yX+Uzjbykjxm3/yJztS4JN7JRfApeA2IWgu8PFjUjqQIBAQAAAAC3eJxVjrEOgzAMRH+lyswAhCSEuZ26dmrVISRGQgKCgqmEEP9eu0vFeO/OT94FbjOI5iKW0SW8AoLHZ5xAZISQEHWFrmSulLHW1oq5j4lvbJGd7h6kWoi/xAxpiZN4n/vbByb8DbgYAV1w6CjvB2XvRkichKq1a40v21ZXpfSyC6EyYBX/NJOtx57kzWVah4HQukD6pz6wog5tXmgI3uUKSi3LThpTg2PFGAMMd9h4B/ySOL74m1BI", "action": { "action": "add", "newUpdateId": "f558b9fc-7759-4739-9f21-f16aba6a730e", "modelKey": "event", "id": "8db016edca05e2632f3778ea" }, "data": { "type": "smartDetectZone", "start": 1643055799985, "score": 91, "smartDetectTypes": [ "person" ], "smartDetectEvents": [], "metadata": {}, "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "id": "8db016edca05e2632f3778ea", "modelKey": "event" } }, "24.39886120002484": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBG4auUWRtok0nTeAPp2gNM8k+goKmLUBHp3SVuH3zvS5Lbtle6DiQAXQaq+r6/IE1v6JVtZhX2xtopGy5ezBJnZ/KMxG6yGmXs7LlDH6t+utFDa+tx+y+QRQr8GHQRF5KglBQDlM4fHQYkrgIBAQAAAACweJxVjrsOgzAMRX8FZe4QHiGhczt17VZ1cGIjIRFAYCohxL837tCqm8/x1dXdFW8TqXOm4sjdOKhTphaGmZPK66rUxjidVy4XH8ZZolrumDIXYgp8TwVL0o/nv7++aODvIxIDAkPi/UgcINIspIyrwdtQeF9XRRnKFrGy1BiZMqW27rPrnA1r3ye1LjT/qEOpwADQotGWHJTWA7atbyySVMQRqb/RJjmSSep4A6JJSrs=", "action": { "action": "add", "newUpdateId": "42c4ea45-221c-4f5a-8963-c6db4312e9a0", "modelKey": "event", "id": "dcaafd507e8a37badffb97de" }, "data": { "type": "motion", "start": 1643055801481, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": {}, "camera": "586ab7c2bb6423c3fdd47e95", "partition": null, "user": null, "id": "dcaafd507e8a37badffb97de", "modelKey": "event" } }, "24.42201829998521": { "raw": "AQEBAAAAAHR4nB2MSwrCMBRFt1Le2EBs82ncgTh2AbfvAwVNHURFxL1LOrzncO6XwG3dKp0Gej4ETekwUNX3dR9n6SKxwocgLvgEF4TFzXEq7mjjmBKsIHLP7pvo7aKf3uhLa+tw3S+EAZPos86Y8gIxW0oWpd8f5YQmcAIBAQAAAAA7eJyrVirOTSwqcUktSU0ucS1LzSspVrJSiFaySEkyMDRLTUlONDBNNTIzNkozNje3SE1Uiq0FAMSlEEY=", "action": { "action": "update", "newUpdateId": "6cea044d-406a-4dcd-8539-1f2266af9a5c", "modelKey": "event", "id": "dcaafd507e8a37badffb97de" }, "data": { "smartDetectEvents": [ "8db016edca05e2632f3778ea" ] } }, "24.43455449998146": { "raw": "AQEBAAAAAHd4nB2MQQrDMAwEvxJ0rsGxpdjuD0rOfYBkKRBoklJSSgn9e3GOO8PsAVz3eVvh2sH7qbwbXDpY7XM/x02bKNTHRIJOs/QOs0fHyOSUQ6CIWLyfWrZsao/Rvq2pvNiLG53PD8oDS6pBZMAQa5xUMVkh+P0Br2QkoQIBAQAAAADDeJxlUD0LgzAQ/Sshs0Oi8YPOHbp0qWNxCOawghpITkHE/94z1kELjyOX9+7d4xbeaY9Pi60d+I3JTCUiTQsZJ3keMd76nboDQo1gSIJuhMCUvXb4T8AEA5ao0dPfwvvDeuFojZ7pFSuS6QmcbmDbWVC7pbjreZt5J1nECCJiyQ5qlKpI5aAm84cdXRCmEZOEOGhlqGmoJ1QrTfot7CmFOIUQlwxXk+Cz7hGsM+3QlPUHzNhBkFfhIq+D+11j/QLKKWP6", "action": { "action": "update", "newUpdateId": "951375b4-d8b1-4804-a4a5-da225344900f", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055812377, "isMotionDetected": true, "isSmartDetected": true, "eventStats": { "motion": { "today": 24, "average": 18, "lastDays": [ 36, 6, 0, 3, 3, 36, 44 ], "recentHours": [ 5, 1, 12, 0, 1, 0, 5, 0, 0, 0, 0, 0, 0 ] }, "smart": { "today": 0, "average": 0, "lastDays": [ 0, 0, 0, 0, 0, 0, 0 ] } }, "recordingSchedules": [], "isRecording": true } }, "25.245077100000344": { "raw": "AQEBAAAAAHR4nB3MQQrCMBBA0auUWRtI02mm4w2kaw+QZGagoKmLoEjx7pJuP7x/QCpt2ytcB0gicBmg6uf+ktT0Jr1yZJpJo5Nko8Pg0XGevBupWCkxFwwne+6ij1W/3ehba+txOxdi7GecjNFsYcRAmXgJBL8/EkojLAIBAQAAAADaeJxtkEFvwjAMhf9K5TOHtElIsvM2aYJx4jbtEBqHVUob1JoJhPrfidnEhLSb3+dn5zkXoPMB4amC1O2/6D1TlwdYVDCRH6nweqmk0NrW0mlTOA7hHzq1eeQtguu+TD4jYUvbsnsq+OPzkb9840D3Ro/kgydf9OUnxlu41YQnjgDLeiejbq2oo/VGSDRCCNVYmBe/uTe+x4eR15RzqNbcg5ltbXGM/MRwTKnoQwnT3Y69o+OE45/qOAOE6IRWMjoVo3VKNWZnnG0Mf1GfA6YVntmHfBHMVxbFY5w=", "action": { "action": "add", "newUpdateId": "969757e6-daf1-4204-9b30-17cfcc6bc42d", "modelKey": "event", "id": "df90543f94ff894427b79827" }, "data": { "type": "lightMotion", "start": 1643055813957, "end": 1643055813957, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "lightId": { "text": "61b3f5c801f8a703e7000428" }, "lightName": { "text": "Flood Light" } }, "camera": null, "partition": null, "user": null, "id": "df90543f94ff894427b79827", "modelKey": "event" } }, "25.51208840002073": { "raw": "AQEBAAAAAHN4nB3MQQoCMQxA0asMWVtw0tRJvYG49gBpksKAdlyUERHvLnX74f0PiPZ1a3CeQMzgMEHz1+1p0v1io0aKKFo5mBQPdCwcSmYLibKeqhbOSQd7bOb3q7+H8d1bH3H9LwhpqUkMfRaZIy6utSIbfH9JgiSLAgEBAAAAAOl4nG1QTWsCMRD9K5KzwibZ7Gqv7RakKAXbU+khJrN0IR+SjKUi+987Wawi9DbvYx5v5szwdAD2MGMZQo5pE3GIgc0Jo05IAm9qWSm15LJRnHgI9h82m5hKTFVmT5tPgGDwjcIz0R+f93z3DQGvggfUVqMmfL70WNsJIPyUDkwZvue8MWIFNSil26qXq1Y2bJz/Nd9qD3c7m338Ajd7NM7bOBkvp94s78+vi1233XVsLLqhiFRahKNzhA/Ud5j+caWOGdINDaUlq0Xd9kpbAVxrLkULpu/F0pYv+mjBvcCp+KAczcZfkCxvsg==", "action": { "action": "add", "newUpdateId": "3432acf8-dabe-40b8-b98d-549c6fcb895c", "modelKey": "event", "id": "4247f5ad2e1aa1327ecff28d" }, "data": { "type": "sensorMotion", "start": 1643055813651, "end": 1643055813651, "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "metadata": { "sensorId": { "text": "5c1b116c29e4e55a70f39736" }, "sensorName": { "text": "Mbohel Cclmdo" }, "type": { "text": "UFP-SENSE" } }, "camera": null, "partition": null, "user": null, "id": "4247f5ad2e1aa1327ecff28d", "modelKey": "event" } }, "25.51315459999023": { "raw": "AQEBAAAAAHV4nB3MUQrCMBAE0KuU/TbgNpuEeAPxuwfYZFcoaCK2IlK8u24/Z4Y3G3Bd597gNMDrIbwqHAZo+p72cBYbCGX0HIpLpN4RF3HMFZ3k5ElDII3Z2L2L3i76MbNoW/rT2nn/CBULYqxjViOcjlf/5xG+P/xBJTQCAQEAAAAAOXicq1bKLPbNL8nMz3NJLUlNLklNUbJSKCkqTdVRUMpFEXcsAcoYmpkYG5iaWhgam5ka1gIAmzwTwg==", "action": { "action": "update", "newUpdateId": "41d23a5b-74e3-4abd-aac1-d9734e554e69", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "isMotionDetected": true, "motionDetectedAt": 1643055813651 } }, "25.84483040001942": { "raw": "AQEBAAAAAHh4nB2MWwrCQAxFt1Ly7cCkmUwz7kD8dgHziFCsjUhFRNy7TD/vOZz7hVy32VY4DvB6tLwpHAZY9X3Zx6l1EYtOSJ4dVR9dQGRXQlKX45iTVAkyUc/u1nQ566c3zey5WL11Pu8vWAVHFX9tMVEujExclAR+fwpKJTwCAQEAAAAASnicq1ZKyilNLcnPL8lwzs/LS00uyczPCy5JLElVslKoVirOTM9LzAksTczJLKkEipgZ6ShABYNLilLz0ksygKK6Zqa1tQADWBoy", "action": { "action": "update", "newUpdateId": "6be71305-3c06-4115-b49e-a62a98c84873", "modelKey": "doorlock", "id": "1c812e80fd693ab51535be38" }, "data": { "bluetoothConnectionState": { "signalQuality": 62, "signalStrength": -65 } } }, "25.853134399978444": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbWCapnXGG4hrD5BkfqGgidiKiHh3SZf/Pd7/UszbUgudOno9LG6gQ0cF7+s+ztYEgkUJPjlLGS7IMDphUZd7g6iMHP3csns13C74tGZFWeuz0WX/YA9oUp37o006hAmcJTPT7w8IiCV8AgEBAAAAAEp4nKtWSsopTS3Jzy/JcM7Py0tNLsnMzwsuSSxJVbJSqFYqzkzPS8wJLE3MySypBIqYmOooQAWDS4pS89JLMoCiuuZGtbUAA2saMQ==", "action": { "action": "update", "newUpdateId": "e4da842b-dbce-4835-8089-c1de89850a2f", "modelKey": "sensor", "id": "02ee9b99f17d69346e0c8c00" }, "data": { "bluetoothConnectionState": { "signalQuality": 45, "signalStrength": -72 } } }, "25.86031219997676": { "raw": "AQEBAAAAAHV4nB3MSwrDMAwE0KsErWsI/shyb1C6zgEUySSGfLpwKSX07o2znBneHMBSy77BvYP3S7lmuHWw5c9whYe2IbIjJREjOLLxTL1hi2IwOUWyIY19amzdNS/P/G1mKdNcW1muC8fKkYIinpBIowbrPTL8/s3eJMkCAQEAAAAAPHicq1bKLA7ILPLNL8nMz3NJLUlNLklNUbJSKCkqTdVRUMpJLC6ByAHFDM1MjA1MTS0MjS1NzWsBW5ESsw==", "action": { "action": "update", "newUpdateId": "7a38d8cc-c6ba-4a80-a26c-693d68259b09", "modelKey": "light", "id": "3ada785d6626c88d7d52446a" }, "data": { "isPirMotionDetected": true, "lastMotion": 1643055813957 } }, "25.863951799983624": { "raw": "AQEBAAAAAHd4nB2MWwrCMBBFt1Lm20ASOnm4A/HbBcwkM1DQRGyLiLh3ST/vOZz7BSrb0hucJ9iflTaB0wRN3rdjXOoQhYNEQmcQE5sZ1RvOWU0OUYOUuRDTyB69yv0qn9Gs0tb+GnQ5PrxlJz5xJlSu6mywrLEm+P0BHxQmYAIBAQAAAABKeJyrVkrKKU0tyc8vyXDOz8tLTS7JzM8LLkksSVWyUqhWKs5Mz0vMCSxNzMksqQSKmJjqKEAFg0uKUvPSSzKAorrmRrW1AANrGjE=", "action": { "action": "update", "newUpdateId": "cb6e7a51-558b-45f2-b99f-967f6ec4caba", "modelKey": "sensor", "id": "20b1e28b9a5fbdf1060bf7d8" }, "data": { "bluetoothConnectionState": { "signalQuality": 45, "signalStrength": -72 } } }, "25.977149199985433": { "raw": "AQEBAAAAAHZ4nB2MWwrCQAxFt1Ly7UCdZ+IOpN8uIJNEKNhWpEWKuHedft5zOecDLOu4zHDpYHsqrwanDmZ7345x1XYoCWH25Hyt6mLss8Pkg9MsiuVMVXps2rSoPQbbmyM82YsbHY9Gwsy1yL+Qow8S7qqxGCX4/gDWFyUeAgEBAAAAAD54nKtWykksLvHNL8nMz1OyUjA0MzE2MDW1MDS2sDTWUVAqSk3OL0rJzEsPTs5ITSnNSS0GKoqOrQUA3B4RaA==", "action": { "action": "update", "newUpdateId": "d9c98629-2bbd-4406-8523-d6cd8719bc08", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055813893, "recordingSchedules": [] } }, "26.084475699986797": { "raw": "AQEBAAAAAHd4nB2MSwrDMAxErxK0rkGOHH96g5J1DyBLWgSapJSEUkru3jqrYebx5gss27QucO1gfypvBpcOFnvfz3LTBlQkIHF1UXvvQvGDKxWLy0gUfPlHkqbNq9pjtE9zhGd7cVun80NC7AWTVo3EmnHgRD5LguMHuuAkaQIBAQAAAAAieJyrVipKTc4vSsnMSw9OzkhNKc1JLVayUoiOrQUAhrYJrA==", "action": { "action": "update", "newUpdateId": "dcc403ab-6d21-4915-9b09-80334198037c", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [] } }, "26.112119500001427": { "raw": "AQEBAAAAAHZ4nB2MSwrCQBAFrxJ67YDzMd3jDSRrD9D2vIGASUQSJIh3l8nyVVHvS2rruMx07Wh7FV1Bp45mfO7HuJUm+povKUlxqFFdgvROYMEZB/OcWTj4lk1LwXPA3hrTCW9tdDw+ksYYS86xQhAgicPD8tnT7w/bGiTNAgEBAAAAACJ4nKtWKkpNzi9KycxLD07OSE0pzUktVrJSiI6tBQCGtgms", "action": { "action": "update", "newUpdateId": "6f95448d-ef3a-4e86-8ec2-c72c17978721", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [] } }, "26.113241899991408": { "raw": "AQEBAAAAAHZ4nB2MSwqDQBAFryK9zoA6bWY6Nwiuc4Ce/oAQNQQlSMjdw7h8VdT7Ass2rQvcGthfypvBpYHFPo9z3LUKj617dgyZWgmI1AWOxkFKxCtmQaJUs3lVe4521EZ4tjdXOp0fXKL1yXsdBmfNXVKWVArB7w/xDCXaAgEBAAAAACJ4nKtWKkpNzi9KycxLD07OSE0pzUktVrJSiI6tBQCGtgms", "action": { "action": "update", "newUpdateId": "f30ff8f4-890c-4491-a3ea-cb34648c4997", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [] } }, "26.122019900009036": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbWAaE5vxBtK1B5hmfqBgW5EWKeLdNV2+93n/Q5rXcZnp2tD2NF1Bp4ZmvO8H3KwO8GraRe9SYXOB2TvhPw45JoWIcLKaTYvh0WOvTdYJL612PD5a4JwkYggovu2Yw6VI8Zm+P+ApJQ4CAQEAAAAAj3icTYvBCsIwEER/JeSs0KpR8NqzB+1RPIRkmyzEjSZbpJT+u1u8eJt582bWBVwuHin0LoIfE1R9VvfHRukPDthlInCMmXq2DDLN2kUrMEluzV68ocB7BHKTEHM6GkGvON1++q5ppFcMZNN1tAl51do/2nMBChwFbw/ruVb0UvQlF8aqOvuEYvWyfAEj/TdB", "action": { "action": "update", "newUpdateId": "e2ada752-8f0d-4002-9052-bc58ae99908d", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "recordingSchedules": [], "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -45, "ssid": "Mortis Camera" } } }, "26.123121900018305": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbWBijc54A3HtASaZHyjYVqRSinh3SZf/Pd7/kpVlmCe6dvR5uS2gQ0cT1sc+bt5Erhy1sIXY1xROF0bIaghISbI7w1haNs6O5x1ba4qNeFujw/5RuXhMWTJc0YuoHuVsUuj3BxYCJgACAQEAAAAAInicq1YqSk3OL0rJzEsPTs5ITSnNSS1WslKIjq0FAIa2Caw=", "action": { "action": "update", "newUpdateId": "bf019c0a-13f5-470e-b9ae-e558bdd0ea08", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [] } }, "26.12451479997253": { "raw": "AQEBAAAAAHd4nB2MSwrDMAwFrxK0rsEf2Up6g5J1DyBZCgSapJSUUkLvXpzlm2HeAVz3eVvh2sH7qbwbXDpY7XM/x02bKDEhaQmupkQOfd87iZJdUfJIIZtladmyqT1G+7am8mIvbnQ+P0IdOKqgTsUmZSRO2Q9i8PsDti4lNQIBAQAAAACheJxNjTEPgjAQhf8K6awJBYvGlZlBGY1DUw56SW2xPWII4b97jYvj+9539zbhdKIuEAYvroVsTnWp1EXWsioPhYhgQhzQT72xMCwOEkuPJzcfHLEN3oPJpz1pAq42Yaxm6PIvVbM3Rngv4M3KRJ0bxWi26/2nV2UeSTh57W6LdkhZk3+0pwh+Isv4qKqMEw4cRBciYSpa/YKoxb5/Adb/Puo=", "action": { "action": "update", "newUpdateId": "62347d61-c337-4088-b2b5-6d704715ee5b", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055813120, "recordingSchedules": [], "wifiConnectionState": { "channel": 153, "frequency": 5765, "phyRate": 200, "signalQuality": 100, "signalStrength": -52, "ssid": "Mortis Camera" } } }, "26.125862699991558": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSAZQ5p4A3HdA8x0fqBgW5GKSPHumi7/+7y3k4zbtC506ej1MNlAp44WvIdjXK0dIdek4N5xPEcXNcGpD8Vp1FB84Gylb9q8Gu43fJozyoynNDodDXCtXgz/kK8srNAUcir0/QHRhiVgAgEBAAAAAKN4nGWNuw7CMAxFf6XKDFJfSRFr5w7QETFEqdtYCgkkrlBV9d9xhcTC5nPutb0KpxN1gTB4cc4KVVe5lKeiUlIeMvG0y1UTcCIbxggmxAH91BsLw+wgcXK7c/LGEdvgPZj9Uk/fpVUYq1k6nhW3xgivGbxZmMu6av4/JJy8dpdZO6S9VeT5z/YUwU9kWR9VueuEA4PoQiRMWasfELXYtg+pBkKb", "action": { "action": "update", "newUpdateId": "18f6be27-2434-4b6e-b019-b4b190128d97", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "lastMotion": 1643055813655, "phyRate": 57, "recordingSchedules": [], "wifiConnectionState": { "channel": 6, "frequency": 2437, "phyRate": 57, "signalQuality": 100, "signalStrength": -62, "ssid": "Mortis Camera" } } }, "26.274484499997925": { "raw": "AQEBAAAAAHV4nB2MWwrCMBBFt1Lm24DGPG7cgfjtAiaZGSjYVqRFRNy7pJ/3HM79Erd1XGa6DLQ9hVelw0Czvu/7uEoX2QwZKTsGzAXU5LiG6Jr5kGMRD4+eTYvo46af3jSe9MWdjvuHHZucYkVVKXoGSvFIjEa/P+VgJUUCAQEAAAABQnicbVJNT8QgFPwrpmdjHqW04FHjTQ+bHo0HtrxtiUgrUHWz6X+XUtP98gYz84Z5Ew6Zw6Z3Stu2bjpUo0Gf3d+8vt3eZD7IMF8Omft52IdEEF6VtBKcEoiKsOIVAAjCRE5KEolvvdNpsumktWji2Y7GRGbn8HNE2+yPkNH2vR4Q1ct28EfY69ZKsxml0WFWM1jBOji0begiClNEtzIEdPv04oCuQRtki0cr7R876dq4ZMR20nicrQziUMcdZ2GmtG/6GLUJqLLZ80sr7Jft14aCdGEuoaSCcMpZLqoqSlfBk1WJLigwVolCCHFKp/nnzbmDyC8c/gTJgxMAMhca9AcaOXj8J0NxJjjLwGICzq/mLzNcOZxmIMAA+JQ+RO+WXg/Z6HF5ByiIkhU0p2mRpc6CsDsCPFYA8beQYpqmX03Ft/w=", "action": { "action": "update", "newUpdateId": "7ff87867-a88f-48b6-ab45-cf24759d2828", "modelKey": "camera", "id": "f0cd15b8bed9e38899286a8c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 18763798310, "txBytes": 700091592161, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852977, "recordingEnd": 1643055794999, "recordingStartLQ": 1639183852992, "recordingEndLQ": 1643055810011, "timelapseStart": 1639183852941, "timelapseEnd": 1643055599988, "timelapseStartLQ": 1639183852941, "timelapseEndLQ": 1643055105008 }, "storage": { "used": 1603096543232, "rate": 415.108794037914 } } } }, "26.32569989998592": { "raw": "AQEBAAAAAHV4nB2MSwrCQBAFrxJ67cA4n+7EG4hrD9C/QMAkIgki4t1lsnxV1PsC6zatC1w62J/Gm8Opg8Xf92NcrQk0ieSZg3hvoUS1MHAcA1biM9ZEUbRl82r+uPmnNcqzv7jR6fioPbKQJhEsKWsezQr5UOH3BwxwJcICAQEAAAABUHicbVJBboMwEPxKxLmqbMBgemzVW3OIOFY9OLABq66h9tI2ivh715AmUOVmz86OZ0Y+RUZ53HaoOxs9bHiWJkwIyVNexHebyEHVuVrbpqxaqAcDnkivbzTxqDBcTpH7eTziNIh5XIhUcsYZMfCCc8ZiyQUj3Tyjybc+6Gm1apW1YOhsB2NocnDwOYCtjlfIaPte9gD1dt/7K+x1Y5XZDcpoDGzBLmCJDmyDLaFsJHSvEMEdpxd7cBVYVA1cpbR/apVrKCVhB2U8BCkD0JcUMhCjWvuqI6sVQh0FzS9dQzfHv1SEyuHcIcWVeZrIjC87fLb1smImGVtVHPZfdkuFNEnkP4U/wlkjzwMB9QcY1Xu44UGsCCsPqTx7WO+vPdxQWHoQucgLOU4/onNzr6do8DC9U5BAkXGexXEagsx15kzexwn9MMZlIopiHMdfrzK/zg==", "action": { "action": "update", "newUpdateId": "6db07e3a-be8d-40cd-9a0f-657a165270bc", "modelKey": "camera", "id": "586ab7c2bb6423c3fdd47e95" }, "data": { "lastMotion": 1643055814192, "recordingSchedules": [], "stats": { "rxBytes": 21295481010, "txBytes": 1002815058176, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640288743861, "recordingEnd": 1643055810800, "recordingStartLQ": 1640288744338, "recordingEndLQ": 1643055810778, "timelapseStart": 1640288743858, "timelapseEnd": 1643055480800, "timelapseStartLQ": 1640288743858, "timelapseEndLQ": 1643055575798 }, "storage": { "used": 1987496116224, "rate": 708.231920183599 } } } }, "26.33047240000451": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSCJjWm8gbjuAWbmT6BgW5GKiHj3ki7/e7z/I9ZtWhe6dvR+gjejU0eLfcZj3NBEylW5GpyPIbnewzuRUJxc2KP0g8agLZtX2ONu39Yoz/biRqfjg+VsMdeIlCpjCBmsWaTQfwcPriZDAgEBAAAAAUF4nG1STU/EIBT8K4azMdAWCh413vSw6dF4YOnbloi0AlU3m/53gZrulzeYmTfMm3BADtTgWm27RvXQTgY8ur95fbu9QT7IkC4H5H4e9iETpK6qEvOS0DoqwoozWhaMM0EEFpH41judJ1UvrQUTz3YyJjI7B58TWLU/Qkbb92YEaF+2oz/CXndWms0kjQ5JTfEKNsGB7UIfUTxHdCtDALfPL47gFNggOzhaaf/YS9fFJSO2k8ZDsjIAYxN3TELUaq+GGFUFaFHy/NItDMv2a0NBupBKYKUgvORxZ55CrYIn22Y6VkRpLRjB7JTO88+bcwfKLxz+BNmDE4JJblp/gJGjh38y1OJUcJaBkWRwNX+Z4crhNAMhDFM+5w8xuKXXA5o85HeqmuCCFYLzpYqlzrKkdwUjvBZV/BO8muf5Fz3Et/I=", "action": { "action": "update", "newUpdateId": "57fcafed-0215-40d0-bb19-b6a0d948c21c", "modelKey": "camera", "id": "ab3e27f2d55fad817dac7bb9" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 17443083157, "txBytes": 653268691909, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183853280, "recordingEnd": 1643055796106, "recordingStartLQ": 1639183853258, "recordingEndLQ": 1643055811017, "timelapseStart": 1639183853279, "timelapseEnd": 1643055611101, "timelapseStartLQ": 1639183853279, "timelapseEndLQ": 1643055116058 }, "storage": { "used": 1471026298880, "rate": 335.261879469184 } } } }, "26.33272589999251": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbSCTTqLxBuLaA6SZP1CwrUiLiHh3TZf/fd77UKnruMx07mh7aFlBh45mvG77uGg7Aoe+WKjOzJKTKOYGpOg8tLIKImdt2rQo7le8m1PLhGdpdNwbDPSnHDEILPDRe0mW/036/gASwCXKAgEBAAAAAU54nG1SwVLDIBD9lU7O6gALhHjU8aaHTo6OB5psE0YkkRC108m/C0mtrfYG7719+3Zhn3msOl8b15RVi/VocchuV88vV6tsCDqkyz7zX3e7MBOKCslyKoFEQTjCwGXOJBMFBRmJT7M1c2HVaufQxjMVEImtx/cRXbWLiMiliJA17rXsEeunTZ+s3Ghtam4ap+161NaEpKaEHNEyeHRNaCN8zcUU8Y0OAf1u7tmjr9AF3eCvmxnuW+2bOGXEttoOmMwsYl/GIZMwq81QdTFsFbDOkueHqbFbxj+uKGgfUhgJBaMFU5zkRZQeBQ+unmkORAhFlGD0lJ7rH9enDlCo/I/DQXDwKDikNQXzhlb3A17IsDzGj+Asg6AgJPlXf57hgsNpBgEgmJrmH9H5Za/7bBww9YEijwY850qlMZZlUrgBAgyAcUoUK6Zp+gbGs7XF", "action": { "action": "update", "newUpdateId": "2123af2c-fff6-454f-be65-0edc1d4e519d", "modelKey": "camera", "id": "1ee3895eb4ef2170046f9f2c" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 8156271630, "txBytes": 346726259136, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -45 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639219284079, "recordingEnd": 1643055808521, "recordingStartLQ": 1639219283987, "recordingEndLQ": 1643055809435, "timelapseStart": 1639219284030, "timelapseEnd": 1643055513560, "timelapseStartLQ": 1639219284030, "timelapseEndLQ": 1643055533528 }, "storage": { "used": 39728447488, "rate": 13.3032332410829 } } } }, "26.341367200016975": { "raw": "AQEBAAAAAHV4nB2MSwqDQBAFryK9zsB8WpzODULWHqB/ghA1BEMIkruHcfmqqHcA6z5vK1w7eD+Nd4dLB6t/xnPcrAkWNFLCUCWmgKIaZEocsM9DFKI+FWvZspk/7v5tjfLiL250Pj+QSylGVCavnr3ikEUpJvj9Afs4JTYCAQEAAAABRnicbVJNT4QwFPwrG87GPOgnHjXe9LDhaDx04S001oJtUTcb/rsta2BXl1OZN286M+kxc1j3rtG2reoOm9Ggz+42L683m8wHFdLPMXPf94cwDwrBioLHD0RkhAXPaSkEJZRSoCxOvvRez6t1p6xFE892NCZO9g4/RrT1YYWMtm/VgNg87wa/wl63VpntqIwOic1gAavg0LahiyhMEd2pENAd5hsHdDXaoFpcpbR/6JRrY8qI7ZXxmKQM4lDFkImYNdrXfbRaB2yypPmpG+xP8ZeKgnIhpeWkzCWRrChLGakL4dE285gSYExIKOaaLveftucKBID+UfglzBoyB+Blqlq/o1GDx2se2DnhwgMHKHL6b//SwxWFcw/RAmF8ml9E7069HrPRY7on+mcUCOGMCJ6CnOqUpLilIGReSuCilNM0/QDA8bgj", "action": { "action": "update", "newUpdateId": "ab4d9c94-8b01-4bcc-bf1a-45270b99513d", "modelKey": "camera", "id": "4a333d993fe8e2e8472bc901" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 27522666607, "txBytes": 1497743444045, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852998, "recordingEnd": 1643055780207, "recordingStartLQ": 1639183853004, "recordingEndLQ": 1643055810069, "timelapseStart": 1639183852995, "timelapseEnd": 1643055600214, "timelapseStartLQ": 1639183852995, "timelapseEndLQ": 1643055100356 }, "storage": { "used": 3005403365376, "rate": 832.407819806798 } } } }, "26.34329859999707": { "raw": "AQEBAAAAAHR4nB2MSwrCQBAFrxJ67cCkaZsZbyCuPUBPfyBgEpFIEPHuMlm+Kup9QXSb1gUuA7yfJpvDaYDF9/sxrtZFidBAs1RHwUQuJVXMkQpVVWIKOnPP5tX8cfNPb1Rmf0mn0/HhGJHFnJvnQMHmjcfCFX5/GOQmNQIBAQAAAAFIeJxtUk1PwzAM/StTzoDy2bUcQdzgMPWIOGSN10aEtKQpME397zitVsa2W/z8/Pzs+EACVG0w1tdl1YAZHPTkfvX6drMifdQxBQcSfh72cUpIWRRKZIpJJMQFZlKpQnHBc1Fg4tvu7FRYNdp7cPjOEN4F+BzAV3uMuRRrhJz172UHYF62XRLyg3Opta29dptBOxsTm1G6oGUM4OvYIHyb8RHxrY4Rwn7q2EGowEddw5+a7R8bHWqcEbGddj0kMQfQlThiIhJj+6pFq1UEQ5LmlzXQzsMvC4o6xGQmk1TQPJdUZWmGhfDkzZwWVKmcMSrVaXqqf96cKYgzhSPhqJHztGn7AU53PVzxIMQp4Z8HxXFx6qL+3MOFwqkH/G3K6DjdQxvmvR7I0EPqs5ZrgT9JC0XTQYR5m0LyO8bxFljOqZJ8HMdfTky1Rw==", "action": { "action": "update", "newUpdateId": "8ffcf2dd-91a2-4ea8-920f-849cc464f456", "modelKey": "camera", "id": "e2ff0ade6be0f2a2beb61869" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 4499536514, "txBytes": 145595232839, "wifi": { "channel": 6, "frequency": 2437, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -62 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1640308840567, "recordingEnd": 1643055811045, "recordingStartLQ": 1640308840563, "recordingEndLQ": 1643055811082, "timelapseStart": 1640308840533, "timelapseEnd": 1643055521005, "timelapseStartLQ": 1640308840533, "timelapseEndLQ": 1643055536010 }, "storage": { "used": 747324309504, "rate": 342.122831820542 } } } }, "26.3449478999828": { "raw": "AQEBAAAAAHZ4nB2M0QrCMAxFf2Xk2ULatEv1D8RnPyBNIgzcJjIREf9dusd7Dud+QXSb1gVOA7weJpvDYYDF39d9nK2LRpS9HCmoaQvZEwWpiQMj5hKjF8Nbz+bV/H7xT29UZn9Kp9P+oXlMimzNRhKrWIQpVmX4/QHc6ST0AgEBAAAAAUV4nGVSTU/EIBT8K5uejeGzpR413vSw6dF4YMvbloi0AlU3m/53gTXtftzKzLxh5tFj4aAdnNK2a9oe1GTAFw+bt/e7TeGDDOlwLNzv4yFkggjGKsJrjnhUhAXHXNAaUVFTRkVkfvRe59G2l9aCid92MiYyewdfE9j2sEJG249mBFCvu9GvsNedlWY7SaNDUnO0gE1wYLvQRxTNEd3JEMAd8o0juBZskB2sVto/9dJ1sWXE9tJ4SFYGYGxiySQslPbtEKO2AVSRPL+1guFUf1lRkC6ktiWtsaCCkxqVUboInq3KNKOI86pidU3P6Tz/sr10wPjK4V+QPQTiiCZB0J9g5OjhNoMQ4lxwkYFXHJf8Zv4qw63DeQYSFVU15z9icKe9HovJg8qPggkhpaCMUZKKnNaJK07vcVmSkkeCsnme/wDmPLg7", "action": { "action": "update", "newUpdateId": "b334e593-cdcb-4e23-a827-7004511e5d0f", "modelKey": "camera", "id": "c462c07dbd63ad805a7318c7" }, "data": { "recordingSchedules": [], "stats": { "rxBytes": 28447259505, "txBytes": 1583903893438, "wifi": { "channel": null, "frequency": null, "linkSpeedMbps": null, "signalQuality": 50, "signalStrength": 0 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1639183852906, "recordingEnd": 1643055774993, "recordingStartLQ": 1639183852911, "recordingEndLQ": 1643055805031, "timelapseStart": 1639183852888, "timelapseEnd": 1643055575165, "timelapseStartLQ": 1639183852888, "timelapseEndLQ": 1643055285277 }, "storage": { "used": 5012226834432, "rate": 1753.16626544334 } } } }, "26.353903600014746": { "raw": "AQEBAAAAAHZ4nB2MQQrCMBBFr1JmbaBpJknjDcS1B/jNzEDBtiIVEfHuki7/e7z/JdR93lY6d/R6CHalU0ervm/HuEgTXCRKD3NxiOyYx+KAMLocc/DJEth8y5ZN9H7VT2sqFn2i0fn48LVgkInFkpqAM0Lsy6T0+wPPQCVuAgEBAAAAAVF4nG1Sy07DMBD8lSpnQH7GCUcQN3qockQc3GSbWBgn2BugqvLv2KkoTcvRs7MzsyMfMqsDrns0vcvuVzQXnEhZUJ7T8maVeah73xjXVnUHzWghRNLLa5wE1Jgeh8x/P+xxHqiSKiJLLvJIwBPMFBGc5UIIQuPgy+zMvFh32jmwyVbyONh5+BjB1fuISJXLCFnj3qoBoFlvhyTlRmuTuWmdtptRW4OJTQk5oRV6cC12Eb6VbIr4ViOC38+eA/gaHOoW/tRMeOy0b+OVEdtpGyCJWYChikcmYtaYUPcxbI3QZEnz0zTQH88/VYTa47FDWjJKRCnLRYdPrjmvmCgpFxWn/efNUkGJC4VfwkIDzTtYPQT4L4M4JywyKE4UVVf7lxmuFM4zSEYUk9P8I3p/7PWQjQGSj2CKi5IJWUqW7ji2mQt2xwkrClpEDzpN0w/ZVb14", "action": { "action": "update", "newUpdateId": "49d5d0af-5254-4489-aa38-757316f6a4f1", "modelKey": "camera", "id": "1c9a2db4df6efda47a3509be" }, "data": { "lastMotion": 1643055813619, "recordingSchedules": [], "stats": { "rxBytes": 7917059346, "txBytes": 270432644401, "wifi": { "channel": 153, "frequency": 5765, "linkSpeedMbps": null, "signalQuality": 100, "signalStrength": -52 }, "battery": { "percentage": null, "isCharging": false, "sleepState": "disconnected" }, "video": { "recordingStart": 1641921049599, "recordingEnd": 1643055810755, "recordingStartLQ": 1641921049574, "recordingEndLQ": 1643055810755, "timelapseStart": 1641921049594, "timelapseEnd": 1643055730717, "timelapseStartLQ": 1641921049594, "timelapseEndLQ": 1643055520725 }, "storage": { "used": 427349245952, "rate": 642.30288184191 } } } }, "26.413267600000836": { "raw": "AQEBAAAAAHd4nB2MQQrCMBBFr1JmbWBiJpPEG5SuPUDMTKGgidiKFPHuki7/e7z/hVy2pVW4DPB+St4UTgNU/VyPMUoXGDMLRTZKgoZm9CYFSYaQYnLCMSD17NFE75PuvVm1ru3V6XJ8+GJv1nI5JyX1PgecXQqO4fcHsyskLwIBAQAAAAA6eJyrVsos9s0vyczPc0ktSU0uSU1RslJIS8wpTtVRUMpFkXAsAUoZmpkYG5iaWhiamJoa1QIArvUUDg==", "action": { "action": "update", "newUpdateId": "08a6d486-e4d0-4f05-97d9-404893d68704", "modelKey": "sensor", "id": "5c1b116c29e4e55a70f39736" }, "data": { "isMotionDetected": false, "motionDetectedAt": 1643055814552 } }, "26.51632429996971": { "raw": "AQEBAAAAAHV4nB2MQQrCMBBFr1JmbSDJTEzjDaRrD5DJTKCgqUhrEfHuki7/e7z/hVzWeWlwGWB7Sl4VTgM03W/HuEoX1ScpIwdDCaMhcsFktKNRSq6wj2zPrmePRfQ+6ac37f3qaD4OPGGoNWRGa8W5JNESsyL8/kiXI4ICAQEAAAAAN3icq1YqLSjJzE1VslIwszC0MLAwMDDQUVDKSSwuCU5NzQMKG5qZGBuYmloYmpiZGtUCAEttDMU=", "action": { "action": "update", "newUpdateId": "f29dc8b5-4937-4415-a308-e491cb27b061", "modelKey": "nvr", "id": "2435ff5ab300d119d704bbe3" }, "data": { "uptime": 681808000, "lastSeen": 1643055814652 } } } uiprotect-6.1.0/tests/test_api.py000066400000000000000000000601131467310220200170560ustar00rootroot00000000000000"""Tests for uiprotect.unifi_protect_server.""" from __future__ import annotations from copy import deepcopy from datetime import datetime, timedelta from io import BytesIO from ipaddress import IPv4Address from typing import TYPE_CHECKING from unittest.mock import AsyncMock, patch import pytest from PIL import Image from tests.conftest import ( TEST_BRIDGE_EXISTS, TEST_CAMERA_EXISTS, TEST_HEATMAP_EXISTS, TEST_LIGHT_EXISTS, TEST_LIVEVIEW_EXISTS, TEST_SENSOR_EXISTS, TEST_SMART_TRACK_EXISTS, TEST_SNAPSHOT_EXISTS, TEST_THUMBNAIL_EXISTS, TEST_VIDEO_EXISTS, TEST_VIEWPORT_EXISTS, MockDatetime, compare_objs, get_time, validate_video_file, ) from tests.sample_data.constants import CONSTANTS from uiprotect.api import ProtectApiClient from uiprotect.data import ( Camera, Event, EventType, ModelType, create_from_unifi_dict, ) from uiprotect.data.types import VideoMode from uiprotect.exceptions import BadRequest, NvrError from uiprotect.utils import to_js_time if TYPE_CHECKING: from uiprotect.data.base import ProtectAdoptableDeviceModel from uiprotect.data.bootstrap import Bootstrap async def check_motion_event(event: Event): data = await event.get_thumbnail() assert data is not None img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} data = await event.get_heatmap() assert data is not None img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} async def check_camera(camera: Camera): if camera.last_motion_event is not None: await check_motion_event(camera.last_motion_event) if camera.last_smart_detect_event is not None: await check_motion_event(camera.last_smart_detect_event) for channel in camera.channels: assert channel._api is not None data = await camera.get_snapshot() assert data is not None img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} camera.last_ring_event # noqa: B018 assert camera.timelapse_url == f"https://127.0.0.1/protect/timelapse/{camera.id}" check_device(camera) for channel in camera.channels: if channel.is_rtsp_enabled: assert ( channel.rtsp_url == f"rtsp://{camera.api.connection_host}:7447/{channel.rtsp_alias}" ) assert ( channel.rtsps_url == f"rtsps://{camera.api.connection_host}:7441/{channel.rtsp_alias}?enableSrtp" ) if VideoMode.HIGH_FPS in camera.feature_flags.video_modes: assert camera.feature_flags.has_highfps else: assert not camera.feature_flags.has_highfps if camera.feature_flags.has_hdr: assert not camera.feature_flags.has_wdr else: assert camera.feature_flags.has_wdr def check_device(device: ProtectAdoptableDeviceModel): assert device.protect_url == f"https://127.0.0.1/protect/devices/{device.id}" async def check_bootstrap(bootstrap: Bootstrap): assert bootstrap.auth_user assert ( bootstrap.nvr.protect_url == f"https://127.0.0.1/protect/devices/{bootstrap.nvr.id}" ) for light in bootstrap.lights.values(): if light.camera is not None: await check_camera(light.camera) light.last_motion_event # noqa: B018 check_device(light) for camera in bootstrap.cameras.values(): await check_camera(camera) for viewer in bootstrap.viewers.values(): assert viewer.liveview check_device(viewer) for sensor in bootstrap.sensors.values(): check_device(sensor) for liveview in bootstrap.liveviews.values(): liveview.owner # noqa: B018 assert ( liveview.protect_url == f"https://127.0.0.1/protect/liveview/{liveview.id}" ) for slot in liveview.slots: expected_ids = set(slot.camera_ids).intersection( set(bootstrap.cameras.keys()), ) assert len(expected_ids) == len(slot.cameras) for user in bootstrap.users.values(): user.groups # noqa: B018 if user.cloud_account is not None: assert user.cloud_account.user == user for event in bootstrap.events.values(): event.smart_detect_events # noqa: B018 if event.type.value in EventType.motion_events() and event.camera is not None: await check_motion_event(event) def test_base_url(protect_client: ProtectApiClient): arg = f"{protect_client.ws_path}?lastUpdateId={protect_client.bootstrap.last_update_id}" assert protect_client.base_url == "https://127.0.0.1" assert protect_client.ws_url == f"wss://127.0.0.1{arg}" protect_client._port = 443 protect_client._update_url() assert protect_client.base_url == "https://127.0.0.1" assert protect_client.ws_url == f"wss://127.0.0.1{arg}" def test_api_client_creation(): """Test we can create the object.""" client = ProtectApiClient( "127.0.0.1", 0, "username", "password", debug=True, store_sessions=False, ) assert client def test_early_bootstrap(): client = ProtectApiClient( "127.0.0.1", 0, "username", "password", debug=True, store_sessions=False, ) with pytest.raises(BadRequest): client.bootstrap # noqa: B018 @pytest.mark.asyncio() @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") async def test_bootstrap_get_device_from_mac(bootstrap): orig_bootstrap = deepcopy(bootstrap) mac = bootstrap["cameras"][0]["mac"] client = ProtectApiClient( "127.0.0.1", 0, "username", "password", debug=True, store_sessions=False, ) client.api_request_obj = AsyncMock(side_effect=[bootstrap, orig_bootstrap]) client.update_device = AsyncMock() bootstrap_obj = await client.get_bootstrap() camera = bootstrap_obj.get_device_from_mac(mac) assert camera is not None assert camera.mac == mac @pytest.mark.asyncio() @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") async def test_bootstrap_get_device_from_mac_bad_mac(bootstrap): orig_bootstrap = deepcopy(bootstrap) client = ProtectApiClient( "127.0.0.1", 0, "username", "password", debug=True, store_sessions=False, ) client.api_request_obj = AsyncMock(side_effect=[bootstrap, orig_bootstrap]) client.update_device = AsyncMock() bootstrap_obj = await client.get_bootstrap() camera = bootstrap_obj.get_device_from_mac("not_a_mac") assert camera is None def test_connection_host(protect_client: ProtectApiClient): protect_client.bootstrap.nvr.hosts = [ IPv4Address("192.168.1.1"), IPv4Address("192.168.2.1"), IPv4Address("192.168.3.1"), "se-gw.local", ] # mismatch between client IP and IP that NVR returns protect_client._connection_host = None protect_client._host = "192.168.10.1" assert protect_client.connection_host == IPv4Address("192.168.1.1") # same IP from client and NVR (first match) protect_client._connection_host = None protect_client._host = "192.168.1.1" assert protect_client.connection_host == IPv4Address("192.168.1.1") # same IP from client and NVR (not first match) protect_client._connection_host = None protect_client._host = "192.168.3.1" assert protect_client.connection_host == IPv4Address("192.168.3.1") # same IP from client and NVR (not first match, DNS host) protect_client._connection_host = None protect_client._host = "se-gw.local" assert protect_client.connection_host == "se-gw.local" def test_connection_host_override(): protect = ProtectApiClient( "127.0.0.1", 443, "test", "test", override_connection_host=True, store_sessions=False, ) expected = IPv4Address("127.0.0.1") assert protect._connection_host == expected @pytest.mark.asyncio() async def test_force_update(protect_client: ProtectApiClient): protect_client._bootstrap = None await protect_client.update() assert protect_client.bootstrap original_bootstrap = protect_client.bootstrap protect_client._bootstrap = None with patch("uiprotect.api.ProtectApiClient.get_bootstrap", AsyncMock()) as mock: await protect_client.update() assert mock.called assert protect_client.bootstrap assert original_bootstrap != protect_client.bootstrap @pytest.mark.asyncio() async def test_get_nvr(protect_client: ProtectApiClient, nvr): """Verifies the `get_nvr` method""" nvr_obj = await protect_client.get_nvr() nvr_dict = nvr_obj.unifi_dict() compare_objs(ModelType.NVR.value, nvr, nvr_dict) @pytest.mark.asyncio() async def test_bootstrap(protect_client: ProtectApiClient): """Verifies lookup of all object via ID""" await check_bootstrap(protect_client.bootstrap) @pytest.mark.asyncio() async def test_bootstrap_cached_property(protect_client: ProtectApiClient): """Test cached property works with bootstrap.""" bootstrap = protect_client.bootstrap assert bootstrap.has_doorbell is True bootstrap.cameras = {} assert bootstrap.has_doorbell is True bootstrap._has_doorbell = None assert bootstrap.has_doorbell is False @pytest.mark.asyncio() async def test_bootstrap_construct(protect_client_no_debug: ProtectApiClient): """Verifies lookup of all object via ID""" await check_bootstrap(protect_client_no_debug.bootstrap) @pytest.mark.asyncio() @patch("uiprotect.utils.datetime", MockDatetime) async def test_get_events_raw_default(protect_client: ProtectApiClient, now: datetime): events = await protect_client.get_events_raw(_allow_manual_paginate=False) end = now + timedelta(seconds=10) protect_client.api_request.assert_called_with( # type: ignore[attr-defined] url="events", method="get", require_auth=True, raise_exception=True, params={ "orderDirection": "ASC", "withoutDescriptions": "false", "start": to_js_time(end - timedelta(hours=1)), "end": to_js_time(end), }, ) assert len(events) == CONSTANTS["event_count"] for event in events: assert event["type"] in EventType.values() assert event["modelKey"] in ModelType.values() @pytest.mark.asyncio() async def test_get_events_raw_limit(protect_client: ProtectApiClient): await protect_client.get_events_raw(limit=10) protect_client.api_request.assert_called_with( # type: ignore[attr-defined] url="events", method="get", require_auth=True, raise_exception=True, params={"orderDirection": "ASC", "withoutDescriptions": "false", "limit": 10}, ) @pytest.mark.asyncio() async def test_get_events_raw_types(protect_client: ProtectApiClient): await protect_client.get_events_raw( limit=10, types=[EventType.MOTION, EventType.SMART_DETECT], ) protect_client.api_request.assert_called_with( # type: ignore[attr-defined] url="events", method="get", require_auth=True, raise_exception=True, params={ "orderDirection": "ASC", "withoutDescriptions": "false", "limit": 10, "types": ["motion", "smartDetectZone"], }, ) # test has a scaling "expected time to complete" based on the number of # events in the last 24 hours @pytest.mark.timeout(CONSTANTS["event_count"] * 0.1) # type: ignore[misc] @pytest.mark.asyncio() async def test_get_events(protect_client: ProtectApiClient, raw_events): expected_events = [] for event in raw_events: if event["score"] >= 50 and event["type"] in EventType.device_events(): expected_events.append(event) protect_client._minimum_score = 50 events = await protect_client.get_events(_allow_manual_paginate=False) assert len(events) == len(expected_events) for index, event in enumerate(events): compare_objs(event.model.value, expected_events[index], event.unifi_dict()) if event.type.value in EventType.motion_events(): await check_motion_event(event) @pytest.mark.asyncio() async def test_get_events_not_event(protect_client: ProtectApiClient, camera): protect_client.get_events_raw = AsyncMock(return_value=[camera]) # type: ignore[method-assign] assert await protect_client.get_events() == [] @pytest.mark.asyncio() async def test_get_events_not_event_with_type(protect_client: ProtectApiClient, camera): camera["type"] = EventType.MOTION.value protect_client.get_events_raw = AsyncMock(return_value=[camera]) # type: ignore[method-assign] assert await protect_client.get_events() == [] @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_device_mismatch(protect_client: ProtectApiClient, camera): protect_client.api_request_obj = AsyncMock(return_value=camera) # type: ignore[method-assign] with pytest.raises(NvrError): await protect_client.get_bridge("test_id") @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_device_not_adopted(protect_client: ProtectApiClient, camera): camera["isAdopted"] = False protect_client.api_request_obj = AsyncMock(return_value=camera) # type: ignore[method-assign] with pytest.raises(NvrError): await protect_client.get_camera("test_id") @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_device_not_adopted_enabled(protect_client: ProtectApiClient, camera): camera["isAdopted"] = False protect_client.ignore_unadopted = False protect_client.api_request_obj = AsyncMock(return_value=camera) # type: ignore[method-assign] obj = create_from_unifi_dict(camera) assert obj == await protect_client.get_camera("test_id") @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_camera(protect_client: ProtectApiClient, camera): obj = create_from_unifi_dict(camera) assert obj == await protect_client.get_camera("test_id") @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_light(protect_client: ProtectApiClient, light): obj = create_from_unifi_dict(light) assert obj == await protect_client.get_light("test_id") @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_sensor(protect_client: ProtectApiClient, sensor): obj = create_from_unifi_dict(sensor) assert obj == await protect_client.get_sensor("test_id") @pytest.mark.skipif(not TEST_VIEWPORT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_viewer(protect_client: ProtectApiClient, viewport): obj = create_from_unifi_dict(viewport) assert obj == await protect_client.get_viewer("test_id") @pytest.mark.skipif(not TEST_BRIDGE_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_bridge(protect_client: ProtectApiClient, bridge): obj = create_from_unifi_dict(bridge) assert obj == await protect_client.get_bridge("test_id") @pytest.mark.skipif(not TEST_LIVEVIEW_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_liveview(protect_client: ProtectApiClient, liveview): obj = create_from_unifi_dict(liveview) assert obj == await protect_client.get_liveview("test_id") @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_devices_mismatch(protect_client: ProtectApiClient, cameras): protect_client.api_request_list = AsyncMock(return_value=cameras) # type: ignore[method-assign] with pytest.raises(NvrError): await protect_client.get_bridges() @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_devices_not_adopted(protect_client: ProtectApiClient, cameras): cameras[0]["isAdopted"] = False protect_client.api_request_list = AsyncMock(return_value=cameras) # type: ignore[method-assign] assert len(await protect_client.get_cameras()) == len(cameras) - 1 @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_devices_not_adopted_enabled( protect_client: ProtectApiClient, cameras, ): cameras[0]["isAdopted"] = False protect_client.ignore_unadopted = False protect_client.api_request_list = AsyncMock(return_value=cameras) # type: ignore[method-assign] assert len(await protect_client.get_cameras()) == len(cameras) @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_cameras(protect_client: ProtectApiClient, cameras): objs = [create_from_unifi_dict(d) for d in cameras] assert objs == await protect_client.get_cameras() @pytest.mark.skipif(not TEST_LIGHT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_lights(protect_client: ProtectApiClient, lights): objs = [create_from_unifi_dict(d) for d in lights] assert objs == await protect_client.get_lights() @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_sensors(protect_client: ProtectApiClient, sensors): objs = [create_from_unifi_dict(d) for d in sensors] assert objs == await protect_client.get_sensors() @pytest.mark.skipif(not TEST_VIEWPORT_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_viewers(protect_client: ProtectApiClient, viewports): objs = [create_from_unifi_dict(d) for d in viewports] assert objs == await protect_client.get_viewers() @pytest.mark.skipif(not TEST_BRIDGE_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_bridges(protect_client: ProtectApiClient, bridges): objs = [create_from_unifi_dict(d) for d in bridges] assert objs == await protect_client.get_bridges() @pytest.mark.skipif(not TEST_LIVEVIEW_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_liveviews(protect_client: ProtectApiClient, liveviews): objs = [create_from_unifi_dict(d) for d in liveviews] assert objs == await protect_client.get_liveviews() @pytest.mark.skipif(not TEST_SNAPSHOT_EXISTS, reason="Missing testdata") @patch("uiprotect.utils.datetime", MockDatetime) @patch("uiprotect.api.time.time", get_time) @pytest.mark.asyncio() async def test_get_camera_snapshot(protect_client: ProtectApiClient, now): data = await protect_client.get_camera_snapshot("test_id") assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "cameras/test_id/snapshot", params={ "ts": to_js_time(now), "force": "true", }, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_SNAPSHOT_EXISTS, reason="Missing testdata") @patch("uiprotect.utils.datetime", MockDatetime) @patch("uiprotect.api.time.time", get_time) @pytest.mark.asyncio() async def test_get_pacakge_camera_snapshot(protect_client: ProtectApiClient, now): data = await protect_client.get_package_camera_snapshot("test_id") assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "cameras/test_id/package-snapshot", params={ "ts": to_js_time(now), "force": "true", }, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_SNAPSHOT_EXISTS, reason="Missing testdata") @patch("uiprotect.utils.datetime", MockDatetime) @patch("uiprotect.api.time.time", get_time) @pytest.mark.asyncio() async def test_get_camera_snapshot_args(protect_client: ProtectApiClient, now): data = await protect_client.get_camera_snapshot("test_id", 1920, 1080) assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "cameras/test_id/snapshot", params={ "ts": to_js_time(now), "force": "true", "w": 1920, "h": 1080, }, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_SNAPSHOT_EXISTS, reason="Missing testdata") @patch("uiprotect.utils.datetime", MockDatetime) @patch("uiprotect.api.time.time", get_time) @pytest.mark.asyncio() async def test_get_package_camera_snapshot_args(protect_client: ProtectApiClient, now): data = await protect_client.get_package_camera_snapshot("test_id", 1920, 1080) assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "cameras/test_id/package-snapshot", params={ "ts": to_js_time(now), "force": "true", "w": 1920, "h": 1080, }, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_VIDEO_EXISTS, reason="Missing testdata") @patch("uiprotect.api.datetime", MockDatetime) @patch("uiprotect.api.time.time", get_time) @pytest.mark.asyncio() async def test_get_camera_video(protect_client: ProtectApiClient, now, tmp_binary_file): camera = next(iter(protect_client.bootstrap.cameras.values())) start = now - timedelta(seconds=CONSTANTS["camera_video_length"]) data = await protect_client.get_camera_video(camera.id, start, now) assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "video/export", params={ "camera": camera.id, "channel": 0, "start": to_js_time(start), "end": to_js_time(now), }, raise_exception=False, ) tmp_binary_file.write(data) tmp_binary_file.close() validate_video_file(tmp_binary_file.name, CONSTANTS["camera_video_length"]) @pytest.mark.skipif(not TEST_THUMBNAIL_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_event_thumbnail(protect_client: ProtectApiClient): data = await protect_client.get_event_thumbnail("e-test_id") assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "events/test_id/thumbnail", params={}, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_THUMBNAIL_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_event_thumbnail_args(protect_client: ProtectApiClient): data = await protect_client.get_event_thumbnail("test_id", 1920, 1080) assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "events/test_id/thumbnail", params={ "w": 1920, "h": 1080, }, raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_HEATMAP_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_event_heatmap(protect_client: ProtectApiClient): data = await protect_client.get_event_heatmap("e-test_id") assert data is not None protect_client.api_request_raw.assert_called_with( # type: ignore[attr-defined] "events/test_id/heatmap", raise_exception=False, ) img = Image.open(BytesIO(data)) assert img.format in {"PNG", "JPEG"} @pytest.mark.skipif(not TEST_SMART_TRACK_EXISTS, reason="Missing testdata") @pytest.mark.asyncio() async def test_get_event_smart_detect_track(protect_client: ProtectApiClient): data = await protect_client.get_event_smart_detect_track("test_id") assert data.camera protect_client.api_request.assert_called_with( # type: ignore[attr-defined] url="events/test_id/smartDetectTrack", method="get", require_auth=True, raise_exception=True, ) uiprotect-6.1.0/tests/test_api_polling.py000066400000000000000000000143471467310220200206120ustar00rootroot00000000000000"""Tests for uiprotect.unifi_protect_server.""" from __future__ import annotations from datetime import timedelta from typing import TYPE_CHECKING from unittest.mock import patch import pytest from tests.conftest import MockDatetime from uiprotect.data import Camera, EventType from uiprotect.utils import to_js_time if TYPE_CHECKING: from uiprotect import ProtectApiClient @pytest.mark.asyncio() async def test_process_events_none(protect_client: ProtectApiClient, camera): def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] bootstrap_before = protect_client.bootstrap.unifi_dict() camera_before = get_camera().copy() async def get_events(*args, **kwargs): return [] protect_client.get_events_raw = get_events # type: ignore[method-assign] await protect_client.update() assert protect_client.bootstrap.unifi_dict() == bootstrap_before assert get_camera() == camera_before def _reset_events(camera: Camera) -> None: camera.last_ring_event_id = None camera.last_ring = None camera.last_motion_event_id = None camera.last_motion = None camera.last_smart_detect = None camera.last_smart_detect_event_id = None camera.last_smart_detects = {} camera.last_smart_detect_event_ids = {} @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_process_events_ring(protect_client: ProtectApiClient, now, camera): def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] camera_before = get_camera().copy() expected_event_id = "bf9a241afe74821ceffffd05" async def get_events(*args, **kwargs): return [ { "id": expected_event_id, "type": "ring", "start": to_js_time(now - timedelta(seconds=1)), "end": to_js_time(now), "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", }, ] protect_client.get_events_raw = get_events # type: ignore[method-assign] await protect_client.update() # fetch initial bootstrap await protect_client.poll_events() # process events since bootstrap camera = get_camera() event = camera.last_ring_event _reset_events(camera) _reset_events(camera_before) assert camera.dict() == camera_before.dict() assert event.id == expected_event_id assert event.type == EventType.RING assert event.thumbnail_id == f"e-{expected_event_id}" assert event.heatmap_id == f"e-{expected_event_id}" @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_process_events_motion(protect_client: ProtectApiClient, now, camera): def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] camera_before = get_camera().copy() expected_event_id = "bf9a241afe74821ceffffd05" async def get_events(*args, **kwargs): return [ { "id": expected_event_id, "type": "motion", "start": to_js_time(now - timedelta(seconds=30)), "end": to_js_time(now), "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", }, ] protect_client.get_events_raw = get_events # type: ignore[method-assign] await protect_client.update() # fetch initial bootstrap await protect_client.poll_events() # process events since bootstrap camera_before.is_motion_detected = False camera = get_camera() event = camera.last_motion_event _reset_events(camera) _reset_events(camera_before) assert camera.dict() == camera_before.dict() assert event.id == expected_event_id assert event.type == EventType.MOTION assert event.thumbnail_id == f"e-{expected_event_id}" assert event.heatmap_id == f"e-{expected_event_id}" assert event.start == (now - timedelta(seconds=30)) @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_process_events_smart(protect_client: ProtectApiClient, now, camera): def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] camera_before = get_camera().copy() expected_event_id = "bf9a241afe74821ceffffd05" async def get_events(*args, **kwargs): return [ { "id": expected_event_id, "type": "smartDetectZone", "start": to_js_time(now - timedelta(seconds=30)), "end": to_js_time(now), "score": 0, "smartDetectTypes": ["person"], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", }, ] protect_client.get_events_raw = get_events # type: ignore[method-assign] await protect_client.update() # fetch initial bootstrap await protect_client.poll_events() # process events since bootstrap camera = get_camera() smart_event = camera.last_smart_detect_event assert camera.last_smart_detect == smart_event.start _reset_events(camera) _reset_events(camera_before) assert camera.dict() == camera_before.dict() assert smart_event.id == expected_event_id assert smart_event.type == EventType.SMART_DETECT assert smart_event.thumbnail_id == f"e-{expected_event_id}" assert smart_event.heatmap_id == f"e-{expected_event_id}" assert smart_event.start == (now - timedelta(seconds=30)) assert smart_event.end == now uiprotect-6.1.0/tests/test_api_ws.py000066400000000000000000000430121467310220200175660ustar00rootroot00000000000000"""Tests for uiprotect.unifi_protect_server.""" from __future__ import annotations import asyncio import base64 import logging from copy import deepcopy from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any from unittest.mock import MagicMock, Mock, patch import pytest from tests.conftest import ( TEST_CAMERA_EXISTS, TEST_SENSOR_EXISTS, MockDatetime, MockWebsocket, ) from uiprotect.data import EventType, WSPacket from uiprotect.data.base import ProtectModel from uiprotect.data.devices import EVENT_PING_INTERVAL, Camera from uiprotect.data.types import ModelType from uiprotect.data.websocket import ( WSAction, WSJSONPacketFrame, WSSubscriptionMessage, ) from uiprotect.utils import print_ws_stat_summary, to_js_time, utc_now from uiprotect.websocket import WebsocketState if TYPE_CHECKING: from collections.abc import Callable from pytest_benchmark.fixture import BenchmarkFixture from uiprotect import ProtectApiClient PACKET_RAW = "AQEBAAAAAHR4nCXMQQrCMBBA0auUWRvIpJOm8Qbi2gNMZiZQ0NRFqIh4dwluP7z/AZa+7Q3OE7AqnCZo9ro9lbtddFRPhCayuOorOyqYXfEJXcprEqQZ55UHe+xq96u9h7HDWh9x+y+UqeZAsUrQrCFajFQWgu8PBLYjMAIBAQAAAAC2eJxVjr0KwzAMhF8leO6QOLZDOrdT126lg2PLxRA7wVYKIeTdK1PoD2jQfTodtzFcZ2DHiiUfH+xQsYw6IYFGtbyplaKRnDhE+0u7N81mSuW9LnugzxMgGLxSaCZ8u//z8xMifg4BUFuNmvS2kzY6QCqKdaaXsrNOcSN1ywfbgXGtg1JwpjSPfopkjMs4EloypK/ypSirrRau50I6w21vuQQpxaBEiQiThfECa/FBqcT2F6ZyTac=" @pytest.fixture(name="packet") def packet_fixture(): return WSPacket(base64.b64decode(PACKET_RAW)) class SubscriptionTest: callback_count: int = 0 unsub: Callable[[], None] | None = None def callback(self, msg: WSSubscriptionMessage): self.callback_count += 1 assert isinstance(msg.new_obj, ProtectModel) if msg.action == WSAction.ADD: assert msg.old_obj is None else: assert isinstance(msg.old_obj, ProtectModel) assert msg.old_obj.update_from_dict(msg.changed_data) == msg.new_obj if self.callback_count == 2: raise Exception if self.callback_count >= 3 and self.unsub is not None: self.unsub() @pytest.mark.benchmark(group="websockets") @pytest.mark.asyncio() @pytest.mark.timeout(0) async def test_ws_all( protect_client_ws: ProtectApiClient, ws_messages: dict[str, dict[str, Any]], benchmark: BenchmarkFixture, ): protect_client = protect_client_ws sub = SubscriptionTest() sub.unsub = protect_client.subscribe_websocket(sub.callback) _orig = protect_client.bootstrap.process_ws_packet websocket = protect_client._get_websocket() while not websocket.is_connected: await asyncio.sleep(0.05) stats = benchmark._make_stats(1) processed_packets = 0 def benchmark_process_ws_packet(*args, **kwargs): nonlocal processed_packets processed_packets += 1 runner = benchmark._make_runner(_orig, args, kwargs) duration, result = runner(None) stats.update(duration) return result # bypass pydantic checks object.__setattr__( protect_client.bootstrap, "process_ws_packet", benchmark_process_ws_packet, ) ws_connect: MockWebsocket | None = websocket._ws_connection # type: ignore[assignment] assert ws_connect is not None while websocket.is_connected: await asyncio.sleep(0.05) assert sub.callback_count == 3 @pytest.mark.benchmark(group="websockets") @pytest.mark.asyncio() @pytest.mark.timeout(0) async def test_ws_filtered( protect_client_ws: ProtectApiClient, benchmark: BenchmarkFixture, ): protect_client = protect_client_ws protect_client.bootstrap.capture_ws_stats = True protect_client._ignore_stats = True protect_client._subscribed_models = { ModelType.EVENT, ModelType.CAMERA, ModelType.LIGHT, ModelType.VIEWPORT, ModelType.SENSOR, ModelType.LIVEVIEW, } sub = SubscriptionTest() sub.unsub = protect_client.subscribe_websocket(sub.callback) _orig = protect_client.bootstrap.process_ws_packet websocket = protect_client._get_websocket() while not websocket.is_connected: await asyncio.sleep(0.05) stats = benchmark._make_stats(1) def benchmark_process_ws_packet(*args, **kwargs): runner = benchmark._make_runner(_orig, args, kwargs) duration, result = runner(None) stats.update(duration) return result # bypass pydantic checks object.__setattr__( protect_client.bootstrap, "process_ws_packet", benchmark_process_ws_packet, ) ws_connect: MockWebsocket | None = websocket._ws_connection # type: ignore[assignment] assert ws_connect is not None while websocket.is_connected: await asyncio.sleep(0.05) print_ws_stat_summary(protect_client.bootstrap.ws_stats) @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_ws_event_ring( protect_client_no_debug: ProtectApiClient, now, camera, packet: WSPacket, ): protect_client = protect_client_no_debug def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] camera_before = get_camera().copy() expected_updated_id = "0441ecc6-f0fa-4b19-b071-7987c143138a" expected_event_id = "bf9a241afe74821ceffffd05" action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data["newUpdateId"] = expected_updated_id data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = { "id": expected_event_id, "type": "ring", "start": to_js_time(now - timedelta(seconds=1)), "end": to_js_time(now), "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", } msg = MagicMock() msg.data = packet.pack_frames() protect_client._process_ws_message(msg) camera = get_camera() event = camera.last_ring_event camera_before.last_ring_event_id = None camera.last_ring_event_id = None assert camera.last_ring == event.start camera.last_ring = None camera_before.last_ring = None assert camera.dict() == camera_before.dict() assert event.id == expected_event_id assert event.type == EventType.RING assert event.thumbnail_id == f"e-{expected_event_id}" assert event.heatmap_id == f"e-{expected_event_id}" for channel in camera.channels: assert channel._api is not None @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_ws_event_motion( protect_client_no_debug: ProtectApiClient, now, camera, packet: WSPacket, ): protect_client = protect_client_no_debug def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] camera_before = get_camera().copy() expected_updated_id = "0441ecc6-f0fa-4b19-b071-7987c143138a" expected_event_id = "bf9a241afe74821ceffffd05" action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data["newUpdateId"] = expected_updated_id data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = { "id": expected_event_id, "type": "motion", "start": to_js_time(now - timedelta(seconds=30)), "end": to_js_time(now), "score": 0, "smartDetectTypes": [], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", } msg = MagicMock() msg.data = packet.pack_frames() protect_client._process_ws_message(msg) camera = get_camera() event = camera.last_motion_event camera_before.last_motion_event_id = None camera.last_motion_event_id = None assert camera.last_motion == event.start camera_before.last_motion = None camera.last_motion = None assert camera.dict() == camera_before.dict() assert event.id == expected_event_id assert event.type == EventType.MOTION assert event.thumbnail_id == f"e-{expected_event_id}" assert event.heatmap_id == f"e-{expected_event_id}" assert event.start == (now - timedelta(seconds=30)) for channel in camera.channels: assert channel._api is not None @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_ws_event_smart( protect_client_no_debug: ProtectApiClient, now, camera, packet: WSPacket, ): protect_client = protect_client_no_debug def get_camera(): return protect_client.bootstrap.cameras[camera["id"]] bootstrap_before = protect_client.bootstrap.unifi_dict() camera_before = get_camera().copy() expected_updated_id = "0441ecc6-f0fa-4b19-b071-7987c143138a" expected_event_id = "bf9a241afe74821ceffffd05" action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data["newUpdateId"] = expected_updated_id data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = { "id": expected_event_id, "type": "smartDetectZone", "start": to_js_time(now - timedelta(seconds=30)), "end": to_js_time(now), "score": 0, "smartDetectTypes": ["person"], "smartDetectEvents": [], "camera": camera["id"], "partition": None, "user": None, "metadata": {}, "thumbnail": f"e-{expected_event_id}", "heatmap": f"e-{expected_event_id}", "modelKey": "event", } msg = MagicMock() msg.data = packet.pack_frames() protect_client._process_ws_message(msg) bootstrap_before["lastUpdateId"] = expected_updated_id bootstrap = protect_client.bootstrap.unifi_dict() camera = get_camera() smart_event = camera.last_smart_detect_event camera.last_smart_detect_event_id = None camera.last_smart_detect = None camera_before.last_smart_detect_event_id = None camera_before.last_smart_detect = None assert bootstrap == bootstrap_before assert camera.dict() == camera_before.dict() assert smart_event.id == expected_event_id assert smart_event.type == EventType.SMART_DETECT assert smart_event.thumbnail_id == f"e-{expected_event_id}" assert smart_event.heatmap_id == f"e-{expected_event_id}" assert smart_event.start == (now - timedelta(seconds=30)) assert smart_event.end == now for channel in camera.channels: assert channel._api is not None @pytest.mark.asyncio() @patch("uiprotect.api.datetime", MockDatetime) async def test_ws_event_update( protect_client_no_debug: ProtectApiClient, now, camera, packet: WSPacket, ): protect_client = protect_client_no_debug def get_camera() -> Camera: return protect_client.bootstrap.cameras[camera["id"]] bootstrap_before = protect_client.bootstrap.unifi_dict() camera_before = get_camera().copy() new_stats = camera_before.stats.unifi_dict() new_stats["rxBytes"] += 100 new_stats["txBytes"] += 100 new_stats["video"]["recordingEnd"] = to_js_time(now) new_stats_unifi = camera_before.unifi_dict(data={"stats": deepcopy(new_stats)}) del new_stats_unifi["stats"]["wifiQuality"] del new_stats_unifi["stats"]["wifiStrength"] expected_updated_id = "0441ecc6-f0fa-4b19-b071-7987c143138a" action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data = { "action": "update", "newUpdateId": expected_updated_id, "modelKey": "camera", "id": camera["id"], } data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = new_stats_unifi msg = MagicMock() msg.data = packet.pack_frames() protect_client._process_ws_message(msg) camera_index = -1 for index, camera_dict in enumerate(bootstrap_before["cameras"]): if camera_dict["id"] == camera["id"]: camera_index = index break bootstrap_before["cameras"][camera_index]["stats"] = new_stats bootstrap_before["lastUpdateId"] = expected_updated_id bootstrap = protect_client.bootstrap.unifi_dict() assert bootstrap == bootstrap_before @pytest.mark.skipif(not TEST_SENSOR_EXISTS, reason="Missing testdata") @patch("uiprotect.data.devices.utc_now") @patch("uiprotect.data.base.EVENT_PING_INTERVAL_SECONDS", 0) @pytest.mark.asyncio() async def test_ws_emit_alarm_callback( mock_now, protect_client_no_debug: ProtectApiClient, now: datetime, sensor, packet: WSPacket, ): mock_now.return_value = now protect_client = protect_client_no_debug protect_client.emit_message = Mock() # type: ignore[method-assign] obj = protect_client.bootstrap.sensors[sensor["id"]] expected_updated_id = "0441ecc6-f0fa-4b19-b071-7987c143138a" data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = {"alarmTriggeredAt": to_js_time(now)} action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data = { "action": "update", "newUpdateId": expected_updated_id, "modelKey": "sensor", "id": sensor["id"], } data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = {"alarmTriggeredAt": to_js_time(now)} msg = MagicMock() msg.data = packet.pack_frames() assert not obj.is_alarm_detected with patch("uiprotect.data.bootstrap.utc_now", mock_now): protect_client._process_ws_message(msg) assert obj.is_alarm_detected mock_now.return_value = utc_now() + EVENT_PING_INTERVAL assert not obj.is_alarm_detected # The event message should be emitted assert protect_client.emit_message.call_count == 1 await asyncio.sleep(0) await asyncio.sleep(0) # An empty messages should be emitted assert protect_client.emit_message.call_count == 2 message: WSSubscriptionMessage = protect_client.emit_message.call_args[0][0] assert message.changed_data == {} assert not obj.is_alarm_detected @pytest.mark.asyncio() async def test_check_ws_connected( protect_client_ws: ProtectApiClient, caplog: pytest.LogCaptureFixture, ): caplog.set_level(logging.DEBUG) unsub = protect_client_ws.subscribe_websocket(lambda _: None) while not protect_client_ws._websocket.is_connected: await asyncio.sleep(0.01) assert protect_client_ws._websocket.is_connected unsub() @pytest.mark.asyncio() async def test_check_ws_connected_state_callback( protect_client_ws: ProtectApiClient, caplog: pytest.LogCaptureFixture, ): websocket = protect_client_ws._websocket assert not websocket.is_connected caplog.set_level(logging.DEBUG) states: list[bool] = [] def _on_state(state: bool): states.append(state) unsub_state = protect_client_ws.subscribe_websocket_state(_on_state) unsub = protect_client_ws.subscribe_websocket(lambda _: None) while websocket._current_state is not WebsocketState.CONNECTED: await asyncio.sleep(0.01) assert states == [WebsocketState.CONNECTED] await protect_client_ws.async_disconnect_ws() while websocket._current_state is not WebsocketState.DISCONNECTED: await asyncio.sleep(0.01) assert states == [WebsocketState.CONNECTED, WebsocketState.DISCONNECTED] unsub() unsub_state() @pytest.mark.asyncio() async def test_check_ws_no_ws_initial( protect_client: ProtectApiClient, caplog: pytest.LogCaptureFixture, ): caplog.set_level(logging.DEBUG) await protect_client.async_disconnect_ws() assert not protect_client._websocket @pytest.mark.skipif(not TEST_CAMERA_EXISTS, reason="Missing testdata") @patch("uiprotect.data.devices.utc_now") @pytest.mark.asyncio() async def test_ws_ignores_nvr_mac_and_guid( mock_now, protect_client_no_debug: ProtectApiClient, now: datetime, camera, packet: WSPacket, ): mock_now.return_value = now protect_client = protect_client_no_debug messages: list[WSSubscriptionMessage] = [] def capture_ws(message: WSSubscriptionMessage) -> None: messages.append(message) unsub = protect_client.subscribe_websocket(capture_ws) action_frame: WSJSONPacketFrame = packet.action_frame # type: ignore[assignment] action_frame.data = { "action": "update", "newUpdateId": "0441ecc6-f0fa-4b19-b071-7987c143138a", "modelKey": "camera", "id": camera["id"], } data_frame: WSJSONPacketFrame = packet.data_frame # type: ignore[assignment] data_frame.data = {"nvrMac": "any", "guid": "any", "isMotionDetected": True} msg = MagicMock() msg.data = packet.pack_frames() assert len(messages) == 0 packet = WSPacket(msg.data) protect_client._process_ws_message(msg) assert len(messages) == 1 action_frame: WSJSONPacketFrame = packet.action_frame action_frame.data = { "action": "update", "newUpdateId": "0441ecc6-f0fa-4b19-b071-7987c143138b", "modelKey": "camera", "id": camera["id"], } data_frame: WSJSONPacketFrame = packet.data_frame data_frame.data = {"nvrMac": "any", "guid": "any"} msg = MagicMock() msg.data = packet.pack_frames() protect_client._process_ws_message(msg) assert len(messages) == 1 unsub() uiprotect-6.1.0/tests/test_cli.py000066400000000000000000000004331467310220200170530ustar00rootroot00000000000000from typer.testing import CliRunner from uiprotect.cli import app runner = CliRunner() def test_help(): """The help message includes the CLI name.""" result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "UniFi Protect CLI" in result.stdout uiprotect-6.1.0/tests/test_dunder_main.py000066400000000000000000000005331467310220200205720ustar00rootroot00000000000000import subprocess import sys def test_can_run_as_python_module(): """Run the CLI as a Python module.""" result = subprocess.run( [sys.executable, "-m", "uiprotect", "--help"], # S603,S607 check=True, capture_output=True, ) assert result.returncode == 0 assert b"uiprotect [OPTIONS]" in result.stdout uiprotect-6.1.0/tests/test_utils.py000066400000000000000000000211521467310220200174450ustar00rootroot00000000000000from __future__ import annotations from datetime import datetime, timezone from enum import Enum from typing import Any from unittest.mock import Mock from uuid import UUID import pytest from pydantic.v1.config import BaseConfig from pydantic.v1.fields import ModelField from uiprotect.utils import ( convert_to_datetime, convert_unifi_data, dict_diff, get_nested_attr, get_nested_attr_as_bool, get_top_level_attr_as_bool, make_enabled_getter, make_required_getter, make_value_getter, to_snake_case, ) def test_dict_diff_equal(): assert dict_diff({}, {}) == {} obj = {"a": 1, "b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]} assert dict_diff(obj, obj) == {} obj = { "a": 1, "b": {"b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}, } assert dict_diff(obj, obj) == {} obj = { "a": 1, "b": {"b": 2, "c": {"c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}}, } assert dict_diff(obj, obj) == {} def test_dict_diff_new_keys(): obj = {"a": 1, "b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]} assert dict_diff({}, obj) == obj assert dict_diff({"a": 1, "b": {}}, {"a": 1, "b": obj}) == {"b": obj} obj = { "a": 1, "b": {"b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}, } assert dict_diff({}, obj) == obj assert dict_diff({"a": 1, "b": {}}, {"a": 1, "b": obj}) == {"b": obj} obj = { "a": 1, "b": {"b": 2, "c": {"c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}}, } assert dict_diff({}, obj) == obj assert dict_diff({"a": 1, "b": {}}, {"a": 1, "b": obj}) == {"b": obj} def test_dict_diff_new_changed(): assert dict_diff( {"a": 1, "b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}, { "a": 3, "b": 2, "c": "test", "d": "test", "e": "test6", "f": [1], "g": [1, 2, 3], }, ) == {"a": 3, "c": "test", "d": "test", "e": "test6", "f": [1]} assert dict_diff( { "a": 1, "b": {"b": 2, "c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}, }, { "a": 3, "b": { "b": 2, "c": "test", "d": "test", "e": "test6", "f": [1], "g": [1, 2, 3], }, }, ) == {"a": 3, "b": {"c": "test", "d": "test", "e": "test6", "f": [1]}} assert dict_diff( { "a": 1, "b": { "b": 2, "c": {"c": None, "d": 2.5, "e": "test", "f": [], "g": [1, 2, 3]}, }, }, { "a": 3, "b": { "b": 2, "c": {"c": "test", "d": "test", "e": "test6", "f": [1], "g": [1, 2, 3]}, }, }, ) == {"a": 3, "b": {"c": {"c": "test", "d": "test", "e": "test6", "f": [1]}}} def test_to_snake_case(): assert to_snake_case("CamelCase") == "camel_case" assert to_snake_case("CamelCamelCase") == "camel_camel_case" assert to_snake_case("Camel2Camel2Case") == "camel2_camel2_case" assert to_snake_case("getHTTPResponseCode") == "get_http_response_code" assert to_snake_case("get2HTTPResponseCode") == "get2_http_response_code" assert to_snake_case("HTTPResponseCode") == "http_response_code" assert to_snake_case("HTTPResponseCodeXYZ") == "http_response_code_xyz" @pytest.mark.parametrize( ("value", "field", "output"), [ ( "00000000-0000-00 0- 000-000000000000", ModelField( name="id", type_=UUID, class_validators=None, model_config=BaseConfig, ), UUID("00000000-0000-0000-0000-000000000000"), ), ( "00000000-0000-0000-0000-000000000000", ModelField( name="id", type_=UUID, class_validators=None, model_config=BaseConfig, ), UUID("00000000-0000-0000-0000-000000000000"), ), ( UUID("00000000-0000-0000-0000-000000000000"), ModelField( name="id", type_=UUID, class_validators=None, model_config=BaseConfig, ), UUID("00000000-0000-0000-0000-000000000000"), ), ], ) def test_convert_unifi_data(value: Any, field: ModelField, output: Any): assert convert_unifi_data(value, field) == output @pytest.mark.asyncio async def test_valid_float_timestamp(): timestamp = 1715563200000.0 expected_datetime = datetime(2024, 5, 13, 1, 20, tzinfo=timezone.utc) assert convert_to_datetime(timestamp).timestamp() * 1000 == timestamp assert convert_to_datetime(timestamp) == expected_datetime @pytest.mark.asyncio async def test_valid_string_timestamp(): timestamp = "1715563200000" expected_datetime = datetime(2024, 5, 13, 1, 20, tzinfo=timezone.utc) assert convert_to_datetime(timestamp).timestamp() * 1000 == int(timestamp) assert convert_to_datetime(timestamp) == expected_datetime @pytest.mark.asyncio async def test_valid_datetime_object(): # Direct datetime object dt = datetime(2024, 6, 11, 12, 0, tzinfo=timezone.utc) assert convert_to_datetime(dt) == dt @pytest.mark.asyncio async def test_none_input(): # None input should return None assert convert_to_datetime(None) is None @pytest.mark.asyncio async def test_invalid_string_input(): # Invalid string should raise ValueError with pytest.raises(ValueError): convert_to_datetime("invalid-date") @pytest.mark.asyncio async def test_caching(): # Test if caching is working by calling the function with the same input multiple times timestamp = 1715563200.0 result1 = convert_to_datetime(timestamp) result2 = convert_to_datetime(timestamp) assert result1 is result2 class _MockEnum(Enum): A = 1 B = 2 C = 3 @pytest.mark.asyncio def test_get_nested_attr(): data = Mock(a=Mock(b=Mock(c=1)), d=3, f=_MockEnum.C) assert get_nested_attr(("a", "b", "c"), data) == 1 assert get_nested_attr(("d",), data) == 3 assert get_nested_attr(("f",), data) == _MockEnum.C @pytest.mark.asyncio def test_get_nested_attr_as_bool(): data = Mock(a=Mock(b=Mock(c=True)), d=False, f=_MockEnum.C) assert get_nested_attr_as_bool(("a", "b", "c"), data) is True assert get_nested_attr_as_bool(("d",), data) is False assert get_nested_attr_as_bool(("f",), data) is True @pytest.mark.asyncio def test_get_top_level_attr_as_bool(): data = Mock(a=True, b=False, c=True, d=None) assert get_top_level_attr_as_bool("a", data) is True assert get_top_level_attr_as_bool("b", data) is False assert get_top_level_attr_as_bool("c", data) is True assert get_top_level_attr_as_bool("d", data) is False @pytest.mark.asyncio def test_make_value_getter(): data = Mock(a=1, b=2, c=3, d=4) assert make_value_getter("a")(data) == 1 assert make_value_getter("b")(data) == 2 assert make_value_getter("c")(data) == 3 assert make_value_getter("d")(data) == 4 @pytest.mark.asyncio def test_make_value_getter_nested(): data = Mock(b=2, c=Mock(q="x")) assert make_value_getter("c.q")(data) == "x" assert make_value_getter("b.x")(data) is None @pytest.mark.asyncio def test_make_enabled_getter(): data = Mock(a=True, b=False, c=True, d=False) assert make_enabled_getter("a")(data) is True assert make_enabled_getter("b")(data) is False assert make_enabled_getter("c")(data) is True assert make_enabled_getter("d")(data) is False @pytest.mark.asyncio def test_make_enabled_getter_nested(): data = Mock(a=Mock(q=True), q=None, c=Mock(q=False)) assert make_enabled_getter("a.q")(data) is True assert make_enabled_getter("q.q")(data) is None assert make_enabled_getter("c.q")(data) is False @pytest.mark.asyncio def test_make_required_getter(): data = Mock(a=1, b=2, c=3, d=_MockEnum.C, e=None) assert make_required_getter("a")(data) is True assert make_required_getter("b")(data) is True assert make_required_getter("c")(data) is True assert make_required_getter("d")(data) is True assert make_required_getter("e")(data) is False @pytest.mark.asyncio def test_make_required_getter_nested(): data = Mock(a=Mock(q=2), b=Mock(q=0), d=Mock(q=_MockEnum.C)) assert make_required_getter("a.q")(data) is True assert make_required_getter("b.q")(data) is False assert make_required_getter("c.q")(data) is True