pax_global_header 0000666 0000000 0000000 00000000064 15003713056 0014512 g ustar 00root root 0000000 0000000 52 comment=8bcd1a98f560b5ec0d5e64d02738aea76c79119c
devolo_home_control_api-0.19.0/ 0000775 0000000 0000000 00000000000 15003713056 0016472 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/.devcontainer/ 0000775 0000000 0000000 00000000000 15003713056 0021231 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/.devcontainer/Dockerfile 0000664 0000000 0000000 00000000225 15003713056 0023222 0 ustar 00root root 0000000 0000000 ARG VARIANT="3.8"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
RUN python -m pip install --upgrade pip && pip install isort ruff
devolo_home_control_api-0.19.0/.devcontainer/devcontainer.json 0000664 0000000 0000000 00000003066 15003713056 0024612 0 ustar 00root root 0000000 0000000 {
"name": "devolo Home Control API",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
"VARIANT": "3.8"
}
},
"customizations": {
"vscode": {
"settings": {
"python.pythonPath": "/usr/local/bin/python",
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.formatting.provider": "black",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"python.linting.enabled": true,
"python.linting.flake8Enabled": false,
"python.linting.mypyEnabled": true,
"python.linting.pylintEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"files.insertFinalNewline": true
},
"extensions": [
"ms-python.vscode-pylance",
"ms-python.isort",
"sourcery.sourcery",
"visualstudioexptteam.vscodeintellicode",
"charliermarsh.ruff"
]
}
},
"postCreateCommand": "pip install -e .[test]"
}
devolo_home_control_api-0.19.0/.github/ 0000775 0000000 0000000 00000000000 15003713056 0020032 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15003713056 0022215 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000001502 15003713056 0024705 0 ustar 00root root 0000000 0000000 ---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
devolo_home_control_api-0.19.0/.github/ISSUE_TEMPLATE/feature_request.md 0000664 0000000 0000000 00000001123 15003713056 0025737 0 ustar 00root root 0000000 0000000 ---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
devolo_home_control_api-0.19.0/.github/dependabot.yml 0000664 0000000 0000000 00000000401 15003713056 0022655 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
target-branch: "main"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
target-branch: "main"
devolo_home_control_api-0.19.0/.github/pull_request_template.md 0000664 0000000 0000000 00000000460 15003713056 0024773 0 ustar 00root root 0000000 0000000 ## Proposed change
## Checklist
- [ ] Changelog is updated.
devolo_home_control_api-0.19.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15003713056 0022067 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/.github/workflows/convert_todos_to_issues.yml 0000664 0000000 0000000 00000000763 15003713056 0027605 0 ustar 00root root 0000000 0000000 name: "TODOs to issues"
on:
push:
branches:
- development
jobs:
build:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@master"
- name: "TODO to Issue"
uses: "alstr/todo-to-issue-action@master"
with:
REPO: ${{ github.repository }}
BEFORE: ${{ github.event.before }}
SHA: ${{ github.sha }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}
LABEL: "# TODO:"
COMMENT_MARKER: "#"
id: "todo"
devolo_home_control_api-0.19.0/.github/workflows/pythonpackage.yml 0000664 0000000 0000000 00000004677 15003713056 0025465 0 ustar 00root root 0000000 0000000 name: Python package
on: [push]
jobs:
format:
name: Check formatting
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.9"
- name: Check formatting
uses: pre-commit/action@v3.0.1
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.9"
- name: Lint with ruff
run: |
python -m pip install --upgrade pip
python -m pip install ruff
ruff check --output-format=github devolo_home_control_api
ruff check --output-format=github --exit-zero tests
- name: Lint with mypy
run: |
pip install mypy types-python-dateutil types-requests
mypy devolo_home_control_api
mypy tests || true
test:
name: Test with Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout sources
uses: actions/checkout@v4.2.2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5.6.0
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
check-latest: true
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[test]
- name: Test with pytest
run: |
pytest --cov=devolo_home_control_api --cov-report=xml
- name: Preserve coverage
uses: actions/upload-artifact@v4.6.2
if: matrix.python-version == '3.9'
with:
name: coverage
path: coverage.xml
coverage:
name: Upload coverage
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout sources
uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: 3.9
- name: Download coverage
uses: actions/download-artifact@v4.3.0
with:
name: coverage
- name: Coveralls
uses: coverallsapp/github-action@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Clean up coverage
uses: geekyeggo/delete-artifact@v5.1.0
with:
name: coverage
devolo_home_control_api-0.19.0/.github/workflows/pythonpublish.yml 0000664 0000000 0000000 00000001164 15003713056 0025524 0 ustar 00root root 0000000 0000000 name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.9"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade build twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python -m build
python -m twine upload dist/*
devolo_home_control_api-0.19.0/.gitignore 0000664 0000000 0000000 00000007725 15003713056 0020475 0 ustar 00root root 0000000 0000000
# Created by https://www.gitignore.io/api/linux,python,pycharm,visualstudiocode
# Edit at https://www.gitignore.io/?templates=linux,python,pycharm,visualstudiocode
###testfile
test.py
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/*
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/**/sonarlint/
# SonarQube Plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator/
### Python ###
# 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
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
### VisualStudioCode ###
.vscode/*
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### venv
venv/
# End of https://www.gitignore.io/api/linux,python,pycharm,visualstudiocode
devolo_home_control_api-0.19.0/.pre-commit-config.yaml 0000664 0000000 0000000 00000000550 15003713056 0022753 0 ustar 00root root 0000000 0000000 repos:
- repo: https://github.com/psf/black
rev: '23.1.0'
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: '5.12.0'
hooks:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 'v4.4.0'
hooks:
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
devolo_home_control_api-0.19.0/LICENSE 0000664 0000000 0000000 00000104515 15003713056 0017505 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
devolo_home_control_api-0.19.0/README.md 0000664 0000000 0000000 00000011012 15003713056 0017744 0 ustar 00root root 0000000 0000000 # devolo Home Control API
[](https://github.com/2Fake/devolo_home_control_api/actions?query=workflow%3A%22Python+package%22)
[](https://pypi.org/project/devolo-home-control-api/)
[](https://codeclimate.com/github/2Fake/devolo_home_control_api)
[](https://coveralls.io/github/2Fake/devolo_home_control_api?branch=main)
This project implements parts of the devolo Home Control API in Python. It is based on reverse engineering and therefore may fail with any new devolo update. If you discover a breakage, please feel free to [report an issue](https://github.com/2Fake/devolo_home_control_api/issues).
## System requirements
Defining the system requirements with exact versions typically is difficult. But there is a tested environment:
* Linux
* Python 3.7.12
* pip 22.0.1
* python-dateutil 2.8.2
* requests 2.27.1
* websocket_client 1.3.1
* zeroconf 0.38.4
Other versions and even other operating systems might work. Feel free to tell us about your experience. If you want to run our unit tests, you also need:
* pytest 7.2.2
* pytest-cov 4.0.0
* pytest-freezer 0.4.6
* requests-mock 3.10.0
* syrupy 4.0.0
## Versioning
In our versioning we follow [Semantic Versioning](https://semver.org/).
## Installing for usage
The Python Package Index takes care for you. Just use pip.
```bash
pip install devolo-home-control-api
```
## Installing for development
First, you need to get the sources.
```bash
git clone git@github.com:2Fake/devolo_home_control_api.git
```
Then you need to take care of the requirements.
```bash
cd devolo_home_control_api
python -m pip install .
```
If you want to run out tests, install the extra requirements and start pytest.
```bash
python -m pip install -e .[test]
pytest
```
## Quick start
To see that basic functionality, please look at our [small example](https://github.com/2Fake/devolo_home_control_api/blob/master/example.py). For this example, a working Home Control Central Unit must be attached to your my devolo account. After entering your my devolo username and password, simply run it:
```bash
python3 example.py
```
You will see changes to any BinarySwitch device (e.g. state or consumption) reported to your console. If the executing device is in the same LAN as the Home Control Central Unit, it will be discovered via Zeroconf. In this case, data will be collected from the Central Unit directly; otherwise data will be collected via cloud from my devolo.
### Connecting to my devolo
If you do not know your gateway ID, you can ask my devolo. For now, no other functionality is implemented, that you would need to access directly. If you discover other use cases, feel free to file a feature request.
```python
mydevolo = Mydevolo.get_instance()
mydevolo.user = "username"
mydevolo.password = "password"
for gateway_id in mydevolo.get_gateway_ids():
print(gateway_id)
```
### Collecting Home Control data
There are two ways of getting data:
1. Let the websocket push data into your object, but still poll the object
1. Subscribe to the publisher and let it push (preferred)
#### Using websockets
When using websocket events, messages will keep the object up to date. Nevertheless, no further action is triggered. So you have to ask yourself. The following example will list the current state of all binary switches. If the state changes, you will not notice unless you ask again.
```python
homecontrol = HomeControl(gateway_id=gateway_id, mydevolo_instance=mydevolo)
for binary_switch in mprm.binary_switch_devices:
for state in binary_switch.binary_switch_property:
print (f"State of {binary_switch.name} ({binary_switch.binary_switch_property[state].element_uid}): {binary_switch.binary_switch_property[state].state}")
```
To execute this example, you again need a configured instance of Mydevolo.
#### Using subscriber
This preferred usage is shown in our [small example](https://github.com/2Fake/devolo_home_control_api/blob/master/example.py). On every websocket event, ```update()``` will be called. That way you can react to changes right away.
## Further usage
You will find snippets discribing other use cases in our [wiki](https://github.com/2Fake/devolo_home_control_api/wiki).
devolo_home_control_api-0.19.0/devolo_home_control_api/ 0000775 0000000 0000000 00000000000 15003713056 0023363 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/__init__.py 0000664 0000000 0000000 00000000417 15003713056 0025476 0 ustar 00root root 0000000 0000000 """the devolo Home Control API."""
from importlib.metadata import PackageNotFoundError, version
try:
__version__ = version("devolo_home_control_api")
except PackageNotFoundError:
# package is not installed - e.g. pulled and run locally
__version__ = "0.0.0"
devolo_home_control_api-0.19.0/devolo_home_control_api/backend/ 0000775 0000000 0000000 00000000000 15003713056 0024752 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/backend/__init__.py 0000664 0000000 0000000 00000003013 15003713056 0027060 0 ustar 00root root 0000000 0000000 """Backends to communicate with."""
from .mprm import Mprm
MESSAGE_TYPES = {
"devolo.BinarySensor": "_binary_sensor",
"devolo.BinarySwitch": "_binary_switch",
"devolo.Blinds": "_multi_level_switch",
"devolo.DevicesPage": "_inspect_devices",
"devolo.DewpointSensor": "_multi_level_sensor",
"devolo.Dimmer": "_multi_level_switch",
"devolo.Grouping": "_grouping",
"devolo.HumidityBarValue": "_humidity_bar",
"devolo.HumidityBarZone": "_humidity_bar",
"devolo.mprm.gw.GatewayAccessibilityFI": "_gateway_accessible",
"devolo.Meter": "_meter",
"devolo.MildewSensor": "_binary_sensor",
"devolo.MultiLevelSensor": "_multi_level_sensor",
"devolo.MultiLevelSwitch": "_multi_level_switch",
"devolo.RemoteControl": "_remote_control",
"devolo.SirenMultiLevelSwitch": "_multi_level_switch",
"devolo.ShutterMovementFI": "_binary_sensor",
"devolo.ValveTemperatureSensor": "_multi_level_sensor",
"devolo.VoltageMultiLevelSensor": "_multi_level_sensor",
"devolo.WarningBinaryFI": "_binary_sensor",
"hdm": "_device_state",
"acs.hdm": "_automatic_calibration",
"bas.hdm": "_binary_async",
"bss.hdm": "_binary_sync",
"cps.hdm": "_parameter",
"gds.hdm": "_general_device",
"lis.hdm": "_led",
"mas.hdm": "_multilevel_async",
"mss.hdm": "_multilevel_sync",
"ps.hdm": "_protection",
"stmss.hdm": "_multilevel_sync",
"sts.hdm": "_switch_type",
"trs.hdm": "_temperature_report",
"vfs.hdm": "_led",
}
__all__ = ["MESSAGE_TYPES", "Mprm"]
devolo_home_control_api-0.19.0/devolo_home_control_api/backend/mprm.py 0000664 0000000 0000000 00000014034 15003713056 0026301 0 ustar 00root root 0000000 0000000 """mPRM communication."""
from __future__ import annotations
import contextlib
import socket
import sys
import time
from abc import ABC
from http import HTTPStatus
from json import JSONDecodeError
from threading import Thread
from urllib.parse import urlsplit
import requests
from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf
from devolo_home_control_api.exceptions import GatewayOfflineError
from .mprm_websocket import MprmWebsocket
class Mprm(MprmWebsocket, ABC):
"""
The abstract Mprm object handles the connection to the devolo Cloud (remote) or the gateway in your LAN (local). Either
way is chosen, depending on detecting the gateway via mDNS.
"""
def __init__(self) -> None:
"""Initialize communication."""
self._zeroconf: Zeroconf | None
super().__init__()
self.detect_gateway_in_lan()
self.create_connection()
def create_connection(self) -> None:
"""
Create session, either locally or remotely via cloud. The remote case has two conditions, that both need to be
fulfilled: Remote access must be allowed and my devolo must not be in maintenance mode.
"""
if self._local_ip:
self.gateway.local_connection = True
self.get_local_session()
elif self.gateway.external_access and not self._mydevolo.maintenance():
self.get_remote_session()
else:
self._logger.error("Cannot connect to gateway. No gateway found in LAN and external access is not possible.")
raise ConnectionError("Cannot connect to gateway.") # noqa: TRY003
def detect_gateway_in_lan(self) -> str:
"""
Detect a gateway in local network via mDNS and check if it is the desired one. Unfortunately, the only way to tell is
to try a connection with the known credentials. If the gateway is not found within 3 seconds, it is assumed that a
remote connection is needed.
:return: Local IP of the gateway, if found
"""
zeroconf = self._zeroconf or Zeroconf()
browser = ServiceBrowser(zeroconf, "_dvl-deviceapi._tcp.local.", handlers=[self._on_service_state_change])
self._logger.info("Searching for gateway in LAN.")
start_time = time.time()
while not time.time() > start_time + 3 and not self._local_ip:
time.sleep(0.05)
Thread(target=browser.cancel, name=f"{self.__class__.__name__}.browser_cancel").start()
if not self._zeroconf:
Thread(target=zeroconf.close, name=f"{self.__class__.__name__}.zeroconf_close").start()
return self._local_ip
def get_local_session(self) -> bool:
"""
Connect to the gateway locally. Calling a special portal URL on the gateway returns a second URL with a token. Calling
that URL establishes the connection.
"""
self._logger.info("Connecting to gateway locally.")
self._url = f"http://{self._local_ip}"
self._logger.debug("Session URL set to '%s'", self._url)
try:
connection = self._session.get(
f"{self._url}/dhlp/portal/full", auth=(self.gateway.local_user, self.gateway.local_passkey), timeout=5
)
except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
self._logger.error("Could not connect to the gateway locally.")
self._logger.debug(sys.exc_info())
raise GatewayOfflineError from None
# After a reboot we can connect to the gateway but it answers with a 503 if not fully started.
if not connection.ok:
self._logger.error("Could not connect to the gateway locally.")
self._logger.debug("Gateway start-up is not finished, yet.")
raise GatewayOfflineError from None
token_url = connection.json()["link"]
self._logger.debug("Got a token URL: %s", token_url)
self._session.get(token_url)
return True
def get_remote_session(self) -> bool:
"""Connect to the gateway remotely. Calling the known portal URL is enough in this case."""
self._logger.info("Connecting to gateway via cloud.")
if not self.gateway.full_url:
self._logger.error("Could not connect to the gateway remotely.")
raise GatewayOfflineError
try:
url = urlsplit(self._session.get(self.gateway.full_url, timeout=15).url)
self._url = f"{url.scheme}://{url.netloc}"
self._logger.debug("Session URL set to '%s'", self._url)
except JSONDecodeError:
self._logger.error("Could not connect to the gateway remotely.")
self._logger.debug(sys.exc_info())
raise GatewayOfflineError from None
return True
def _on_service_state_change(
self, zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange
) -> None:
"""Service handler for Zeroconf state changes."""
if state_change is ServiceStateChange.Added:
service_info = zeroconf.get_service_info(service_type, name)
if service_info and service_info.server and service_info.server.startswith("devolo-homecontrol"):
with contextlib.suppress(requests.exceptions.ReadTimeout), contextlib.suppress(
requests.exceptions.ConnectTimeout
):
self._try_local_connection(service_info.addresses)
def _try_local_connection(self, addresses: list[bytes]) -> None:
"""Try to connect to an mDNS hostname. If connection was successful, save local IP address."""
for address in addresses:
ip = socket.inet_ntoa(address)
if (
requests.get(
f"http://{ip}/dhlp/port/full", auth=(self.gateway.local_user, self.gateway.local_passkey), timeout=0.5
).status_code
== HTTPStatus.OK
):
self._logger.debug("Got successful answer from ip %s. Setting this as local gateway", ip)
self._local_ip = ip
devolo_home_control_api-0.19.0/devolo_home_control_api/backend/mprm_rest.py 0000664 0000000 0000000 00000016246 15003713056 0027345 0 ustar 00root root 0000000 0000000 """mPRM communication via REST."""
from __future__ import annotations
import json
import logging
import sys
from abc import ABC
from enum import IntEnum
from typing import Any
from requests import Session
from requests.exceptions import ConnectionError, ReadTimeout # noqa: A004
from devolo_home_control_api.devices import Gateway
from devolo_home_control_api.exceptions import GatewayOfflineError
from devolo_home_control_api.mydevolo import Mydevolo
class MprmRest(ABC):
"""
The abstract MprmRest object handles calls to the so called mPRM. It does not cover all API calls, just those requested
up to now. All calls are done in a gateway context, so you have to create a derived class, that provides a Gateway object
and a Session object.
"""
def __init__(self) -> None:
"""Initialize REST communication."""
logging.captureWarnings(capture=True)
self._logger = logging.getLogger(self.__class__.__name__)
self._data_id = 0
self._local_ip = ""
self._url = ""
self._mydevolo: Mydevolo
self._session: Session
self.gateway: Gateway
def get_all_devices(self) -> list[str]:
"""
Get all devices.
:return: All devices and their properties.
"""
self._logger.info("Inspecting devices")
data = {"method": "FIM/getFunctionalItems", "params": [["devolo.DevicesPage"], 0]}
response = self._post(data)
self._logger.debug("Response of 'get_all_devices':\n%s", response)
return response["result"]["items"][0]["properties"]["deviceUIDs"]
def get_all_zones(self) -> dict[str, str]:
"""
Get all zones, also called rooms.
:return: All zone IDs and their name.
"""
self._logger.debug("Inspecting zones")
data = {"method": "FIM/getFunctionalItems", "params": [["devolo.Grouping"], 0]}
response = self._post(data)["result"]["items"][0]["properties"]["zones"]
self._logger.debug("Response of 'get_all_zones':\n%s", response)
return {key["id"]: key["name"] for key in response}
def get_data_from_uid_list(self, uids: list[str]) -> list[dict[str, Any]]:
"""
Return data from an element UID list using an RPC call.
:param uids: Element UIDs, something like [devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#2,
devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#1]
:return: Data connected to the element UIDs, payload so to say
"""
data = {"method": "FIM/getFunctionalItems", "params": [uids, 0]}
response = self._post(data)
self._logger.debug("Response of 'get_data_from_uid_list':\n%s", response)
return response["result"]["items"]
def refresh_session(self) -> None:
"""Refresh currently running session. Without this call from time to time especially websockets will terminate."""
self._logger.debug("Refreshing session.")
data = {
"method": "FIM/invokeOperation",
"params": [f"devolo.UserPrefs.{self._mydevolo.uuid()}", "resetSessionTimeout", []],
}
self._post(data)
def set_binary_switch(self, uid: str, state: bool) -> bool:
"""
Set a binary switch state of a device.
:param uid: Element UID, something like devolo.BinarySwitch:hdm:ZWave:CBC56091/24
:param state: True if switching on, False if switching off
:return: True if successfully switched, false otherwise
"""
data: dict[str, str | list] = {"method": "FIM/invokeOperation", "params": [uid, "turnOn" if state else "turnOff", []]}
response = self._post(data)
return self._evaluate_response(uid=uid, value=state, response=response)
def set_multi_level_switch(self, uid: str, value: float) -> bool:
"""
Set a multi level switch value of a device.
:param uid: Element UID, something like devolo.Dimmer:hdm:ZWave:CBC56091/24
:param value: Value the multi level switch shall have
:return: True if successfully switched, false otherwise
"""
data = {"method": "FIM/invokeOperation", "params": [uid, "sendValue", [value]]}
response = self._post(data)
return self._evaluate_response(uid=uid, value=value, response=response)
def set_remote_control(self, uid: str, key_pressed: int) -> bool:
"""
Press the button of a remote control virtually.
:param uid: Element UID, something like devolo.RemoteControl:hdm:ZWave:CBC56091/24
:param key_pressed: Number of the button pressed
:return: True if successfully switched, false otherwise
"""
data = {"method": "FIM/invokeOperation", "params": [uid, "pressKey", [key_pressed]]}
response = self._post(data)
return self._evaluate_response(uid=uid, value=key_pressed, response=response)
def set_setting(self, uid: str, setting: list[str]) -> bool:
"""
Set a setting of a device.
:param uid: Element UID, something like acs.hdm:ZWave:CBC56091/24
:param setting: Settings to set
:return: True if successfully switched, false otherwise
"""
data = {"method": "FIM/invokeOperation", "params": [uid, "save", setting]}
response = self._post(data)
return self._evaluate_response(uid=uid, value=setting, response=response)
def _evaluate_response(self, uid: str, value: bool | float | list[str], response: dict[str, Any]) -> bool:
"""Evaluate the response of setting a device to a value."""
if response["result"].get("status") == RestResponseStatus.VALID:
return True
if response["result"].get("status") == RestResponseStatus.INVALID:
self._logger.debug("Value of %s is already %s.", uid, value)
else:
self._logger.error("Something went wrong setting %s.", uid)
self._logger.debug("Response to set command:\n%s", response)
return False
def _post(self, data: dict[str, Any]) -> dict[str, Any]:
"""
Communicate with the RPC interface. If the call times out, it is assumed that the gateway is offline and the state is
changed accordingly.
:param data: Data to be send
:return: Response to the data
"""
self._data_id += 1
data["jsonrpc"] = "2.0"
data["id"] = self._data_id
try:
response = self._session.post(
f"{self._url}/remote/json-rpc", data=json.dumps(data), headers={"content-type": "application/json"}, timeout=30
).json()
except (ConnectionError, ReadTimeout):
self._logger.error("Gateway is offline.")
self._logger.debug(sys.exc_info())
self.gateway.update_state(online=False)
raise GatewayOfflineError from None
if response["id"] != data["id"]:
self._logger.error("Got an unexpected response after posting data.")
self._logger.debug("Message had ID %s, response had ID %s.", data["id"], response["id"])
raise ValueError("Got an unexpected response after posting data.") # noqa: TRY003
return response
class RestResponseStatus(IntEnum):
"""Status codes for mPRM responses."""
VALID = 1
INVALID = 2
devolo_home_control_api-0.19.0/devolo_home_control_api/backend/mprm_websocket.py 0000664 0000000 0000000 00000016377 15003713056 0030363 0 ustar 00root root 0000000 0000000 """mPRM communication via websocket."""
from __future__ import annotations
import json
import threading
from abc import ABC, abstractmethod
from time import sleep, time
from types import TracebackType
from typing import Any
import requests
import websocket
from urllib3.connection import ConnectTimeoutError
from devolo_home_control_api.exceptions import GatewayOfflineError
from .mprm_rest import MprmRest
try:
from typing import Self # type: ignore[attr-defined,misc]
except ImportError:
from typing_extensions import Self
class MprmWebsocket(MprmRest, ABC):
"""
The abstract MprmWebsocket object handles calls to the mPRM via websockets. It does not cover all API calls, just those
requested up to now. All calls are done in a gateway context, so you have to create a derived class, that provides a
Gateway object and a Session object. Further, the derived class needs to implement methods to connect to the websocket,
either local or remote. Last but not least, the derived class needs to implement a method that is called on new messages.
The websocket connection itself runs in a thread, that might not terminate as expected. Using a with-statement is
recommended.
"""
def __init__(self) -> None:
"""Initialize websocket communication."""
super().__init__()
self._ws: websocket.WebSocketApp | None = None
self._connected = False # This attribute saves, if the websocket is fully established
self._reachable = True # This attribute saves, if the a new session can be established
self._event_sequence = 0
def __enter__(self) -> Self:
"""Connect to the websocket."""
return self
def __exit__(
self,
exception_type: type[BaseException] | None,
exception_value: BaseException | None,
traceback: TracebackType | None,
) -> None:
"""Disconnect from the websocket."""
self.websocket_disconnect()
@abstractmethod
def detect_gateway_in_lan(self) -> str:
"""Detect a gateway in the local network."""
@abstractmethod
def get_local_session(self) -> bool:
"""Connect to the gateway locally."""
@abstractmethod
def get_remote_session(self) -> bool:
"""Connect to the gateway remotely."""
@abstractmethod
def on_update(self, message: dict[str, Any]) -> None:
"""Initialize steps needed to update properties on a new message."""
def wait_for_websocket_establishment(self) -> None:
"""
In some cases it is needed to wait for the websocket to be fully established. This method can be used to block your
current thread for up to one minute.
"""
start_time = time()
while not self._connected and time() < start_time + 600:
sleep(0.1)
if not self._connected:
self._logger.debug("Websocket could not be established")
raise GatewayOfflineError
def websocket_connect(self) -> None:
"""
Set up the websocket connection. The protocol type of the known session URL is exchanged depending on whether TLS is
used or not. After establishing the websocket, a ping is sent every 30 seconds to keep the connection alive. If there
is no response within 5 seconds, the connection is terminated with error state.
"""
ws_url = self._url.replace("https://", "wss://").replace("http://", "ws://")
cookie = "; ".join(f"{name}={value}" for name, value in self._session.cookies.items())
ws_url = (
f"{ws_url}/remote/events/?topics=com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED,"
f"com/prosyst/mbs/services/fim/FunctionalItemEvent/UNREGISTERED"
f"&filter=(|(GW_ID={self.gateway.id})(!(GW_ID=*)))"
)
self._logger.debug("Connecting to %s", ws_url)
self._ws = websocket.WebSocketApp(
ws_url,
cookie=cookie,
on_open=self._on_open,
on_message=self._on_message,
on_error=self._on_error,
on_close=self._on_close,
on_pong=self._on_pong,
)
self._ws.run_forever(ping_interval=30, ping_timeout=5)
def websocket_disconnect(self, event: str = "") -> None:
"""Close the websocket connection."""
if not self._ws:
self._logger.info("Not connected to the web socket.")
return
self._logger.info("Closing web socket connection.")
if event:
self._logger.info("Reason: %s", event)
self._ws.close()
def _on_close(self, *_: Any) -> None:
"""React on closing the websocket."""
self._logger.info("Closed websocket connection.")
def _on_error(self, ws: websocket.WebSocketApp, error: Exception) -> None:
"""React on errors. We will try reconnecting with prolonging intervals."""
self._logger.exception(error)
self._connected = False
self._reachable = False
ws.close()
self._event_sequence = 0
sleep_interval = 16
while not self._reachable:
self._try_reconnect(sleep_interval)
sleep_interval = min(sleep_interval * 2, 3600)
self.websocket_connect()
def _on_message(self, _: websocket.WebSocketApp, message: str) -> None:
"""React on a message."""
msg = json.loads(message)
self._logger.debug("Got message from websocket:\n%s", msg)
event_sequence = msg["properties"]["com.prosyst.mbs.services.remote.event.sequence.number"]
if event_sequence == self._event_sequence:
self._event_sequence += 1
else:
self._logger.warning(
"We missed a websocket message. Internal event_sequence is at %s. Event sequence by websocket is at %s",
self._event_sequence,
event_sequence,
)
self._event_sequence = event_sequence + 1
self._logger.debug("self._event_sequence is set to %s", self._event_sequence)
self.on_update(msg)
def _on_open(self, ws: websocket.WebSocketApp) -> None:
"""Keep the websocket open."""
def run() -> None:
self._logger.info("Starting web socket connection.")
while ws.sock is not None and ws.sock.connected:
sleep(1)
threading.Thread(target=run, name=f"{self.__class__.__name__}.websocket_run").start()
self._connected = True
def _on_pong(self, *_: Any) -> None:
"""Keep the session valid."""
self.refresh_session()
def _try_reconnect(self, sleep_interval: int) -> None:
"""Try to reconnect to the websocket."""
try:
self._logger.info("Trying to reconnect to the websocket.")
self._reachable = self.get_local_session() if self._local_ip else self.get_remote_session()
except (ConnectTimeoutError, GatewayOfflineError):
self._logger.info("Sleeping for %s seconds.", sleep_interval)
sleep(sleep_interval)
except (requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
self._logger.info("Sleeping for %s seconds.", sleep_interval)
sleep(sleep_interval - 3) # mDNS browsing will take up tp 3 seconds by itself
self.detect_gateway_in_lan()
devolo_home_control_api-0.19.0/devolo_home_control_api/devices/ 0000775 0000000 0000000 00000000000 15003713056 0025005 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/devices/__init__.py 0000664 0000000 0000000 00000000216 15003713056 0027115 0 ustar 00root root 0000000 0000000 """Devices appearing in a devolo Home Control setup."""
from .gateway import Gateway
from .zwave import Zwave
__all__ = ["Gateway", "Zwave"]
devolo_home_control_api-0.19.0/devolo_home_control_api/devices/gateway.py 0000664 0000000 0000000 00000005222 15003713056 0027021 0 ustar 00root root 0000000 0000000 """The devolo Home Control Central Unit."""
from __future__ import annotations
from datetime import timezone
from dateutil import tz
from devolo_home_control_api.exceptions import GatewayOfflineError
from devolo_home_control_api.mydevolo import Mydevolo
class Gateway:
"""
Representing object for devolo Home Control Central Units. As it is a gateway from the IP world to the Z-Wave
world, we call it that way. Nearly all attributes are delivered by my devolo.
:param gateway_id: Gateway ID (aka serial number), typically found on the label of the device
:param mydevolo_instance: Mydevolo instance for talking to the devolo Cloud
"""
def __init__(self, gateway_id: str, mydevolo_instance: Mydevolo) -> None:
"""Initialize the central unit."""
self._mydevolo = mydevolo_instance
details = self._mydevolo.get_gateway(gateway_id)
self.id = details["gatewayId"]
self.name = details.get("name")
self.role = details.get("role")
self.local_user = self._mydevolo.uuid()
self.local_passkey = details["localPasskey"]
self.external_access = details.get("externalAccess")
self.timezone = tz.gettz(details["location"].get("timezone") or self._mydevolo.get_timezone()) or timezone.utc
self.firmware_version = details.get("firmwareVersion")
try:
self.full_url: str | None = self._mydevolo.get_full_url(self.id)
except GatewayOfflineError:
self.full_url = None
# Let's assume the gateway is connected remotely. Will be corrected as soon as a real connection is established.
self.local_connection = False
# Let's assume the gateway is offline. Will be corrected the twinkling of an eye.
self.online = False
self.sync = False
self.zones: dict[str, str] = {}
self.home_id = ""
self._update_state(status=details.get("status", ""), state=details.get("state", ""))
def update_state(self, online: bool | None = None) -> None:
"""
Update the state of the gateway. If called without parameter, we will check my devolo.
:param online: Detected state of the gateway
"""
if online is None:
details = self._mydevolo.get_gateway(self.id)
self._update_state(status=details.get("status", ""), state=details.get("state", ""))
else:
self.online = online
self.sync = online
def _update_state(self, status: str, state: str) -> None:
"""Update the internal state."""
self.online = status == "devolo.hc_gateway.status.online"
self.sync = state == "devolo.hc_gateway.state.idle"
devolo_home_control_api-0.19.0/devolo_home_control_api/devices/zwave.py 0000664 0000000 0000000 00000013056 15003713056 0026520 0 ustar 00root root 0000000 0000000 """A Z-Wave device."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING, Any
from devolo_home_control_api.helper import camel_case_to_snake_case, get_device_uid_from_element_uid
from devolo_home_control_api.mydevolo import Mydevolo
if TYPE_CHECKING:
from devolo_home_control_api.properties import (
BinarySensorProperty,
BinarySwitchProperty,
ConsumptionProperty,
HumidityBarProperty,
MultiLevelSensorProperty,
MultiLevelSwitchProperty,
RemoteControlProperty,
SettingsProperty,
)
from devolo_home_control_api.properties.property import Property
class Zwave:
"""
Representing object for Z-Wave devices. Basically, everything can be stored in here. This is to be as flexible to gateway
firmware changes as possible. So if new attributes appear or old ones are removed, they should be handled at least in
reading them. Nevertheless, a few unwanted attributes are filtered.
:param mydevolo_instance: Mydevolo instance for talking to the devolo Cloud
:key batteryLevel: Battery Level of the device in percent, -1 if mains powered
:type batteryLevel: int
:key elementUIDs: All element UIDs the device has
:type elementUIDs: List[str]
:key manID: Manufacturer ID as registered at the Z-Wave alliance
:type manID: str
:key prodID: Product ID as registered at the Z-Wave alliance
:type prodID: str
:key prodTypeID: Product type ID as registered at the Z-Wave alliance
:type prodTypeID: str
:key status: Online status of the device
:type status: int
"""
binary_sensor_property: dict[str, BinarySensorProperty]
binary_switch_property: dict[str, BinarySwitchProperty]
consumption_property: dict[str, ConsumptionProperty]
humidity_bar_property: dict[str, HumidityBarProperty]
settings_property: dict[str, SettingsProperty]
multi_level_sensor_property: dict[str, MultiLevelSensorProperty]
multi_level_switch_property: dict[str, MultiLevelSwitchProperty]
remote_control_property: dict[str, RemoteControlProperty]
brand: str
device_model_uid: str
device_type: str
href: str
identifier: str
is_zwave_plus: bool
name: str
manufacturer_id: str
pending_operations: bool
product_id: str
product_type_id: str
zwave_version: str
def __init__(self, mydevolo_instance: Mydevolo, **kwargs: Any) -> None:
"""Initialize a Z-Wave device."""
self._logger = logging.getLogger(self.__class__.__name__)
self._mydevolo = mydevolo_instance
# Get important values
self.battery_level = kwargs.pop("batteryLevel", -1)
self.battery_low = kwargs.pop("batteryLow", False)
self.element_uids = kwargs.pop("elementUIDs")
self.man_id = kwargs.pop("manID")
self.prod_id = kwargs.pop("prodID")
self.prod_type_id = kwargs.pop("prodTypeID")
self.status = kwargs.pop("status", 1)
# Remove unwanted values
unwanted_value = [
"icon",
"itemName",
"operationStatus",
"zone",
"zoneId",
]
for value in unwanted_value:
del kwargs[value]
# Add all other values
for key, value in kwargs.items():
setattr(self, camel_case_to_snake_case(key), value)
self.uid = get_device_uid_from_element_uid(self.element_uids[0])
# Initialize additional Z-Wave information. Will be filled by Zwave.get_zwave_info, if available.
z_wave_info_list = [
"href",
"manufacturer_id",
"product_type_id",
"product_id",
"name",
"brand",
"identifier",
"is_zwave_plus",
"device_type",
"zwave_version",
"specific_device_class",
"generic_device_class",
]
for key in z_wave_info_list:
setattr(self, key, None)
# Remove battery properties, if device is mains powered.
if self.battery_level == -1:
delattr(self, "battery_level")
delattr(self, "battery_low")
def get_property(self, name: str) -> list[Property]:
"""
Get element UIDs to a specified property.
:param name: Name of the property we want to access
:return: List of UIDs in this property
:raises: AttributeError: The property does not exist in this device type
"""
return [*getattr(self, f"{name}_property").values()]
def get_zwave_info(self) -> None:
"""
Get publicly available information like manufacturer or model from my devolo. For a complete list, please look at
Zwave.__init__.
"""
self._logger.debug("Getting Z-Wave information for %s", self.uid)
zwave_product = self._mydevolo.get_zwave_products(
manufacturer=self.man_id, product_type=self.prod_type_id, product=self.prod_id
)
for key, value in zwave_product.items():
setattr(self, camel_case_to_snake_case(key), value)
# Clean up attributes which are now unwanted.
clean_up_list = [
"man_id",
"prod_id",
"prod_type_id",
"statistics_uid",
"wrong_device_paired",
]
for attribute in clean_up_list:
if hasattr(self, attribute):
delattr(self, attribute)
def is_online(self) -> bool:
"""
Get the online state of a device.
:return: False, if device is offline, else True
"""
return self.status != 1
devolo_home_control_api-0.19.0/devolo_home_control_api/exceptions/ 0000775 0000000 0000000 00000000000 15003713056 0025544 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/exceptions/__init__.py 0000664 0000000 0000000 00000000474 15003713056 0027662 0 ustar 00root root 0000000 0000000 """Expections used in this package."""
from .device import SwitchingProtected, WrongElementError
from .gateway import GatewayOfflineError
from .general import WrongCredentialsError, WrongUrlError
__all__ = ["GatewayOfflineError", "SwitchingProtected", "WrongCredentialsError", "WrongElementError", "WrongUrlError"]
devolo_home_control_api-0.19.0/devolo_home_control_api/exceptions/device.py 0000664 0000000 0000000 00000001227 15003713056 0027357 0 ustar 00root root 0000000 0000000 """Exceptions occuring when talking to a Z-Wave device."""
import re
class WrongElementError(Exception):
"""The element was not meant for this property."""
def __init__(self, element_uid: str, property_class: str) -> None:
"""Initialize error."""
property_name = " ".join(re.findall("[A-Z][^A-Z]*", property_class)).lower()
super().__init__(f"{element_uid} is not a {property_name}.")
class SwitchingProtected(Exception):
"""The device is protected against remote switching."""
def __init__(self) -> None:
"""Initialize error."""
super().__init__("This device is protected against remote switching.")
devolo_home_control_api-0.19.0/devolo_home_control_api/exceptions/gateway.py 0000664 0000000 0000000 00000000451 15003713056 0027557 0 ustar 00root root 0000000 0000000 """Exceptions occurring when talking to the devolo Home Control central unit."""
class GatewayOfflineError(Exception):
"""The requested gateway is offline and cannot be used."""
def __init__(self) -> None:
"""Initialize error."""
super().__init__("Gateway is offline.")
devolo_home_control_api-0.19.0/devolo_home_control_api/exceptions/general.py 0000664 0000000 0000000 00000000635 15003713056 0027537 0 ustar 00root root 0000000 0000000 """General exceptions."""
class WrongCredentialsError(Exception):
"""Wrong credentials were used."""
def __init__(self) -> None:
"""Initialize error."""
super().__init__("Wrong username or password.")
class WrongUrlError(Exception):
"""Wrong URL was used."""
def __init__(self, url: str) -> None:
"""Initialize error."""
super().__init__(f"Wrong URL: {url}")
devolo_home_control_api-0.19.0/devolo_home_control_api/helper/ 0000775 0000000 0000000 00000000000 15003713056 0024642 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/helper/__init__.py 0000664 0000000 0000000 00000001032 15003713056 0026747 0 ustar 00root root 0000000 0000000 """Helper functions used in the package."""
from .names import camel_case_to_snake_case
from .uid import (
get_device_type_from_element_uid,
get_device_uid_from_element_uid,
get_device_uid_from_setting_uid,
get_home_id_from_device_uid,
get_sub_device_uid_from_element_uid,
)
__all__ = [
"camel_case_to_snake_case",
"get_device_type_from_element_uid",
"get_device_uid_from_element_uid",
"get_device_uid_from_setting_uid",
"get_home_id_from_device_uid",
"get_sub_device_uid_from_element_uid",
]
devolo_home_control_api-0.19.0/devolo_home_control_api/helper/names.py 0000664 0000000 0000000 00000001113 15003713056 0026313 0 ustar 00root root 0000000 0000000 """
Helper functions for converting names.
The information we get from the gateway is often returned in a way, that does not fit to our naming convention. This helper
function make names fit in nicely.
"""
import re
def camel_case_to_snake_case(expression: str) -> str:
"""
Turn CamelCaseStrings to snake_case_strings. This is used where the original Java names should by more pythonic.
:param: expression: Expression, that should be converted to snake case
:return: Expression in snake case
"""
return re.sub("([a-z])([A-Z]+)", r"\1_\2", expression).lower()
devolo_home_control_api-0.19.0/devolo_home_control_api/helper/uid.py 0000664 0000000 0000000 00000004415 15003713056 0026001 0 ustar 00root root 0000000 0000000 """
Helper functions for splitting UID.
Element UIDs and setting UIDs are the central identifies in devolo Home Control. However, for grouping information together
you often need to split them. This helper functions will do that for you.
"""
from __future__ import annotations
import re
def get_device_uid_from_element_uid(element_uid: str) -> str:
"""
Return device UID from the given element UID.
:param element_uid: Element UID, something like devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#2
:return: Device UID, something like hdm:ZWave:CBC56091/24
"""
parts = re.search(r".*?:(.*/\d{1,3})", element_uid)
if parts:
return parts.group(1)
raise ValueError("Element UID has a wrong format.") # noqa: TRY003
def get_device_uid_from_setting_uid(setting_uid: str) -> str:
"""
Return the device uid of the given setting UID.
:param setting_uid: Setting UID, something like lis.hdm:ZWave:EB5A9F6C/2
:return: Device UID, something like hdm:ZWave:EB5A9F6C/2
"""
parts = re.search(r".*\.(.*/\d{1,3})", setting_uid)
if parts:
return parts.group(1)
raise ValueError("Settings UID has a wrong format.") # noqa: TRY003
def get_sub_device_uid_from_element_uid(element_uid: str) -> int | None:
"""
Return the sub device uid of the given element UID.
:param element_uid: Element UID, something like devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#2
:return: Sub device UID, something like 2
"""
return None if "#" not in element_uid else int(element_uid.split("#")[-1])
def get_device_type_from_element_uid(element_uid: str) -> str:
"""
Return the device type of the given element UID.
:param element_uid: Element UID, something like devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#2
:return: Device type, something like devolo.MultiLevelSensor
"""
return element_uid.split(":")[0]
def get_home_id_from_device_uid(device_uid: str) -> str:
"""
Return the home id of the given device UID.
:param device_uid: Device UID, something like hdm:ZWave:EB5A9F6C/4
:return: Home ID, something like EB5A9F6C
"""
parts = re.search(r":.*?(:)(.*)(/)", device_uid)
if parts:
return parts.group(2)
raise ValueError("Device UID has a wrong format.") # noqa: TRY003
devolo_home_control_api-0.19.0/devolo_home_control_api/homecontrol.py 0000664 0000000 0000000 00000060137 15003713056 0026275 0 ustar 00root root 0000000 0000000 """devolo Home Control."""
from __future__ import annotations
import threading
from typing import Any
import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry
from zeroconf import Zeroconf
from . import __version__
from .backend import MESSAGE_TYPES, Mprm
from .devices import Gateway, Zwave
from .helper import (
camel_case_to_snake_case,
get_device_type_from_element_uid,
get_device_uid_from_element_uid,
get_device_uid_from_setting_uid,
get_home_id_from_device_uid,
)
from .mydevolo import Mydevolo
from .properties import (
BinarySensorProperty,
BinarySwitchProperty,
ConsumptionProperty,
HumidityBarProperty,
MultiLevelSensorProperty,
MultiLevelSwitchProperty,
RemoteControlProperty,
SettingsProperty,
)
from .publisher import Publisher, Updater
class HomeControl(Mprm):
"""
Representing object for your Home Control setup. This is more or less the glue between your devolo Home Control Central
Unit, your devices and their properties.
:param gateway_id: Gateway ID (aka serial number), typically found on the label of the device
:param mydevolo_instance: Mydevolo instance for talking to the devolo Cloud
:param zeroconf_instance: Zeroconf instance to be potentially reused
"""
def __init__(self, gateway_id: str, mydevolo_instance: Mydevolo, zeroconf_instance: Zeroconf | None = None) -> None:
"""Initialize communication with your Home Control setup."""
retry = Retry(total=5, backoff_factor=0.1, allowed_methods=("GET", "POST"))
adapter = HTTPAdapter(max_retries=retry)
self._mydevolo = mydevolo_instance
self._session = requests.Session()
self._session.headers.update({"User-Agent": f"devolo_home_control_api/{__version__}"})
self._session.mount("http://", adapter)
self._zeroconf = zeroconf_instance
self.gateway = Gateway(gateway_id, mydevolo_instance)
super().__init__()
self._grouping()
# Create the initial device dict
self.devices: dict[str, Zwave] = {}
self._inspect_devices(self.get_all_devices())
self.device_names = {
f"{device.settings_property['general_device_settings'].name}\\"
f"{device.settings_property['general_device_settings'].zone}": device.uid
for device in self.devices.values()
}
self.gateway.home_id = get_home_id_from_device_uid(next(iter(self.device_names.values())))
self.publisher = Publisher(self.devices.keys())
self.updater = Updater(devices=self.devices, gateway=self.gateway, publisher=self.publisher)
self.updater.on_device_change = self.device_change
threading.Thread(target=self.websocket_connect, name=f"{self.__class__.__name__}.websocket_connect").start()
self.wait_for_websocket_establishment()
@property
def binary_sensor_devices(self) -> list[Zwave]:
"""Get all binary sensor devices."""
return [uid for uid in self.devices.values() if hasattr(uid, "binary_sensor_property")]
@property
def binary_switch_devices(self) -> list[Zwave]:
"""Get all binary switch devices."""
return [uid for uid in self.devices.values() if hasattr(uid, "binary_switch_property")]
@property
def blinds_devices(self) -> list[Zwave]:
"""Get all blinds devices."""
blinds_devices = []
for device in self.multi_level_switch_devices:
blinds_devices.extend(
[
self.devices[device.uid]
for multi_level_switch_property in device.multi_level_switch_property
if multi_level_switch_property.startswith("devolo.Blinds")
]
)
return blinds_devices
@property
def multi_level_sensor_devices(self) -> list[Zwave]:
"""Get all multi level sensor devices."""
return [uid for uid in self.devices.values() if hasattr(uid, "multi_level_sensor_property")]
@property
def multi_level_switch_devices(self) -> list[Zwave]:
"""Get all multi level switch devices. This also includes blinds devices."""
return [uid for uid in self.devices.values() if hasattr(uid, "multi_level_switch_property")]
@property
def remote_control_devices(self) -> list[Zwave]:
"""Get all remote control devices."""
return [uid for uid in self.devices.values() if hasattr(uid, "remote_control_property")]
def device_change(self, device_uids: list[str]) -> tuple[str, str]:
"""
React on new devices or removed devices. As the Z-Wave controller can only be in inclusion or exclusion mode, we
assume, that you cannot add and remove devices at the same time. So if the number of devices increases, there is
a new one and if the number decreases, a device was removed.
:param device_uids: List of UIDs known by the backend
"""
if len(device_uids) > len(self.devices):
devices = [device for device in device_uids if device not in self.devices]
mode = "add"
self._inspect_devices([devices[0]])
self._logger.debug("Device %s added.", devices[0])
else:
devices = [device for device in self.devices if device not in device_uids]
mode = "del"
self.devices.pop(devices[0])
self._logger.debug("Device %s removed.", devices[0])
self.updater.devices = self.devices
return (devices[0], mode)
def on_update(self, message: dict[str, Any]) -> None:
"""
Initialize steps needed to update properties on a new message.
:param message: Message because of which we need to update properties
"""
self.updater.update(message)
def _binary_sensor(self, uid_info: dict[str, Any]) -> None:
"""Process BinarySensor properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
if not hasattr(self.devices[device_uid], "binary_sensor_property"):
self.devices[device_uid].binary_sensor_property = {}
self._logger.debug("Adding binary sensor property to %s.", device_uid)
self.devices[device_uid].binary_sensor_property[uid_info["UID"]] = BinarySensorProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
state=bool(uid_info["properties"]["state"]),
sensor_type=uid_info["properties"]["sensorType"],
sub_type=uid_info["properties"]["subType"],
)
def _binary_switch(self, uid_info: dict[str, Any]) -> None:
"""Process BinarySwitch properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
if not hasattr(self.devices[device_uid], "binary_switch_property"):
self.devices[device_uid].binary_switch_property = {}
self._logger.debug("Adding binary switch property to %s.", device_uid)
self.devices[device_uid].binary_switch_property[uid_info["UID"]] = BinarySwitchProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_binary_switch,
state=bool(uid_info["properties"]["state"]),
enabled=uid_info["properties"]["guiEnabled"],
)
def _general_device(self, uid_info: dict[str, Any]) -> None:
"""Process general device setting (gds) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding general device settings to %s.", device_uid)
self.devices[device_uid].settings_property["general_device_settings"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
events_enabled=uid_info["properties"]["settings"]["eventsEnabled"],
name=uid_info["properties"]["settings"]["name"],
zone_id=uid_info["properties"]["settings"]["zoneID"],
icon=uid_info["properties"]["settings"]["icon"],
zones=self.gateway.zones,
)
def _grouping(self) -> None:
"""Get all zones (also called rooms)."""
self.gateway.zones = self.get_all_zones()
def _humidity_bar(self, uid_info: dict[str, Any]) -> None:
"""
Process HumidityBarZone and HumidityBarValue properties.
For whatever reason, the zone and the position within that zone are two different FIs. Nevertheless, it does not make
sense to separate them. So we fake an FI and a sensorType to combine them together into one object.
"""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
fake_element_uid = f"devolo.HumidityBar:{uid_info['UID'].split(':', 1)[1]}"
if not hasattr(self.devices[device_uid], "humidity_bar_property"):
self.devices[device_uid].humidity_bar_property = {}
if self.devices[device_uid].humidity_bar_property.get(fake_element_uid) is None:
self.devices[device_uid].humidity_bar_property[fake_element_uid] = HumidityBarProperty(
element_uid=fake_element_uid,
tz=self.gateway.timezone,
sensorType="humidityBar",
)
if uid_info["properties"]["sensorType"] == "humidityBarZone":
self._logger.debug("Adding humidity bar zone property to %s.", device_uid)
self.devices[device_uid].humidity_bar_property[fake_element_uid].zone = uid_info["properties"]["value"]
elif uid_info["properties"]["sensorType"] == "humidityBarPos":
self._logger.debug("Adding humidity bar position property to %s.", device_uid)
self.devices[device_uid].humidity_bar_property[fake_element_uid].value = uid_info["properties"]["value"]
def _inspect_devices(self, devices: list[str]) -> None:
"""Inspect device properties of given list of devices."""
devices_properties = self.get_data_from_uid_list(devices)
for device_properties in devices_properties:
properties = device_properties["properties"]
self.devices[device_properties["UID"]] = Zwave(mydevolo_instance=self._mydevolo, **properties)
self.devices[device_properties["UID"]].settings_property = {}
threading.Thread(
target=self.devices[device_properties["UID"]].get_zwave_info,
name=f"{self.__class__.__name__}.{self.devices[device_properties['UID']].uid}",
).start()
# List comprehension gets the list of uids from every device
nested_uids_lists = [
(uid["properties"].get("settingUIDs") + uid["properties"]["elementUIDs"]) for uid in devices_properties
]
# List comprehension gets all uids into one list to make one big call against the mPRM
uid_list = [uid for sublist in nested_uids_lists for uid in sublist]
device_properties_list = self.get_data_from_uid_list(uid_list)
for uid_info in device_properties_list:
message_type = MESSAGE_TYPES.get(get_device_type_from_element_uid(uid_info["UID"]), "_unknown")
getattr(self, message_type)(uid_info)
try:
uid = self.devices[get_device_uid_from_element_uid(uid_info["UID"])]
except KeyError:
uid = self.devices[get_device_uid_from_setting_uid(uid_info["UID"])]
uid.pending_operations = uid.pending_operations or bool(uid_info["properties"].get("pendingOperations"))
# Last activity messages sometimes arrive before a device was initialized and therefore need to be handled afterwards.
for uid_info in device_properties_list:
if uid_info["UID"].startswith("devolo.LastActivity"):
self._last_activity(uid_info)
def _automatic_calibration(self, uid_info: dict[str, Any]) -> None:
"""Process automatic calibration (acs) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding automatic calibration setting to %s.", device_uid)
self.devices[device_uid].settings_property["automatic_calibration"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
calibration_status=bool(uid_info["properties"]["calibrationStatus"]),
)
def _binary_sync(self, uid_info: dict[str, Any]) -> None:
"""
Process binary sync setting (bss) properties. Currently only the direction of a shutter in known to be a binary sync
setting property, so it is named nicely.
"""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding binary sync setting to %s.", device_uid)
self.devices[device_uid].settings_property["movement_direction"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
inverted=uid_info["properties"]["value"],
)
def _binary_async(self, uid_info: dict[str, Any]) -> None:
"""Process binary async setting (bas) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding binary async settings to %s.", device_uid)
settings_property = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
value=uid_info["properties"]["value"],
)
# The siren needs to be handled differently, as otherwise their binary async setting will not be named nicely
if self.devices[device_uid].device_model_uid == "devolo.model.Siren":
self.devices[device_uid].settings_property["muted"] = settings_property
# As some devices have multiple binary async settings, we use the settings UID split after a '#' as key
else:
key = camel_case_to_snake_case(uid_info["UID"].split("#")[-1])
self.devices[device_uid].settings_property[key] = settings_property
def _last_activity(self, uid_info: dict[str, Any]) -> None:
"""
Process last activity properties. Those don't go into an own property but will be appended to a parent property.
This parent property is found by string replacement.
"""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
parent_element_uid = uid_info["UID"].replace("LastActivity", "BinarySensor")
try:
self.devices[device_uid].binary_sensor_property[parent_element_uid].last_activity = uid_info["properties"][
"lastActivityTime"
]
except AttributeError:
parent_element_uid = uid_info["UID"].replace("LastActivity", "SirenMultiLevelSwitch")
self.devices[device_uid].multi_level_switch_property[parent_element_uid].last_activity = uid_info["properties"][
"lastActivityTime"
]
def _led(self, uid_info: dict[str, Any]) -> None:
"""Process LED information setting (lis) and visual feedback setting (vfs) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding led settings to %s.", device_uid)
try:
led_setting = uid_info["properties"]["led"]
except KeyError:
led_setting = uid_info["properties"]["feedback"]
self.devices[device_uid].settings_property["led"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
led_setting=led_setting,
)
def _meter(self, uid_info: dict[str, Any]) -> None:
"""Process meter properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
if not hasattr(self.devices[device_uid], "consumption_property"):
self.devices[device_uid].consumption_property = {}
self._logger.debug("Adding consumption property to %s.", device_uid)
self.devices[device_uid].consumption_property[uid_info["UID"]] = ConsumptionProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
current=uid_info["properties"]["currentValue"],
total=uid_info["properties"]["totalValue"],
total_since=uid_info["properties"]["sinceTime"],
)
def _multilevel_async(self, uid_info: dict[str, Any]) -> None:
"""Process multilevel async setting (mas) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
try:
name = camel_case_to_snake_case(uid_info["properties"]["itemId"])
# The Metering Plug has an multilevel async setting without an ID
except TypeError:
if self.devices[device_uid].device_model_uid == "devolo.model.Wall:Plug:Switch:and:Meter":
name = "flash_mode"
else:
raise
self._logger.debug("Adding %s setting to %s.", name, device_uid)
self.devices[device_uid].settings_property[name] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
value=uid_info["properties"]["value"],
)
def _multilevel_sync(self, uid_info: dict[str, Any]) -> None:
"""Process multilevel sync setting (mss) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
# The siren needs to be handled differently, as otherwise their multilevel sync setting will not be named nicely.
if self.devices[device_uid].device_model_uid == "devolo.model.Siren":
self._logger.debug("Adding tone settings to %s.", device_uid)
self.devices[device_uid].settings_property["tone"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
tone=uid_info["properties"]["value"],
)
# The shutter needs to be handled differently, as otherwise their multilevel sync setting will not be named nicely.
elif self.devices[device_uid].device_model_uid in ("devolo.model.OldShutter", "devolo.model.Shutter"):
self._logger.debug("Adding shutter duration settings to %s.", device_uid)
self.devices[device_uid].settings_property["shutter_duration"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
shutter_duration=uid_info["properties"]["value"],
)
# Other devices are up to now always motion sensors.
else:
self._logger.debug("Adding motion sensitivity settings to %s.", device_uid)
self.devices[device_uid].settings_property["motion_sensitivity"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
motion_sensitivity=uid_info["properties"]["value"],
)
def _multi_level_sensor(self, uid_info: dict[str, Any]) -> None:
"""Process multi level sensor properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
if not hasattr(self.devices[device_uid], "multi_level_sensor_property"):
self.devices[device_uid].multi_level_sensor_property = {}
self._logger.debug("Adding multi level sensor property %s to %s.", uid_info["UID"], device_uid)
self.devices[device_uid].multi_level_sensor_property[uid_info["UID"]] = MultiLevelSensorProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
value=uid_info["properties"]["value"],
unit=uid_info["properties"]["unit"],
sensor_type=uid_info["properties"]["sensorType"],
)
def _multi_level_switch(self, uid_info: dict[str, Any]) -> None:
"""Process multi level switch properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
if not hasattr(self.devices[device_uid], "multi_level_switch_property"):
self.devices[device_uid].multi_level_switch_property = {}
self._logger.debug("Adding multi level switch property %s to %s.", uid_info["UID"], device_uid)
self.devices[device_uid].multi_level_switch_property[uid_info["UID"]] = MultiLevelSwitchProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_multi_level_switch,
value=uid_info["properties"]["value"],
switch_type=uid_info["properties"]["switchType"],
max=uid_info["properties"]["max"],
min=uid_info["properties"]["min"],
)
def _parameter(self, uid_info: dict[str, Any]) -> None:
"""Process custom parameter setting (cps) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding parameter settings to %s.", device_uid)
self.devices[device_uid].settings_property["param_changed"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
param_changed=uid_info["properties"]["paramChanged"],
)
def _protection(self, uid_info: dict[str, Any]) -> None:
"""Process protection setting (ps) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding protection settings to %s.", device_uid)
self.devices[device_uid].settings_property["protection"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
local_switching=uid_info["properties"]["localSwitch"],
remote_switching=uid_info["properties"]["remoteSwitch"],
)
def _remote_control(self, uid_info: dict[str, Any]) -> None:
"""Process remote control properties."""
device_uid = get_device_uid_from_element_uid(uid_info["UID"])
self._logger.debug("Adding remote control to %s.", device_uid)
if not hasattr(self.devices[device_uid], "remote_control_property"):
self.devices[device_uid].remote_control_property = {}
self.devices[device_uid].remote_control_property[uid_info["UID"]] = RemoteControlProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_remote_control,
key_count=uid_info["properties"]["keyCount"],
key_pressed=uid_info["properties"]["keyPressed"],
type=uid_info["properties"]["type"],
)
def _switch_type(self, uid_info: dict[str, Any]) -> None:
"""
Process switch type setting (sts) properties. Interestingly, a switch with two buttons reports a switchType of 1 and a
switch with four buttons reports a switchType of 2. This confusing behavior is corrected by doubling the value.
"""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding switch type setting to %s.", device_uid)
self.devices[device_uid].settings_property["switch_type"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
value=uid_info["properties"]["switchType"] * 2,
)
def _temperature_report(self, uid_info: dict[str, Any]) -> None:
"""Process temperature report setting (trs) properties."""
device_uid = get_device_uid_from_setting_uid(uid_info["UID"])
self._logger.debug("Adding temperature report settings to %s.", device_uid)
self.devices[device_uid].settings_property["temperature_report"] = SettingsProperty(
element_uid=uid_info["UID"],
tz=self.gateway.timezone,
setter=self.set_setting,
temp_report=uid_info["properties"]["tempReport"],
target_temp_report=uid_info["properties"]["targetTempReport"],
)
def _unknown(self, uid_info: dict[str, Any]) -> None:
"""Ignore unknown properties."""
ignore = ("devolo.SirenBinarySensor", "devolo.SirenMultiLevelSensor", "ss", "mcs")
if not uid_info["UID"].startswith(ignore):
self._logger.debug("Found an unexpected element uid: %s", uid_info["UID"])
devolo_home_control_api-0.19.0/devolo_home_control_api/mydevolo.py 0000664 0000000 0000000 00000015123 15003713056 0025575 0 ustar 00root root 0000000 0000000 """my devolo."""
from __future__ import annotations
import logging
from functools import lru_cache
from http import HTTPStatus
from typing import Any
import requests
from . import __version__
from .exceptions.gateway import GatewayOfflineError
from .exceptions.general import WrongCredentialsError, WrongUrlError
class Mydevolo:
"""
The Mydevolo object handles calls to the my devolo API v1. It does not cover all API calls, just those requested up to now.
All calls are done in a user context, so you need to provide credentials of that user.
"""
def __init__(self) -> None:
"""Initialize my devolo communication."""
self._logger = logging.getLogger(self.__class__.__name__)
self._user = ""
self._password = ""
self.url = "https://www.mydevolo.com"
@property
def user(self) -> str:
"""The user (also known as my devolo ID) is used for basic authentication."""
return self._user
@user.setter
def user(self, user: str) -> None:
"""Invalidate uuid and gateway IDs on user name change."""
self._user = user
self.uuid.cache_clear()
@property
def password(self) -> str:
"""The password is used for basic authentication."""
return self._password
@password.setter
def password(self, password: str) -> None:
"""Invalidate uuid and gateway IDs on password change."""
self._password = password
self.uuid.cache_clear()
def credentials_valid(self) -> bool:
"""
Check if current credentials are valid. This is done by trying to get the UUID. If that fails, credentials must be
wrong. If it succeeds, we can reuse the UUID for later usages.
"""
try:
self.uuid()
return True
except WrongCredentialsError:
return False
def get_gateway_ids(self) -> list[str]:
"""Get all gateway IDs attached to current account."""
self._logger.debug("Getting list of gateways")
items = self._call(f"{self.url}/v1/users/{self.uuid()}/hc/gateways/status")["items"]
gateway_ids = [gateway["gatewayId"] for gateway in items]
if not gateway_ids:
self._logger.error("Could not get gateway list. No gateway attached to account?")
raise IndexError("No gateways found.") # noqa: TRY003
return gateway_ids
def get_gateway(self, gateway_id: str) -> dict[str, Any]:
"""
Get gateway details like name, local passkey and other.
:param gateway_id: Gateway ID
:return: Gateway details
"""
self._logger.debug("Getting details for gateway %s", gateway_id)
try:
details = self._call(f"{self.url}/v1/users/{self.uuid()}/hc/gateways/{gateway_id}")
except WrongUrlError:
self._logger.error("Could not get full URL. Wrong gateway ID used?")
raise
details["location"] = self._call(details["location"]) if details["location"] else {}
return details
def get_full_url(self, gateway_id: str) -> str:
"""
Get gateway's portal URL.
:param gateway_id: Gateway ID
:return: URL to access the gateway's portal.
"""
self._logger.debug("Getting full URL of gateway.")
return self._call(f"{self.url}/v1/users/{self.uuid()}/hc/gateways/{gateway_id}/fullURL")["url"]
def get_timezone(self) -> str:
"""
Get user's standard timezone.
:return: Standard timezone of the user based on his country settings.
"""
self._logger.debug("Getting the user's standard timezone.")
return self._call(f"{self.url}/v1/users/{self.uuid()}/standardTimezone")["timezone"]
def get_zwave_products(self, manufacturer: str, product_type: str, product: str) -> dict[str, Any]:
"""
Get information about a Z-Wave device.
:param manufacturer: The manufacturer ID in hex.
:param product_type: The product type ID in hex.
:param product: The product ID in hex.
:return: All known product information.
"""
self._logger.debug("Getting information for %s/%s/%s", manufacturer, product_type, product)
try:
device_info = self._call(f"{self.url}/v1/zwave/products/{manufacturer}/{product_type}/{product}")
except WrongUrlError:
# At some devices no device information are returned
self._logger.debug("No device info found")
device_info = {
"brand": "devolo" if manufacturer == "0x0175" else "Unknown",
"deviceType": "Unknown",
"genericDeviceClass": "Unknown",
"href": f"{self.url}/v1/zwave/products/{manufacturer}/{product_type}/{product}",
"identifier": "Unknown",
"isZWavePlus": False,
"manufacturerId": manufacturer,
"name": "Unknown",
"productId": product,
"productTypeId": product_type,
"specificDeviceClass": "Unknown",
"zwaveVersion": "Unknown",
}
return device_info
def maintenance(self) -> bool:
"""If devolo Home Control is in maintenance, there is not much we can do via cloud."""
state = self._call(f"{self.url}/v1/hc/maintenance")["state"]
if state == "on":
return False
self._logger.warning("devolo Home Control is in maintenance mode.")
return True
@lru_cache(maxsize=1) # noqa: B019
def uuid(self) -> str:
"""Get the uuid. The uuid is a central attribute in my devolo. Most URLs in the user's context contain it."""
self._logger.debug("Getting UUID")
return self._call(f"{self.url.rstrip('/')}/v1/users/uuid")["uuid"]
def _call(self, url: str) -> dict[str, Any]:
"""Make a call to any entry point with the user's context."""
headers = {"content-type": "application/json", "User-Agent": f"devolo_home_control_api/{__version__}"}
responds = requests.get(url, auth=(self._user, self._password), headers=headers, timeout=60)
if responds.status_code == HTTPStatus.FORBIDDEN:
self._logger.error("Could not get full URL. Wrong username or password?")
raise WrongCredentialsError
if responds.status_code == HTTPStatus.NOT_FOUND:
raise WrongUrlError(url)
if responds.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
# mydevolo sends a 503, if the gateway is offline
self._logger.warning("The requested gateway seems to be offline.")
raise GatewayOfflineError
return responds.json()
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/ 0000775 0000000 0000000 00000000000 15003713056 0025557 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/properties/__init__.py 0000664 0000000 0000000 00000001346 15003713056 0027674 0 ustar 00root root 0000000 0000000 """Properties a device might have."""
from .binary_sensor_property import BinarySensorProperty
from .binary_switch_property import BinarySwitchProperty
from .consumption_property import ConsumptionProperty
from .humidity_bar_property import HumidityBarProperty
from .multi_level_sensor_property import MultiLevelSensorProperty
from .multi_level_switch_property import MultiLevelSwitchProperty
from .remote_control_property import RemoteControlProperty
from .settings_property import SettingsProperty
__all__ = [
"BinarySensorProperty",
"BinarySwitchProperty",
"ConsumptionProperty",
"HumidityBarProperty",
"MultiLevelSensorProperty",
"MultiLevelSwitchProperty",
"RemoteControlProperty",
"SettingsProperty",
]
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/binary_sensor_property.py 0000664 0000000 0000000 00000004306 15003713056 0032755 0 ustar 00root root 0000000 0000000 """Binary Sensors."""
from datetime import datetime, timezone, tzinfo
from typing import Any
from devolo_home_control_api.exceptions import WrongElementError
from .sensor_property import SensorProperty
class BinarySensorProperty(SensorProperty):
"""
Object for binary sensors. It stores the binary sensor state.
:param element_uid: Element UID, something like devolo.BinarySensor:hdm:ZWave:CBC56091/24
:param tz: Timezone the last activity is recorded in
:key state: State of the binary sensor
:type state: bool
"""
def __init__(self, element_uid: str, tz: tzinfo, **kwargs: Any) -> None:
"""Initialize the binary sensor."""
if not element_uid.startswith(
("devolo.BinarySensor:", "devolo.MildewSensor:", "devolo.ShutterMovementFI:", "devolo.WarningBinaryFI:")
):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz, **kwargs)
self._state: bool = kwargs.pop("state", False)
@property
def last_activity(self) -> datetime:
"""Date and time the state of the binary sensor was last updated."""
return super().last_activity
@last_activity.setter
def last_activity(self, timestamp: int) -> None:
"""
Set the last activity of the binary sensor. The gateway persists the last activity only for some of the binary sensors.
They can be initialized with that value. The others stay with a default timestamp until first update.
"""
if timestamp != -1:
self._last_activity = datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc).replace(tzinfo=self._timezone)
self._logger.debug("last_activity of element_uid %s set to %s.", self.element_uid, self._last_activity)
@property
def state(self) -> bool:
"""State of the binary sensor."""
return self._state
@state.setter
def state(self, state: bool) -> None:
"""Update state of the binary sensor and set point in time of the last_activity."""
self._state = state
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("state of element_uid %s set to %s.", self.element_uid, state)
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/binary_switch_property.py 0000664 0000000 0000000 00000003760 15003713056 0032750 0 ustar 00root root 0000000 0000000 """Binary Switches."""
from datetime import datetime, tzinfo
from typing import Callable
from devolo_home_control_api.exceptions import SwitchingProtected, WrongElementError
from .property import Property
class BinarySwitchProperty(Property):
"""
Object for binary switches. It stores the binary switch state.
:param element_uid: Element UID, something like devolo.BinarySwitch:hdm:ZWave:CBC56091/24#2
:param tz: Timezone the last activity is recorded in
:param setter: Method to call on setting the state
:key enabled: State of the remote protection setting
:type enabled: bool
:key state: State the switch has at time of creating this instance
:type state: bool
"""
def __init__(self, element_uid: str, tz: tzinfo, setter: Callable[[str, bool], bool], **kwargs: bool) -> None:
"""Initialize the binary switch."""
if not element_uid.startswith("devolo.BinarySwitch:"):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz)
self._setter = setter
self._state: bool = kwargs.pop("state", False)
self.enabled: bool = kwargs.pop("enabled", False)
@property
def state(self) -> bool:
"""State of the binary sensor."""
return self._state
@state.setter
def state(self, state: bool) -> None:
"""Update state of the binary sensor and set point in time of the last_activity."""
self._state = state
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("State of %s set to %s.", self.element_uid, state)
def set(self, state: bool) -> bool:
"""
Set the binary switch of the given element_uid to the given state.
:param state: True if switching on, False if switching off
"""
if not self.enabled:
raise SwitchingProtected
if self._setter(self.element_uid, state):
self.state = state
return True
return False
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/consumption_property.py 0000664 0000000 0000000 00000005414 15003713056 0032457 0 ustar 00root root 0000000 0000000 """Consumption Meters."""
from __future__ import annotations
from datetime import datetime, timezone, tzinfo
from devolo_home_control_api.exceptions import WrongElementError
from .property import Property
class ConsumptionProperty(Property):
"""
Object for consumption meters. It stores the current and total consumption and the corresponding units.
:param element_uid: Element UID, something like devolo.Meter:hdm:ZWave:CBC56091/24#2
:param tz: Timezone the last activity is recorded in
:key current: Consumption value valid at time of creating the instance
:type current: float
:key total: Total consumption since last reset
:type total: float
:key total_since: Timestamp in milliseconds of last reset
:type total_since: int
"""
def __init__(self, element_uid: str, tz: tzinfo, **kwargs: float) -> None:
"""Initialize the consumption meter."""
if not element_uid.startswith("devolo.Meter:"):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz)
self._current: float = kwargs.pop("current", 0.0)
self.current_unit = "W"
self._total: float = kwargs.pop("total", 0.0)
self.total_unit = "kWh"
self._total_since = datetime.fromtimestamp(kwargs.pop("total_since", 0) / 1000, tz=timezone.utc).replace(
tzinfo=self._timezone
)
@property
def current(self) -> float:
"""Consumption value."""
return self._current
@current.setter
def current(self, current: float) -> None:
"""Update current consumption and set point in time of the last_activity."""
self._current = current
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("current of element_uid %s set to %s.", self.element_uid, current)
@property
def total(self) -> float:
"""Total consumption value."""
return self._total
@total.setter
def total(self, total: float) -> None:
"""Update total consumption and set point in time of the last_activity."""
self._total = total
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("total of element_uid %s set to %s.", self.element_uid, total)
@property
def total_since(self) -> datetime:
"""Date and time the binary sensor was last triggered."""
return self._total_since
@total_since.setter
def total_since(self, timestamp: int) -> None:
"""Convert a timestamp in millisecond to a datetime object."""
self._total_since = datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc).replace(tzinfo=self._timezone)
self._logger.debug("total_since of element_uid %s set to %s.", self.element_uid, self._total_since)
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/humidity_bar_property.py 0000664 0000000 0000000 00000003042 15003713056 0032554 0 ustar 00root root 0000000 0000000 """Humidity Bars."""
from datetime import datetime, tzinfo
from typing import Any
from devolo_home_control_api.exceptions import WrongElementError
from .sensor_property import SensorProperty
class HumidityBarProperty(SensorProperty):
"""
Object for humidity bars. It stores the zone and the position inside that zone.
:param element_uid: Fake element UID, something like devolo.HumidityBar:hdm:ZWave:CBC56091/24
:param tz: Timezone the last activity is recorded in
:key value: Position inside a zone
:type value: int
:key zone: Humidity zone from 0 (very dry) to 4 (very humid)
:type zone: int
"""
def __init__(self, element_uid: str, tz: tzinfo, **kwargs: Any) -> None:
"""Initialize the humidity bar."""
if not element_uid.startswith("devolo.HumidityBar:"):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz, **kwargs)
self._value: int = kwargs.pop("value", 0)
self.zone: int = kwargs.pop("zone", 0)
@property
def value(self) -> int:
"""Position inside a zone."""
return self._value
@value.setter
def value(self, value: int) -> None:
"""
Update value and set point in time of the last_activity. As zone never changes without a value, just the value sets
the last activity.
"""
self._value = value
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("value of element_uid %s set to %s.", self.element_uid, value)
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/multi_level_sensor_property.py 0000664 0000000 0000000 00000004406 15003713056 0034013 0 ustar 00root root 0000000 0000000 """Multi Level Sensors."""
from datetime import datetime, tzinfo
from typing import Any
from devolo_home_control_api.exceptions import WrongElementError
from .sensor_property import SensorProperty
class MultiLevelSensorProperty(SensorProperty):
"""
Object for multi level sensors. It stores the multi level sensor state and additional information that help displaying the
state in the right context.
:param element_uid: Element UID, something like devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#MultilevelSensor(1)
:param tz: Timezone the last activity is recorded in
:key value: Multi level value
:type value: float
:key unit: Unit of that value
:type unit: int
"""
def __init__(self, element_uid: str, tz: tzinfo, **kwargs: Any) -> None:
"""Initialize the multi level sensor."""
if not element_uid.startswith(
(
"devolo.DewpointSensor:",
"devolo.MultiLevelSensor:",
"devolo.ValveTemperatureSensor",
"devolo.VoltageMultiLevelSensor:",
)
):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz, **kwargs)
self._value: float = kwargs.pop("value", 0.0)
self._unit: int = kwargs.pop("unit", 0)
@property
def unit(self) -> str:
"""Human readable unit of the property."""
units = {
"dewpoint": {0: "°C", 1: "°F"},
"humidity": {0: "%", 1: "g/m³"},
"light": {0: "%", 1: "lx"},
"temperature": {0: "°C", 1: "°F"},
"Seismic Intensity": {0: ""},
"voltage": {0: "V", 1: "mV"},
}
try:
return units[self.sensor_type].get(self._unit, str(self._unit))
except KeyError:
return str(self._unit)
@property
def value(self) -> float:
"""Multi level value."""
return self._value
@value.setter
def value(self, value: float) -> None:
"""Update value of the multilevel sensor and set point in time of the last_activity."""
self._value = value
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("value of element_uid %s set to %s.", self.element_uid, value)
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/multi_level_switch_property.py 0000664 0000000 0000000 00000007162 15003713056 0034005 0 ustar 00root root 0000000 0000000 """Multi Level Switches."""
from __future__ import annotations
from datetime import datetime, timezone, tzinfo
from typing import Any, Callable
from devolo_home_control_api.exceptions import WrongElementError
from .property import Property
class MultiLevelSwitchProperty(Property):
"""
Object for multi level switches. It stores the multi level state and additional information that help displaying the state
in the right context.
:param element_uid: Element UID, something like devolo.Dimmer:hdm:ZWave:CBC56091/24#2
:param tz: Timezone the last activity is recorded in
:key value: Value the multi level switch has at time of creating this instance
:type value: float
:key switch_type: Type this switch is of, e.g. temperature
:type switch_type: str
:key max: Highest possible value, that can be set
:type max: float
:key min: Lowest possible value, that can be set
:type min: float
"""
def __init__(self, element_uid: str, tz: tzinfo, setter: Callable[[str, float], bool], **kwargs: Any) -> None:
"""Initialize the multi level switch."""
if not element_uid.startswith(
("devolo.Blinds:", "devolo.Dimmer:", "devolo.MultiLevelSwitch:", "devolo.SirenMultiLevelSwitch:")
):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz)
self._setter = setter
self._value: float = kwargs.pop("value", 0.0)
self.switch_type: str = kwargs.pop("switch_type", "")
self.max: float = kwargs.pop("max", 100.0)
self.min: float = kwargs.pop("min", 0.0)
@property
def last_activity(self) -> datetime:
"""Date and time the state of the multi level switch was last updated."""
return super().last_activity
@last_activity.setter
def last_activity(self, timestamp: int) -> None:
"""
Set the last activity of the multi level switch. The gateway persists the last activity only for some of the multi
level switchs. They can be initialized with that value. The others stay with a default timestamp until first update.
"""
if timestamp != -1:
self._last_activity = datetime.fromtimestamp(timestamp / 1000, tz=timezone.utc).replace(tzinfo=self._timezone)
self._logger.debug("last_activity of element_uid %s set to %s.", self.element_uid, self._last_activity)
@property
def unit(self) -> str | None:
"""Human readable unit of the property. Defaults to percent."""
units = {
"temperature": "°C",
"tone": None,
}
return units.get(self.switch_type, "%")
@property
def value(self) -> float:
"""Multi level value."""
return self._value
@value.setter
def value(self, value: float) -> None:
"""Update value of the multilevel value and set point in time of the last_activity."""
self._value = value
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("Value of %s set to %s.", self.element_uid, value)
def set(self, value: float) -> bool:
"""
Set the multilevel switch of the given element_uid to the given value.
:param value: Value to set
"""
if value > self.max or value < self.min:
raise ValueError( # noqa: TRY003
f"Set value {value} is too {'low' if value < self.min else 'high'}. "
f"The min value is {self.min}. The max value is {self.max}"
)
if self._setter(self.element_uid, value):
self.value = value
return True
return False
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/property.py 0000664 0000000 0000000 00000001726 15003713056 0030023 0 ustar 00root root 0000000 0000000 """Generic Properties."""
import logging
from abc import ABC
from datetime import datetime, tzinfo
from devolo_home_control_api.helper import get_device_uid_from_element_uid
class Property(ABC):
"""
Abstract base object for properties.
:param element_uid: Element UID, something like devolo.BinarySwitch:hdm:ZWave:CBC56091/24#2
:param tz: Timezone the last activity is recorded in
"""
def __init__(self, element_uid: str, tz: tzinfo) -> None:
"""Initialize the property."""
self.element_uid = element_uid
self.device_uid = get_device_uid_from_element_uid(element_uid)
self._logger = logging.getLogger(self.__class__.__name__)
self._last_activity = datetime.fromtimestamp(0, tz=tz) # Set last activity to 1.1.1970. Will be corrected.
self._timezone = tz
@property
def last_activity(self) -> datetime:
"""Date and time the property was last updated."""
return self._last_activity
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/remote_control_property.py 0000664 0000000 0000000 00000004321 15003713056 0033130 0 ustar 00root root 0000000 0000000 """Remote Controls."""
from datetime import datetime, tzinfo
from typing import Callable
from devolo_home_control_api.exceptions import WrongElementError
from .property import Property
class RemoteControlProperty(Property):
"""
Object for remote controls. It stores the button state and additional information that help displaying the state in the
right context.
:param element_uid: Element UID, something like devolo.RemoteControl:hdm:ZWave:CBC56091/24#2
:param tz: Timezone the last activity is recorded in
:param setter: Method to call on setting the state
:key key_count: Number of buttons this remote control has
:type key_count: int
:key key_pressed: Number of the button pressed
:type key_pressed: int
"""
def __init__(self, element_uid: str, tz: tzinfo, setter: Callable[[str, int], bool], **kwargs: int) -> None:
"""Initialize the remote control."""
if not element_uid.startswith("devolo.RemoteControl"):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz)
self._setter = setter
self._key_pressed: int = kwargs.pop("key_pressed", 0)
self.key_count: int = kwargs.pop("key_count", 0)
@property
def key_pressed(self) -> int:
"""Multi level value."""
return self._key_pressed
@key_pressed.setter
def key_pressed(self, key_pressed: int) -> None:
"""Update value of the multilevel value and set point in time of the last_activity."""
self._key_pressed = key_pressed
self._last_activity = datetime.now(tz=self._timezone)
self._logger.debug("key_pressed of element_uid %s set to %s.", self.element_uid, key_pressed)
def set(self, key_pressed: int) -> bool:
"""
Trigger a button press of a remote control like if the button was physically pressed.
:param key_pressed: Number of the button to press
"""
if key_pressed > self.key_count or key_pressed <= 0:
raise ValueError(f"Set value {key_pressed} is invalid.") # noqa: TRY003
if self._setter(self.element_uid, key_pressed):
self.key_pressed = key_pressed
return True
return False
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/sensor_property.py 0000664 0000000 0000000 00000001423 15003713056 0031406 0 ustar 00root root 0000000 0000000 """Generic Sensors."""
from abc import ABC
from datetime import tzinfo
from .property import Property
class SensorProperty(Property, ABC):
"""
Abstract object for sensors. It stores the sensor and sub type.
:param element_uid: Element UID
:param tz: Timezone the last activity is recorded in
:key sensor_type: Type of the sensor sensor, something like 'alarm'
:type sensor_type: str
:key sub_type: Subtype of the sensor, something like 'overload'
:type sub_type: str
"""
def __init__(self, element_uid: str, tz: tzinfo, **kwargs: str) -> None:
"""Initialize the sensor."""
super().__init__(element_uid, tz)
self.sensor_type: str = kwargs.pop("sensor_type", "")
self.sub_type: str = kwargs.pop("sub_type", "")
devolo_home_control_api-0.19.0/devolo_home_control_api/properties/settings_property.py 0000664 0000000 0000000 00000015512 15003713056 0031741 0 ustar 00root root 0000000 0000000 """Settings."""
from __future__ import annotations
from datetime import tzinfo
from enum import IntEnum
from typing import Any, Callable
from devolo_home_control_api.exceptions import WrongElementError
from .property import Property
class SettingsProperty(Property):
"""
Object for settings. Basically, everything can be stored in here as long as there is a corresponding functional item on
the gateway. This is to be as flexible to gateway firmware changes as possible. So if new attributes appear or old ones
are removed, they should be handled at least in reading them. Nevertheless, a few unwanted attributes are filtered.
:param element_uid: Element UID, something like acs.hdm:ZWave:CBC56091/24
:param tz: Timezone the last activity is recorded in
:param setter: Method to call on setting the state
:param **kwargs: Any setting, that shall be available in this object
"""
calibration_status: bool
direction: bool
events_enabled: bool
icon: str
inverted: int
led_setting: bool
local_switching: bool
motion_sensitivity: int
name: str
param_changed: bool
remote_switching: bool
temp_report: bool
tone: int
value: bool
zone_id: str
def __init__(self, element_uid: str, tz: tzinfo, setter: Callable[..., bool], **kwargs: Any) -> None:
"""Initialize the setting."""
if not element_uid.startswith(
("acs", "bas", "bss", "cps", "gds", "lis", "mas", "mss", "ps", "sts", "stmss", "trs", "vfs")
):
raise WrongElementError(element_uid, self.__class__.__name__)
super().__init__(element_uid, tz)
self._setter = setter
if element_uid.startswith("gds") and {"zones", "zone_id"} <= kwargs.keys():
self.zone = kwargs.pop("zones")[kwargs["zone_id"]]
for key, value in kwargs.items():
setattr(self, key, value)
setter_method: dict[str, Callable[..., bool]] = {
"bas": self._set_bas,
"gds": self._set_gds,
"lis": self._set_lis,
"mss": self._set_mss,
"ps": self._set_ps,
"trs": self._set_trs,
"vfs": self._set_lis,
}
# Depending on the type of setting property, this will create a callable named "set".
# However, this methods are not working, if the gateway is connected locally, yet.
self.set = setter_method.get(element_uid.split(".")[0], lambda: False)
# Clean up attributes which are unwanted.
clean_up_list = ["device_uid"]
for attribute in clean_up_list:
delattr(self, attribute)
def _set_bas(self, value: bool) -> bool:
"""
Set a binary async setting. This is e.g. the muted setting of a siren or the three way switch setting of a dimmer.
:param value: New state
"""
if self._setter(self.element_uid, [value]):
self.value = value
self._logger.debug("Binary async setting property %s set to %s", self.element_uid, value)
return True
return False
def _set_gds(self, **kwargs: Any) -> bool:
"""
Set one or more general device setting.
:key events_enabled: Show events in diary
:type events_enabled: bool
:key icon: New icon name
:type icon: str
:key name: New device name
:type name: str
:key zone_id: New zone_id (ATTENTION: This is NOT the name of the location)
:type zone_id: str
"""
events_enabled: bool = kwargs.pop("events_enabled", self.events_enabled)
icon: str = kwargs.pop("icon", self.icon)
name: str = kwargs.pop("name", self.name)
zone_id: str = kwargs.pop("zone_id", self.zone_id)
settings = {
"events_enabled": events_enabled,
"icon": icon,
"name": name,
"zone_id": zone_id,
}
if self._setter(self.element_uid, [settings]):
self.events_enabled = events_enabled
self.icon = icon
self.name = name
self.zone_id = zone_id
self._logger.debug("General device setting %s changed.", self.element_uid)
return True
return False
def _set_lis(self, led_setting: bool) -> bool:
"""
Set led settings.
:param led_setting: LED indication setting
"""
if self._setter(self.element_uid, [led_setting]):
self.led_setting = led_setting
self._logger.debug("LED indication setting property %s set to %s", self.element_uid, led_setting)
return True
return False
def _set_mss(self, motion_sensitivity: int) -> bool:
"""
Set motion sensitivity.
:param motion_sensitivity: Integer for the motion sensitivity setting.
"""
if not MotionSensitivity.MIN <= motion_sensitivity <= MotionSensitivity.MAX:
raise ValueError(f"Value must be between {MotionSensitivity.MIN} and {MotionSensitivity.MAX}") # noqa: TRY003
if self._setter(self.element_uid, [motion_sensitivity]):
self.motion_sensitivity = motion_sensitivity
self._logger.debug("Motion sensitivity setting property %s set to %s", self.element_uid, motion_sensitivity)
return True
return False
def _set_ps(self, **kwargs: bool) -> bool:
"""
Set one or both protection settings.
:key local_switching: Allow local switching
:type local_switching: bool
:key remote_switching: Allow local switching
:type remote_switching: bool
"""
local_switching = kwargs.pop("local_switching", self.local_switching)
remote_switching = kwargs.pop("remote_switching", self.remote_switching)
if self._setter(
self.element_uid,
[
{
"localSwitch": local_switching,
"remoteSwitch": remote_switching,
}
],
):
self.local_switching = local_switching
self.remote_switching = remote_switching
self._logger.debug(
"Protection setting property %s set to %s (local) and %s (remote).",
self.element_uid,
local_switching,
remote_switching,
)
return True
return False
def _set_trs(self, temp_report: bool) -> bool:
"""
Set temperature report setting.
:param temp_report: Boolean of the target value
"""
if self._setter(self.element_uid, [temp_report]):
self.temp_report = temp_report
self._logger.debug("Temperature report setting property %s set to %s", self.element_uid, temp_report)
return True
return False
class MotionSensitivity(IntEnum):
"""Motion sensitivity boundary value."""
MIN = 0
MAX = 100
devolo_home_control_api-0.19.0/devolo_home_control_api/publisher/ 0000775 0000000 0000000 00000000000 15003713056 0025360 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/devolo_home_control_api/publisher/__init__.py 0000664 0000000 0000000 00000000203 15003713056 0027464 0 ustar 00root root 0000000 0000000 """Pubish websocket messages."""
from .publisher import Publisher
from .updater import Updater
__all__ = ["Publisher", "Updater"]
devolo_home_control_api-0.19.0/devolo_home_control_api/publisher/publisher.py 0000664 0000000 0000000 00000003542 15003713056 0027733 0 ustar 00root root 0000000 0000000 """The Publisher."""
from __future__ import annotations
import logging
from typing import Any, Callable, KeysView
class Publisher:
"""The Publisher send messages to attached subscribers."""
def __init__(self, events: list[Any] | KeysView) -> None:
"""Initialize the publisher."""
self._logger = logging.getLogger(self.__class__.__name__)
self._events: dict[Any, Any] = {event: {} for event in events}
def add_event(self, event: str) -> None:
"""Add a new event to listen to."""
self._events[event] = {}
def delete_event(self, event: str) -> None:
"""Delete a not longer needed event."""
self._events.pop(event)
def dispatch(self, event: str, message: tuple[Any, ...]) -> None:
"""Dispatch the message to the subscribers."""
for callback in self._get_subscribers_for_specific_event(event).values():
callback(message)
def register(self, event: str, who: Any, callback: Callable | None = None) -> None:
"""
As a new subscriber for an event, add a callback function to call on new message.
If no callback is given, it registers update().
:raises AttributeError: The supposed callback is not callable.
"""
if callback is None:
callback = who.update
self._get_subscribers_for_specific_event(event)[who] = callback
self._logger.debug("Subscriber registered for event %s", event)
def unregister(self, event: str, who: Any) -> None:
"""Remove a subscriber for a specific event."""
del self._get_subscribers_for_specific_event(event)[who]
self._logger.debug("Subscriber deleted for event %s", event)
def _get_subscribers_for_specific_event(self, event: str) -> dict[Any, Any]:
"""All subscribers listening to an event."""
return self._events.get(event, {})
devolo_home_control_api-0.19.0/devolo_home_control_api/publisher/updater.py 0000664 0000000 0000000 00000054235 15003713056 0027407 0 ustar 00root root 0000000 0000000 """The Updater."""
from __future__ import annotations
import json
import logging
from contextlib import suppress
from typing import Any, Callable
from devolo_home_control_api.backend import MESSAGE_TYPES
from devolo_home_control_api.devices import Gateway, Zwave
from devolo_home_control_api.helper import (
camel_case_to_snake_case,
get_device_type_from_element_uid,
get_device_uid_from_element_uid,
get_device_uid_from_setting_uid,
)
from .publisher import Publisher
class Updater:
"""
The Updater takes care of new states and values of devices and sends them to the Publisher object. Using methods in here
do not effect the real device states.
:param devices: List of devices to await updates for
:param gateway: Instance of a Gateway object
:param publisher: Instance of a Publisher object
"""
def __init__(self, devices: dict[str, Zwave], gateway: Gateway, publisher: Publisher) -> None:
"""Initialize the updater."""
self._logger = logging.getLogger(self.__class__.__name__)
self._gateway = gateway
self._publisher = publisher
self.devices = devices
self.on_device_change: Callable[[list[str]], tuple[str, str]] | None = None
def update(self, message: dict[str, Any]) -> None:
"""
Update states and values depending on the message type.
:param message: Message to process
"""
unwanted_properties = [
".unregistering",
"assistantsConnected",
"operationStatus",
]
# Early return on unwanted messages
if (
"UNREGISTERED" in message["topic"]
or message["properties"]["property.name"] in unwanted_properties
or "smartGroup" in message["properties"]["uid"]
):
return
# Handle pending operations messages
if "property.name" in message["properties"] and message["properties"]["property.name"] == "pendingOperations":
self._pending_operations(message)
return
# Handle all other messages
message_type = MESSAGE_TYPES.get(get_device_type_from_element_uid(message["properties"]["uid"]), "_unknown")
with suppress(AttributeError, KeyError): # Sometime we receive already messages although the device is not setup yet.
getattr(self, message_type)(message)
def _automatic_calibration(self, message: dict[str, Any]) -> None:
"""Update a automatic calibration message."""
try:
calibration_status = message["properties"]["property.value.new"]["status"]
self._update_automatic_calibration(
element_uid=message["properties"]["uid"], calibration_status=calibration_status != 2 # noqa: PLR2004
)
except (KeyError, TypeError):
if type(message["properties"]["property.value.new"]) not in [dict, list]:
self._update_automatic_calibration(
element_uid=message["properties"]["uid"],
calibration_status=bool(message["properties"]["property.value.new"]),
)
def _binary_async(self, message: dict[str, Any]) -> None:
"""Update a binary async setting."""
if type(message["properties"]["property.value.new"]) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
value = bool(message["properties"]["property.value.new"])
device_uid = get_device_uid_from_setting_uid(element_uid)
try:
self.devices[device_uid].settings_property[camel_case_to_snake_case(element_uid).split("#")[-1]].value = value
except KeyError:
# Siren setting is not initialized like others.
self.devices[device_uid].settings_property["muted"].value = value
self._logger.debug("Updating state of %s to %s", element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _binary_sync(self, message: dict[str, Any]) -> None:
"""Update a binary sync setting."""
element_uid: str = message["properties"]["uid"]
value = bool(message["properties"]["property.value.new"])
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].settings_property["movement_direction"].inverted = value
self._logger.debug("Updating state of %s to %s", element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _binary_sensor(self, message: dict[str, Any]) -> None:
"""Update a binary sensor's state."""
if message["properties"]["property.value.new"] is not None:
element_uid: str = message["properties"]["uid"]
value = bool(message["properties"]["property.value.new"])
device_uid = get_device_uid_from_element_uid(element_uid)
self.devices[device_uid].binary_sensor_property[element_uid].state = value
self._logger.debug("Updating state of %s to %s", element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _binary_switch(self, message: dict[str, Any]) -> None:
"""Update a binary switch's state."""
if message["properties"]["property.name"] == "targetState" and message["properties"]["property.value.new"] is not None:
element_uid: str = message["properties"]["uid"]
value = bool(message["properties"]["property.value.new"])
device_uid = get_device_uid_from_element_uid(element_uid)
self.devices[device_uid].binary_switch_property[element_uid].state = value
self._logger.debug("Updating state of %s to %s", element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _pending_operations(self, message: dict[str, Any]) -> None:
"""Update pending operation state."""
element_uid: str = message["properties"]["uid"]
# Early return on useless messages
if [
uid
for uid in ["devolo.HttpRequest", "devolo.PairDevice", "devolo.RemoveDevice", "devolo.mprm.gw.GatewayManager"]
if (uid in element_uid)
]:
return
pending_operations = bool(message["properties"].get("property.value.new"))
try:
device_uid = get_device_uid_from_element_uid(element_uid)
self.devices[device_uid].pending_operations = pending_operations
except KeyError:
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].pending_operations = pending_operations
self._logger.debug("Updating pending operations of device %s to %s", device_uid, pending_operations)
self._publisher.dispatch(device_uid, ("pending_operations", pending_operations))
def _current_consumption(self, message: dict[str, Any]) -> None:
"""Update current consumption."""
self._update_consumption(element_uid=message["uid"], consumption="current", value=message["property.value.new"])
def _device_state(self, message: dict[str, Any]) -> None:
"""Update the device state."""
property_name = {
"batteryLevel": "battery_level",
"batteryLow": "battery_low",
"status": "status",
}
device_uid = message["properties"]["uid"]
name = message["properties"]["property.name"]
value = message["properties"]["property.value.new"]
try:
self._logger.debug("Updating %s of %s to %s", property_name[name], device_uid, value)
setattr(self.devices[device_uid], property_name[name], value)
self._publisher.dispatch(device_uid, (device_uid, value, property_name[name]))
except KeyError:
self._unknown(message)
def _gateway_accessible(self, message: dict[str, Any]) -> None:
"""Update the gateway's state."""
if message["properties"]["property.name"] == "gatewayAccessible":
accessible = message["properties"]["property.value.new"]["accessible"]
online_sync = message["properties"]["property.value.new"]["onlineSync"]
self._logger.debug("Updating status and state of gateway to status: %s and state: %s", accessible, online_sync)
self._gateway.online = accessible
self._gateway.sync = online_sync
def _general_device(self, message: dict[str, Any]) -> None:
"""Update general device settings."""
self._update_general_device_settings(
element_uid=message["properties"]["uid"],
events_enabled=message["properties"]["property.value.new"]["eventsEnabled"],
icon=message["properties"]["property.value.new"]["icon"],
name=message["properties"]["property.value.new"]["name"],
zone_id=message["properties"]["property.value.new"]["zoneID"],
zones=self._gateway.zones,
)
def _grouping(self, message: dict[str, Any]) -> None:
"""Update zone (also called room) of a device."""
self._gateway.zones = {key["id"]: key["name"] for key in message["properties"]["property.value.new"]}
self._logger.debug("Updating gateway zones.")
def _gui_enabled(self, message: dict[str, Any]) -> None:
"""Update protection setting of binary switches."""
device_uid = get_device_uid_from_element_uid(message["uid"])
enabled = message["property.value.new"]
for element_uid in self.devices[device_uid].binary_switch_property:
self.devices[device_uid].binary_switch_property[element_uid].enabled = enabled
self._logger.debug("Updating enabled state of %s to %s", element_uid, enabled)
self._publisher.dispatch(device_uid, (element_uid, enabled, "gui_enabled"))
def _humidity_bar(self, message: dict[str, Any]) -> None:
"""Update a humidity bar."""
fake_element_uid = f"devolo.HumidityBar:{message['properties']['uid'].split(':', 1)[1]}"
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_element_uid(fake_element_uid)
if message["properties"]["uid"].startswith("devolo.HumidityBarZone"):
self.devices[device_uid].humidity_bar_property[fake_element_uid].zone = value
self._logger.debug("Updating humidity bar zone of %s to %s", fake_element_uid, value)
elif message["properties"]["uid"].startswith("devolo.HumidityBarValue"):
self.devices[device_uid].humidity_bar_property[fake_element_uid].value = value
self._logger.debug("Updating humidity bar value of %s to %s", fake_element_uid, value)
self._publisher.dispatch(
device_uid,
(
fake_element_uid,
self.devices[device_uid].humidity_bar_property[fake_element_uid].zone,
self.devices[device_uid].humidity_bar_property[fake_element_uid].value,
),
)
def _inspect_devices(self, message: dict[str, Any]) -> None:
"""Call method if a new device appears or an old one disappears."""
if not callable(self.on_device_change):
self._logger.error("on_device_change is not set.")
return
if (
not isinstance(message["properties"]["property.value.new"], list)
or message["properties"]["uid"] != "devolo.DevicesPage"
):
return
device_uid, mode = self.on_device_change(message["properties"]["property.value.new"])
if mode == "add":
self._logger.info("%s added.", device_uid)
self._publisher.add_event(event=device_uid)
self._publisher.dispatch(device_uid, (device_uid, mode))
else:
self._publisher.dispatch(device_uid, (device_uid, mode))
self._publisher.delete_event(event=device_uid)
def _led(self, message: dict[str, Any]) -> None:
"""Update LED settings."""
if type(message["properties"]["property.value.new"]) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_setting_uid(element_uid)
self._logger.debug("Updating %s to %s.", element_uid, value)
self.devices[device_uid].settings_property["led"].led_setting = value
self._publisher.dispatch(device_uid, (element_uid, value))
def _meter(self, message: dict[str, Any]) -> None:
"""Update a meter value."""
property_name = {
"currentValue": self._current_consumption,
"totalValue": self._total_consumption,
"sinceTime": self._since_time,
"guiEnabled": self._gui_enabled,
}
property_name[message["properties"]["property.name"]](message["properties"])
def _multilevel_async(self, message: dict[str, Any]) -> None:
"""Update multilevel async setting (mas) properties."""
device_uid = get_device_uid_from_setting_uid(message["properties"]["uid"])
try:
name = camel_case_to_snake_case(message["properties"]["itemId"])
# The Metering Plug has an multilevel async setting without an ID
except KeyError:
if self.devices[device_uid].device_model_uid == "devolo.model.Wall:Plug:Switch:and:Meter":
name = "flash_mode"
else:
raise
self.devices[device_uid].settings_property[name].value = message["properties"]["property.value.new"]
def _multi_level_sensor(self, message: dict[str, Any]) -> None:
"""Update a multi level sensor."""
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_element_uid(element_uid)
self._logger.debug("Updating %s to %s.", element_uid, value)
self.devices[device_uid].multi_level_sensor_property[element_uid].value = value
self._publisher.dispatch(device_uid, (element_uid, value))
def _multi_level_switch(self, message: dict[str, Any]) -> None:
"""Update a multi level switch."""
if not isinstance(message["properties"]["property.value.new"], (list, dict, type(None))):
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_element_uid(element_uid)
self._logger.debug("Updating %s to %s.", element_uid, value)
self.devices[device_uid].multi_level_switch_property[element_uid].value = value
self._publisher.dispatch(device_uid, (element_uid, value))
def _multilevel_sync(self, message: dict[str, Any]) -> None:
"""Update multilevel sync settings."""
if type(message["properties"]["property.value.new"]) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_setting_uid(element_uid)
device_model = self.devices[device_uid].device_model_uid
self._logger.debug("Updating %s to %s.", element_uid, value)
sync_type = {
"devolo.model.Siren": "tone",
"devolo.model.OldShutter": "shutter_duration",
"devolo.model.Shutter": "shutter_duration",
}
try:
setattr(self.devices[device_uid].settings_property[sync_type[device_model]], sync_type[device_model], value)
except KeyError:
# Other devices are up to now always motion sensors.
self.devices[device_uid].settings_property["motion_sensitivity"].motion_sensitivity = value
self._publisher.dispatch(device_uid, (element_uid, value))
def _parameter(self, message: dict[str, Any]) -> None:
"""Update parameter settings."""
if type(message["properties"].get("property.value.new")) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
param_changed = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].settings_property["param_changed"].param_changed = param_changed
self._logger.debug("Updating %s to %s.", element_uid, param_changed)
self._publisher.dispatch(device_uid, (element_uid, param_changed))
def _protection(self, message: dict[str, Any]) -> None:
"""Update protection settings."""
if type(message["properties"].get("property.value.new")) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
name = message["properties"]["property.name"]
device_uid = get_device_uid_from_setting_uid(element_uid)
switching_type = {
"targetLocalSwitch": "local_switching",
"localSwitch": "local_switching",
"targetRemoteSwitch": "remote_switching",
"remoteSwitch": "remote_switching",
}
setattr(self.devices[device_uid].settings_property["protection"], switching_type[name], value)
self._logger.debug("Updating %s protection of %s to %s", switching_type[name], element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value, switching_type[name]))
def _remote_control(self, message: dict[str, Any]) -> None:
"""Update a remote control."""
element_uid: str = message["properties"]["uid"]
key_pressed = message["properties"]["property.value.new"]
# The message for the diary needs to be ignored
if key_pressed is not None:
device_uid = get_device_uid_from_element_uid(element_uid)
old_key_pressed = self.devices[device_uid].remote_control_property[element_uid].key_pressed
self.devices[device_uid].remote_control_property[element_uid].key_pressed = key_pressed
self._logger.debug(
"Updating remote control of %s. Key %s",
element_uid,
f"pressed: {key_pressed}" if key_pressed != 0 else f"released: {old_key_pressed}",
)
self._publisher.dispatch(device_uid, (element_uid, key_pressed))
def _since_time(self, message: dict[str, Any]) -> None:
"""Update point in time the total consumption was reset."""
element_uid = message["uid"]
total_since = message["property.value.new"]
device_uid = get_device_uid_from_element_uid(element_uid)
self.devices[device_uid].consumption_property[element_uid].total_since = total_since
self._logger.debug("Updating total since of %s to %s", element_uid, total_since)
self._publisher.dispatch(device_uid, (element_uid, total_since, "total_since"))
def _switch_type(self, message: dict[str, Any]) -> None:
"""Update switch type setting (sts)."""
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"] * 2 # FWR, value.new is 1 for 2 buttons and 2 for 4 buttons.
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].settings_property["switch_type"].value = value
self.devices[device_uid].remote_control_property[f"devolo.RemoteControl:{device_uid}"].key_count = value
self._logger.debug("Updating switch type of %s to %s", device_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _temperature_report(self, message: dict[str, Any]) -> None:
"""Update temperature report settings."""
if type(message["properties"].get("property.value.new")) not in [dict, list]:
element_uid: str = message["properties"]["uid"]
value = message["properties"]["property.value.new"]
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].settings_property["temperature_report"].temp_report = value
self._logger.debug("Updating temperature report of %s to %s", element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value))
def _total_consumption(self, message: dict[str, Any]) -> None:
"""Update total consumption."""
self._update_consumption(element_uid=message["uid"], consumption="total", value=message["property.value.new"])
def _unknown(self, message: dict[str, Any]) -> None:
"""Ignore unknown messages."""
ignore = (
"devolo.DeviceEvents",
"devolo.PairDevice",
"devolo.SirenBinarySensor",
"devolo.SirenMultiLevelSensor",
"devolo.mprm.gw.GatewayManager",
"devolo.mprm.gw.PortalManager",
"ss",
"mcs",
)
if not message["properties"]["uid"].startswith(ignore):
self._logger.debug(json.dumps(message, indent=4))
def _update_automatic_calibration(self, element_uid: str, calibration_status: bool) -> None:
"""Update automatic calibration setting of a device."""
device_uid = get_device_uid_from_setting_uid(element_uid)
self.devices[device_uid].settings_property["automatic_calibration"].calibration_status = calibration_status
self._logger.debug("Updating value of %s to %s", element_uid, calibration_status)
self._publisher.dispatch(device_uid, (element_uid, calibration_status))
def _update_consumption(self, element_uid: str, consumption: str, value: float) -> None:
"""Update the consumption of a device."""
device_uid = get_device_uid_from_element_uid(element_uid)
setattr(self.devices[device_uid].consumption_property[element_uid], consumption, value)
self._logger.debug("Updating %s consumption of %s to %s", consumption, element_uid, value)
self._publisher.dispatch(device_uid, (element_uid, value, consumption))
def _update_general_device_settings(self, element_uid: str, **kwargs: Any) -> None:
"""Update general device settings."""
device_uid = get_device_uid_from_setting_uid(element_uid)
for key, value in kwargs.items():
setattr(self.devices[device_uid].settings_property["general_device_settings"], key, value)
self._logger.debug("Updating attribute: %s of %s to %s", key, element_uid, value)
self._publisher.dispatch(device_uid, (key, value))
devolo_home_control_api-0.19.0/devolo_home_control_api/py.typed 0000664 0000000 0000000 00000000000 15003713056 0025050 0 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/docs/ 0000775 0000000 0000000 00000000000 15003713056 0017422 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/docs/CHANGELOG.md 0000664 0000000 0000000 00000016407 15003713056 0021243 0 ustar 00root root 0000000 0000000 # Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v0.19.0] - 2025/04/28
### Added
- Set methods of properties now return True on success and False on failure
- Last activities attributes are now timezone aware
### Changed
- Drop support for Python 3.7 and 3.8
- Use SPDX license identifier for project metadata
## [v0.18.3] - 2023/03/14
### Fixed
- Allow local connections even if remote URL is not available
## [v0.18.2] - 2022/05/06
### Fixed
- Handle unreliable session refreshs more graceful
## [v0.18.1] - 2022/04/11
### Fixed
- Incorrect typehints
## [v0.18.0] - 2022/04/11
### Changed
- Drop Support for Python 3.6
### Fixed
- Ignore pending operation messages of HTTP devices
## [v0.17.4] - 2021/07/01
### Fixed
- Getting the version during runtime was broken
- Support websocket-client 1.1.0
## [v0.17.3] - 2021/04/09
### Fixed
- Session refresh with old websocket_client versions was not working
## [v0.17.2] - 2021/04/09
### Added
- Restored support for older websocket_client versions although not recommended to still use them
## [v0.17.1] - 2021/03/25
### Fixed
- Sometimes reconnecting the websocket failed
## [v0.17.0] - 2021/03/12
### Added
- Added devices are now added to the Publisher.
- Multilevel Async Settings are now updated via websocket
### Fixed
- websocket_client 0.58.0 support
## [v0.16.0] - 2020/11/07
### Added
- Removed devices are now announced via Publisher
- The gateway object contains the Z-Wave home_id
### Changed
- **BREAKING**: The siren now contains only a multi level switch to turn on a certain sound. The other properties moved to settings.
- **BREAKING**: Mydevolo is no longer a singleton
### Fixed
- Smart Groups are not ignored in all cases
## [v0.15.1] - 2020/10/13
### Fixed
- operationStatus messages were misinterpreted as new state
- pendingOperations of smart groups were misinterpreted as new values for devices
## [v0.15.0] - 2020/09/29
### Added
- Support for Eurotronic Spirit
- Shared zeroconf instances can now be used
- More units for multi level sensors
- Prevent switching of binary switches that have active remote switching protection
- Send changes of battery level to the subscribers
### Fixed
- Protection mode settings don't lead to websocket errors any more - [#65](https://github.com/2Fake/devolo_home_control_api/issues/65)
## [v0.14.0] - 2020/08/21
### Added
- Settings now update via websocket
- Last activity now updates via websocket
- Support for shutter movement as binary sensor
- Support for shutter overload warning as binary sensor
- Support for pending operations
### Changed
- **BREAKING**: Name and zone (also called room) moved into the general device settings property because they may change.
### Fixed
- Datetime objects are now in local time - [#59](https://github.com/2Fake/devolo_home_control_api/issues/59)
## [v0.13.0] - 2020/08/10
### Added
- Support for devolo Key-Fob
- Support for devolo Wall Switch
### Changed
- **BREAKING**: MildewProperty was reimplemented as BinarySensorProperty and DewpointProperty was reimplemented as MultiLevelSensorProperty.
- **BREAKING**: Devices attributes manID and manufacturerId, prodID and productId as well as prodTypeID and productTypeId were merged
to manufacturer_id, product_id and product_type_id respectively.
### Fixed
- Publisher sends consumption type additionally to element_uid and consumption value. - [#54](https://github.com/2Fake/devolo_home_control_api/issues/54)
## [v0.12.0] - 2020/06/09
### Added
- Support for devolo Siren
### Changed
- **BREAKING**: The naming of some setting properties changed
### Fixed
- Searching for devices now also works, if multiple devices have the same name.
- Websocket messages in unexpected format are handled gracefully.
## [v0.11.0] - 2020/05/26
### Added
- Support for devolo Radiator Thermostat
- Support for devolo Room Thermostat
- Support for devolo Dimmer FM
- Support for devolo Shutter FM
- Support for Danfoss Living Connect Z Radiator Thermostat
- Support for Danfoss Living Connect Z Room Thermostat
- Support for Qubino Flush Dimmer
- Support for Qubino Flush Shutter
- Multilevel sensor properties now have human readable units, if known
## [v0.10.0] - 2020/05/07
### Changed
- **BREAKING**: The method to set the state of a binary switch is now called "set"
### Fixed
- The websocket was closed after 30 minutes
## [v0.9.1] - 2020/04/27
### Fixed
- Fix package structure
## [v0.9.0] - 2020/04/27
### Changed
- **BREAKING**: Gateways being offline now throw a GatewayOfflineError
- Exceptions now have their own files
### Fixed
- Devices were always shown as offline
- Sometimes the websocket was closed before it was established
- Accessing an offline from the start gateway was not handled
## [v0.8.0] - 2020/04/20
### Added
- Support for Fibaro Floor Sensor
- Support for Fibaro Motion Sensor
- Support for Fibaro Smoke Sensor
### Changed
- **BREAKING**: We rethought the usage of Python properties and made some of them to regular methods.
- Z-Wave product information are filled with default values, if we cannot find the device in the database
## [v0.7.0] - 2020/04/16
### Added
- Support for devolo Door/Window Contact
- Support for devolo Flood Sensor
- Support for devolo Humidity Sensor
- Support for devolo Motion Sensor
- Support for devolo Smoke Detector
### Changed
- **BREAKING**: The properties don't have fetch methods any longer. Instead, the attributes are initially updated and should be used directly.
## [v0.6.4] - 2020/04/04
### Fixed
- Handle secure settings correctly
## [v0.6.3] - 2020/03/30
### Fixed
- Handle secure included devices correctly
## [v0.6.2] - 2020/03/19
### Changed
- Add install_requires to setup.py
### Fixed
- Correct websocket log message event on close
## [v0.6.1] - 2020/03/19
### Changed
- Small adjustments when closing the websocket. This is just needed for Home Assistant.
## [v0.6.0] - 2020/03/18
### Added
- with-statement can be used to properly terminate the websocket
### Changed
- The inheritance of objects changed. This should not brake anything.
### Fixed
- Correct state of a device, if device was offline during communication
## [v0.5.0] - 2020/03/03
### Changed
- Speed up device collection
### Fixed
- Catch error if a smart group is switched
## [v0.4.0] - 2020/02/27
### Added
- React to a new/deleted device
### Fixed
- Fix crash if setting_uid is None
## [v0.3.0] - 2020/02/21
### Added
- Support for Fibaro Double Relay Switch
- Lookup for Z-Wave device information
- React on device online state
### Changed
- **BREAKING**: The relation between the objects changed completely.
## [v0.2.0] - 2020/02/05
### Added
- Lookup methods, if UIDs are unknown
- React on maintenance mode of mydevolo
- Handle offline gateways
- Handle offline devices
### Changed
- Publisher now returns a list of element_uid and value
- Rename MprmDeviceError to MprmDeviceCommunicationError
## [v0.1.0] - 2020/01/31
### Added
- Support for devolo Metering Plug v1
- Support for devolo Metering Plug v2
- Support for devolo Switch FM
- Support for Qubino Flush 1 Relay
- Support for Qubino Flush 1D Relay
- Support for Fibaro Wall Plug
devolo_home_control_api-0.19.0/docs/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000006454 15003713056 0022232 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, 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.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers 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, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at guido.schmitz@fedaix.de or m.bong@famabo.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
devolo_home_control_api-0.19.0/docs/CONTRIBUTING.md 0000664 0000000 0000000 00000006530 15003713056 0021657 0 ustar 00root root 0000000 0000000 # Contributing
Thank you very much for considering to contribute to out project. devolo Home Control is a complex system covering a lot of use cases. Nevertheless we could think of use cases it does not cover and did not want to wait for those use cases to be tackled. Other projects have those use cases and having the possibility to integrate devolo Home Control into those project is our goal. To achieve that goal, help is welcome.
The following guidelines will help you to understand how you can help. They will also make transparent to you the time we need to manage and develop this open source project. In return, we will reciprocate that respect in addressing your issues, assessing changes, and helping you finalize your pull requests.
## Table of contents
1. [Reporting an issue](#reporting-an-issue)
1. [Requesting a feature](#requesting-a-feature)
1. [Code style guide](#code-style-guide)
## Reporting an issue
If you are having issues with our module, especially, if you found a bug, we want to know. Please [create an issue](https://github.com/2Fake/devolo_home_control_api/issues). However, you should keep in mind that it might take some time for us to respond to your issue. We will try to get in contact with you within two weeks. Please also respond within two weeks, if we have further inquiries.
## Requesting a feature
While we develop this module, we have our own use cases in mind. Those use cases do not necessarily meet your use cases. Nevertheless we want to hear about them, so you can either [create an issue](https://github.com/2Fake/devolo_home_control_api/issues) or create a merge request. By choosing the first option, you tell us to take care about your use case. That will take time as we will prioritize it with our own needs. By choosing the second option, you can speed up this process. Please read our code style guide.
If you are using a device we do not support, we want to know about it. Please [create an issue](https://github.com/2Fake/devolo_home_control_api/issues) and attach an [HAR file](https://en.wikipedia.org/wiki/HAR_(file_format)) of your device page. Of course we can only support devices devolo Home Control supports itself (at least rudimentary).
## Code style guide
We set up [yapf](https://github.com/google/yapf) to format the code. We basically follow [PEP8](https://www.python.org/dev/peps/pep-0008/), but deviate in some points for - as we think - good reasons. If you have good reasons to stick strictly to PEP8 or even have good reasons to deviate from our deviation, feel free to convince us.
We limit out lines to 127 characters, as that is maximum length still allowing code reviews on GitHub without horizontal scrolling.
We set up [flake8](https://flake8.pycqa.org/en/latest/), [mypy](http://mypy-lang.org/) and [pylint](https://www.pylint.org/) to lint the code. But again we accept good reasons not to follow the findings and even disable complete tests (e.g. E303 and W503).
A pre-commit hook should automatically help you following our guide. Additionally, a GitHub action will assist you.
## Testing
We cover our code with unit tests written in pytest, but we do not push them to hard. We want public methods covered, but we skip nested and trivial methods. Often we also skip constructors. If you want to contribute, please make sure to keep the unit tests green and to deliver new ones, if you extend the functionality.
devolo_home_control_api-0.19.0/example.py 0000664 0000000 0000000 00000001532 15003713056 0020500 0 ustar 00root root 0000000 0000000 import logging
from devolo_home_control_api.homecontrol import HomeControl
from devolo_home_control_api.mydevolo import Mydevolo
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(name)s %(levelname)s: %(message)s")
user = "username"
password = "password"
class Subscriber:
"""Subscriber, that just prints received messages."""
def __init__(self, name):
self.name = name
def update(self, message):
"""Typically your magic goes here."""
print(f'{self.name} got message "{message}"')
mydevolo = Mydevolo()
mydevolo.user = user
mydevolo.password = password
gateway_id = mydevolo.get_gateway_ids()[0]
homecontrol = HomeControl(gateway_id=gateway_id, mydevolo_instance=mydevolo)
for device in homecontrol.devices:
subscriber = Subscriber(device)
homecontrol.publisher.register(device, subscriber)
devolo_home_control_api-0.19.0/pyproject.toml 0000664 0000000 0000000 00000003366 15003713056 0021416 0 ustar 00root root 0000000 0000000 [build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools>=77.0", "setuptools_scm[toml]>=6.2"]
[project]
authors = [
{ name = "Markus Bong", email = "m.bong@famabo.de" },
{ name = "Guido Schmitz", email = "guido.schmitz@fedaix.de"}
]
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
description = "devolo Home Control API in Python"
dependencies = [
"python-dateutil>=2.8.0",
"requests>=2.4.0",
"typing-extensions>=4.1.0;python_version<'3.11'",
"websocket_client>=0.58.0",
"zeroconf>=0.38.0",
]
dynamic = [
"version",
]
license = "GPL-3.0-or-later"
name = "devolo_home_control_api"
readme = "README.md"
requires-python = ">= 3.9"
urls = {changelog = "https://github.com/2Fake/devolo_home_control_api/docs/CHANGELOG.md", homepage = "https://github.com/2Fake/devolo_home_control_api"}
[project.optional-dependencies]
dev = [
"pre-commit",
]
test = [
"pytest",
"pytest-cov",
"pytest-freezer",
"requests-mock",
"syrupy",
]
[tool.black]
line-length = 127
[tool.isort]
combine_as_imports = true
filter_files = true
forced_separate = ["tests"]
line_length = 127
profile = "black"
[tool.mypy]
ignore_missing_imports = true
[tool.ruff]
line-length = 127
lint.ignore = ["ANN401", "B024", "COM812", "D203", "D205", "D212", "EM", "FBT001", "N818", "TCH", "TRY300", "TRY400"]
lint.select = ["ALL"]
target-version = "py37"
[tool.ruff.lint.isort]
combine-as-imports = true
forced-separate = ["tests"]
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["PLR2004", "PT004", "PT011", "S101", "S105"]
[tool.setuptools]
packages = { find = {exclude=["docs*", "tests*"]} }
[tool.setuptools.package-data]
devolo_home_control_api = ["py.typed"]
[tool.setuptools_scm]
devolo_home_control_api-0.19.0/tests/ 0000775 0000000 0000000 00000000000 15003713056 0017634 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/tests/__init__.py 0000664 0000000 0000000 00000004511 15003713056 0021746 0 ustar 00root root 0000000 0000000 """Tests for devolo_home_control_api."""
from __future__ import annotations
import json
import re
from functools import lru_cache
from pathlib import Path
from typing import Any
from unittest.mock import Mock
from syrupy.extensions.amber import AmberSnapshotExtension
from syrupy.location import PyTestLocation
MYDEVOLO_URL = "https://www.mydevolo.com"
HOMECONTROL_URL = "https://homecontrol.mydevolo.com"
GATEWAY_DETAILS_URL = re.compile(
MYDEVOLO_URL + r"/v1/users/[0-9A-F]{8}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{12}/hc/gateways/\d{16}"
)
GATEWAY_FULLURL = re.compile(
MYDEVOLO_URL + r"/v1/users/[0-9A-F]{8}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{12}/hc/gateways/\d{16}/fullURL"
)
GATEWAY_LOCATION_URL = re.compile(
MYDEVOLO_URL
+ r"/v1/users/[0-9A-F]{8}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{12}/hc/gateways/\d{16}/location"
)
GATEWAY_STATUS_URL = re.compile(
MYDEVOLO_URL + r"/v1/users/[0-9A-F]{8}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{12}/hc/gateways/status"
)
MAINTENANCE_URL = f"{MYDEVOLO_URL}/v1/hc/maintenance"
STANDARD_TIMEZONE_URL = re.compile(
MYDEVOLO_URL + r"/v1/users/[0-9A-F]{8}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{4}\b-[0-9A-F]{12}/standardTimezone"
)
UUID_URL = f"{MYDEVOLO_URL}/v1/users/uuid"
ZWAVE_PRODUCTS_URL = re.compile(MYDEVOLO_URL + r"/v1/zwave/products/0x[0-9a-f]{4}/0x[0-9a-f]{4}/0x[0-9a-f]{4}")
def get_fixtures_path() -> Path:
"""Get path of the fixtures."""
return Path(__file__).parent.joinpath("fixtures")
@lru_cache()
def load_fixture(name: str) -> dict[str, Any]:
"""Load a fixture."""
fixture_path = get_fixtures_path().joinpath(f"{name}.json")
with Path.open(fixture_path, encoding="UTF-8") as fixture:
return json.loads(fixture.read())
class Subscriber:
"""Subscriber used during testing."""
def __init__(self, name: str) -> None:
"""Initialize the subscriber."""
self.name = name
self.update = Mock()
class DifferentDirectoryExtension(AmberSnapshotExtension):
"""Extention for Syrupy to change the directory in which snapshots are stored."""
@classmethod
def dirname(cls, *, test_location: PyTestLocation) -> str:
"""Store snapshots in `snapshots`rather than `__snapshots__`."""
return str(Path(test_location.filepath).parent.joinpath("snapshots"))
devolo_home_control_api-0.19.0/tests/conftest.py 0000664 0000000 0000000 00000011706 15003713056 0022040 0 ustar 00root root 0000000 0000000 """Configure tests."""
from socket import inet_aton
from typing import Generator
from unittest.mock import patch
import pytest
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from zeroconf import ServiceInfo
from devolo_home_control_api.homecontrol import HomeControl
from devolo_home_control_api.mydevolo import Mydevolo
from . import (
GATEWAY_DETAILS_URL,
GATEWAY_FULLURL,
GATEWAY_LOCATION_URL,
GATEWAY_STATUS_URL,
HOMECONTROL_URL,
MAINTENANCE_URL,
STANDARD_TIMEZONE_URL,
UUID_URL,
ZWAVE_PRODUCTS_URL,
DifferentDirectoryExtension,
load_fixture,
)
from .mocks import MockServiceBrowser, MockWebSocketApp
@pytest.fixture(autouse=True)
def block_communication(gateway_ip: str) -> Generator[None, None, None]:
"""Block unwanted external communication."""
service_info = ServiceInfo(
type_="_dvl-deviceapi._tcp.local.",
name="dvl-deviceapi._dvl-deviceapi._tcp.local.",
server="devolo-homecontrol.local.",
addresses=[inet_aton(gateway_ip)],
)
with patch("devolo_home_control_api.backend.mprm.ServiceBrowser", MockServiceBrowser), patch(
"devolo_home_control_api.backend.mprm_websocket.websocket.WebSocketApp", MockWebSocketApp
), patch("devolo_home_control_api.backend.mprm.Zeroconf.get_service_info", return_value=service_info):
yield
@pytest.fixture
def disable_external_access() -> Generator[None, None, None]:
"""Temporary forbidd external access."""
gateway_details = load_fixture("mydevolo_gateway_details")
gateway_details["externalAccess"] = False
yield
gateway_details["externalAccess"] = True
@pytest.fixture
def gateway_id() -> str:
"""Get a valid gateway ID."""
details = load_fixture("mydevolo_gateway_details")
return details["gatewayId"]
@pytest.fixture
def gateway_ip() -> str:
"""Get the gateway's IP address."""
return "192.0.2.1"
@pytest.fixture
def local_gateway(
mydevolo: Mydevolo, gateway_id: str, gateway_ip: str, requests_mock: Mocker
) -> Generator[HomeControl, None, None]:
"""Emulate a local gateway connection."""
connection = load_fixture("homecontrol_local_session")
connection["link"] = f"http://{gateway_ip}/dhlp/portal/full/?token=54e8c82fc921ee7e&"
requests_mock.get(f"http://{gateway_ip}/dhlp/port/full")
requests_mock.get(f"http://{gateway_ip}/dhlp/portal/full", json=connection)
requests_mock.get(connection["link"])
requests_mock.post(
f"http://{gateway_ip}/remote/json-rpc",
[
{"json": load_fixture("homecontrol_zones")},
{"json": load_fixture("homecontrol_device_page")},
{"json": load_fixture("homecontrol_devices")},
{"json": load_fixture("homecontrol_device_details")},
],
)
homecontrol = HomeControl(gateway_id, mydevolo)
yield homecontrol
homecontrol.websocket_disconnect("Test finished.")
@pytest.fixture
def maintenance_mode(requests_mock: Mocker) -> None:
"""Simulate mydevolo maitenance mode."""
requests_mock.get(MAINTENANCE_URL, json={"state": "off"})
@pytest.fixture
def remote_gateway(mydevolo: Mydevolo, gateway_id: str, requests_mock: Mocker) -> Generator[HomeControl, None, None]:
"""Emulate a remote gateway connection."""
requests_mock.get(
f"{HOMECONTROL_URL}/dhp/portal/fullLogin/?token=1410000000001_1:24b1516b93adebf7&X-MPRM-LB=1410000000001_1"
)
requests_mock.post(
f"{HOMECONTROL_URL}/remote/json-rpc",
[
{"json": load_fixture("homecontrol_zones")},
{"json": load_fixture("homecontrol_device_page")},
{"json": load_fixture("homecontrol_devices")},
{"json": load_fixture("homecontrol_device_details")},
],
)
with patch("devolo_home_control_api.homecontrol.Mprm.detect_gateway_in_lan"):
homecontrol = HomeControl(gateway_id, mydevolo)
yield homecontrol
homecontrol.websocket_disconnect("Test finished.")
@pytest.fixture
def mydevolo(requests_mock: Mocker) -> Mydevolo:
"""Create a mydevolo object with static test data."""
requests_mock.get(UUID_URL, json=load_fixture("mydevolo_uuid"))
requests_mock.get(GATEWAY_STATUS_URL, json=load_fixture("mydevolo_gateway_status"))
requests_mock.get(GATEWAY_DETAILS_URL, json=load_fixture("mydevolo_gateway_details"))
requests_mock.get(GATEWAY_LOCATION_URL, json=load_fixture("mydevolo_gateway_location"))
requests_mock.get(GATEWAY_FULLURL, json=load_fixture("mydevolo_gateway_fullurl"))
requests_mock.get(ZWAVE_PRODUCTS_URL, json=load_fixture("mydevolo_zwave_products"))
requests_mock.get(MAINTENANCE_URL, json=load_fixture("mydevolo_maintenance"))
requests_mock.get(STANDARD_TIMEZONE_URL, json=load_fixture("mydevolo_standard_timezone"))
return Mydevolo()
@pytest.fixture
def snapshot(snapshot: SnapshotAssertion) -> SnapshotAssertion:
"""Return snapshot assertion fixture with nicer path."""
return snapshot.use_extension(DifferentDirectoryExtension)
devolo_home_control_api-0.19.0/tests/fixtures/ 0000775 0000000 0000000 00000000000 15003713056 0021505 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_binary_sensor.json 0000664 0000000 0000000 00000006664 15003713056 0027702 0 ustar 00root root 0000000 0000000 {
"battery_low": {
"properties": {
"property.name": "batteryLow",
"data": 1,
"property.value.new": 1,
"uid": "hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"battery_level": {
"properties": {
"property.name": "batteryLevel",
"data": 1,
"property.value.new": 5,
"uid": "hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Door",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"led": {
"properties": {
"property.name": "led",
"data": 1,
"property.value.new": false,
"uid": "lis.hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"sensor_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.BinarySensor:hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"temperature_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 10,
"uid": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"temperature_report": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": false,
"uid": "trs.hdm:ZWave:CBC56091/3",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_binary_switch.json 0000664 0000000 0000000 00000011524 15003713056 0027661 0 ustar 00root root 0000000 0000000 {
"current_event": {
"properties": {
"property.name": "currentValue",
"data": 1,
"property.value.new": 20,
"uid": "devolo.Meter:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"gui_enabled": {
"properties": {
"property.name": "guiEnabled",
"data": 1,
"property.value.new": false,
"uid": "devolo.Meter:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"fail": {
"id": 5,
"result": {
"status": 2
}
},
"flash_mode": {
"properties": {
"property.name": "mas",
"data": 1,
"property.value.new": 2,
"uid": "mas.hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Lamp",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"led": {
"properties": {
"property.name": "led",
"data": 1,
"property.value.new": false,
"uid": "lis.hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"protection": {
"properties": {
"property.name": "remoteSwitch",
"data": 1,
"property.value.new": false,
"uid": "ps.hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
},
"switch_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 0,
"uid": "devolo.BinarySwitch:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"total_event": {
"properties": {
"property.name": "totalValue",
"data": 1,
"property.value.new": 29.00,
"uid": "devolo.Meter:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"total_since": {
"properties": {
"property.name": "sinceTime",
"data": 1,
"property.value.new": 1496131874998,
"uid": "devolo.Meter:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 2,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"voltage_event": {
"properties": {
"property.name": "value",
"data": 1,
"property.value.new": 230,
"uid": "devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_blinds.json 0000664 0000000 0000000 00000005377 15003713056 0026300 0 ustar 00root root 0000000 0000000 {
"automatic_calibration": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"status": 2
},
"uid": "acs.hdm:ZWave:CBC56091/9",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"automatic_calibration_old": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "acs.hdm:ZWave:CBC56091/9",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Window",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/9",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"movement_direction": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "bss.hdm:ZWave:CBC56091/9",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"movement_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 22,
"uid": "devolo.Blinds:hdm:ZWave:CBC56091/9",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_device_del.json 0000664 0000000 0000000 00000001144 15003713056 0027074 0 ustar 00root root 0000000 0000000 {
"properties": {
"property.name": "devolo.DevicesPage",
"data": 1,
"property.value.new": [
"hdm:ZWave:CBC56091/2",
"hdm:ZWave:CBC56091/3",
"hdm:ZWave:CBC56091/4",
"hdm:ZWave:CBC56091/5",
"hdm:ZWave:CBC56091/6",
"hdm:ZWave:CBC56091/7",
"hdm:ZWave:CBC56091/8"
],
"uid": "devolo.DevicesPage",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_device_details.json 0000664 0000000 0000000 00000063171 15003713056 0027765 0 ustar 00root root 0000000 0000000 {
"id": 4,
"result": {
"items": [
{
"UID": "gds.hdm:ZWave:CBC56091/2",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "light-bulb",
"name": "Light Bulb"
}
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/2",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "lis.hdm:ZWave:CBC56091/2",
"properties": {
"pendingOperations": null,
"operationStatus": null,
"targetLed": true,
"led": true,
"itemId": null
}
},
{
"UID": "ps.hdm:ZWave:CBC56091/2",
"properties": {
"pendingOperations": null,
"operationStatus": null,
"remoteSwitch": true,
"targetRemoteSwitch": true,
"targetLocalSwitch": true,
"itemId": null,
"localSwitch": true
}
},
{
"UID": "mas.hdm:ZWave:CBC56091/2",
"properties": {
"targetValue": 3,
"value": 3,
"pendingOperations": null,
"operationStatus": null,
"itemId": null
}
},
{
"UID": "devolo.Meter:hdm:ZWave:CBC56091/2",
"properties": {
"pendingOperations": null,
"sensorType": "energy",
"guiEnabled": true,
"sinceTime": 1496131864998,
"currentValue": 10,
"operationStatus": null,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/2",
"totalValue": 28.38,
"itemId": null
}
},
{
"UID": "devolo.BinarySwitch:hdm:ZWave:CBC56091/2",
"properties": {
"targetState": 0,
"state": 0,
"pendingOperations": null,
"guiEnabled": true,
"operationStatus": null,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/2",
"itemId": null,
"switchType": "normal"
}
},
{
"UID": "devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)",
"properties": {
"state": 0,
"sensorType": "alarm",
"guiEnabled": true,
"subType": "overload",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/2",
"itemId": null
}
},
{
"UID": "devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2",
"properties": {
"value": 233.5,
"unit": 0,
"sensorType": "voltage",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/2",
"itemId": null
}
},
{
"UID": "devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2",
"properties": {
"state": 0,
"sensorType": "warning",
"guiEnabled": true,
"subType": "overload",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/2",
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.label.BinarySensorFI",
"com.devolo.fi.widget.WidgetElement",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "gds.hdm:ZWave:CBC56091/3",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_16",
"name": "Window"
},
"itemId": null
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/3",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "trs.hdm:ZWave:CBC56091/3",
"properties": {
"pendingOperations": null,
"tempReport": true,
"operationStatus": null,
"targetTempReport": true,
"itemId": null
}
},
{
"UID": "vfs.hdm:ZWave:CBC56091/3",
"properties": {
"pendingOperations": null,
"feedback": true,
"targetFeedback": true,
"operationStatus": null,
"itemId": null
}
},
{
"UID": "devolo.BinarySensor:hdm:ZWave:CBC56091/3",
"properties": {
"state": 0,
"sensorType": "door",
"guiEnabled": true,
"subType": "",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/3",
"itemId": ""
}
},
{
"UID": "devolo.LastActivity:hdm:ZWave:CBC56091/3",
"properties": {
"lastActivityTime": 1682581124279,
"guiEnabled": true,
"type": 2,
"subType": "",
"widgetUID": "hdm:ZWave:CBC56091/3",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)",
"properties": {
"value": 20,
"unit": 0,
"sensorType": "temperature",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/3",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)",
"properties": {
"value": 61,
"unit": 0,
"sensorType": "light",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/3",
"itemId": null
}
},
{
"UID": "gds.hdm:ZWave:CBC56091/4",
"properties": {
"settings": {
"zoneID": "hz_2",
"eventsEnabled": true,
"icon": "icon_16",
"name": "Shower"
},
"itemId": null
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/4",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "trs.hdm:ZWave:CBC56091/4",
"properties": {
"pendingOperations": null,
"tempReport": true,
"operationStatus": null,
"targetTempReport": true,
"itemId": null
}
},
{
"UID": "vfs.hdm:ZWave:CBC56091/4",
"properties": {
"pendingOperations": null,
"feedback": true,
"targetFeedback": true,
"operationStatus": null,
"itemId": null
}
},
{
"UID": "devolo.HumidityBarZone:hdm:ZWave:CBC56091/4",
"properties": {
"value": 0,
"sensorType": "humidityBarZone",
"guiEnabled": true,
"subType": "",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/4",
"itemId": ""
}
},
{
"UID": "devolo.HumidityBarValue:hdm:ZWave:CBC56091/4",
"properties": {
"value": 0,
"sensorType": "humidityBarPos",
"guiEnabled": true,
"subType": "",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/4",
"itemId": ""
}
},
{
"UID": "devolo.MildewSensor:hdm:ZWave:CBC56091/4",
"properties": {
"state": 1,
"unit": 0,
"sensorType": "",
"subType": "",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/4",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)",
"properties": {
"value": 20,
"unit": 0,
"sensorType": "temperature",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/4",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)",
"properties": {
"value": 61,
"unit": 0,
"sensorType": "light",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/4",
"itemId": null
}
},
{
"UID": "gds.hdm:ZWave:CBC56091/5",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_27",
"name": "Thermostat"
},
"itemId": null
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/5",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "ss.hdm:ZWave:CBC56091/5:0",
"properties": {
"keyNum": 1,
"scene": null,
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5",
"properties": {
"value": 21.3,
"unit": 0,
"sensorType": "temperature",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/5",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)",
"properties": {
"targetValue": 21,
"value": 21,
"min": 4,
"pendingOperations": null,
"max": 28,
"guiEnabled": true,
"operationStatus": null,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/5",
"switchType": "temperature",
"itemId": null
}
},
{
"UID": "devolo.RemoteControl:hdm:ZWave:CBC56091/5",
"properties": {
"keyCount": 1,
"keyPressed": 0,
"guiEnabled": true,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/5",
"itemId": null
}
},
{
"UID": "gds.hdm:ZWave:CBC56091/6",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_34",
"name": "Siren"
},
"itemId": null
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/6",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "bas.hdm:ZWave:CBC56091/6",
"properties": {
"targetValue": true,
"value": true,
"pendingOperations": null,
"operationStatus": null,
"itemId": null
}
},
{
"UID": "mss.hdm:ZWave:CBC56091/6",
"properties": {
"value": 7,
"itemId": null
}
},
{
"UID": "devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6",
"properties": {
"targetValue": 0,
"value": 0,
"min": 0,
"pendingOperations": null,
"max": 9,
"guiEnabled": true,
"operationStatus": null,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/6",
"itemId": null,
"switchType": "tone"
}
},
{
"UID": "devolo.SirenBinarySensor:hdm:ZWave:CBC56091/6",
"properties": {
"state": 1,
"sensorType": "volume",
"guiEnabled": true,
"subType": "",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/6",
"itemId": null
}
},
{
"UID": "devolo.SirenMultiLevelSensor:hdm:ZWave:CBC56091/6",
"properties": {
"value": 7,
"unit": 0,
"sensorType": "tone",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/6",
"itemId": null
}
},
{
"UID": "devolo.LastActivity:hdm:ZWave:CBC56091/6",
"properties": {
"lastActivityTime": 1682664236957,
"guiEnabled": true,
"type": 2,
"subType": "",
"widgetUID": "hdm:ZWave:CBC56091/6",
"itemId": null
}
},
{
"UID": "gds.hdm:ZWave:CBC56091/7",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_47",
"name": "Wall Switch"
},
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.GeneralDeviceSettings",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "cps.hdm:ZWave:CBC56091/7",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.ConfigurationParameterSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "ss.hdm:ZWave:CBC56091/7:0",
"properties": {
"keyNum": 1,
"scene": null,
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.SceneSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "ss.hdm:ZWave:CBC56091/7:1",
"properties": {
"keyNum": 2,
"scene": {
"controlData": "0",
"widgetElementUID": "devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/7",
"operName": ""
},
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.SceneSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "ss.hdm:ZWave:CBC56091/7:2",
"properties": {
"keyNum": 3,
"scene": null,
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.SceneSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "ss.hdm:ZWave:CBC56091/7:3",
"properties": {
"keyNum": 4,
"scene": {
"controlData": "0",
"widgetElementUID": "devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/7",
"operName": ""
},
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.SceneSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "sts.hdm:ZWave:CBC56091/7",
"properties": {
"switchType": 2,
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.setting.SwitchTypeSetting",
"com.devolo.fi.setting.DeviceSetting",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "devolo.RemoteControl:hdm:ZWave:CBC56091/7",
"properties": {
"keyCount": 4,
"keyPressed": 0,
"guiEnabled": true,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/7",
"itemId": null
},
"attributes": {},
"objectClass": [
"com.devolo.fi.control.RemoteControlFI",
"com.devolo.fi.widget.WidgetElement",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"tags": []
},
{
"UID": "gds.hdm:ZWave:CBC56091/8",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_16",
"name": "Motion"
},
"itemId": null
}
},
{
"UID": "cps.hdm:ZWave:CBC56091/8",
"properties": {
"value": -1,
"paramChanged": false,
"itemId": null
}
},
{
"UID": "mss.hdm:ZWave:CBC56091/8",
"properties": {
"value": 7,
"itemId": null
}
},
{
"UID": "trs.hdm:ZWave:CBC56091/8",
"properties": {
"pendingOperations": null,
"tempReport": true,
"operationStatus": null,
"targetTempReport": true,
"itemId": null
}
},
{
"UID": "vfs.hdm:ZWave:CBC56091/8",
"properties": {
"pendingOperations": null,
"feedback": true,
"targetFeedback": true,
"operationStatus": null,
"itemId": null
}
},
{
"UID": "devolo.BinarySensor:hdm:ZWave:CBC56091/8",
"properties": {
"state": 0,
"sensorType": "door",
"guiEnabled": true,
"subType": "",
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/8",
"itemId": ""
}
},
{
"UID": "devolo.LastActivity:hdm:ZWave:CBC56091/8",
"properties": {
"lastActivityTime": 1682581124279,
"guiEnabled": true,
"type": 2,
"subType": "",
"widgetUID": "hdm:ZWave:CBC56091/8",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)",
"properties": {
"value": 20,
"unit": 0,
"sensorType": "temperature",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/8",
"itemId": null
}
},
{
"UID": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)",
"properties": {
"value": 61,
"unit": 0,
"sensorType": "light",
"guiEnabled": true,
"type": 2,
"widgetUID": "hdm:ZWave:CBC56091/8",
"itemId": null
}
},
{
"UID": "acs.hdm:ZWave:CBC56091/9",
"properties": {
"calibrationStatus": 1,
"itemId": null
}
},
{
"UID": "bas.hdm:ZWave:CBC56091/9#i2",
"properties": {
"value": true,
"itemId": null
}
},
{
"UID": "bss.hdm:ZWave:CBC56091/9",
"properties": {
"value": 0,
"itemId": null
}
},
{
"UID": "gds.hdm:ZWave:CBC56091/9",
"properties": {
"settings": {
"zoneID": "hz_1",
"eventsEnabled": true,
"icon": "icon_16",
"name": "Shutter"
},
"itemId": null
}
},
{
"UID": "mss.hdm:ZWave:CBC56091/9",
"properties": {
"value": 20,
"itemId": null
}
},
{
"UID": "devolo.Blinds:hdm:ZWave:CBC56091/9",
"properties": {
"targetValue": 21,
"value": 21,
"min": 0,
"pendingOperations": null,
"max": 100,
"guiEnabled": true,
"operationStatus": null,
"type": 1,
"widgetUID": "hdm:ZWave:CBC56091/9",
"switchType": "",
"itemId": null
}
}
]
},
"jsonrpc": "2.0"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_device_new.json 0000664 0000000 0000000 00000001255 15003713056 0027124 0 ustar 00root root 0000000 0000000 {
"properties": {
"property.name": "devolo.DevicesPage",
"data": 1,
"property.value.new": [
"hdm:ZWave:CBC56091/2",
"hdm:ZWave:CBC56091/3",
"hdm:ZWave:CBC56091/4",
"hdm:ZWave:CBC56091/5",
"hdm:ZWave:CBC56091/6",
"hdm:ZWave:CBC56091/7",
"hdm:ZWave:CBC56091/8",
"hdm:ZWave:CBC56091/9",
"hdm:ZWave:CBC56091/10"
],
"uid": "devolo.DevicesPage",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_device_page.json 0000664 0000000 0000000 00000001141 15003713056 0027241 0 ustar 00root root 0000000 0000000 {
"id": 2,
"result": {
"items": [
{
"properties": {
"deviceUIDs": [
"hdm:ZWave:CBC56091/2",
"hdm:ZWave:CBC56091/3",
"hdm:ZWave:CBC56091/4",
"hdm:ZWave:CBC56091/5",
"hdm:ZWave:CBC56091/6",
"hdm:ZWave:CBC56091/7",
"hdm:ZWave:CBC56091/8",
"hdm:ZWave:CBC56091/9"
]
}
}
]
},
"jsonrpc": "2.0"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_devices.json 0000664 0000000 0000000 00000032354 15003713056 0026442 0 ustar 00root root 0000000 0000000 {
"id": 3,
"result": {
"items": [
{
"UID": "hdm:ZWave:CBC56091/2",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0001",
"statisticsUID": "st.hdm:ZWave:CBC56091/2",
"secureInclusionCode": 0,
"prodID": "0x0011",
"status": 2,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/2",
"cps.hdm:ZWave:CBC56091/2",
"lis.hdm:ZWave:CBC56091/2",
"ps.hdm:ZWave:CBC56091/2",
"mas.hdm:ZWave:CBC56091/2"
],
"manID": "0x0175",
"batteryLevel": -1,
"icon": "light-bulb",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Wall:Plug:Switch:and:Meter",
"itemName": "Light Bulb",
"elementUIDs": [
"devolo.Meter:hdm:ZWave:CBC56091/2",
"devolo.BinarySwitch:hdm:ZWave:CBC56091/2",
"devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)",
"devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2",
"devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/3",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0002",
"statisticsUID": "st.hdm:ZWave:CBC56091/3",
"secureInclusionCode": 0,
"prodID": "0x000e",
"status": 2,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/3",
"cps.hdm:ZWave:CBC56091/3",
"trs.hdm:ZWave:CBC56091/3",
"vfs.hdm:ZWave:CBC56091/3"
],
"manID": "0x0175",
"batteryLevel": 100,
"icon": "icon_16",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Door/Window:Sensor",
"itemName": "Window",
"elementUIDs": [
"devolo.BinarySensor:hdm:ZWave:CBC56091/3",
"devolo.LastActivity:hdm:ZWave:CBC56091/3",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/4",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0002",
"statisticsUID": "st.hdm:ZWave:CBC56091/4",
"secureInclusionCode": 0,
"prodID": "0x000e",
"status": 2,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/3",
"cps.hdm:ZWave:CBC56091/3",
"trs.hdm:ZWave:CBC56091/3",
"vfs.hdm:ZWave:CBC56091/3"
],
"manID": "0x0175",
"batteryLevel": 100,
"icon": "icon_16",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Door/Window:Sensor",
"itemName": "Window",
"elementUIDs": [
"devolo.HumidityBarZone:hdm:ZWave:CBC56091/4",
"devolo.HumidityBarValue:hdm:ZWave:CBC56091/4",
"devolo.MildewSensor:hdm:ZWave:CBC56091/4",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/5",
"objectClass": [
"com.devolo.fi.widget.DeviceWidget",
"com.devolo.fi.widget.BaseWidget",
"com.devolo.fi.widget.Widget",
"com.devolo.fi.async.base.AsyncFunctionalItem",
"com.prosyst.mbs.services.fim.FunctionalItem"
],
"attributes": {},
"properties": {
"isOwn": true,
"operationStatus": null,
"wrongDevicePaired": false,
"assistantsEnabled": 1,
"deviceModelUID": "devolo.model.Room:Thermostat",
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/5",
"cps.hdm:ZWave:CBC56091/5",
"ss.hdm:ZWave:CBC56091/5:0"
],
"isSecurelyIncluded": false,
"statisticsUID": "st.hdm:ZWave:CBC56091/5",
"icon": "icon_27",
"description": null,
"prodID": "0x8010",
"prodTypeID": "0x0003",
"secureInclusionCode": 0,
"itemName": "Thermostat",
"pendingOperations": null,
"zone": "Office",
"elementUIDs": [
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5",
"devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)",
"devolo.RemoteControl:hdm:ZWave:CBC56091/5"
],
"manID": "0x0002",
"zoneId": "hz_1",
"batteryLow": false,
"status": 2,
"batteryLevel": 84
}
},
{
"UID": "hdm:ZWave:CBC56091/6",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0004",
"statisticsUID": null,
"secureInclusionCode": 0,
"prodID": "0x000a",
"status": 2,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/6",
"cps.hdm:ZWave:CBC56091/6",
"bas.hdm:ZWave:CBC56091/6",
"mss.hdm:ZWave:CBC56091/6"
],
"manID": "0x0175",
"batteryLevel": -1,
"icon": "icon_34",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Siren",
"itemName": "Siren",
"elementUIDs": [
"devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6",
"devolo.SirenBinarySensor:hdm:ZWave:CBC56091/6",
"devolo.SirenMultiLevelSensor:hdm:ZWave:CBC56091/6",
"devolo.LastActivity:hdm:ZWave:CBC56091/6"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/7",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0100",
"statisticsUID": null,
"secureInclusionCode": 0,
"prodID": "0x0101",
"status": 1,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/7",
"cps.hdm:ZWave:CBC56091/7",
"ss.hdm:ZWave:CBC56091/7:0",
"ss.hdm:ZWave:CBC56091/7:1",
"ss.hdm:ZWave:CBC56091/7:2",
"ss.hdm:ZWave:CBC56091/7:3",
"sts.hdm:ZWave:CBC56091/7"
],
"manID": "0x0175",
"batteryLevel": 60,
"icon": "icon_47",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Wall:Control",
"itemName": "Wall Switch",
"elementUIDs": [
"devolo.RemoteControl:hdm:ZWave:CBC56091/7"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/8",
"properties": {
"pendingOperations": null,
"batteryLow": false,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Office",
"prodTypeID": "0x0002",
"statisticsUID": "st.hdm:ZWave:CBC56091/8",
"secureInclusionCode": 0,
"prodID": "0x000e",
"status": 2,
"settingUIDs": [
"gds.hdm:ZWave:CBC56091/8",
"cps.hdm:ZWave:CBC56091/8",
"mss.hdm:ZWave:CBC56091/8",
"trs.hdm:ZWave:CBC56091/8",
"vfs.hdm:ZWave:CBC56091/8"
],
"manID": "0x0175",
"batteryLevel": 100,
"icon": "icon_16",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"deviceModelUID": "devolo.model.Motion:Sensor",
"itemName": "Motion",
"elementUIDs": [
"devolo.BinarySensor:hdm:ZWave:CBC56091/8",
"devolo.LastActivity:hdm:ZWave:CBC56091/8",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)",
"devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)"
]
}
},
{
"UID": "hdm:ZWave:CBC56091/9",
"properties": {
"pendingOperations": null,
"batteryLevel": -1,
"batteryLow": null,
"zoneId": "hz_1",
"isSecurelyIncluded": false,
"assistantsEnabled": 1,
"zone": "Shutter",
"prodTypeID": "0x0002",
"statisticsUID": "st.hdm:ZWave:CBC56091/9",
"secureInclusionCode": 0,
"prodID": "0x000e",
"status": 2,
"settingUIDs": [
"acs.hdm:ZWave:CBC56091/9",
"bas.hdm:ZWave:CBC56091/9#i2",
"bss.hdm:ZWave:CBC56091/9",
"gds.hdm:ZWave:CBC56091/9",
"mss.hdm:ZWave:CBC56091/9"
],
"manID": "0x0175",
"icon": "icon_16",
"operationStatus": null,
"description": null,
"isOwn": true,
"wrongDevicePaired": false,
"calibrationStatus": 1,
"i2": true,
"inverted": false,
"deviceModelUID": "devolo.model.Shutter",
"itemName": "Motion",
"elementUIDs": [
"devolo.Blinds:hdm:ZWave:CBC56091/9"
]
}
}
]
},
"jsonrpc": "2.0"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_gateway_status.json 0000664 0000000 0000000 00000000664 15003713056 0030063 0 ustar 00root root 0000000 0000000 {
"properties": {
"property.name": "gatewayAccessible",
"data": 1,
"property.value.new": {
"accessible": 1,
"onlineSync": 1
},
"uid": "devolo.mprm.gw.GatewayAccessibilityFI",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_grouping.json 0000664 0000000 0000000 00000001157 15003713056 0026647 0 ustar 00root root 0000000 0000000 {
"properties": {
"property.name": "zones",
"data": 1,
"property.value.new": [
{
"id": "hz_1",
"name": "Office"
},
{
"id": "hz_2",
"name": "Bathroom"
},
{
"id": "hz_3",
"name": "Kitchen"
}
],
"uid": "devolo.Grouping",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_humidity_sensor.json 0000664 0000000 0000000 00000010555 15003713056 0030244 0 ustar 00root root 0000000 0000000 {
"battery_low": {
"properties": {
"property.name": "batteryLow",
"data": 1,
"property.value.new": 1,
"uid": "hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"battery_level": {
"properties": {
"property.name": "batteryLevel",
"data": 1,
"property.value.new": 5,
"uid": "hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Sink",
"zoneID": "hz_1"
},
"uid": "gds.hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"led": {
"properties": {
"property.name": "led",
"data": 1,
"property.value.new": false,
"uid": "lis.hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"mildew_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 0,
"uid": "devolo.MildewSensor:hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
},
"temperature_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 10,
"uid": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"temperature_report": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": false,
"uid": "trs.hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"value_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.HumidityBarValue:hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"zone_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.HumidityBarZone:hdm:ZWave:CBC56091/4",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133756
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_local_session.json 0000664 0000000 0000000 00000000044 15003713056 0027644 0 ustar 00root root 0000000 0000000 {
"link": "",
"code": 200
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_motion_sensor.json 0000664 0000000 0000000 00000010672 15003713056 0027715 0 ustar 00root root 0000000 0000000 {
"battery_low": {
"properties": {
"property.name": "batteryLow",
"data": 1,
"property.value.new": 1,
"uid": "hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"battery_level": {
"properties": {
"property.name": "batteryLevel",
"data": 1,
"property.value.new": 5,
"uid": "hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"fail": {
"id": 5,
"result": {
"status": 2
}
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Presence",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"led": {
"properties": {
"property.name": "led",
"data": 1,
"property.value.new": false,
"uid": "lis.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"motion_sensitivity": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "mss.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"pending_operation": {
"properties": {
"property.name": "pendingOperations",
"data": 1,
"property.value.new": 1,
"uid": "lis.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"sensor_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.BinarySensor:hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
},
"temperature_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 10,
"uid": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"temperature_report": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": false,
"uid": "trs.hdm:ZWave:CBC56091/8",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_pending_operation.json 0000664 0000000 0000000 00000000472 15003713056 0030520 0 ustar 00root root 0000000 0000000 {
"properties": {
"property.name": "pendingOperations",
"data": 1,
"property.value.new": [],
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_remote_control.json 0000664 0000000 0000000 00000005431 15003713056 0030047 0 ustar 00root root 0000000 0000000 {
"battery_low": {
"properties": {
"property.name": "batteryLow",
"data": 1,
"property.value.new": 1,
"uid": "hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"battery_level": {
"properties": {
"property.name": "batteryLevel",
"data": 1,
"property.value.new": 5,
"uid": "hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"button_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.RemoteControl:hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"count_change": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "sts.hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"fail": {
"id": 5,
"result": {
"status": 2
}
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Remote Control",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/7",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_siren.json 0000664 0000000 0000000 00000004575 15003713056 0026144 0 ustar 00root root 0000000 0000000 {
"fail": {
"id": 5,
"result": {
"status": 2
}
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Troublemaker",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/6",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"muted": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 0,
"uid": "bas.hdm:ZWave:CBC56091/6",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/6",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"switch_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
},
"tone": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "mss.hdm:ZWave:CBC56091/6",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_thermostat.json 0000664 0000000 0000000 00000007307 15003713056 0027212 0 ustar 00root root 0000000 0000000 {
"battery_low": {
"properties": {
"property.name": "batteryLow",
"data": 1,
"property.value.new": 1,
"uid": "hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"battery_level": {
"properties": {
"property.name": "batteryLevel",
"data": 1,
"property.value.new": 5,
"uid": "hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"button_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 0,
"uid": "devolo.RemoteControl:hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 2,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"fail": {
"id": 5,
"result": {
"status": 2
}
},
"general_device_settings": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": {
"eventsEnabled": false,
"icon": "icon_15",
"name": "Roomthermostat",
"zoneID": "hz_2"
},
"uid": "gds.hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"param_changed": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 1,
"uid": "cps.hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"pending_operation": {
"properties": {
"property.name": "pendingOperations",
"data": 1,
"property.value.new": 22,
"uid": "devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"sensor_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 22.1,
"uid": "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5",
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"switch_event": {
"properties": {
"property.name": "targetState",
"data": 1,
"property.value.new": 22,
"uid": "devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
"timestamp": 1682530133755
},
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED"
},
"success": {
"id": 5,
"result": {
"status": 1
}
}
}
devolo_home_control_api-0.19.0/tests/fixtures/homecontrol_zones.json 0000664 0000000 0000000 00000001462 15003713056 0026152 0 ustar 00root root 0000000 0000000 {
"id": 1,
"result": {
"items": [
{
"properties": {
"zones": [
{
"deviceUIDs": [
"hdm:ZWave:CBC56091/2",
"hdm:ZWave:CBC56091/3"
],
"id": "hz_1",
"name": "Office"
},
{
"deviceUIDs": [
"hdm:ZWave:CBC56091/4"
],
"id": "hz_2",
"name": "Bathroom"
}
]
}
}
]
},
"jsonrpc": "2.0"
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_gateway_details.json 0000664 0000000 0000000 00000001575 15003713056 0027474 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/1409301750000598",
"gatewayId": "1409301750000598",
"name": "DVT Lab 1409301750000598",
"role": "user",
"localPasskey": "6b96ad097aa389209f1ceeaed6fe7029",
"externalAccess": true,
"location": "https://www.mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/1409301750000598/location",
"status": "devolo.hc_gateway.status.online",
"state": "devolo.hc_gateway.state.idle",
"type": "M",
"firmwareVersion": "8.0.45_2016-11-17",
"partner": "string",
"hcuPartner": "string",
"users": {
"href": "string"
},
"invitations": {
"href": "string"
},
"scenes": {
"href": "string"
},
"sceneActions": {
"href": "string"
},
"sceneActionJournal": {
"href": "string"
}
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_gateway_fullurl.json 0000664 0000000 0000000 00000000472 15003713056 0027527 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/1409301750000598/fullURL",
"apiSession": "535512AB-165D-11E7-A4E2-000C29D76CCA",
"url": "https://homecontrol.mydevolo.com/dhp/portal/fullLogin/?token=1410000000001_1:24b1516b93adebf7&X-MPRM-LB=1410000000001_1"
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_gateway_location.json 0000664 0000000 0000000 00000000672 15003713056 0027654 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/addresses/535512AB-165D-11E7-A4E2-000C29D76CCA",
"name": "main",
"company": "devolo AG",
"street": "Charlottenburger Allee",
"houseno": "67",
"zipcode": "52068",
"city": "Aachen",
"country": "DE",
"timezone": "Europe/Berlin",
"timezoneCEEJ": "CET",
"latitude": "50.7910659763063",
"longitude": "6.139662697677296"
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_gateway_status.json 0000664 0000000 0000000 00000000567 15003713056 0027372 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/status",
"items": [
{
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/1409301750000598/status",
"gatewayId": "1409301750000598",
"status": "off",
"disabled": true
}
]
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_maintenance.json 0000664 0000000 0000000 00000000114 15003713056 0026574 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/hc/maintenance",
"state": "on"
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_standard_timezone.json 0000664 0000000 0000000 00000000242 15003713056 0030026 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/standardTimezone",
"timezone": "Europe/Berlin",
"timezoneCEEJ": "CET"
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_uuid.json 0000664 0000000 0000000 00000000251 15003713056 0025262 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA",
"uuid": "535512AB-165D-11E7-A4E2-000C29D76CCA",
"temporary_account": false
}
devolo_home_control_api-0.19.0/tests/fixtures/mydevolo_zwave_products.json 0000664 0000000 0000000 00000000713 15003713056 0027376 0 ustar 00root root 0000000 0000000 {
"href": "https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002",
"manufacturerId": "0x0060",
"productTypeId": "0x0001",
"productId": "0x0002",
"name": "Everspring PIR Sensor SP814",
"brand": "Everspring",
"identifier": "SP814-US",
"isZWavePlus": true,
"deviceType": "Door Lock Keypad",
"zwaveVersion": "6.51.07",
"specificDeviceClass": "Secure Keypad Door Lock",
"genericDeviceClass": "Entry Control"
}
devolo_home_control_api-0.19.0/tests/mocks/ 0000775 0000000 0000000 00000000000 15003713056 0020750 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/tests/mocks/__init__.py 0000664 0000000 0000000 00000000311 15003713056 0023054 0 ustar 00root root 0000000 0000000 """Mocks used while testing."""
from .mock_websocket import WEBSOCKET, MockWebSocketApp
from .mock_zeroconf import MockServiceBrowser
__all__ = ["WEBSOCKET", "MockServiceBrowser", "MockWebSocketApp"]
devolo_home_control_api-0.19.0/tests/mocks/mock_websocket.py 0000664 0000000 0000000 00000006572 15003713056 0024333 0 ustar 00root root 0000000 0000000 """Mock methods from the websocket package."""
from __future__ import annotations
from typing import Any, Callable
from websocket import ABNF, WebSocket, WebSocketApp
from websocket._url import parse_url
class Dispatcher:
"""Custom dispatcher that makes the read callback available to the outside."""
def __init__(self) -> None:
"""Initialize the dispatcher."""
self.read_callback: Callable[[], None]
def read(self, _: Any, read_callback: Callable[[], None]) -> None:
"""Store the callback on read activity."""
self.read_callback = read_callback
def signal(self, *args: Any) -> None:
"""Mock signaling."""
def abort(self, *args: Any) -> None:
"""Mock aborting."""
class SockMock:
"""Mock receiving data via websocket."""
def __init__(self) -> None:
"""Initialize the mock."""
self.data: list[bytes] = []
def add_packet(self, data: str) -> None:
"""Add data to the socket's stack."""
frame = ABNF.create_frame(data.encode("UTF-8"), ABNF.OPCODE_TEXT)
self.data.append(frame.format())
DISPATCHER.read_callback()
def close(self) -> None:
"""Mock closing the socket."""
def gettimeout(self) -> None:
"""Mock a timeout value."""
def recv(self, bufsize: int) -> bytes:
"""Mock receiving data."""
if self.data:
datum = self.data.pop(0)
if isinstance(datum, Exception):
raise datum
if len(datum) > bufsize:
self.data.insert(0, datum[bufsize:])
return datum[:bufsize]
return b""
class WebSocketMock(WebSocket):
"""Mock the websocket interface."""
def __init__(self, **kwargs: Any) -> None:
"""Initialize the websocket."""
super().__init__(**kwargs)
self.connected = True
self.sock = SockMock()
self.recv_packet = self.sock.add_packet
def close(self, *_: Any, **__: Any) -> None:
"""Mock closing the socket."""
self.sock.add_packet(str(ABNF.OPCODE_CLOSE))
def error(self) -> None:
"""Mock an error during an open connection."""
self.sock.add_packet("")
def recv_pong(self) -> None:
"""Mock receiving a pong message."""
self.sock.add_packet(str(ABNF.OPCODE_PONG))
class MockWebSocketApp(WebSocketApp):
"""Mock of the websocket app."""
sock: WebSocketMock
def run_forever(self, *_: Any, **__: Any) -> None:
"""Create a forever running websocket connection."""
def check() -> bool:
"""Mock checking the websocket connectivity."""
return True
def read() -> None:
"""Call registered callback method."""
data = self.sock.recv()
if not data:
self._callback(self.on_error, self, Exception)
elif data == str(ABNF.OPCODE_PONG):
self._callback(self.on_pong, data)
elif data == str(ABNF.OPCODE_CLOSE):
self._callback(self.on_close, data, "")
else:
self._callback(self.on_message, data)
self.keep_running = True
self.sock = WEBSOCKET
self._callback(self.on_open)
dispatcher = self.create_dispatcher(None, DISPATCHER, parse_url(self.url)[3])
dispatcher.read(self.sock.sock, read, check)
DISPATCHER = Dispatcher()
WEBSOCKET = WebSocketMock()
devolo_home_control_api-0.19.0/tests/mocks/mock_zeroconf.py 0000664 0000000 0000000 00000000766 15003713056 0024171 0 ustar 00root root 0000000 0000000 """Mock methods from the zeroconf package."""
from __future__ import annotations
from typing import Any, Callable
from unittest.mock import Mock
from zeroconf import ServiceStateChange, Zeroconf
class MockServiceBrowser:
"""Mock of the ServiceBrowser."""
cancel = Mock()
def __init__(self, zeroconf: Zeroconf, type_: str, handlers: list[Callable], **__: Any) -> None:
"""Initialize the service browser."""
handlers[0](zeroconf, type_, type_, ServiceStateChange.Added)
devolo_home_control_api-0.19.0/tests/snapshots/ 0000775 0000000 0000000 00000000000 15003713056 0021656 5 ustar 00root root 0000000 0000000 devolo_home_control_api-0.19.0/tests/snapshots/test_homecontrol.ambr 0000664 0000000 0000000 00000160215 15003713056 0026116 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_setup_local
dict({
'hdm:ZWave:CBC56091/2': Zwave(
assistants_enabled=1,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='alarm',
state=False,
sub_type='overload',
),
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='warning',
state=False,
sub_type='overload',
),
}),
binary_switch_property=dict({
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2': BinarySwitchProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
enabled=True,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
state=False,
),
}),
brand='Everspring',
consumption_property=dict({
'devolo.Meter:hdm:ZWave:CBC56091/2': ConsumptionProperty(
current=10,
current_unit='W',
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.Meter:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total=28.38,
total_since=FakeDatetime(2017, 5, 30, 8, 11, 4, 998000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total_unit='kWh',
),
}),
description=None,
device_model_uid='devolo.model.Wall:Plug:Switch:and:Meter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Meter:hdm:ZWave:CBC56091/2',
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='voltage',
sub_type='',
unit='V',
value=233.5,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/2',
'cps.hdm:ZWave:CBC56091/2',
'lis.hdm:ZWave:CBC56091/2',
'ps.hdm:ZWave:CBC56091/2',
'mas.hdm:ZWave:CBC56091/2',
]),
settings_property=dict({
'flash_mode': SettingsProperty(
element_uid='mas.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=3,
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/2',
events_enabled=True,
icon='light-bulb',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Light Bulb',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='lis.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'protection': SettingsProperty(
element_uid='ps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
local_switching=True,
remote_switching=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/2',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/3': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/3': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/3',
'devolo.LastActivity:hdm:ZWave:CBC56091/3',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/3',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Window',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/3',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/4': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.MildewSensor:hdm:ZWave:CBC56091/4': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MildewSensor:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
state=True,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.HumidityBarZone:hdm:ZWave:CBC56091/4',
'devolo.HumidityBarValue:hdm:ZWave:CBC56091/4',
'devolo.MildewSensor:hdm:ZWave:CBC56091/4',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
humidity_bar_property=dict({
'devolo.HumidityBar:hdm:ZWave:CBC56091/4': HumidityBarProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.HumidityBar:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(2023, 4, 28, 10, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
sub_type='',
value=0,
zone=0,
),
}),
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/4',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shower',
zone='Bathroom',
zone_id='hz_2',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/4',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/5': Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/6': Zwave(
assistants_enabled=1,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Siren',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
'devolo.SirenBinarySensor:hdm:ZWave:CBC56091/6',
'devolo.SirenMultiLevelSensor:hdm:ZWave:CBC56091/6',
'devolo.LastActivity:hdm:ZWave:CBC56091/6',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/6',
element_uid='devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(2023, 4, 28, 6, 43, 56, 957000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=9,
min=0,
switch_type='tone',
unit=None,
value=0,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/6',
'cps.hdm:ZWave:CBC56091/6',
'bas.hdm:ZWave:CBC56091/6',
'mss.hdm:ZWave:CBC56091/6',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/6',
events_enabled=True,
icon='icon_34',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Siren',
zone='Office',
zone_id='hz_1',
),
'muted': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'tone': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
tone=7,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/6',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/7': Zwave(
assistants_enabled=1,
battery_level=60,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Wall:Control',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.RemoteControl:hdm:ZWave:CBC56091/7',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/7': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/7',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/7',
key_count=4,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/7',
'cps.hdm:ZWave:CBC56091/7',
'ss.hdm:ZWave:CBC56091/7:0',
'ss.hdm:ZWave:CBC56091/7:1',
'ss.hdm:ZWave:CBC56091/7:2',
'ss.hdm:ZWave:CBC56091/7:3',
'sts.hdm:ZWave:CBC56091/7',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/7',
events_enabled=True,
icon='icon_47',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Wall Switch',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'switch_type': SettingsProperty(
element_uid='sts.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=4,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=1,
uid='hdm:ZWave:CBC56091/7',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/8': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/8': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Motion:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/8',
'devolo.LastActivity:hdm:ZWave:CBC56091/8',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/8',
'cps.hdm:ZWave:CBC56091/8',
'mss.hdm:ZWave:CBC56091/8',
'trs.hdm:ZWave:CBC56091/8',
'vfs.hdm:ZWave:CBC56091/8',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/8',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Motion',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'motion_sensitivity': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
motion_sensitivity=7,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/8',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/9': Zwave(
assistants_enabled=1,
brand='Everspring',
calibration_status=1,
description=None,
device_model_uid='devolo.model.Shutter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Blinds:hdm:ZWave:CBC56091/9',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
i2=True,
identifier='SP814-US',
inverted=False,
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.Blinds:hdm:ZWave:CBC56091/9': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/9',
element_uid='devolo.Blinds:hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=100,
min=0,
switch_type='',
unit='%',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'acs.hdm:ZWave:CBC56091/9',
'bas.hdm:ZWave:CBC56091/9#i2',
'bss.hdm:ZWave:CBC56091/9',
'gds.hdm:ZWave:CBC56091/9',
'mss.hdm:ZWave:CBC56091/9',
]),
settings_property=dict({
'automatic_calibration': SettingsProperty(
calibration_status=True,
element_uid='acs.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/9',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shutter',
zone='Office',
zone_id='hz_1',
),
'i2': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/9#i2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'movement_direction': SettingsProperty(
element_uid='bss.hdm:ZWave:CBC56091/9',
inverted=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'shutter_duration': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
shutter_duration=20,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/9',
zwave_version='6.51.07',
),
})
# ---
# name: test_setup_remote
dict({
'hdm:ZWave:CBC56091/2': Zwave(
assistants_enabled=1,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='alarm',
state=False,
sub_type='overload',
),
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='warning',
state=False,
sub_type='overload',
),
}),
binary_switch_property=dict({
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2': BinarySwitchProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
enabled=True,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
state=False,
),
}),
brand='Everspring',
consumption_property=dict({
'devolo.Meter:hdm:ZWave:CBC56091/2': ConsumptionProperty(
current=10,
current_unit='W',
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.Meter:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total=28.38,
total_since=FakeDatetime(2017, 5, 30, 8, 11, 4, 998000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total_unit='kWh',
),
}),
description=None,
device_model_uid='devolo.model.Wall:Plug:Switch:and:Meter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Meter:hdm:ZWave:CBC56091/2',
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='voltage',
sub_type='',
unit='V',
value=233.5,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/2',
'cps.hdm:ZWave:CBC56091/2',
'lis.hdm:ZWave:CBC56091/2',
'ps.hdm:ZWave:CBC56091/2',
'mas.hdm:ZWave:CBC56091/2',
]),
settings_property=dict({
'flash_mode': SettingsProperty(
element_uid='mas.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=3,
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/2',
events_enabled=True,
icon='light-bulb',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Light Bulb',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='lis.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'protection': SettingsProperty(
element_uid='ps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
local_switching=True,
remote_switching=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/2',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/3': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/3': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/3',
'devolo.LastActivity:hdm:ZWave:CBC56091/3',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/3',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Window',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/3',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/4': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.MildewSensor:hdm:ZWave:CBC56091/4': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MildewSensor:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
state=True,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.HumidityBarZone:hdm:ZWave:CBC56091/4',
'devolo.HumidityBarValue:hdm:ZWave:CBC56091/4',
'devolo.MildewSensor:hdm:ZWave:CBC56091/4',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
humidity_bar_property=dict({
'devolo.HumidityBar:hdm:ZWave:CBC56091/4': HumidityBarProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.HumidityBar:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(2023, 4, 28, 10, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
sub_type='',
value=0,
zone=0,
),
}),
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/4',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shower',
zone='Bathroom',
zone_id='hz_2',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/4',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/5': Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/6': Zwave(
assistants_enabled=1,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Siren',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
'devolo.SirenBinarySensor:hdm:ZWave:CBC56091/6',
'devolo.SirenMultiLevelSensor:hdm:ZWave:CBC56091/6',
'devolo.LastActivity:hdm:ZWave:CBC56091/6',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/6',
element_uid='devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(2023, 4, 28, 6, 43, 56, 957000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=9,
min=0,
switch_type='tone',
unit=None,
value=0,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/6',
'cps.hdm:ZWave:CBC56091/6',
'bas.hdm:ZWave:CBC56091/6',
'mss.hdm:ZWave:CBC56091/6',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/6',
events_enabled=True,
icon='icon_34',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Siren',
zone='Office',
zone_id='hz_1',
),
'muted': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'tone': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
tone=7,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/6',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/7': Zwave(
assistants_enabled=1,
battery_level=60,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Wall:Control',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.RemoteControl:hdm:ZWave:CBC56091/7',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/7': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/7',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/7',
key_count=4,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/7',
'cps.hdm:ZWave:CBC56091/7',
'ss.hdm:ZWave:CBC56091/7:0',
'ss.hdm:ZWave:CBC56091/7:1',
'ss.hdm:ZWave:CBC56091/7:2',
'ss.hdm:ZWave:CBC56091/7:3',
'sts.hdm:ZWave:CBC56091/7',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/7',
events_enabled=True,
icon='icon_47',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Wall Switch',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'switch_type': SettingsProperty(
element_uid='sts.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=4,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=1,
uid='hdm:ZWave:CBC56091/7',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/8': Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/8': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Motion:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/8',
'devolo.LastActivity:hdm:ZWave:CBC56091/8',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/8',
'cps.hdm:ZWave:CBC56091/8',
'mss.hdm:ZWave:CBC56091/8',
'trs.hdm:ZWave:CBC56091/8',
'vfs.hdm:ZWave:CBC56091/8',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/8',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Motion',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'motion_sensitivity': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
motion_sensitivity=7,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/8',
zwave_version='6.51.07',
),
'hdm:ZWave:CBC56091/9': Zwave(
assistants_enabled=1,
brand='Everspring',
calibration_status=1,
description=None,
device_model_uid='devolo.model.Shutter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Blinds:hdm:ZWave:CBC56091/9',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
i2=True,
identifier='SP814-US',
inverted=False,
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.Blinds:hdm:ZWave:CBC56091/9': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/9',
element_uid='devolo.Blinds:hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=100,
min=0,
switch_type='',
unit='%',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'acs.hdm:ZWave:CBC56091/9',
'bas.hdm:ZWave:CBC56091/9#i2',
'bss.hdm:ZWave:CBC56091/9',
'gds.hdm:ZWave:CBC56091/9',
'mss.hdm:ZWave:CBC56091/9',
]),
settings_property=dict({
'automatic_calibration': SettingsProperty(
calibration_status=True,
element_uid='acs.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/9',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shutter',
zone='Office',
zone_id='hz_1',
),
'i2': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/9#i2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'movement_direction': SettingsProperty(
element_uid='bss.hdm:ZWave:CBC56091/9',
inverted=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'shutter_duration': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
shutter_duration=20,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/9',
zwave_version='6.51.07',
),
})
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_metering_plug.ambr 0000664 0000000 0000000 00000011716 15003713056 0026427 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_getting_devices
list([
Zwave(
assistants_enabled=1,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='alarm',
state=False,
sub_type='overload',
),
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='warning',
state=False,
sub_type='overload',
),
}),
binary_switch_property=dict({
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2': BinarySwitchProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
enabled=True,
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
state=False,
),
}),
brand='Everspring',
consumption_property=dict({
'devolo.Meter:hdm:ZWave:CBC56091/2': ConsumptionProperty(
current=10,
current_unit='W',
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.Meter:hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total=28.38,
total_since=datetime.datetime(2017, 5, 30, 8, 11, 4, 998000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total_unit='kWh',
),
}),
description=None,
device_model_uid='devolo.model.Wall:Plug:Switch:and:Meter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Meter:hdm:ZWave:CBC56091/2',
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='voltage',
sub_type='',
unit='V',
value=233.5,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/2',
'cps.hdm:ZWave:CBC56091/2',
'lis.hdm:ZWave:CBC56091/2',
'ps.hdm:ZWave:CBC56091/2',
'mas.hdm:ZWave:CBC56091/2',
]),
settings_property=dict({
'flash_mode': SettingsProperty(
element_uid='mas.hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=3,
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/2',
events_enabled=True,
icon='light-bulb',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Light Bulb',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='lis.hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'protection': SettingsProperty(
element_uid='ps.hdm:ZWave:CBC56091/2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
local_switching=True,
remote_switching=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/2',
zwave_version='6.51.07',
),
])
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_mydevolo.ambr 0000664 0000000 0000000 00000003715 15003713056 0025424 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_get_gateway
dict({
'externalAccess': True,
'firmwareVersion': '8.0.45_2016-11-17',
'gatewayId': '1409301750000598',
'hcuPartner': 'string',
'href': 'https://mydevolo.com/v1/users/535512AB-165D-11E7-A4E2-000C29D76CCA/hc/gateways/1409301750000598',
'invitations': dict({
'href': 'string',
}),
'localPasskey': '6b96ad097aa389209f1ceeaed6fe7029',
'location': dict({
}),
'name': 'DVT Lab 1409301750000598',
'partner': 'string',
'role': 'user',
'sceneActionJournal': dict({
'href': 'string',
}),
'sceneActions': dict({
'href': 'string',
}),
'scenes': dict({
'href': 'string',
}),
'state': 'devolo.hc_gateway.state.idle',
'status': 'devolo.hc_gateway.status.online',
'type': 'M',
'users': dict({
'href': 'string',
}),
})
# ---
# name: test_get_gateway_ids
list([
'1409301750000598',
])
# ---
# name: test_get_uuid
'535512AB-165D-11E7-A4E2-000C29D76CCA'
# ---
# name: test_get_zwave_products
dict({
'brand': 'Everspring',
'deviceType': 'Door Lock Keypad',
'genericDeviceClass': 'Entry Control',
'href': 'https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
'identifier': 'SP814-US',
'isZWavePlus': True,
'manufacturerId': '0x0060',
'name': 'Everspring PIR Sensor SP814',
'productId': '0x0002',
'productTypeId': '0x0001',
'specificDeviceClass': 'Secure Keypad Door Lock',
'zwaveVersion': '6.51.07',
})
# ---
# name: test_get_zwave_products.1
dict({
'brand': 'Unknown',
'deviceType': 'Unknown',
'genericDeviceClass': 'Unknown',
'href': 'https://www.mydevolo.com/v1/zwave/products/0x1060/0x0001/0x0002',
'identifier': 'Unknown',
'isZWavePlus': False,
'manufacturerId': '0x1060',
'name': 'Unknown',
'productId': '0x0002',
'productTypeId': '0x0001',
'specificDeviceClass': 'Unknown',
'zwaveVersion': 'Unknown',
})
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_shutter.ambr 0000664 0000000 0000000 00000005714 15003713056 0025265 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_getting_devices
list([
Zwave(
assistants_enabled=1,
brand='Everspring',
calibration_status=1,
description=None,
device_model_uid='devolo.model.Shutter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Blinds:hdm:ZWave:CBC56091/9',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
i2=True,
identifier='SP814-US',
inverted=False,
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.Blinds:hdm:ZWave:CBC56091/9': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/9',
element_uid='devolo.Blinds:hdm:ZWave:CBC56091/9',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=100,
min=0,
switch_type='',
unit='%',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'acs.hdm:ZWave:CBC56091/9',
'bas.hdm:ZWave:CBC56091/9#i2',
'bss.hdm:ZWave:CBC56091/9',
'gds.hdm:ZWave:CBC56091/9',
'mss.hdm:ZWave:CBC56091/9',
]),
settings_property=dict({
'automatic_calibration': SettingsProperty(
calibration_status=True,
element_uid='acs.hdm:ZWave:CBC56091/9',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/9',
events_enabled=True,
icon='icon_16',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shutter',
zone='Office',
zone_id='hz_1',
),
'i2': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/9#i2',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'movement_direction': SettingsProperty(
element_uid='bss.hdm:ZWave:CBC56091/9',
inverted=0,
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'shutter_duration': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/9',
last_activity=datetime.datetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
shutter_duration=20,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/9',
zwave_version='6.51.07',
),
])
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_thermostat.ambr 0000664 0000000 0000000 00000104406 15003713056 0025757 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_getting_devices
list([
Zwave(
assistants_enabled=1,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='alarm',
state=False,
sub_type='overload',
),
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='warning',
state=False,
sub_type='overload',
),
}),
binary_switch_property=dict({
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2': BinarySwitchProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
enabled=True,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
state=False,
),
}),
brand='Everspring',
consumption_property=dict({
'devolo.Meter:hdm:ZWave:CBC56091/2': ConsumptionProperty(
current=10,
current_unit='W',
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.Meter:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total=28.38,
total_since=FakeDatetime(2017, 5, 30, 8, 11, 4, 998000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total_unit='kWh',
),
}),
description=None,
device_model_uid='devolo.model.Wall:Plug:Switch:and:Meter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Meter:hdm:ZWave:CBC56091/2',
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='voltage',
sub_type='',
unit='V',
value=233.5,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/2',
'cps.hdm:ZWave:CBC56091/2',
'lis.hdm:ZWave:CBC56091/2',
'ps.hdm:ZWave:CBC56091/2',
'mas.hdm:ZWave:CBC56091/2',
]),
settings_property=dict({
'flash_mode': SettingsProperty(
element_uid='mas.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=3,
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/2',
events_enabled=True,
icon='light-bulb',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Light Bulb',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='lis.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'protection': SettingsProperty(
element_uid='ps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
local_switching=True,
remote_switching=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/2',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/3': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/3',
'devolo.LastActivity:hdm:ZWave:CBC56091/3',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/3',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Window',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/3',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.MildewSensor:hdm:ZWave:CBC56091/4': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MildewSensor:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
state=True,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.HumidityBarZone:hdm:ZWave:CBC56091/4',
'devolo.HumidityBarValue:hdm:ZWave:CBC56091/4',
'devolo.MildewSensor:hdm:ZWave:CBC56091/4',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
humidity_bar_property=dict({
'devolo.HumidityBar:hdm:ZWave:CBC56091/4': HumidityBarProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.HumidityBar:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(2023, 4, 28, 10, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
sub_type='',
value=0,
zone=0,
),
}),
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/4',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shower',
zone='Bathroom',
zone_id='hz_2',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/4',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/8': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Motion:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/8',
'devolo.LastActivity:hdm:ZWave:CBC56091/8',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/8',
'cps.hdm:ZWave:CBC56091/8',
'mss.hdm:ZWave:CBC56091/8',
'trs.hdm:ZWave:CBC56091/8',
'vfs.hdm:ZWave:CBC56091/8',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/8',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Motion',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'motion_sensitivity': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
motion_sensitivity=7,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/8',
zwave_version='6.51.07',
),
])
# ---
# name: test_getting_devices.1
list([
Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Siren',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
'devolo.SirenBinarySensor:hdm:ZWave:CBC56091/6',
'devolo.SirenMultiLevelSensor:hdm:ZWave:CBC56091/6',
'devolo.LastActivity:hdm:ZWave:CBC56091/6',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/6',
element_uid='devolo.SirenMultiLevelSwitch:hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(2023, 4, 28, 6, 43, 56, 957000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=9,
min=0,
switch_type='tone',
unit=None,
value=0,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/6',
'cps.hdm:ZWave:CBC56091/6',
'bas.hdm:ZWave:CBC56091/6',
'mss.hdm:ZWave:CBC56091/6',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/6',
events_enabled=True,
icon='icon_34',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Siren',
zone='Office',
zone_id='hz_1',
),
'muted': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'tone': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/6',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
tone=7,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/6',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
brand='Everspring',
calibration_status=1,
description=None,
device_model_uid='devolo.model.Shutter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Blinds:hdm:ZWave:CBC56091/9',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
i2=True,
identifier='SP814-US',
inverted=False,
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_switch_property=dict({
'devolo.Blinds:hdm:ZWave:CBC56091/9': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/9',
element_uid='devolo.Blinds:hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=100,
min=0,
switch_type='',
unit='%',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'acs.hdm:ZWave:CBC56091/9',
'bas.hdm:ZWave:CBC56091/9#i2',
'bss.hdm:ZWave:CBC56091/9',
'gds.hdm:ZWave:CBC56091/9',
'mss.hdm:ZWave:CBC56091/9',
]),
settings_property=dict({
'automatic_calibration': SettingsProperty(
calibration_status=True,
element_uid='acs.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/9',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shutter',
zone='Office',
zone_id='hz_1',
),
'i2': SettingsProperty(
element_uid='bas.hdm:ZWave:CBC56091/9#i2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=True,
),
'movement_direction': SettingsProperty(
element_uid='bss.hdm:ZWave:CBC56091/9',
inverted=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
'shutter_duration': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/9',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
shutter_duration=20,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/9',
zwave_version='6.51.07',
),
])
# ---
# name: test_getting_devices.2
list([
Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=60,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Wall:Control',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.RemoteControl:hdm:ZWave:CBC56091/7',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/7': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/7',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/7',
key_count=4,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/7',
'cps.hdm:ZWave:CBC56091/7',
'ss.hdm:ZWave:CBC56091/7:0',
'ss.hdm:ZWave:CBC56091/7:1',
'ss.hdm:ZWave:CBC56091/7:2',
'ss.hdm:ZWave:CBC56091/7:3',
'sts.hdm:ZWave:CBC56091/7',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/7',
events_enabled=True,
icon='icon_47',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Wall Switch',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'switch_type': SettingsProperty(
element_uid='sts.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=4,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=1,
uid='hdm:ZWave:CBC56091/7',
zwave_version='6.51.07',
),
])
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_wall_switch.ambr 0000664 0000000 0000000 00000013137 15003713056 0026105 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_getting_devices
list([
Zwave(
assistants_enabled=1,
battery_level=84,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Room:Thermostat',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
'devolo.RemoteControl:hdm:ZWave:CBC56091/5',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=21.3,
),
}),
multi_level_switch_property=dict({
'devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)': MultiLevelSwitchProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.MultiLevelSwitch:hdm:ZWave:CBC56091/5#ThermostatSetpoint(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
max=28,
min=4,
switch_type='temperature',
unit='°C',
value=21,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/5': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/5',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/5',
key_count=1,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/5',
'cps.hdm:ZWave:CBC56091/5',
'ss.hdm:ZWave:CBC56091/5:0',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/5',
events_enabled=True,
icon='icon_27',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Thermostat',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/5',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/5',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=60,
battery_low=False,
brand='Everspring',
description=None,
device_model_uid='devolo.model.Wall:Control',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.RemoteControl:hdm:ZWave:CBC56091/7',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
remote_control_property=dict({
'devolo.RemoteControl:hdm:ZWave:CBC56091/7': RemoteControlProperty(
device_uid='hdm:ZWave:CBC56091/7',
element_uid='devolo.RemoteControl:hdm:ZWave:CBC56091/7',
key_count=4,
key_pressed=0,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
),
}),
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/7',
'cps.hdm:ZWave:CBC56091/7',
'ss.hdm:ZWave:CBC56091/7:0',
'ss.hdm:ZWave:CBC56091/7:1',
'ss.hdm:ZWave:CBC56091/7:2',
'ss.hdm:ZWave:CBC56091/7:3',
'sts.hdm:ZWave:CBC56091/7',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/7',
events_enabled=True,
icon='icon_47',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Wall Switch',
zone='Office',
zone_id='hz_1',
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'switch_type': SettingsProperty(
element_uid='sts.hdm:ZWave:CBC56091/7',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=4,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=1,
uid='hdm:ZWave:CBC56091/7',
zwave_version='6.51.07',
),
])
# ---
devolo_home_control_api-0.19.0/tests/snapshots/test_window_contact.ambr 0000664 0000000 0000000 00000041517 15003713056 0026612 0 ustar 00root root 0000000 0000000 # serializer version: 1
# name: test_getting_devices
list([
Zwave(
assistants_enabled=1,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='alarm',
state=False,
sub_type='overload',
),
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='warning',
state=False,
sub_type='overload',
),
}),
binary_switch_property=dict({
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2': BinarySwitchProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
enabled=True,
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
state=False,
),
}),
brand='Everspring',
consumption_property=dict({
'devolo.Meter:hdm:ZWave:CBC56091/2': ConsumptionProperty(
current=10,
current_unit='W',
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.Meter:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total=28.38,
total_since=FakeDatetime(2017, 5, 30, 8, 11, 4, 998000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
total_unit='kWh',
),
}),
description=None,
device_model_uid='devolo.model.Wall:Plug:Switch:and:Meter',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.Meter:hdm:ZWave:CBC56091/2',
'devolo.BinarySwitch:hdm:ZWave:CBC56091/2',
'devolo.BinarySensor:hdm:ZWave:CBC56091/2#Alarm(0)',
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
'devolo.WarningBinaryFI:hdm:ZWave:CBC56091/2',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/2',
element_uid='devolo.VoltageMultiLevelSensor:hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='voltage',
sub_type='',
unit='V',
value=233.5,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/2',
'cps.hdm:ZWave:CBC56091/2',
'lis.hdm:ZWave:CBC56091/2',
'ps.hdm:ZWave:CBC56091/2',
'mas.hdm:ZWave:CBC56091/2',
]),
settings_property=dict({
'flash_mode': SettingsProperty(
element_uid='mas.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
value=3,
),
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/2',
events_enabled=True,
icon='light-bulb',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Light Bulb',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='lis.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'protection': SettingsProperty(
element_uid='ps.hdm:ZWave:CBC56091/2',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
local_switching=True,
remote_switching=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/2',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/3': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/3',
'devolo.LastActivity:hdm:ZWave:CBC56091/3',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/3',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/3#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/3',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Window',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/3',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/3',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.MildewSensor:hdm:ZWave:CBC56091/4': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MildewSensor:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
state=True,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Door/Window:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.HumidityBarZone:hdm:ZWave:CBC56091/4',
'devolo.HumidityBarValue:hdm:ZWave:CBC56091/4',
'devolo.MildewSensor:hdm:ZWave:CBC56091/4',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
humidity_bar_property=dict({
'devolo.HumidityBar:hdm:ZWave:CBC56091/4': HumidityBarProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.HumidityBar:hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(2023, 4, 28, 10, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='',
sub_type='',
value=0,
zone=0,
),
}),
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/4',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/4#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/3',
'cps.hdm:ZWave:CBC56091/3',
'trs.hdm:ZWave:CBC56091/3',
'vfs.hdm:ZWave:CBC56091/3',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/4',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Shower',
zone='Bathroom',
zone_id='hz_2',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/4',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/4',
zwave_version='6.51.07',
),
Zwave(
assistants_enabled=1,
battery_level=100,
battery_low=False,
binary_sensor_property=dict({
'devolo.BinarySensor:hdm:ZWave:CBC56091/8': BinarySensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.BinarySensor:hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(2023, 4, 27, 7, 38, 44, 279000, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='door',
state=False,
sub_type='',
),
}),
brand='Everspring',
description=None,
device_model_uid='devolo.model.Motion:Sensor',
device_type='Door Lock Keypad',
element_uids=list([
'devolo.BinarySensor:hdm:ZWave:CBC56091/8',
'devolo.LastActivity:hdm:ZWave:CBC56091/8',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
]),
generic_device_class='Entry Control',
href='https://mydevolo.com/v1/zwave/products/0x0060/0x0001/0x0002',
identifier='SP814-US',
is_own=True,
is_securely_included=False,
is_zwave_plus=True,
manufacturer_id='0x0060',
multi_level_sensor_property=dict({
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(1)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='temperature',
sub_type='',
unit='°C',
value=20,
),
'devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)': MultiLevelSensorProperty(
device_uid='hdm:ZWave:CBC56091/8',
element_uid='devolo.MultiLevelSensor:hdm:ZWave:CBC56091/8#MultilevelSensor(3)',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
sensor_type='light',
sub_type='',
unit='%',
value=61,
),
}),
name='Everspring PIR Sensor SP814',
pending_operations=False,
product_id='0x0002',
product_type_id='0x0001',
secure_inclusion_code=0,
setting_uids=list([
'gds.hdm:ZWave:CBC56091/8',
'cps.hdm:ZWave:CBC56091/8',
'mss.hdm:ZWave:CBC56091/8',
'trs.hdm:ZWave:CBC56091/8',
'vfs.hdm:ZWave:CBC56091/8',
]),
settings_property=dict({
'general_device_settings': SettingsProperty(
element_uid='gds.hdm:ZWave:CBC56091/8',
events_enabled=True,
icon='icon_16',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
name='Motion',
zone='Office',
zone_id='hz_1',
),
'led': SettingsProperty(
element_uid='vfs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
led_setting=True,
),
'motion_sensitivity': SettingsProperty(
element_uid='mss.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
motion_sensitivity=7,
),
'param_changed': SettingsProperty(
element_uid='cps.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
param_changed=False,
),
'temperature_report': SettingsProperty(
element_uid='trs.hdm:ZWave:CBC56091/8',
last_activity=FakeDatetime(1970, 1, 1, 1, 0, tzinfo=tzfile('/usr/share/zoneinfo/Europe/Berlin')),
target_temp_report=True,
temp_report=True,
),
}),
specific_device_class='Secure Keypad Door Lock',
status=2,
uid='hdm:ZWave:CBC56091/8',
zwave_version='6.51.07',
),
])
# ---
devolo_home_control_api-0.19.0/tests/test_homecontrol.py 0000664 0000000 0000000 00000021777 15003713056 0023614 0 ustar 00root root 0000000 0000000 """Test the Home Control setup."""
import json
import sys
from http import HTTPStatus
from unittest.mock import patch
import pytest
import requests
from dateutil import tz
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.exceptions import GatewayOfflineError
from devolo_home_control_api.homecontrol import HomeControl
from devolo_home_control_api.mydevolo import Mydevolo
from . import (
GATEWAY_DETAILS_URL,
GATEWAY_FULLURL,
GATEWAY_STATUS_URL,
MAINTENANCE_URL,
STANDARD_TIMEZONE_URL,
UUID_URL,
ZWAVE_PRODUCTS_URL,
Subscriber,
load_fixture,
)
from .mocks import WEBSOCKET
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
@pytest.mark.freeze_time("2023-04-28T08:00:00")
def test_setup_local(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test setting up locally."""
assert local_gateway.gateway.local_connection
assert local_gateway.devices == snapshot
@pytest.mark.usefixtures("maintenance_mode")
def test_setup_local_while_in_maintenance(local_gateway: HomeControl) -> None:
"""Test setting up locally while mydevolo is in maintenance mode."""
assert local_gateway.gateway.local_connection
@pytest.mark.usefixtures("disable_external_access")
def test_setup_local_without_external_access(local_gateway: HomeControl) -> None:
"""Test setting up locally while external access is prohibited."""
assert local_gateway.gateway.local_connection
@pytest.mark.usefixtures("local_gateway")
def test_setup_local_gateway_offline(mydevolo: Mydevolo, gateway_id: str, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test setup failure when gateway is offline."""
requests_mock.get(f"http://{gateway_ip}/dhlp/portal/full", exc=requests.exceptions.ConnectionError)
with pytest.raises(GatewayOfflineError):
HomeControl(gateway_id, mydevolo)
requests_mock.get(f"http://{gateway_ip}/dhlp/portal/full", status_code=HTTPStatus.SERVICE_UNAVAILABLE)
with pytest.raises(GatewayOfflineError):
HomeControl(gateway_id, mydevolo)
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
@pytest.mark.freeze_time("2023-04-28T08:00:00")
def test_setup_remote(remote_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test setting up remotely."""
assert not remote_gateway.gateway.local_connection
assert remote_gateway.devices == snapshot
@pytest.mark.usefixtures("remote_gateway")
def test_setup_remote_gateway_offline(mydevolo: Mydevolo, gateway_id: str, requests_mock: Mocker) -> None:
"""Test setup failure when gateway is offline."""
requests_mock.get(GATEWAY_FULLURL, status_code=HTTPStatus.SERVICE_UNAVAILABLE)
with pytest.raises(GatewayOfflineError):
HomeControl(gateway_id, mydevolo)
@pytest.mark.usefixtures("remote_gateway")
def test_setup_remote_maintenance(mydevolo: Mydevolo, gateway_id: str, requests_mock: Mocker) -> None:
"""Test failing setup remotely while mydevolo is in maintenance mode."""
requests_mock.get(MAINTENANCE_URL, json={"state": "off"})
with pytest.raises(ConnectionError):
HomeControl(gateway_id, mydevolo)
@pytest.mark.usefixtures("remote_gateway", "disable_external_access")
def test_setup_remote_external_access(mydevolo: Mydevolo, gateway_id: str) -> None:
"""Test failing setup remotely while external access is prohibited."""
with pytest.raises(ConnectionError):
HomeControl(gateway_id, mydevolo)
def test_timezone_with_location(local_gateway: HomeControl) -> None:
"""Test getting the gateway's timezone, if a location is set."""
assert local_gateway.gateway.timezone == tz.gettz("Europe/Berlin")
def test_timezone_without_location(gateway_id: str, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test getting the gateway's timezone, if no location is set."""
connection = load_fixture("homecontrol_local_session")
connection["link"] = f"http://{gateway_ip}/dhlp/portal/full/?token=54e8c82fc921ee7e&"
gateway_details = load_fixture("mydevolo_gateway_details")
gateway_details["location"] = None
requests_mock.get(UUID_URL, json=load_fixture("mydevolo_uuid"))
requests_mock.get(GATEWAY_STATUS_URL, json=load_fixture("mydevolo_gateway_status"))
requests_mock.get(GATEWAY_DETAILS_URL, json=gateway_details)
requests_mock.get(GATEWAY_FULLURL, json=load_fixture("mydevolo_gateway_fullurl"))
requests_mock.get(ZWAVE_PRODUCTS_URL, json=load_fixture("mydevolo_zwave_products"))
requests_mock.get(MAINTENANCE_URL, json=load_fixture("mydevolo_maintenance"))
requests_mock.get(STANDARD_TIMEZONE_URL, json=load_fixture("mydevolo_standard_timezone"))
requests_mock.get(f"http://{gateway_ip}/dhlp/port/full")
requests_mock.get(f"http://{gateway_ip}/dhlp/portal/full", json=connection)
requests_mock.get(connection["link"])
requests_mock.post(
f"http://{gateway_ip}/remote/json-rpc",
[
{"json": load_fixture("homecontrol_zones")},
{"json": load_fixture("homecontrol_device_page")},
{"json": load_fixture("homecontrol_devices")},
{"json": load_fixture("homecontrol_device_details")},
],
)
mydevolo = Mydevolo()
with HomeControl(gateway_id, mydevolo) as homecontrol:
assert homecontrol.gateway.timezone == tz.gettz("Europe/Berlin")
def test_update_online_state(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test updating the online state."""
local_gateway.gateway.online = False
local_gateway.gateway.sync = False
local_gateway.gateway.update_state()
assert local_gateway.gateway.online
assert local_gateway.gateway.sync
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", exc=requests.exceptions.ConnectionError)
with pytest.raises(GatewayOfflineError):
local_gateway.refresh_session()
assert not local_gateway.gateway.online
assert not local_gateway.gateway.sync
WEBSOCKET.recv_packet(json.dumps(load_fixture("homecontrol_gateway_status")))
assert local_gateway.gateway.online
assert local_gateway.gateway.sync
def test_update_zones(local_gateway: HomeControl) -> None:
"""Update zones."""
fixture = load_fixture("homecontrol_grouping")
WEBSOCKET.recv_packet(json.dumps(fixture))
assert len(local_gateway.gateway.zones) == len(fixture["properties"]["property.value.new"])
@pytest.mark.usefixtures("local_gateway")
def test_device_added() -> None:
"""Test handling an added device."""
fixture = load_fixture("homecontrol_device_new")
with patch("devolo_home_control_api.homecontrol.HomeControl._inspect_devices") as inspect_devices:
WEBSOCKET.recv_packet(json.dumps(fixture))
inspect_devices.assert_called_once_with([fixture["properties"]["property.value.new"][-1]])
def test_device_deleted(local_gateway: HomeControl) -> None:
"""Test handling an deleted device."""
fixture = load_fixture("homecontrol_device_del")
WEBSOCKET.recv_packet(json.dumps(fixture))
assert len(local_gateway.devices) == len(fixture["properties"]["property.value.new"])
@pytest.mark.parametrize(
"useless", ["devolo.HttpRequest", "devolo.PairDevice", "devolo.RemoveDevice", "devolo.mprm.gw.GatewayManager"]
)
def test_ignore_pending_operations(local_gateway: HomeControl, useless: str) -> None:
"""Test ignoring certail pending operations."""
subscriber = Subscriber(useless)
local_gateway.publisher.register(useless, subscriber)
fixture = load_fixture("homecontrol_pending_operation")
fixture["properties"]["uid"] = useless
WEBSOCKET.recv_packet(json.dumps(fixture))
subscriber.update.assert_not_called()
@pytest.mark.usefixtures("local_gateway")
def test_ignore_unwanted_messages() -> None:
"""Test ignoring updates on unwanted mesages."""
with patch("devolo_home_control_api.publisher.updater.get_device_type_from_element_uid") as device_type:
message = {
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/UNREGISTERED",
"properties": {
"com.prosyst.mbs.services.remote.event.sequence.number": 0,
},
}
WEBSOCKET.recv_packet(json.dumps(message))
device_type.assert_not_called()
message = {
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED",
"properties": {
"property.name": "assistantsConnected",
"com.prosyst.mbs.services.remote.event.sequence.number": 1,
},
}
WEBSOCKET.recv_packet(json.dumps(message))
device_type.assert_not_called()
message = {
"topic": "com/prosyst/mbs/services/fim/FunctionalItemEvent/PROPERTY_CHANGED",
"properties": {
"property.name": "zones",
"uid": "smartGroup",
"com.prosyst.mbs.services.remote.event.sequence.number": 2,
},
}
WEBSOCKET.recv_packet(json.dumps(message))
device_type.assert_not_called()
devolo_home_control_api-0.19.0/tests/test_humidity_sensor.py 0000664 0000000 0000000 00000017414 15003713056 0024501 0 ustar 00root root 0000000 0000000 """Test interacting with a humidity sensor."""
import json
from requests_mock import Mocker
from devolo_home_control_api.homecontrol import HomeControl
from . import HOMECONTROL_URL, Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/4"
FIXTURE = load_fixture("homecontrol_humidity_sensor")
def test_state_change(local_gateway: HomeControl) -> None:
"""Test state change of a humidity sensor."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
humidity_sensor = local_gateway.devices[ELEMENT_ID]
humidity_bar = humidity_sensor.humidity_bar_property[f"devolo.HumidityBar:{ELEMENT_ID}"]
value = humidity_bar.value
zone = humidity_bar.zone
last_activity = humidity_bar.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["value_event"]))
assert value != humidity_bar.value
WEBSOCKET.recv_packet(json.dumps(FIXTURE["zone_event"]))
assert zone != humidity_bar.zone
assert last_activity != humidity_bar.last_activity
assert subscriber.update.call_count == 2
def test_temperature_report(local_gateway: HomeControl) -> None:
"""Test processing a temperature report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
humidity_sensor = local_gateway.devices[ELEMENT_ID]
temperature_sensor = humidity_sensor.multi_level_sensor_property[
f"devolo.MultiLevelSensor:{ELEMENT_ID}#MultilevelSensor(1)"
]
value = temperature_sensor.value
last_activity = temperature_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["temperature_event"]))
assert value != temperature_sensor.value
assert last_activity != temperature_sensor.last_activity
assert subscriber.update.call_count == 1
def test_mildew_report(local_gateway: HomeControl) -> None:
"""Test processing a mildew report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
humidity_sensor = local_gateway.devices[ELEMENT_ID]
mildew_sensor = humidity_sensor.binary_sensor_property[f"devolo.MildewSensor:{ELEMENT_ID}"]
state = mildew_sensor.state
last_activity = mildew_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["mildew_event"]))
assert state != mildew_sensor.state
assert last_activity != mildew_sensor.last_activity
assert subscriber.update.call_count == 1
def test_battery_report(local_gateway: HomeControl) -> None:
"""Test processing a battery report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
humidity_sensor = local_gateway.devices[ELEMENT_ID]
battery_level = humidity_sensor.battery_level
battery_low = humidity_sensor.battery_low
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_low"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_level"]))
assert battery_level != humidity_sensor.battery_level
assert battery_low != humidity_sensor.battery_low
assert subscriber.update.call_count == 2
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
humidity_sensor = local_gateway.devices[ELEMENT_ID]
events_enabled = humidity_sensor.settings_property["general_device_settings"].events_enabled
icon = humidity_sensor.settings_property["general_device_settings"].icon
name = humidity_sensor.settings_property["general_device_settings"].name
zone_id = humidity_sensor.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != humidity_sensor.settings_property["general_device_settings"].events_enabled
assert icon != humidity_sensor.settings_property["general_device_settings"].icon
assert name != humidity_sensor.settings_property["general_device_settings"].name
assert zone_id != humidity_sensor.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
humidity_sensor = remote_gateway.devices[ELEMENT_ID]
events_enabled = humidity_sensor.settings_property["general_device_settings"].events_enabled
icon = humidity_sensor.settings_property["general_device_settings"].icon
name = humidity_sensor.settings_property["general_device_settings"].name
zone_id = humidity_sensor.settings_property["general_device_settings"].zone_id
humidity_sensor.settings_property["general_device_settings"].set(events_enabled=False)
assert humidity_sensor.settings_property["general_device_settings"].events_enabled != events_enabled
assert icon == humidity_sensor.settings_property["general_device_settings"].icon
assert name == humidity_sensor.settings_property["general_device_settings"].name
assert zone_id == humidity_sensor.settings_property["general_device_settings"].zone_id
def test_changing_led_settings(local_gateway: HomeControl) -> None:
"""Test changing led settings."""
fixture = load_fixture("homecontrol_humidity_sensor")
humidity_sensor = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(fixture["led"]))
assert not humidity_sensor.settings_property["led"].led_setting
def test_setting_led(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting led settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
humidity_sensor = remote_gateway.devices[ELEMENT_ID]
led_setting = humidity_sensor.settings_property["led"].led_setting
humidity_sensor.settings_property["led"].set(led_setting=False)
assert humidity_sensor.settings_property["led"].led_setting != led_setting
def test_temperature_report_setting(local_gateway: HomeControl) -> None:
"""Test processing the temperature report setting."""
fixture = load_fixture("homecontrol_humidity_sensor")
humidity_sensor = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(fixture["temperature_report"]))
assert not humidity_sensor.settings_property["temperature_report"].temp_report
def test_setting_temperature_report(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test chaning the temperature report setting."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
humidity_sensor = remote_gateway.devices[ELEMENT_ID]
humidity_sensor.settings_property["temperature_report"].set(temp_report=False)
assert not humidity_sensor.settings_property["temperature_report"].temp_report
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test processing expert settings."""
humidity_sensor = local_gateway.devices[ELEMENT_ID]
param_changed = humidity_sensor.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != humidity_sensor.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
humidity_sensor = local_gateway.devices[ELEMENT_ID]
assert humidity_sensor.get_property("humidity_bar") == list(humidity_sensor.humidity_bar_property.values())
assert humidity_sensor.get_property("multi_level_sensor") == list(humidity_sensor.multi_level_sensor_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
humidity_sensor = local_gateway.devices[ELEMENT_ID]
assert humidity_sensor.is_online()
devolo_home_control_api-0.19.0/tests/test_metering_plug.py 0000664 0000000 0000000 00000031246 15003713056 0024114 0 ustar 00root root 0000000 0000000 """Test interacting with a metering plug."""
import json
import sys
from datetime import datetime, timezone
import pytest
from dateutil import tz
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.exceptions import SwitchingProtected
from devolo_home_control_api.homecontrol import HomeControl
from . import HOMECONTROL_URL, Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/2"
FIXTURE = load_fixture("homecontrol_binary_switch")
TIMEZONE = tz.gettz()
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_getting_devices(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test getting binary switch devices."""
assert local_gateway.binary_switch_devices == snapshot
def test_switching(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test switching a binary switch."""
response = FIXTURE["success"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
metering_plug = local_gateway.devices[ELEMENT_ID]
binary_switch = metering_plug.binary_switch_property[f"devolo.BinarySwitch:{ELEMENT_ID}"]
state = binary_switch.state
last_activity = binary_switch.last_activity
assert binary_switch.set(state=not state)
assert state != binary_switch.state
assert last_activity != binary_switch.last_activity
state = binary_switch.state
last_activity = binary_switch.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["switch_event"]))
assert state != binary_switch.state
assert last_activity != binary_switch.last_activity
assert subscriber.update.call_count == 1
def test_switching_same_state(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test switching a binary switch to the same state."""
response = FIXTURE["fail"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
metering_plug = local_gateway.devices[ELEMENT_ID]
binary_switch = metering_plug.binary_switch_property[f"devolo.BinarySwitch:{ELEMENT_ID}"]
state = binary_switch.state
assert not binary_switch.set(state=not state)
assert state == binary_switch.state
def test_switching_protected(local_gateway: HomeControl) -> None:
"""Test switching a binary switch that is protected."""
metering_plug = local_gateway.devices[ELEMENT_ID]
binary_switch = metering_plug.binary_switch_property[f"devolo.BinarySwitch:{ELEMENT_ID}"]
binary_switch.enabled = False
with pytest.raises(SwitchingProtected):
binary_switch.set(state=True)
def test_consumption(local_gateway: HomeControl) -> None:
"""Test consumption a mitering plug."""
element = next(
item
for item in load_fixture("homecontrol_device_details")["result"]["items"]
if item["UID"] == f"devolo.Meter:{ELEMENT_ID}"
)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
metering_plug = local_gateway.devices[ELEMENT_ID]
consumption = metering_plug.consumption_property[f"devolo.Meter:{ELEMENT_ID}"]
assert consumption.current == element["properties"]["currentValue"]
assert consumption.total == element["properties"]["totalValue"]
assert consumption.total_since == datetime.fromtimestamp(
element["properties"]["sinceTime"] / 1000, tz=timezone.utc
).replace(tzinfo=tz.gettz("Europe/Berlin"))
last_activity = consumption.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["current_event"]))
assert consumption.current == FIXTURE["current_event"]["properties"]["property.value.new"]
assert last_activity != consumption.last_activity
assert subscriber.update.call_count == 1
last_activity = consumption.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["total_event"]))
assert consumption.total == FIXTURE["total_event"]["properties"]["property.value.new"]
assert last_activity != consumption.last_activity
assert subscriber.update.call_count == 2
WEBSOCKET.recv_packet(json.dumps(FIXTURE["total_since"]))
assert consumption.total_since == datetime.fromtimestamp(
FIXTURE["total_since"]["properties"]["property.value.new"] / 1000, tz=timezone.utc
).replace(tzinfo=tz.gettz("Europe/Berlin"))
assert subscriber.update.call_count == 3
def test_voltage(local_gateway: HomeControl) -> None:
"""Test voltage sensor of a metering plug."""
element = next(
item
for item in load_fixture("homecontrol_device_details")["result"]["items"]
if item["UID"] == f"devolo.VoltageMultiLevelSensor:{ELEMENT_ID}"
)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
metering_plug = local_gateway.devices[ELEMENT_ID]
voltage = metering_plug.multi_level_sensor_property[f"devolo.VoltageMultiLevelSensor:{ELEMENT_ID}"]
assert voltage.value == element["properties"]["value"]
last_activity = voltage.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["voltage_event"]))
assert voltage.value == FIXTURE["voltage_event"]["properties"]["property.value.new"]
assert last_activity != voltage.last_activity
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
metering_plug = local_gateway.devices[ELEMENT_ID]
events_enabled = metering_plug.settings_property["general_device_settings"].events_enabled
icon = metering_plug.settings_property["general_device_settings"].icon
name = metering_plug.settings_property["general_device_settings"].name
zone_id = metering_plug.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != metering_plug.settings_property["general_device_settings"].events_enabled
assert icon != metering_plug.settings_property["general_device_settings"].icon
assert name != metering_plug.settings_property["general_device_settings"].name
assert zone_id != metering_plug.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
events_enabled = metering_plug.settings_property["general_device_settings"].events_enabled
icon = metering_plug.settings_property["general_device_settings"].icon
name = metering_plug.settings_property["general_device_settings"].name
zone_id = metering_plug.settings_property["general_device_settings"].zone_id
assert metering_plug.settings_property["general_device_settings"].set(icon="icon_15")
assert metering_plug.settings_property["general_device_settings"].events_enabled == events_enabled
assert icon != metering_plug.settings_property["general_device_settings"].icon
assert name == metering_plug.settings_property["general_device_settings"].name
assert zone_id == metering_plug.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings_same_state(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings to the same state."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
icon = metering_plug.settings_property["general_device_settings"].icon
assert not metering_plug.settings_property["general_device_settings"].set(icon="icon_15")
assert icon == metering_plug.settings_property["general_device_settings"].icon
def test_changing_led_settings(local_gateway: HomeControl) -> None:
"""Test changing led settings."""
metering_plug = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["led"]))
assert not metering_plug.settings_property["led"].led_setting
def test_setting_led(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting led settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
led_setting = metering_plug.settings_property["led"].led_setting
assert metering_plug.settings_property["led"].set(led_setting=False)
assert metering_plug.settings_property["led"].led_setting != led_setting
def test_setting_led_same_state(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting led settings to the same state."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
led_setting = metering_plug.settings_property["led"].led_setting
assert not metering_plug.settings_property["led"].set(led_setting=True)
assert metering_plug.settings_property["led"].led_setting == led_setting
def test_changing_protection_mode(local_gateway: HomeControl) -> None:
"""Test changing the protection mode."""
metering_plug = local_gateway.devices[ELEMENT_ID]
binary_switch = metering_plug.binary_switch_property[f"devolo.BinarySwitch:{ELEMENT_ID}"]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["protection"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["gui_enabled"]))
assert not metering_plug.settings_property["protection"].remote_switching
assert not binary_switch.enabled
FIXTURE["protection"]["properties"]["property.name"] = "localSwitch"
FIXTURE["protection"]["properties"]["com.prosyst.mbs.services.remote.event.sequence.number"] = 2
metering_plug = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["protection"]))
assert not metering_plug.settings_property["protection"].local_switching
def test_protection_mode(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting the protection mode."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
local_switching = metering_plug.settings_property["protection"].local_switching
remote_switching = metering_plug.settings_property["protection"].remote_switching
assert metering_plug.settings_property["protection"].set(local_switching=False)
assert metering_plug.settings_property["protection"].local_switching != local_switching
assert metering_plug.settings_property["protection"].remote_switching == remote_switching
def test_protection_mode_same_state(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting the protection mode to the same state."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
metering_plug = remote_gateway.devices[ELEMENT_ID]
local_switching = metering_plug.settings_property["protection"].local_switching
assert not metering_plug.settings_property["protection"].set(local_switching=False)
assert metering_plug.settings_property["protection"].local_switching == local_switching
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
metering_plug = local_gateway.devices[ELEMENT_ID]
param_changed = metering_plug.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != metering_plug.settings_property["param_changed"].param_changed
def test_changing_flash_mode_settings(local_gateway: HomeControl) -> None:
"""Test changing flash mode settings."""
metering_plug = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["flash_mode"]))
assert metering_plug.settings_property["flash_mode"].value == 2
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
metering_plug = local_gateway.devices[ELEMENT_ID]
assert metering_plug.get_property("binary_switch") == list(metering_plug.binary_switch_property.values())
assert metering_plug.get_property("consumption") == list(metering_plug.consumption_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
metering_plug = local_gateway.devices[ELEMENT_ID]
assert metering_plug.is_online()
devolo_home_control_api-0.19.0/tests/test_motion_sensor.py 0000664 0000000 0000000 00000022323 15003713056 0024145 0 ustar 00root root 0000000 0000000 """Test interacting with a window contact."""
import json
from requests_mock import Mocker
from devolo_home_control_api.homecontrol import HomeControl
from . import HOMECONTROL_URL, Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/8"
FIXTURE = load_fixture("homecontrol_motion_sensor")
def test_state_change(local_gateway: HomeControl) -> None:
"""Test state change of a binary sensor."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
motion_sensor = local_gateway.devices[ELEMENT_ID]
binary_sensor = motion_sensor.binary_sensor_property[f"devolo.BinarySensor:{ELEMENT_ID}"]
state = binary_sensor.state
last_activity = binary_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["sensor_event"]))
assert state != binary_sensor.state
assert last_activity != binary_sensor.last_activity
assert subscriber.update.call_count == 1
def test_temperature_report(local_gateway: HomeControl) -> None:
"""Test processing a temperature report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
motion_sensor = local_gateway.devices[ELEMENT_ID]
temperature_sensor = motion_sensor.multi_level_sensor_property[f"devolo.MultiLevelSensor:{ELEMENT_ID}#MultilevelSensor(1)"]
value = temperature_sensor.value
last_activity = temperature_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["temperature_event"]))
assert value != temperature_sensor.value
assert last_activity != temperature_sensor.last_activity
assert subscriber.update.call_count == 1
def test_battery_report(local_gateway: HomeControl) -> None:
"""Test processing a battery report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
motion_sensor = local_gateway.devices[ELEMENT_ID]
battery_level = motion_sensor.battery_level
battery_low = motion_sensor.battery_low
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_low"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_level"]))
assert battery_level != motion_sensor.battery_level
assert battery_low != motion_sensor.battery_low
assert subscriber.update.call_count == 2
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
events_enabled = motion_sensor.settings_property["general_device_settings"].events_enabled
icon = motion_sensor.settings_property["general_device_settings"].icon
name = motion_sensor.settings_property["general_device_settings"].name
zone_id = motion_sensor.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != motion_sensor.settings_property["general_device_settings"].events_enabled
assert icon != motion_sensor.settings_property["general_device_settings"].icon
assert name != motion_sensor.settings_property["general_device_settings"].name
assert zone_id != motion_sensor.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
events_enabled = motion_sensor.settings_property["general_device_settings"].events_enabled
icon = motion_sensor.settings_property["general_device_settings"].icon
name = motion_sensor.settings_property["general_device_settings"].name
zone_id = motion_sensor.settings_property["general_device_settings"].zone_id
motion_sensor.settings_property["general_device_settings"].set(zone_id="hz_2")
assert motion_sensor.settings_property["general_device_settings"].events_enabled == events_enabled
assert icon == motion_sensor.settings_property["general_device_settings"].icon
assert name == motion_sensor.settings_property["general_device_settings"].name
assert zone_id != motion_sensor.settings_property["general_device_settings"].zone_id
def test_changing_led_settings(local_gateway: HomeControl) -> None:
"""Test changing led settings."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["led"]))
assert not motion_sensor.settings_property["led"].led_setting
def test_setting_led(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting led settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
led_setting = motion_sensor.settings_property["led"].led_setting
motion_sensor.settings_property["led"].set(led_setting=False)
assert motion_sensor.settings_property["led"].led_setting != led_setting
def test_pending_operation(local_gateway: HomeControl) -> None:
"""Test processing of a pending operation."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["pending_operation"]))
assert motion_sensor.pending_operations
def test_changing_motion_sensitivity(local_gateway: HomeControl) -> None:
"""Test changing motion sensitivity settings."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
motion_sensitivity = motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["motion_sensitivity"]))
assert motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity != motion_sensitivity
def test_setting_motion_sensitivity(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting motion sensitivity settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
motion_sensitivity = motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity
assert motion_sensor.settings_property["motion_sensitivity"].set(9)
assert motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity != motion_sensitivity
def test_setting_motion_sensitivity_same_value(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting motion sensitivity settings using the same value."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
motion_sensitivity = motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity
assert not motion_sensor.settings_property["motion_sensitivity"].set(9)
assert motion_sensor.settings_property["motion_sensitivity"].motion_sensitivity == motion_sensitivity
def test_temperature_report_setting(local_gateway: HomeControl) -> None:
"""Test processing the temperature report setting."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["temperature_report"]))
assert not motion_sensor.settings_property["temperature_report"].temp_report
def test_setting_temperature_report(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test chaning the temperature report setting."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
assert motion_sensor.settings_property["temperature_report"].set(temp_report=False)
assert not motion_sensor.settings_property["temperature_report"].temp_report
def test_setting_temperature_report_same_value(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test chaning the temperature report setting using the same value."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
motion_sensor = remote_gateway.devices[ELEMENT_ID]
assert not motion_sensor.settings_property["temperature_report"].set(temp_report=False)
assert motion_sensor.settings_property["temperature_report"].temp_report
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
param_changed = motion_sensor.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != motion_sensor.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
assert motion_sensor.get_property("binary_sensor") == list(motion_sensor.binary_sensor_property.values())
assert motion_sensor.get_property("multi_level_sensor") == list(motion_sensor.multi_level_sensor_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
motion_sensor = local_gateway.devices[ELEMENT_ID]
assert motion_sensor.is_online()
devolo_home_control_api-0.19.0/tests/test_mydevolo.py 0000664 0000000 0000000 00000006376 15003713056 0023117 0 ustar 00root root 0000000 0000000 """Test mydevolo."""
import sys
from http import HTTPStatus
import pytest
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.exceptions import GatewayOfflineError, WrongUrlError
from devolo_home_control_api.mydevolo import Mydevolo
from . import GATEWAY_DETAILS_URL, GATEWAY_FULLURL, GATEWAY_STATUS_URL, MAINTENANCE_URL, UUID_URL, ZWAVE_PRODUCTS_URL
def test_cache_clear(mydevolo: Mydevolo) -> None:
"""Test clearing the UUID cache on new username or password."""
mydevolo.user = "test@test.com"
mydevolo.password = "secret"
assert mydevolo.user == "test@test.com"
assert mydevolo.password == "secret"
assert mydevolo.uuid.cache_info().currsize == 0
def test_credential_verification(mydevolo: Mydevolo, requests_mock: Mocker) -> None:
"""Test credentil verification."""
mydevolo.user = "test@test.com"
mydevolo.password = "secret"
assert mydevolo.credentials_valid()
requests_mock.get(UUID_URL, status_code=HTTPStatus.FORBIDDEN)
mydevolo.uuid.cache_clear()
assert not mydevolo.credentials_valid()
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_get_gateway_ids(mydevolo: Mydevolo, requests_mock: Mocker, snapshot: SnapshotAssertion) -> None:
"""Test getting gateway serial numbers."""
gateway_ids = mydevolo.get_gateway_ids()
assert gateway_ids == snapshot
requests_mock.get(GATEWAY_STATUS_URL, json={"items": []})
with pytest.raises(IndexError):
mydevolo.get_gateway_ids()
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_get_gateway(mydevolo: Mydevolo, gateway_id: str, requests_mock: Mocker, snapshot: SnapshotAssertion) -> None:
"""Test getting gateway details."""
gateway = mydevolo.get_gateway(gateway_id)
assert gateway == snapshot
requests_mock.get(GATEWAY_DETAILS_URL, status_code=HTTPStatus.NOT_FOUND)
with pytest.raises(WrongUrlError):
mydevolo.get_gateway(gateway_id)
requests_mock.get(GATEWAY_FULLURL, status_code=HTTPStatus.SERVICE_UNAVAILABLE)
with pytest.raises(GatewayOfflineError):
mydevolo.get_full_url(gateway_id)
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_get_zwave_products(mydevolo: Mydevolo, requests_mock: Mocker, snapshot: SnapshotAssertion) -> None:
"""Test getting zwave product information."""
details = mydevolo.get_zwave_products("0x0060", "0x0001", "0x0002")
assert details == snapshot
requests_mock.get(ZWAVE_PRODUCTS_URL, status_code=HTTPStatus.NOT_FOUND)
details = mydevolo.get_zwave_products("0x1060", "0x0001", "0x0002")
assert details == snapshot
def test_maintenance(mydevolo: Mydevolo, requests_mock: Mocker) -> None:
"""Test maintenance mode state."""
assert not mydevolo.maintenance()
requests_mock.get(MAINTENANCE_URL, json={"state": "off"})
assert mydevolo.maintenance()
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_get_uuid(mydevolo: Mydevolo, snapshot: SnapshotAssertion) -> None:
"""Test getting user's UUID."""
uuid = mydevolo.uuid()
assert uuid == snapshot
devolo_home_control_api-0.19.0/tests/test_publisher.py 0000664 0000000 0000000 00000002137 15003713056 0023245 0 ustar 00root root 0000000 0000000 """Test the publisher."""
from devolo_home_control_api.publisher import Publisher
from . import Subscriber
def test_adding() -> None:
"""Test adding an event."""
publisher = Publisher(events=[])
publisher.add_event("test_event")
subscriber = Subscriber("test_subscriber")
publisher.register("test_event", subscriber)
publisher.dispatch("test_event", ())
subscriber.update.assert_called_once()
def test_deleting() -> None:
"""Test deleting an event."""
publisher = Publisher(events=["test_event"])
subscriber = Subscriber("test_subscriber")
publisher.register("test_event", subscriber)
publisher.delete_event("test_event")
publisher.dispatch("test_event", ())
subscriber.update.assert_not_called()
def test_unregister() -> None:
"""Test unregistering from the publisher."""
publisher = Publisher(events=["test_event"])
subscriber = Subscriber("test_subscriber")
publisher.register("test_event", subscriber)
publisher.unregister("test_event", subscriber)
publisher.dispatch("test_event", ())
subscriber.update.assert_not_called()
devolo_home_control_api-0.19.0/tests/test_shutter.py 0000664 0000000 0000000 00000011121 15003713056 0022737 0 ustar 00root root 0000000 0000000 """Test interacting with a shutter."""
import json
import sys
import pytest
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.homecontrol import HomeControl
from . import HOMECONTROL_URL, Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/9"
FIXTURE = load_fixture("homecontrol_blinds")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
def test_getting_devices(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test getting shutter devices."""
assert local_gateway.blinds_devices == snapshot
def test_state_change(local_gateway: HomeControl) -> None:
"""Test state change of a shutter."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
shutter = local_gateway.devices[ELEMENT_ID]
state = shutter.multi_level_switch_property[f"devolo.Blinds:{ELEMENT_ID}"]
value = state.value
last_activity = state.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["movement_event"]))
assert value != state.value
assert last_activity != state.last_activity
assert subscriber.update.call_count == 1
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
shutter = local_gateway.devices[ELEMENT_ID]
events_enabled = shutter.settings_property["general_device_settings"].events_enabled
icon = shutter.settings_property["general_device_settings"].icon
name = shutter.settings_property["general_device_settings"].name
zone_id = shutter.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != shutter.settings_property["general_device_settings"].events_enabled
assert icon != shutter.settings_property["general_device_settings"].icon
assert name != shutter.settings_property["general_device_settings"].name
assert zone_id != shutter.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
shutter = remote_gateway.devices[ELEMENT_ID]
events_enabled = shutter.settings_property["general_device_settings"].events_enabled
icon = shutter.settings_property["general_device_settings"].icon
name = shutter.settings_property["general_device_settings"].name
zone_id = shutter.settings_property["general_device_settings"].zone_id
shutter.settings_property["general_device_settings"].set(zone_id="hz_2", icon="icon_17")
assert shutter.settings_property["general_device_settings"].events_enabled == events_enabled
assert icon != shutter.settings_property["general_device_settings"].icon
assert name == shutter.settings_property["general_device_settings"].name
assert zone_id != shutter.settings_property["general_device_settings"].zone_id
def test_setting_automatic_calibration(remote_gateway: HomeControl) -> None:
"""Test setting automatic calibration settings."""
shutter = remote_gateway.devices[ELEMENT_ID]
automatic_calibration = shutter.settings_property["automatic_calibration"].calibration_status
WEBSOCKET.recv_packet(json.dumps(FIXTURE["automatic_calibration"]))
assert automatic_calibration != shutter.settings_property["automatic_calibration"].calibration_status
WEBSOCKET.recv_packet(json.dumps(FIXTURE["automatic_calibration_old"]))
assert automatic_calibration == shutter.settings_property["automatic_calibration"].calibration_status
def test_setting_movement_direction(remote_gateway: HomeControl) -> None:
"""Test setting the movement direction."""
shutter = remote_gateway.devices[ELEMENT_ID]
inverted = shutter.settings_property["movement_direction"].inverted
WEBSOCKET.recv_packet(json.dumps(FIXTURE["movement_direction"]))
assert inverted != shutter.settings_property["movement_direction"].inverted
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
shutter = local_gateway.devices[ELEMENT_ID]
assert shutter.get_property("multi_level_switch") == list(shutter.multi_level_switch_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
shutter = local_gateway.devices[ELEMENT_ID]
assert shutter.is_online()
devolo_home_control_api-0.19.0/tests/test_siren.py 0000664 0000000 0000000 00000012652 15003713056 0022373 0 ustar 00root root 0000000 0000000 """Test interacting with a siren."""
import json
from requests_mock import Mocker
from devolo_home_control_api.homecontrol import HomeControl
from . import HOMECONTROL_URL, Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/6"
FIXTURE = load_fixture("homecontrol_siren")
def test_state_change(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test state change of a siren."""
response = FIXTURE["success"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
siren = local_gateway.devices[ELEMENT_ID]
multi_level_switch = siren.multi_level_switch_property[f"devolo.SirenMultiLevelSwitch:{ELEMENT_ID}"]
value = multi_level_switch.value
last_activity = multi_level_switch.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["switch_event"]))
assert value != multi_level_switch.value
assert last_activity != multi_level_switch.last_activity
assert subscriber.update.call_count == 1
multi_level_switch.set(value + 1)
assert value + 1 == multi_level_switch.value
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
siren = local_gateway.devices[ELEMENT_ID]
events_enabled = siren.settings_property["general_device_settings"].events_enabled
icon = siren.settings_property["general_device_settings"].icon
name = siren.settings_property["general_device_settings"].name
zone_id = siren.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != siren.settings_property["general_device_settings"].events_enabled
assert icon != siren.settings_property["general_device_settings"].icon
assert name != siren.settings_property["general_device_settings"].name
assert zone_id != siren.settings_property["general_device_settings"].zone_id
def test_setting_general_device_settings(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test setting general device settings."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
siren = remote_gateway.devices[ELEMENT_ID]
events_enabled = siren.settings_property["general_device_settings"].events_enabled
icon = siren.settings_property["general_device_settings"].icon
name = siren.settings_property["general_device_settings"].name
zone_id = siren.settings_property["general_device_settings"].zone_id
siren.settings_property["general_device_settings"].set(events_enabled=False, icon="icon_20")
assert siren.settings_property["general_device_settings"].events_enabled != events_enabled
assert icon != siren.settings_property["general_device_settings"].icon
assert name == siren.settings_property["general_device_settings"].name
assert zone_id == siren.settings_property["general_device_settings"].zone_id
def test_changing_mute_state(local_gateway: HomeControl) -> None:
"""Test muting the siren."""
siren = local_gateway.devices[ELEMENT_ID]
mute = siren.settings_property["muted"].value
WEBSOCKET.recv_packet(json.dumps(FIXTURE["muted"]))
assert mute != siren.settings_property["muted"].value
def test_setting_mute_state(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test muting the siren."""
response = FIXTURE["success"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
siren = remote_gateway.devices[ELEMENT_ID]
mute = siren.settings_property["muted"].value
assert siren.settings_property["muted"].set(value=False)
assert mute != siren.settings_property["muted"].value
def test_setting_mute_state_same_value(remote_gateway: HomeControl, requests_mock: Mocker) -> None:
"""Test muting the siren using the same value."""
response = FIXTURE["fail"]
requests_mock.post(f"{HOMECONTROL_URL}/remote/json-rpc", json=response)
siren = remote_gateway.devices[ELEMENT_ID]
mute = siren.settings_property["muted"].value
assert not siren.settings_property["muted"].set(value=False)
assert mute == siren.settings_property["muted"].value
def test_changing_default_tone(local_gateway: HomeControl) -> None:
"""Test changing the default tone."""
siren = local_gateway.devices[ELEMENT_ID]
tone = siren.settings_property["tone"].tone
WEBSOCKET.recv_packet(json.dumps(FIXTURE["tone"]))
assert tone != siren.settings_property["tone"].tone
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
siren = local_gateway.devices[ELEMENT_ID]
param_changed = siren.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != siren.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
siren = local_gateway.devices[ELEMENT_ID]
assert siren.get_property("multi_level_switch") == list(siren.multi_level_switch_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
siren = local_gateway.devices[ELEMENT_ID]
assert siren.is_online()
devolo_home_control_api-0.19.0/tests/test_thermostat.py 0000664 0000000 0000000 00000015173 15003713056 0023446 0 ustar 00root root 0000000 0000000 """Test interacting with a room thermostat."""
import json
import sys
import pytest
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.homecontrol import HomeControl
from . import Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/5"
FIXTURE = load_fixture("homecontrol_thermostat")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
@pytest.mark.freeze_time("2023-04-28T08:00:00")
def test_getting_devices(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test getting multi level sensor and switch devices."""
assert local_gateway.multi_level_sensor_devices == snapshot
assert local_gateway.multi_level_switch_devices == snapshot
assert local_gateway.remote_control_devices == snapshot
def test_state_change(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test state change of a room thermostat."""
response = FIXTURE["success"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
thermostat = local_gateway.devices[ELEMENT_ID]
sensor = thermostat.multi_level_sensor_property[f"devolo.MultiLevelSensor:{ELEMENT_ID}"]
value = sensor.value
last_activity = sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["sensor_event"]))
assert value != sensor.value
assert last_activity != sensor.last_activity
assert subscriber.update.call_count == 1
switch = thermostat.multi_level_switch_property[f"devolo.MultiLevelSwitch:{ELEMENT_ID}#ThermostatSetpoint(1)"]
value = switch.value
assert switch.set(value - 1)
assert value - 1 == switch.value
value = switch.value
last_activity = switch.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["switch_event"]))
assert value != switch.value
assert last_activity != switch.last_activity
assert subscriber.update.call_count == 2
FIXTURE["success"]["id"] += 1
button = thermostat.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
assert button.set(1)
assert button.key_pressed == 1
WEBSOCKET.recv_packet(json.dumps(FIXTURE["button_event"]))
assert button.key_pressed == 0
assert last_activity != switch.last_activity
assert subscriber.update.call_count == 3
def test_switching_same_state(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test switching a room thermostat to the same state."""
response = FIXTURE["fail"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
thermostat = local_gateway.devices[ELEMENT_ID]
switch = thermostat.multi_level_switch_property[f"devolo.MultiLevelSwitch:{ELEMENT_ID}#ThermostatSetpoint(1)"]
value = switch.value
assert not switch.set(value=value)
assert value == switch.value
def test_invalid_set(local_gateway: HomeControl) -> None:
"""Test setting an invalid value."""
thermostat = local_gateway.devices[ELEMENT_ID]
switch = thermostat.multi_level_switch_property[f"devolo.MultiLevelSwitch:{ELEMENT_ID}#ThermostatSetpoint(1)"]
with pytest.raises(ValueError):
switch.set(switch.max + 1)
button = thermostat.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
with pytest.raises(ValueError):
button.set(button.key_count + 1)
def test_pending_operation(local_gateway: HomeControl) -> None:
"""Test processing of a pending operation."""
thermostat = local_gateway.devices[ELEMENT_ID]
sensor = thermostat.multi_level_sensor_property[f"devolo.MultiLevelSensor:{ELEMENT_ID}"]
value = sensor.value
last_activity = sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["pending_operation"]))
assert value == sensor.value
assert last_activity == sensor.last_activity
assert thermostat.pending_operations
def test_battery_report(local_gateway: HomeControl) -> None:
"""Test processing a battery report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
thermostat = local_gateway.devices[ELEMENT_ID]
battery_level = thermostat.battery_level
battery_low = thermostat.battery_low
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_low"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_level"]))
assert battery_level != thermostat.battery_level
assert battery_low != thermostat.battery_low
assert subscriber.update.call_count == 2
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
thermostat = local_gateway.devices[ELEMENT_ID]
events_enabled = thermostat.settings_property["general_device_settings"].events_enabled
icon = thermostat.settings_property["general_device_settings"].icon
name = thermostat.settings_property["general_device_settings"].name
zone_id = thermostat.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != thermostat.settings_property["general_device_settings"].events_enabled
assert icon != thermostat.settings_property["general_device_settings"].icon
assert name != thermostat.settings_property["general_device_settings"].name
assert zone_id != thermostat.settings_property["general_device_settings"].zone_id
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
thermostat = local_gateway.devices[ELEMENT_ID]
param_changed = thermostat.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != thermostat.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
thermostat = local_gateway.devices[ELEMENT_ID]
assert thermostat.get_property("multi_level_sensor") == list(thermostat.multi_level_sensor_property.values())
assert thermostat.get_property("multi_level_switch") == list(thermostat.multi_level_switch_property.values())
assert thermostat.get_property("remote_control") == list(thermostat.remote_control_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
thermostat = local_gateway.devices[ELEMENT_ID]
assert thermostat.is_online()
devolo_home_control_api-0.19.0/tests/test_uuid.py 0000664 0000000 0000000 00000002454 15003713056 0022220 0 ustar 00root root 0000000 0000000 """Helper functions for splitting UID."""
from devolo_home_control_api.helper.uid import (
get_device_type_from_element_uid,
get_device_uid_from_element_uid,
get_device_uid_from_setting_uid,
get_home_id_from_device_uid,
get_sub_device_uid_from_element_uid,
)
DEVICE_UID = "hdm:ZWave:CBC56091/24"
ELEMENT_UID = "devolo.MultiLevelSensor:hdm:ZWave:CBC56091/24#2"
SETTING_UID = "lis.hdm:ZWave:CBC56091/24"
def test_device_uid_from_element_uid() -> None:
"""Test getting the device uid from an element uid."""
assert get_device_uid_from_element_uid(ELEMENT_UID) == DEVICE_UID
def test_device_uid_from_setting_uid() -> None:
"""Test getting the device uid from a setting uid."""
assert get_device_uid_from_setting_uid(SETTING_UID) == DEVICE_UID
def test_sub_device_uid_from_element_uid() -> None:
"""Test getting the sub device uid from an element uid."""
assert get_sub_device_uid_from_element_uid(ELEMENT_UID) == 2
def test_device_type_from_element_uid() -> None:
"""Test getting the device type from an element uid."""
assert get_device_type_from_element_uid(ELEMENT_UID) == "devolo.MultiLevelSensor"
def test_home_id_from_device_uid() -> None:
"""Test getting the home ID from a device UID."""
assert get_home_id_from_device_uid(DEVICE_UID) == "CBC56091"
devolo_home_control_api-0.19.0/tests/test_wall_switch.py 0000664 0000000 0000000 00000011722 15003713056 0023570 0 ustar 00root root 0000000 0000000 """Test interacting with a wall switch."""
import json
import sys
import pytest
from requests_mock import Mocker
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.homecontrol import HomeControl
from . import Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/7"
FIXTURE = load_fixture("homecontrol_remote_control")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
@pytest.mark.freeze_time("2023-04-28T08:00:00")
def test_getting_devices(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test getting remote control devices."""
assert local_gateway.remote_control_devices == snapshot
def test_state_change(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test state change of a wall switch."""
response = FIXTURE["success"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
wall_swtich = local_gateway.devices[ELEMENT_ID]
button = wall_swtich.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
key_pressed = button.key_pressed
last_activity = button.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["button_event"]))
assert key_pressed != button.key_pressed
assert last_activity != button.last_activity
assert subscriber.update.call_count == 1
button = wall_swtich.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
assert button.set(2)
assert button.key_pressed == 2
def test_state_change_same_value(local_gateway: HomeControl, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test state change of a wall switch using the same value."""
response = FIXTURE["fail"]
requests_mock.post(f"http://{gateway_ip}/remote/json-rpc", json=response)
wall_swtich = local_gateway.devices[ELEMENT_ID]
button = wall_swtich.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
assert not button.set(2)
def test_type_change(local_gateway: HomeControl) -> None:
"""Test processing a type change."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
wall_switch = local_gateway.devices[ELEMENT_ID]
button = wall_switch.remote_control_property[f"devolo.RemoteControl:{ELEMENT_ID}"]
switch_type = button.key_count
WEBSOCKET.recv_packet(json.dumps(FIXTURE["count_change"]))
assert switch_type != button.key_count
assert subscriber.update.call_count == 1
def test_battery_report(local_gateway: HomeControl) -> None:
"""Test processing a battery report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
wall_switch = local_gateway.devices[ELEMENT_ID]
battery_level = wall_switch.battery_level
battery_low = wall_switch.battery_low
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_low"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_level"]))
assert battery_level != wall_switch.battery_level
assert battery_low != wall_switch.battery_low
assert subscriber.update.call_count == 2
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
wall_switch = local_gateway.devices[ELEMENT_ID]
events_enabled = wall_switch.settings_property["general_device_settings"].events_enabled
icon = wall_switch.settings_property["general_device_settings"].icon
name = wall_switch.settings_property["general_device_settings"].name
zone_id = wall_switch.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != wall_switch.settings_property["general_device_settings"].events_enabled
assert icon != wall_switch.settings_property["general_device_settings"].icon
assert name != wall_switch.settings_property["general_device_settings"].name
assert zone_id != wall_switch.settings_property["general_device_settings"].zone_id
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
wall_switch = local_gateway.devices[ELEMENT_ID]
param_changed = wall_switch.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != wall_switch.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
wall_switch = local_gateway.devices[ELEMENT_ID]
assert wall_switch.get_property("remote_control") == list(wall_switch.remote_control_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
wall_switch = local_gateway.devices[ELEMENT_ID]
assert not wall_switch.is_online()
devolo_home_control_api-0.19.0/tests/test_websocket.py 0000664 0000000 0000000 00000006212 15003713056 0023234 0 ustar 00root root 0000000 0000000 """Test interacting with the websocket."""
import logging
from unittest.mock import patch
import pytest
import requests
from requests_mock import Mocker
from devolo_home_control_api.exceptions import GatewayOfflineError
from devolo_home_control_api.homecontrol import HomeControl
from devolo_home_control_api.mydevolo import Mydevolo
from . import load_fixture
from .mocks import WEBSOCKET
def test_setup_websocket_broken(mydevolo: Mydevolo, gateway_id: str, gateway_ip: str, requests_mock: Mocker) -> None:
"""Test setup failure when websocket cannot be established."""
connection = load_fixture("homecontrol_local_session")
connection["link"] = f"http://{gateway_ip}/dhlp/portal/full/?token=54e8c82fc921ee7e&"
requests_mock.get(f"http://{gateway_ip}/dhlp/port/full")
requests_mock.get(f"http://{gateway_ip}/dhlp/portal/full", json=connection)
requests_mock.get(connection["link"])
requests_mock.post(
f"http://{gateway_ip}/remote/json-rpc",
[
{"json": load_fixture("homecontrol_zones")},
{"json": load_fixture("homecontrol_device_page")},
{"json": load_fixture("homecontrol_devices")},
{"json": load_fixture("homecontrol_device_details")},
],
)
with patch(
"devolo_home_control_api.backend.mprm_websocket.time", side_effect=[1682620298.7491188, 1682620898.7491188]
), patch("devolo_home_control_api.backend.mprm_websocket.MprmWebsocket.websocket_connect"), pytest.raises(
GatewayOfflineError
):
HomeControl(gateway_id, mydevolo)
def test_websocket_breakdown(local_gateway: HomeControl) -> None:
"""Test reconnect behavior on websocket breakdown."""
with patch(
"devolo_home_control_api.backend.mprm_websocket.MprmWebsocket.websocket_connect",
wraps=local_gateway.websocket_connect,
) as websocket_connect, patch(
"devolo_home_control_api.backend.mprm.Mprm.get_local_session",
side_effect=[GatewayOfflineError, requests.exceptions.ConnectTimeout, True],
) as get_local_session, patch(
"devolo_home_control_api.backend.mprm.Mprm.detect_gateway_in_lan",
) as detect_gateway_in_lan, patch(
"devolo_home_control_api.backend.mprm_websocket.sleep",
):
WEBSOCKET.error()
assert get_local_session.call_count == 3
websocket_connect.assert_called_once()
detect_gateway_in_lan.assert_called_once()
@pytest.mark.usefixtures("local_gateway")
def test_websocket_closed(caplog: pytest.LogCaptureFixture) -> None:
"""Test reveiving a close message."""
caplog.set_level(logging.INFO)
WEBSOCKET.close()
assert "Closed websocket connection." in caplog.messages
@pytest.mark.usefixtures("local_gateway")
def test_websocket_session_refresh() -> None:
"""Test refreshing the session from time to time."""
with patch("devolo_home_control_api.backend.mprm_rest.MprmRest._post") as post:
WEBSOCKET.recv_pong()
post.assert_called_once_with(
{
"method": "FIM/invokeOperation",
"params": ["devolo.UserPrefs.535512AB-165D-11E7-A4E2-000C29D76CCA", "resetSessionTimeout", []],
}
)
devolo_home_control_api-0.19.0/tests/test_window_contact.py 0000664 0000000 0000000 00000012266 15003713056 0024276 0 ustar 00root root 0000000 0000000 """Test interacting with a window contact."""
import json
import sys
import pytest
from syrupy.assertion import SnapshotAssertion
from devolo_home_control_api.homecontrol import HomeControl
from . import Subscriber, load_fixture
from .mocks import WEBSOCKET
ELEMENT_ID = "hdm:ZWave:CBC56091/3"
FIXTURE = load_fixture("homecontrol_binary_sensor")
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Tests with snapshots need at least Python 3.8")
@pytest.mark.freeze_time("2023-04-28T08:00:00")
def test_getting_devices(local_gateway: HomeControl, snapshot: SnapshotAssertion) -> None:
"""Test getting binary sensor devices."""
assert local_gateway.binary_sensor_devices == snapshot
def test_state_change(local_gateway: HomeControl) -> None:
"""Test state change of a binary sensor."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
window_contact = local_gateway.devices[ELEMENT_ID]
binary_sensor = window_contact.binary_sensor_property[f"devolo.BinarySensor:{ELEMENT_ID}"]
state = binary_sensor.state
last_activity = binary_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["sensor_event"]))
assert state != binary_sensor.state
assert last_activity != binary_sensor.last_activity
assert subscriber.update.call_count == 1
def test_temperature_report(local_gateway: HomeControl) -> None:
"""Test processing a temperature report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
window_contact = local_gateway.devices[ELEMENT_ID]
temperature_sensor = window_contact.multi_level_sensor_property[
f"devolo.MultiLevelSensor:{ELEMENT_ID}#MultilevelSensor(1)"
]
value = temperature_sensor.value
last_activity = temperature_sensor.last_activity
WEBSOCKET.recv_packet(json.dumps(FIXTURE["temperature_event"]))
assert value != temperature_sensor.value
assert last_activity != temperature_sensor.last_activity
assert subscriber.update.call_count == 1
def test_battery_report(local_gateway: HomeControl) -> None:
"""Test processing a battery report."""
subscriber = Subscriber(ELEMENT_ID)
local_gateway.publisher.register(ELEMENT_ID, subscriber)
window_contact = local_gateway.devices[ELEMENT_ID]
battery_level = window_contact.battery_level
battery_low = window_contact.battery_low
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_low"]))
WEBSOCKET.recv_packet(json.dumps(FIXTURE["battery_level"]))
assert battery_level != window_contact.battery_level
assert battery_low != window_contact.battery_low
assert subscriber.update.call_count == 2
def test_changing_general_device_settings(local_gateway: HomeControl) -> None:
"""Test changing general device settings."""
window_contact = local_gateway.devices[ELEMENT_ID]
events_enabled = window_contact.settings_property["general_device_settings"].events_enabled
icon = window_contact.settings_property["general_device_settings"].icon
name = window_contact.settings_property["general_device_settings"].name
zone_id = window_contact.settings_property["general_device_settings"].zone_id
WEBSOCKET.recv_packet(json.dumps(FIXTURE["general_device_settings"]))
assert events_enabled != window_contact.settings_property["general_device_settings"].events_enabled
assert icon != window_contact.settings_property["general_device_settings"].icon
assert name != window_contact.settings_property["general_device_settings"].name
assert zone_id != window_contact.settings_property["general_device_settings"].zone_id
def test_changing_led_settings(local_gateway: HomeControl) -> None:
"""Test changing led settings."""
window_contact = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["led"]))
assert not window_contact.settings_property["led"].led_setting
def test_temperature_report_setting(local_gateway: HomeControl) -> None:
"""Test processing the temperature report setting."""
window_contact = local_gateway.devices[ELEMENT_ID]
WEBSOCKET.recv_packet(json.dumps(FIXTURE["temperature_report"]))
assert not window_contact.settings_property["temperature_report"].temp_report
def test_changing_expert_settings(local_gateway: HomeControl) -> None:
"""Test changing expert settings."""
window_contact = local_gateway.devices[ELEMENT_ID]
param_changed = window_contact.settings_property["param_changed"].param_changed
WEBSOCKET.recv_packet(json.dumps(FIXTURE["param_changed"]))
assert param_changed != window_contact.settings_property["param_changed"].param_changed
def test_getting_properties(local_gateway: HomeControl) -> None:
"""Test getting a property of the device."""
window_contact = local_gateway.devices[ELEMENT_ID]
assert window_contact.get_property("binary_sensor") == list(window_contact.binary_sensor_property.values())
assert window_contact.get_property("multi_level_sensor") == list(window_contact.multi_level_sensor_property.values())
def test_getting_state(local_gateway: HomeControl) -> None:
"""Test getting the online state of the device."""
window_contact = local_gateway.devices[ELEMENT_ID]
assert window_contact.is_online()