diff --git a/.github/workflows/python-package.yml b/.github/workflows/lint_and_test.yml
similarity index 77%
rename from .github/workflows/python-package.yml
rename to .github/workflows/lint_and_test.yml
index 892f484..2884ccf 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/lint_and_test.yml
@@ -1,33 +1,34 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
-name: Python package
+name: Linting and testing
on:
push:
- branches: [ master ]
+ branches: [ main ]
pull_request:
- branches: [ master ]
+ branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
- python-version: ['3.5', '3.6', '3.7', '3.8', '3.9']
+ python-version: ['3.9', '3.10','3.11', '3.12', '3.13.0-rc.1']
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install flake8 nose coverage
- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+ python -m pip install flake8 nose2 coverage
+ python -m pip install .
- name: Lint with flake8
run: |
# stop the test if there are Python syntax errors or undefined names
@@ -36,7 +37,7 @@ jobs:
flake8 enocean --count --exit-zero --max-complexity=15 --max-line-length=127 --statistics
- name: Test with nose
run: |
- nosetests -s -q --with-coverage --cover-package=enocean
+ python -m nose2 -s . --quiet --log-level 100 --with-coverage
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop
with:
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 4e1ef42..5402bda 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -8,24 +8,29 @@ on:
types: [created]
jobs:
- deploy:
-
+ testpypi-publish:
+ name: Upload release to PyPI
+ # name: Upload release to TestPyPI
runs-on: ubuntu-latest
-
+ environment:
+ name: pypi
+ url: https://pypi.org/p/enocean4ha
+ # name: testpypi
+ # url: https://test.pypi.org/p/enocean4ha
+ permissions:
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
steps:
- - uses: actions/checkout@v2
- - name: Set up Python
- uses: actions/setup-python@v2
- with:
- python-version: '3.x'
- - name: Install dependencies
- run: |
- python -m pip install --upgrade pip
- pip install setuptools wheel twine
- - name: Build and publish
- env:
- TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
- TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
- run: |
- python setup.py sdist bdist_wheel
- twine upload dist/*
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install setuptools wheel twine build
+ - name: Build
+ run: |
+ python -m build
+ - name: Publish package distributions to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ # with:
+ # verbose: true
+ # repository-url: https://test.pypi.org/legacy/
diff --git a/.gitignore b/.gitignore
index 2bb1c65..17110e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ dist
pip-selfcheck.json
.vscode/
+.idea/
diff --git a/README.md b/README.md
index 0395414..7c02bb4 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,20 @@
# Python EnOcean #
-[](https://travis-ci.org/kipe/enocean)
-[](https://coveralls.io/github/kipe/enocean?branch=master)
+[](https://github.com/topic2k/enocean4ha/actions/workflows/lint_and_test.yml)
+[](https://coveralls.io/github/topic2k/enocean4ha?branch=main)
+[](https://pypi.org/project/enocean4ha/)
+
A Python library for reading and controlling [EnOcean](http://www.enocean.com/) devices.
-Started as a part of [Forget Me Not](http://www.element14.com/community/community/design-challenges/forget-me-not)
+It started as a part of the [Forget Me Not](http://www.element14.com/community/community/design-challenges/forget-me-not)
design challenge @ [element14](http://www.element14.com/).
+This fork was created, because the [original repo](https://github.com/kipe/enocean) seems to be inactive, and i needed
+to add EEPs to make some devices usable in [Home Assistant](https://www.home-assistant.io/).
+
+
+
## Install ##
If not installed already, install [pip](https://pypi.python.org/pypi/pip) by running
@@ -16,7 +23,7 @@ If not installed already, install [pip](https://pypi.python.org/pypi/pip) by run
After pip is installed, install the module by running
-`sudo pip install enocean` (or `sudo pip install git+https://github.com/kipe/enocean.git` if you want the "bleeding edge").
+`sudo pip install enocean4ha` (or `sudo pip install git+https://github.com/topic2k/enocean4ha.git` if you want the "bleeding edge").
After this, it's just a matter of running `enocean_example.py` and pressing the
learn button on magnetic contact or temperature switch or pressing the rocker switch.
@@ -25,3 +32,6 @@ You should be displayed with a log of the presses, as well as parsed values
(assuming the sensors are the ones provided in the [EnOcean Starter Kit](https://www.enocean.com/en/enocean_modules/esk-300)).
The example script can be stopped by pressing `CTRL+C`
+
+---
+
diff --git a/SUPPORTED_PROFILES.md b/SUPPORTED_PROFILES.md
index 1ca36d8..f92aaf0 100644
--- a/SUPPORTED_PROFILES.md
+++ b/SUPPORTED_PROFILES.md
@@ -1,7 +1,98 @@
# Supported profiles
-All profiles (should) correspond to the official [EEP](http://www.enocean-alliance.org/eep/) by EnOcean.
+All profiles (should) correspond to the official [EEP](https://www.enocean-alliance.org/eep/) by EnOcean.
+
+ RPS Telegram (0xF6)
+
+- [FUNC 0x01 - TYPE 0x01 - Push Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x01---type-0x01---push-button)
+- [FUNC 0x02 - TYPE 0x01 - Light and Blind Control - Application Style 1](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x02---type-0x01---light-and-blind-control---application-style-1)
+- [FUNC 0x02 - TYPE 0x02 - Light and Blind Control - Application Style 2](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x02---type-0x02---light-and-blind-control---application-style-2)
+- [FUNC 0x05 - TYPE 0x01 - Liquid Leakage Sensor (mechanic harvester)](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x05---type-0x01---liquid-leakage-sensor-(mechanic-harvester))
+- [FUNC 0x05 - TYPE 0x02 - Smoke Detector](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x05---type-0x02---smoke-detector)
+- [FUNC 0x10 - TYPE 0x00 - Window Handle](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xF6---func-0x10---type-0x00---window-handle)
+
+
+
+ 1BS Telegram (0xD5)
+
+- [FUNC 0x00 - TYPE 0x01 - Single Input Contact](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD5---func-0x00---type-0x01---single-input-contact)
+
+
+
+ 4BS Telegram (0xA5)
+
+- [FUNC 0x02 - TYPE 0x01 - Temperature Sensor Range -40°C to 0°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x01---temperature-sensor-range--40°c-to-0°c)
+- [FUNC 0x02 - TYPE 0x02 - Temperature Sensor Range -30°C to +10°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x02---temperature-sensor-range--30°c-to-+10°c)
+- [FUNC 0x02 - TYPE 0x03 - Temperature Sensor Range -20°C to +20°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x03---temperature-sensor-range--20°c-to-+20°c)
+- [FUNC 0x02 - TYPE 0x04 - Temperature Sensor Range -10°C to +30°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x04---temperature-sensor-range--10°c-to-+30°c)
+- [FUNC 0x02 - TYPE 0x05 - Temperature Sensor Range 0°C to +40°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x05---temperature-sensor-range-0°c-to-+40°c)
+- [FUNC 0x02 - TYPE 0x06 - Temperature Sensor Range +10°C to +50°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x06---temperature-sensor-range-+10°c-to-+50°c)
+- [FUNC 0x02 - TYPE 0x07 - Temperature Sensor Range +20°C to +60°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x07---temperature-sensor-range-+20°c-to-+60°c)
+- [FUNC 0x02 - TYPE 0x08 - Temperature Sensor Range +30°C to +70°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x08---temperature-sensor-range-+30°c-to-+70°c)
+- [FUNC 0x02 - TYPE 0x09 - Temperature Sensor Range +40°C to +80°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x09---temperature-sensor-range-+40°c-to-+80°c)
+- [FUNC 0x02 - TYPE 0x0A - Temperature Sensor Range +50°C to +90°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x0A---temperature-sensor-range-+50°c-to-+90°c)
+- [FUNC 0x02 - TYPE 0x0B - Temperature Sensor Range +60°C to +100°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x0B---temperature-sensor-range-+60°c-to-+100°c)
+- [FUNC 0x02 - TYPE 0x10 - Temperature Sensor Range -60°C to +20°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x10---temperature-sensor-range--60°c-to-+20°c)
+- [FUNC 0x02 - TYPE 0x11 - Temperature Sensor Range -50°C to +30°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x11---temperature-sensor-range--50°c-to-+30°c)
+- [FUNC 0x02 - TYPE 0x12 - Temperature Sensor Range -40°C to +40°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x12---temperature-sensor-range--40°c-to-+40°c)
+- [FUNC 0x02 - TYPE 0x13 - Temperature Sensor Range -30°C to +50°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x13---temperature-sensor-range--30°c-to-+50°c)
+- [FUNC 0x02 - TYPE 0x14 - Temperature Sensor Range -20°C to +60°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x14---temperature-sensor-range--20°c-to-+60°c)
+- [FUNC 0x02 - TYPE 0x15 - Temperature Sensor Range -10°C to +70°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x15---temperature-sensor-range--10°c-to-+70°c)
+- [FUNC 0x02 - TYPE 0x16 - Temperature Sensor Range 0°C to +80°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x16---temperature-sensor-range-0°c-to-+80°c)
+- [FUNC 0x02 - TYPE 0x17 - Temperature Sensor Range +10°C to +90°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x17---temperature-sensor-range-+10°c-to-+90°c)
+- [FUNC 0x02 - TYPE 0x18 - Temperature Sensor Range +20°C to +100°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x18---temperature-sensor-range-+20°c-to-+100°c)
+- [FUNC 0x02 - TYPE 0x19 - Temperature Sensor Range +30°C to +110°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x19---temperature-sensor-range-+30°c-to-+110°c)
+- [FUNC 0x02 - TYPE 0x1A - Temperature Sensor Range +40°C to +120°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x1A---temperature-sensor-range-+40°c-to-+120°c)
+- [FUNC 0x02 - TYPE 0x1B - Temperature Sensor Range +50°C to +130°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x1B---temperature-sensor-range-+50°c-to-+130°c)
+- [FUNC 0x02 - TYPE 0x20 - 10 Bit Temperature Sensor Range -10°C to +41.2°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x20---10-bit-temperature-sensor-range--10°c-to-+41.2°c)
+- [FUNC 0x02 - TYPE 0x30 - 10 Bit Temperature Sensor Range -40°C to +62.3°C](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x02---type-0x30---10-bit-temperature-sensor-range--40°c-to-+62.3°c)
+- [FUNC 0x04 - TYPE 0x01 - Range 0°C to +40°C and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x01---range-0°c-to-+40°c-and-0%-to-100%)
+- [FUNC 0x04 - TYPE 0x02 - Range -20°C to +60°C and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x02---range--20°c-to-+60°c-and-0%-to-100%)
+- [FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x03---range--20°c-to-+60°c-10bit-measurement-and-0%-to-100%)
+- [FUNC 0x04 - TYPE 0x04 - Range -40°C to +120°C 12bit-measurement and 0% to 100%](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x04---type-0x04---range--40°c-to-+120°c-12bit-measurement-and-0%-to-100%)
+- [FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x01---range-300lx-to-60.000lx)
+- [FUNC 0x06 - TYPE 0x02 - Range 0lx to 1.020lx](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x06---type-0x02---range-0lx-to-1.020lx)
+- [FUNC 0x07 - TYPE 0x01 - Occupancy with Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x01---occupancy-with-supply-voltage-monitor)
+- [FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x07---type-0x03---occupancy-with-supply-voltage-monitor-and-10-bit-illumination-measurement)
+- [FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x01---range-0lx-to-510lx,-0°c-to-+51°c-and-occupancy-button)
+- [FUNC 0x08 - TYPE 0x02 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x02---range-0lx-to-1020lx,-0°c-to-+51°c-and-occupancy-button)
+- [FUNC 0x08 - TYPE 0x03 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x08---type-0x03---range-0lx-to-1020lx,-0°c-to-+51°c-and-occupancy-button)
+- [FUNC 0x09 - TYPE 0x04 - CO2 Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x04---co2-sensor)
+- [FUNC 0x09 - TYPE 0x05 - VOC Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x05---voc-sensor)
+- [FUNC 0x09 - TYPE 0x09 - Gas Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x09---type-0x09---gas-sensor)
+- [FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x03---temperature-sensor-and-set-point)
+- [FUNC 0x10 - TYPE 0x05 - Temperature Sensor, Set Point and Occupancy Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x05---temperature-sensor,-set-point-and-occupancy-control)
+- [FUNC 0x10 - TYPE 0x06 - Temperature Sensor, Set Point and Day/Night Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x06---temperature-sensor,-set-point-and-day/night-control)
+- [FUNC 0x10 - TYPE 0x10 - Temperature and Humidity Sensor, Set Point and Occupancy Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x10---temperature-and-humidity-sensor,-set-point-and-occupancy-control)
+- [FUNC 0x10 - TYPE 0x12 - Temperature and Humidity Sensor and Set Point](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x10---type-0x12---temperature-and-humidity-sensor-and-set-point)
+- [FUNC 0x11 - TYPE 0x02 - Temperature Controller Output](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x11---type-0x02---temperature-controller-output)
+- [FUNC 0x11 - TYPE 0x03 - Blind Status](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x11---type-0x03---blind-status)
+- [FUNC 0x13 - TYPE 0x01 - Weather Station](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x13---type-0x01---weather-station)
+- [FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x14---type-0x01---single-input-contact-(window/door),-supply-voltage-monitor)
+- [FUNC 0x20 - TYPE 0x01 - Battery Powered Actuator (BI-DIR)](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x20---type-0x01---battery-powered-actuator-(bi-dir))
+- [FUNC 0x20 - TYPE 0x06 - HVAC Components,Harvesting-powered actuator with local temperature offset control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x20---type-0x06---hvac-components,harvesting-powered-actuator-with-local-temperature-offset-control)
+- [FUNC 0x12 - TYPE 0x01 - Electricity](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x12---type-0x01---electricity)
+- [FUNC 0x30 - TYPE 0x03 - Digital Inputs, Wake and Temperature](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x30---type-0x03---digital-inputs,-wake-and-temperature)
+- [FUNC 0x38 - TYPE 0x08 - Gateway](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xA5---func-0x38---type-0x08---gateway)
+
+
+
+ VLD Telegram (0xD2)
+
+- [FUNC 0x01 - TYPE 0x01 - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x01---electronic-switch-with-local-control)
+- [FUNC 0x01 - TYPE 0x0E - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x0E---electronic-switch-with-local-control)
+- [FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x0F---electronic-switch-with-local-control)
+- [FUNC 0x01 - TYPE 0x12 - Electronic switch with Local Control](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x01---type-0x12---electronic-switch-with-local-control)
+- [FUNC 0x05 - TYPE 0x00 - Type 0x00](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x05---type-0x00---type-0x00)
+- [FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor](https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#rorg-0xD2---func-0x14---type-0x41---indoor--temperature,-humidity-xyz-acceleration,-illumination-sensor)
+
+
+
+
+
+---
### RPS Telegram (0xF6)
+
##### RORG 0xF6 - FUNC 0x01 - TYPE 0x01 - Push Button
|shortcut|description |type |values |
@@ -10,7 +101,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - Pressed |
-
##### RORG 0xF6 - FUNC 0x02 - TYPE 0x01 - Light and Blind Control - Application Style 1
|shortcut|description |type |values |
@@ -30,7 +120,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|T21 |T21 |status | |
|NU |NU |status | |
-
##### RORG 0xF6 - FUNC 0x02 - TYPE 0x02 - Light and Blind Control - Application Style 2
|shortcut|description |type |values |
@@ -51,7 +140,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|NU |NU |status | |
-
##### RORG 0xF6 - FUNC 0x05 - TYPE 0x01 - Liquid Leakage Sensor (mechanic harvester)
|shortcut|description |type |values |
@@ -62,7 +150,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|T21 |T21 |status | |
|NU |NU |status | |
-
##### RORG 0xF6 - FUNC 0x05 - TYPE 0x02 - Smoke Detector
|shortcut|description |type |values |
@@ -72,7 +159,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |48 - Energy LOW |
-
##### RORG 0xF6 - FUNC 0x10 - TYPE 0x00 - Window Handle
|shortcut|description |type |values |
@@ -85,8 +171,8 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|NU |NU |status | |
-
### 1BS Telegram (0xD5)
+
##### RORG 0xD5 - FUNC 0x00 - TYPE 0x01 - Single Input Contact
|shortcut|description |type |values |
@@ -95,176 +181,152 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - closed |
-
### 4BS Telegram (0xA5)
+
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x01 - Temperature Sensor Range -40°C to 0°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -40.0-0.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x02 - Temperature Sensor Range -30°C to +10°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -30.0-10.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x03 - Temperature Sensor Range -20°C to +20°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -20.0-20.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x04 - Temperature Sensor Range -10°C to +30°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -10.0-30.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x05 - Temperature Sensor Range 0°C to +40°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-40.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x06 - Temperature Sensor Range +10°C to +50°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 10.0-50.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x07 - Temperature Sensor Range +20°C to +60°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 20.0-60.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x08 - Temperature Sensor Range +30°C to +70°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 30.0-70.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x09 - Temperature Sensor Range +40°C to +80°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 40.0-80.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x0A - Temperature Sensor Range +50°C to +90°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 50.0-90.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x0B - Temperature Sensor Range +60°C to +100°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 60.0-100.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x10 - Temperature Sensor Range -60°C to +20°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -60.0-20.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x11 - Temperature Sensor Range -50°C to +30°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -50.0-30.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x12 - Temperature Sensor Range -40°C to +40°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -40.0-40.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x13 - Temperature Sensor Range -30°C to +50°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -30.0-50.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x14 - Temperature Sensor Range -20°C to +60°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -20.0-60.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x15 - Temperature Sensor Range -10°C to +70°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ -10.0-70.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x16 - Temperature Sensor Range 0°C to +80°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-80.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x17 - Temperature Sensor Range +10°C to +90°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 10.0-90.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x18 - Temperature Sensor Range +20°C to +100°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 20.0-100.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x19 - Temperature Sensor Range +30°C to +110°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 30.0-110.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x1A - Temperature Sensor Range +40°C to +120°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 40.0-120.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x1B - Temperature Sensor Range +50°C to +130°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 50.0-130.0 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x20 - 10 Bit Temperature Sensor Range -10°C to +41.2°C
|shortcut|description |type |values |
|--------|--------------------------------------------------|--------|---- |
|TMP |Temperature (linear) |value |1023.0-0.0 ↔ -10.0-41.2 °C |
-
##### RORG 0xA5 - FUNC 0x02 - TYPE 0x30 - 10 Bit Temperature Sensor Range -40°C to +62.3°C
|shortcut|description |type |values |
@@ -272,7 +334,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|TMP |Temperature (linear) |value |1023.0-0.0 ↔ -40.0-62.3 °C |
-
##### RORG 0xA5 - FUNC 0x04 - TYPE 0x01 - Range 0°C to +40°C and 0% to 100%
|shortcut|description |type |values |
@@ -282,6 +343,14 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|TSN |Availability of the Temperature Sensor |enum |0 - not available |
| | | |1 - available |
+##### RORG 0xA5 - FUNC 0x04 - TYPE 0x02 - Range -20°C to +60°C and 0% to 100%
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|HUM |Rel. Humidity (linear) |value |0.0-250.0 ↔ 0.0-100.0 % |
+|TMP |Temperature (linear) |value |0.0-250.0 ↔ -20.0-60.0 °C |
+|TSN |Availability of the Temperature Sensor |enum |0 - not available |
+| | | |1 - available |
##### RORG 0xA5 - FUNC 0x04 - TYPE 0x03 - Range -20°C to +60°C 10bit-measurement and 0% to 100%
@@ -292,6 +361,12 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|TTP |Telegram Type |enum |0 - Heartbeat |
| | | |1 - Event triggered |
+##### RORG 0xA5 - FUNC 0x04 - TYPE 0x04 - Range -40°C to +120°C 12bit-measurement and 0% to 100%
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|HUM |Rel. Humidity (linear) |value |0.0-199.0 ↔ 0.0-100.0 % |
+|TMP |Temperature (linear) |value |0.0-1599.0 ↔ -40.0-120.0 °C |
##### RORG 0xA5 - FUNC 0x06 - TYPE 0x01 - Range 300lx to 60.000lx
@@ -304,7 +379,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|RS |Range select |enum |0 - Range acc. to DB_1 (ILL1) |
| | | |1 - Range acc. to DB_2 (ILL2) |
-
##### RORG 0xA5 - FUNC 0x06 - TYPE 0x02 - Range 0lx to 1.020lx
|shortcut|description |type |values |
@@ -316,7 +390,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - Range acc. to DB_2 (ILL2) |
-
##### RORG 0xA5 - FUNC 0x07 - TYPE 0x01 - Occupancy with Supply voltage monitor
|shortcut|description |type |values |
@@ -325,6 +398,14 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|PIR |PIR Status |enum |0 - off |
| | | |1 - on |
+##### RORG 0xA5 - FUNC 0x07 - TYPE 0x03 - Occupancy with Supply voltage monitor and 10-bit illumination measurement
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|SVC |Supply voltage (OPTIONAL) |value |0.0-250.0 ↔ 0.0-5.0 V |
+|ILL |Illumination (linear) |value |0.0-1000.0 ↔ 0.0-1000.0 lx |
+|PIR |PIR Status |enum |0 - Uncertain of occupancy status |
+| | | |1 - Motion detected |
##### RORG 0xA5 - FUNC 0x08 - TYPE 0x01 - Range 0lx to 510lx, 0°C to +51°C and Occupancy Button
@@ -339,6 +420,29 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|OCC |Occupancy Button |enum |0 - Button pressed |
| | | |1 - Button released |
+##### RORG 0xA5 - FUNC 0x08 - TYPE 0x02 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|SVC |Supply voltage (linear) |value |0.0-255.0 ↔ 0.0-5.1 V |
+|ILL |Illumination (linear) |value |0.0-255.0 ↔ 0.0-1020.0 lx |
+|TMP |Temperature (linear) |value |0.0-255.0 ↔ 0.0-51.0 °C |
+|PIRS |PIR Status |enum |0 - PIR on |
+| | | |1 - PIR off |
+|OCC |Occupancy Button |enum |0 - Button pressed |
+| | | |1 - Button released |
+
+##### RORG 0xA5 - FUNC 0x08 - TYPE 0x03 - Range 0lx to 1020lx, 0°C to +51°C and Occupancy Button
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|SVC |Supply voltage (linear) |value |0.0-255.0 ↔ 0.0-5.1 V |
+|ILL |Illumination (linear) |value |0.0-255.0 ↔ 0.0-1530.0 lx |
+|TMP |Temperature (linear) |value |0.0-255.0 ↔ -30.0-50.0 °C |
+|PIRS |PIR Status |enum |0 - PIR on |
+| | | |1 - PIR off |
+|OCC |Occupancy Button |enum |0 - Button pressed |
+| | | |1 - Button released |
##### RORG 0xA5 - FUNC 0x09 - TYPE 0x04 - CO2 Sensor
@@ -349,7 +453,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|Conc |Concentration (linear) |value |0.0-255.0 ↔ 0.0-2550.0 ppm |
|TMP |Temperature (linear) |value |0.0-255.0 ↔ 0.0-51.0 °C |
-
##### RORG 0xA5 - FUNC 0x09 - TYPE 0x05 - VOC Sensor
|shortcut|description |type |values |
@@ -383,6 +486,11 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |26 - Diethyl ether |
| | | |255 - ozone |
+##### RORG 0xA5 - FUNC 0x09 - TYPE 0x09 - Gas Sensor
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CO2 |CO2 Measurement |value |0.0-255.0 ↔ 0.0-2000.0 ppm |
##### RORG 0xA5 - FUNC 0x10 - TYPE 0x03 - Temperature Sensor and Set Point
@@ -392,7 +500,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|SP |Set Point (linear) |value |0.0-255.0 ↔ 0.0-255.0 % |
|TMP |Temperature (linear) |value |255.0-0.0 ↔ 0.0-40.0 °C |
-
##### RORG 0xA5 - FUNC 0x10 - TYPE 0x05 - Temperature Sensor, Set Point and Occupancy Control
|shortcut|description |type |values |
@@ -402,7 +509,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|OCC |Occupancy Button |enum |0 - Button pressed |
| | | |1 - Button released |
-
##### RORG 0xA5 - FUNC 0x10 - TYPE 0x06 - Temperature Sensor, Set Point and Day/Night Control
|shortcut|description |type |values |
@@ -412,7 +518,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|SLSW |Slide switch |enum |0 - Position I / Night / Off |
| | | |1 - Position O / Day / On |
-
##### RORG 0xA5 - FUNC 0x10 - TYPE 0x10 - Temperature and Humidity Sensor, Set Point and Occupancy Control
|shortcut|description |type |values |
@@ -423,7 +528,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|OCC |Occupancy Button |enum |0 - Button pressed |
| | | |1 - Button released |
-
##### RORG 0xA5 - FUNC 0x10 - TYPE 0x12 - Temperature and Humidity Sensor and Set Point
|shortcut|description |type |values |
@@ -433,7 +537,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|TMP |Temperature (linear) |value |0.0-250.0 ↔ 0.0-40.0 °C |
-
##### RORG 0xA5 - FUNC 0x11 - TYPE 0x02 - Temperature Controller Output
|shortcut|description |type |values |
@@ -463,7 +566,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |2 - StandBy |
| | | |3 - Frost |
-
##### RORG 0xA5 - FUNC 0x11 - TYPE 0x03 - Blind Status
|shortcut|description |type |values |
@@ -494,6 +596,71 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - Inverse ode |
+##### RORG 0xA5 - FUNC 0x13 - TYPE 0x01 - Weather Station
+
+###### command: 1
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|DWS |Dawn sensor |value |0.0-255.0 ↔ 0.0-999.0 lx |
+|TMP |Outdoor Temp |value |0.0-255.0 ↔ -40.0-80.0 °C |
+|WND |Wind speed |value |0.0-255.0 ↔ 0.0-70.0 m/s |
+|D/N |Day / Night |enum |0 - day |
+| | | |1 - night |
+|RAN |Rain Indication |enum |0 - no rain |
+| | | |1 - rain |
+
+###### command: 2
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|SNW |Sun - West |value |0.0-255.0 ↔ 0.0-150.0 klx |
+|SNS |Sun - South |value |0.0-255.0 ↔ 0.0-150.0 klx |
+|SNE |Sun - East |value |0.0-255.0 ↔ 0.0-150.0 klx |
+|HEM |Hemisphere |enum |0 - North |
+| | | |1 - South |
+
+###### command: 3
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|DY |Day |value |1.0-31.0 ↔ 1.0-31.0 |
+|MTH |Month |value |1.0-12.0 ↔ 1.0-12.0 |
+|YR |Year |value |0.0-99.0 ↔ 2000.0-2099.0 |
+|SRC |Source |enum |0 - Real Time Clock |
+| | | |1 - GPS or equivalent |
+
+###### command: 4
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|WDY |Weekday |enum |1 - Monday |
+| | | |2 - Tuesday |
+| | | |3 - Wednesday |
+| | | |4 - Thursday |
+| | | |5 - Friday |
+| | | |6 - Saturday |
+| | | |7 - Sunday |
+|HR |Hour |value |0.0-23.0 ↔ 0.0-23.0 |
+|MIN |Minute |value |0.0-59.0 ↔ 0.0-59.0 |
+|SEC |Second |value |0.0-59.0 ↔ 0.0-59.0 |
+|TMF |Time Format |enum |0 - 24 Hours |
+| | | |1 - 12 Hours |
+|A/PM |AM/PM |enum |0 - AM |
+| | | |1 - PM |
+|SRC |Source |enum |0 - Real Time Clock |
+| | | |1 - GPS or equivalent |
+
+###### command: 5
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|ELV |Elevation |value |0.0-180.0 ↔ -90.0-90.0 ° |
+|AZM |Azimut |value |0.0-359.0 ↔ 0.0-359.0 ° |
+
+###### command: 6
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+| |Latitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 |
+|LOT(MSB)|Longitude(MSB) |value |0.0-15.0 ↔ 0.0-15.0 |
+|LAT(LSB)|Latitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 |
+|LOT(LSB)|Longitude(LSB) |value |0.0-255.0 ↔ 0.0-255.0 |
+
##### RORG 0xA5 - FUNC 0x14 - TYPE 0x01 - Single Input Contact (Window/Door), Supply voltage monitor
@@ -504,7 +671,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |0 - closed |
-
##### RORG 0xA5 - FUNC 0x20 - TYPE 0x01 - Battery Powered Actuator (BI-DIR)
###### direction: 1
@@ -551,6 +717,65 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|RCU |Select function |enum |0 - RCU |
| | | |1 - service on |
+##### RORG 0xA5 - FUNC 0x20 - TYPE 0x06 - HVAC Components,Harvesting-powered actuator with local temperature offset control
+
+###### direction: 1
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CV |Current Valve |value |0.0-100.0 ↔ 0.0-100.0 % |
+|LOM |Local Offset Mode |enum |0 - LO is relative - Offsettemperature |
+| | | |1 - LO is absolute - Absoluttemperature |
+|LO |Local Offset |enum |0-80 - Temperature setpoint °C +/- local offset °C |
+| | | |0x0 - Local Offset 0C |
+| | | |1 - Local Offset 1C |
+| | | |2 - Local Offset 2C |
+| | | |3 - Local Offset 3C |
+| | | |4 - Local Offset 4C |
+| | | |5 - Local Offset 5C |
+| | | |123 - Local Offset -5C |
+| | | |124 - Local Offset -4C |
+| | | |125 - Local Offset -3C |
+| | | |126 - Local Offset -2C |
+| | | |127 - Local Offset -1C |
+|TMP |Temperature |enum |0-160 - Local Ambient or Feed temperature (Selected by Direction 2, DB1.1)|
+|TSL |Tempertature Selection |enum |0 - Ambient Sensor Temperature |
+| | | |1 - Feed Sensor Temperature |
+|ENIE |Energy Input Enabled |enum |0 - Not Harvesting |
+| | | |1 - Harvesting active |
+|ES |Energy Storage |enum |0 - Low - almost discharged |
+| | | |1 - Sufficently charged |
+|DWO |Window open detection |enum |0 - NO window open detected |
+| | | |1 - Window open detected |
+|RCE |Radio Com Error |enum |0 - Radio communication is stable |
+| | | |1 - 6 or more consecutive communication erros have occured |
+|RSS |Radio Signal strength |enum |0 - Radio signal is strong |
+| | | |1 - Radio signal is weak under -77dBm |
+|ACO |Actuator obstructed |enum |0 - Actuator working correctly |
+| | | |1 - Actuator blocked |
+
+###### direction: 2
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|SP |Setpoint |enum | |
+|TMP |Room temperature from control unit |value |0.0-160.0 ↔ 0.0-40.0 % |
+|REF |Reference run |enum |0 - Normal operation |
+| | | |1 - Reference run |
+|RFC |RF Communication intervall |enum |0 - Auto |
+| | | |1 - 2 minutes |
+| | | |2 - 5 minutes |
+| | | |3 - 10 minutes |
+| | | |4 - 20 minutes |
+| | | |5 - 30 minutes |
+| | | |6 - 60 minutes |
+| | | |7 - 120 minutes |
+|SB |Initiate summer mode Bit |enum |0 - Normal operation |
+| | | |1 - Summer mode with 8hours radio duty cycle |
+|SPS |Set point selection |enum |0 - Valve position mode |
+| | | |1 - Temperature setpoint |
+|TSL |Temperature Selection |enum |0 - Request ambient temperature |
+| | | |1 - Request feedtemperature |
+|SBY |Standbye |enum |0 - Normal operation |
+| | | |1 - Enter standbye |
##### RORG 0xA5 - FUNC 0x12 - TYPE 0x01 - Electricity
@@ -567,7 +792,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |3 - x/1000 |
-
##### RORG 0xA5 - FUNC 0x30 - TYPE 0x03 - Digital Inputs, Wake and Temperature
|shortcut|description |type |values |
@@ -585,7 +809,6 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - High |
-
##### RORG 0xA5 - FUNC 0x38 - TYPE 0x08 - Gateway
###### command: 1
@@ -614,8 +837,8 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |1 - On |
-
### VLD Telegram (0xD2)
+
##### RORG 0xD2 - FUNC 0x01 - TYPE 0x01 - Electronic switch with Local Control
###### command: 4
@@ -659,6 +882,210 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
| | | |101-126 - Not used |
| | | |127 - output value not valid / not set |
+##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0E - Electronic switch with Local Control
+
+###### command: 4
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported |
+| | | |1 - Power Failure Detection enabled |
+|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled |
+| | | |1 - Power Failure Detection Detected |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported |
+| | | |1 - Over current switch off: executed |
+|EL |Error level |enum |0 - Error level 0: hardware OK |
+| | | |1 - Error level 1: hardware warning |
+| | | |2 - Error level 2: hardware failure |
+| | | |3 - Error level not supported |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|LC |Local control |enum |0 - Local control disabled / not supported |
+| | | |1 - Local control enabled |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 1
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|DV |Dim value |enum |0 - Switch to new output value |
+| | | |1 - Dim to new output level - dim timer 1 |
+| | | |2 - Dim to new output level - dim timer 2 |
+| | | |3 - Dim to new output level - dim timer 3 |
+| | | |4 - Stop dimming |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 6
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|qu |Measurement to query |enum |0 - Query energy |
+| | | |1 - Query power |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+
+###### command: 7
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|UN |Unit |enum |0 - Ws |
+| | | |1 - Wh |
+| | | |2 - kWh |
+| | | |3 - W |
+| | | |4 - kW |
+| | | |5-7 - Not used |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None |
+
+##### RORG 0xD2 - FUNC 0x01 - TYPE 0x0F - Electronic switch with Local Control
+
+###### command: 4
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported |
+| | | |1 - Power Failure Detection enabled |
+|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled |
+| | | |1 - Power Failure Detection Detected |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported |
+| | | |1 - Over current switch off: executed |
+|EL |Error level |enum |0 - Error level 0: hardware OK |
+| | | |1 - Error level 1: hardware warning |
+| | | |2 - Error level 2: hardware failure |
+| | | |3 - Error level not supported |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|LC |Local control |enum |0 - Local control disabled / not supported |
+| | | |1 - Local control enabled |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 1
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|DV |Dim value |enum |0 - Switch to new output value |
+| | | |1 - Dim to new output level - dim timer 1 |
+| | | |2 - Dim to new output level - dim timer 2 |
+| | | |3 - Dim to new output level - dim timer 3 |
+| | | |4 - Stop dimming |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 6
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|qu |Measurement to query |enum |0 - Query energy |
+| | | |1 - Query power |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+
+###### command: 7
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|UN |Unit |enum |0 - Ws |
+| | | |1 - Wh |
+| | | |2 - kWh |
+| | | |3 - W |
+| | | |4 - kW |
+| | | |5-7 - Not used |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None |
+
+##### RORG 0xD2 - FUNC 0x01 - TYPE 0x12 - Electronic switch with Local Control
+
+###### command: 4
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|PF |Power Failure |enum |0 - Power Failure Detection disabled/not supported |
+| | | |1 - Power Failure Detection enabled |
+|PFD |Power Failure Detection |enum |0 - Power Failure Detection not detected/not supported/disabled |
+| | | |1 - Power Failure Detection Detected |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|OC |Over current switch off |enum |0 - Over current switch off: ready / not supported |
+| | | |1 - Over current switch off: executed |
+|EL |Error level |enum |0 - Error level 0: hardware OK |
+| | | |1 - Error level 1: hardware warning |
+| | | |2 - Error level 2: hardware failure |
+| | | |3 - Error level not supported |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|LC |Local control |enum |0 - Local control disabled / not supported |
+| | | |1 - Local control enabled |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 1
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|DV |Dim value |enum |0 - Switch to new output value |
+| | | |1 - Dim to new output level - dim timer 1 |
+| | | |2 - Dim to new output level - dim timer 2 |
+| | | |3 - Dim to new output level - dim timer 3 |
+| | | |4 - Stop dimming |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+| | | |32-127 - Output channel {value} (to load) |
+|OV |Output value |enum |0 - Output value 0% or OFF |
+| | | |1-100 - Output value {value}% or ON |
+| | | |101-126 - Not used |
+| | | |127 - output value not valid / not set |
+
+###### command: 6
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|qu |Measurement to query |enum |0 - Query energy |
+| | | |1 - Query power |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - All output channels supported by the device |
+| | | |31 - Input channel (from mains supply) |
+
+###### command: 7
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|CMD |Command indentifier |enum |0-13 - Command ID {value} |
+|UN |Unit |enum |0 - Ws |
+| | | |1 - Wh |
+| | | |2 - kWh |
+| | | |3 - W |
+| | | |4 - kW |
+| | | |5-7 - Not used |
+|IO |I/O channel |enum |0-29 - Output channel {value} (to load) |
+| | | |30 - Not applicable, do not use |
+| | | |31 - Input channel (from mains supply) |
+|MV |Measurement value |value |1.0-4294967295.0 ↔ 1.0-4294967295.0 None |
##### RORG 0xD2 - FUNC 0x05 - TYPE 0x00 - Type 0x00
@@ -720,4 +1147,20 @@ All profiles (should) correspond to the official [EEP](http://www.enocean-allian
|CMD |Command Id |enum |0-5 - Command ID {value} |
+##### RORG 0xD2 - FUNC 0x14 - TYPE 0x41 - Indoor -Temperature, Humidity XYZ Acceleration, Illumination Sensor
+
+|shortcut|description |type |values |
+|--------|--------------------------------------------------|--------|---- |
+|TMP |Temperature 10 |value |0.0-1000.0 ↔ -40.0-60.0 °C |
+|HUM |Rel. Humidity linear) |value |0.0-200.0 ↔ 0.0-100.0 % |
+|ILL |Illumination linear) |value |0.0-100000.0 ↔ 0.0-100000.0 lx |
+|ACC |Acceleration Status |enum |0 - Periodic Update |
+| | | |1 - Threshold 1 exceeded |
+| | | |2 - Threshold 2 exceeded |
+|ACX |Absolute Acceleration on X axis |value |0.0-1000.0 ↔ -2.5-2.5 g |
+|ACY |Absolute Acceleration on Y axis |value |0.0-1000.0 ↔ -2.5-2.5 g |
+|ACZ |Absolute Acceleration on Z axis |value |0.0-1000.0 ↔ -2.5-2.5 g |
+|CO |Contact |enum |0 - Open |
+| | | |1 - Closed |
+
diff --git a/enocean/decorators.py b/decorators.py
similarity index 86%
rename from enocean/decorators.py
rename to decorators.py
index e953709..82c95bd 100644
--- a/enocean/decorators.py
+++ b/decorators.py
@@ -1,16 +1,16 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division
-import time
+
import functools
+import time
from os import environ
def timing(rounds=1, limit=None):
- '''
+ """
Wrapper to implement simple timing of tests.
Allows running multiple rounds to calculate average time.
Limit (in milliseconds) can be set to assert, if (average) duration is too high.
- '''
+ """
def decorator(method):
@functools.wraps(method)
def f():
@@ -26,7 +26,7 @@ def f():
# Use milliseconds for duration counter
duration = duration * 1e3
- print('Test "%s.%s" took %.06f ms.' % (method.__module__, method.__name__, duration))
+ print(f'Test "{method.__module__}.{method.__name__}" took {duration:.06f} ms.')
if limit is not None:
assert limit > duration, 'Timing failure: %.06f > %.06f' % (duration, limit)
diff --git a/enocean/__init__.py b/enocean/__init__.py
index e69de29..ea28939 100644
--- a/enocean/__init__.py
+++ b/enocean/__init__.py
@@ -0,0 +1,3 @@
+# -*- encoding: utf-8 -*-
+
+__version__ = '0.71.0'
diff --git a/enocean/communicators/__init__.py b/enocean/communicators/__init__.py
index 7408bbe..91a5b90 100644
--- a/enocean/communicators/__init__.py
+++ b/enocean/communicators/__init__.py
@@ -1,4 +1,5 @@
-''' Provider for different Communicator -classes for EnOcean. '''
-from enocean.communicators.communicator import Communicator
-from enocean.communicators.serialcommunicator import SerialCommunicator
-from enocean.communicators.tcpcommunicator import TCPCommunicator
+""" Provider for different Communicator -classes for EnOcean. """
+
+from .communicator import Communicator
+from .serialcommunicator import SerialCommunicator
+from .tcpcommunicator import TCPCommunicator
diff --git a/enocean/communicators/communicator.py b/enocean/communicators/communicator.py
index 9aff7c0..1d3951f 100644
--- a/enocean/communicators/communicator.py
+++ b/enocean/communicators/communicator.py
@@ -1,26 +1,27 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-import logging
-import datetime
+import datetime
+import logging
+import queue
import threading
-try:
- import queue
-except ImportError:
- import Queue as queue
-from enocean.protocol.packet import Packet, UTETeachInPacket
-from enocean.protocol.constants import PACKET, PARSE_RESULT, RETURN_CODE
+from typing import Union
+
+from ..protocol.constants import COMMON_COMMAND, PACKET, PARSE_RESULT, RETURN_CODE
+from ..protocol.packet import Packet, UTETeachInPacket
+from ..protocol.version_info import VersionInfo
+
+LOGGER = logging.getLogger('enocean.communicators.Communicator')
class Communicator(threading.Thread):
- '''
+ """
Communicator base-class for EnOcean.
Not to be used directly, only serves as base class for SerialCommunicator etc.
- '''
- logger = logging.getLogger('enocean.communicators.Communicator')
+ """
- def __init__(self, callback=None, teach_in=True):
- super(Communicator, self).__init__()
+ def __init__(self, callback: callable = None, teach_in: bool = True, loglevel=logging.NOTSET) -> None:
+ super().__init__()
+ LOGGER.setLevel(loglevel)
# Create an event to stop the thread
self._stop_flag = threading.Event()
# Input buffer
@@ -32,33 +33,34 @@ def __init__(self, callback=None, teach_in=True):
self.__callback = callback
# Internal variable for the Base ID of the module.
self._base_id = None
+ # Internal variable for the version info of the module.
+ self._version_info = None
# Should new messages be learned automatically? Defaults to True.
# TODO: Not sure if we should use CO_WR_LEARNMODE??
self.teach_in = teach_in
- def _get_from_send_queue(self):
- ''' Get message from send queue, if one exists '''
+ def _get_from_send_queue(self) -> Union[Packet, None]:
+ """ Get message from send queue, if one exists """
try:
packet = self.transmit.get(block=False)
- self.logger.info('Sending packet')
- self.logger.debug(packet)
return packet
except queue.Empty:
pass
return None
- def send(self, packet):
+ def send(self, packet: Packet) -> bool:
+ LOGGER.debug(f'sending: {packet}')
if not isinstance(packet, Packet):
- self.logger.error('Object to send must be an instance of Packet')
+ LOGGER.error('Object to send must be an instance of Packet')
return False
self.transmit.put(packet)
return True
- def stop(self):
+ def stop(self) -> None:
self._stop_flag.set()
- def parse(self):
- ''' Parses messages and puts them to receive queue '''
+ def parse(self) -> Union[None, PARSE_RESULT]:
+ """ Parses messages and puts them to receive queue """
# Loop while we get new messages
while True:
status, self._buffer, packet = Packet.parse_msg(self._buffer)
@@ -72,32 +74,49 @@ def parse(self):
if isinstance(packet, UTETeachInPacket) and self.teach_in:
response_packet = packet.create_response_packet(self.base_id)
- self.logger.info('Sending response to UTE teach-in.')
+ LOGGER.info('Sending response to UTE teach-in.')
self.send(response_packet)
+ LOGGER.debug(f"received: {packet}")
if self.__callback is None:
self.receive.put(packet)
else:
self.__callback(packet)
- self.logger.debug(packet)
+
+ @property # getter
+ def callback(self):
+ return self.__callback
+
+ @callback.setter
+ def callback(self, callback):
+ self.__callback = callback
@property
- def base_id(self):
- ''' Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. '''
+ def base_id(self) -> Union[None, list[int, int, int, int]]:
+ """ Fetches Base ID from the transmitter, if required. Otherwise, returns the currently set Base ID. """
# If base id is already set, return it.
if self._base_id is not None:
return self._base_id
+ start = datetime.datetime.now()
+
# Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module
- self.send(Packet(PACKET.COMMON_COMMAND, data=[0x08]))
- # Loop over 10 times, to make sure we catch the response.
- # Thanks to timeout, shouldn't take more than a second.
- # Unfortunately, all other messages received during this time are ignored.
- for i in range(0, 10):
+ self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_IDBASE.value], optional=[]))
+
+ # wait at most 1 second for the response
+ while True:
+ seconds_elapsed = (datetime.datetime.now() - start).total_seconds()
+ if seconds_elapsed > 1:
+ self.logger.error("Could not obtain base id from module within 1 second (timeout).")
+ break
try:
packet = self.receive.get(block=True, timeout=0.1)
# We're only interested in responses to the request in question.
- if packet.packet_type == PACKET.RESPONSE and packet.response == RETURN_CODE.OK and len(packet.response_data) == 4: # noqa: E501
+ if (
+ packet.packet_type == PACKET.RESPONSE
+ and packet.response == RETURN_CODE.OK
+ and len(packet.response_data) == 4
+ ):
# Base ID is set in the response data.
self._base_id = packet.response_data
# Put packet back to the Queue, so the user can also react to it if required...
@@ -111,6 +130,69 @@ def base_id(self):
return self._base_id
@base_id.setter
- def base_id(self, base_id):
- ''' Sets the Base ID manually, only for testing purposes. '''
+ def base_id(self, base_id: list[int, int, int, int]):
+ """ Sets the Base ID manually, only for testing purposes. """
self._base_id = base_id
+
+ @property
+ def chip_id(self):
+ ''' Fetches Chip ID from the transmitter, if required. Otherwise returns the currently set Chip ID. '''
+ if self.version_info is not None:
+ return self.version_info.chip_id
+
+ return None
+
+ @property
+ def version_info(self):
+ ''' Fetches version info from the transmitter, if required. Otherwise returns the currently set version info. '''
+
+ # If version info is already set, return it.
+ if self._version_info is not None:
+ return self._version_info
+
+ start = datetime.datetime.now()
+
+ # Send COMMON_COMMAND 0x03, CO_RD_VERSION request to the module
+ self.send(Packet(PACKET.COMMON_COMMAND, data=[COMMON_COMMAND.CO_RD_VERSION.value], optional=[]))
+
+ # wait at most 1 second for the response
+ while True:
+ seconds_elapsed = (datetime.datetime.now() - start).total_seconds()
+ if seconds_elapsed > 1:
+ LOGGER.warning("Could not obtain version info from module within 1 second (timeout).")
+ break
+
+ try:
+ packet = self.receive.get(block=True, timeout=0.1)
+ if packet.packet_type == PACKET.RESPONSE and packet.response == RETURN_CODE.OK and len(packet.response_data) == 32:
+ # interpret the version info
+ self._version_info: VersionInfo = VersionInfo()
+ res = packet.response_data
+
+ self._version_info.app_version.main = res[0]
+ self._version_info.app_version.beta = res[1]
+ self._version_info.app_version.alpha = res[2]
+ self._version_info.app_version.build = res[3]
+
+ self._version_info.api_version.main = res[4]
+ self._version_info.api_version.beta = res[5]
+ self._version_info.api_version.alpha = res[6]
+ self._version_info.api_version.build = res[7]
+
+ self._version_info.chip_id = [
+ res[8], res[9], res[10], res[11]
+ ]
+ self._version_info.chip_version = int.from_bytes(res[12:15], 'big')
+
+ self._version_info.app_description = bytearray(res[16:32]).decode('utf8').strip()
+
+ # Put packet back to the Queue, so the user can also react to it if required...
+ self.receive.put(packet)
+ break
+ # Put other packets back to the Queue.
+ self.receive.put(packet)
+ except queue.Empty:
+ continue
+ # Return the current version info (might be None).
+ return self._version_info
+
diff --git a/enocean/communicators/serialcommunicator.py b/enocean/communicators/serialcommunicator.py
index ffe4201..6a1829d 100644
--- a/enocean/communicators/serialcommunicator.py
+++ b/enocean/communicators/serialcommunicator.py
@@ -1,23 +1,26 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import logging
-import serial
import time
-from enocean.communicators.communicator import Communicator
+import serial
+
+from .communicator import Communicator
+
+LOGGER = logging.getLogger('enocean.communicators.SerialCommunicator')
class SerialCommunicator(Communicator):
- ''' Serial port communicator class for EnOcean radio '''
- logger = logging.getLogger('enocean.communicators.SerialCommunicator')
+ """ Serial port communicator class for EnOcean radio """
- def __init__(self, port='/dev/ttyAMA0', callback=None):
- super(SerialCommunicator, self).__init__(callback)
+ def __init__(self, port: str = '/dev/ttyAMA0', callback: callable = None, loglevel=logging.NOTSET) -> None:
+ super().__init__(callback, loglevel=loglevel)
+ LOGGER.setLevel(loglevel)
# Initialize serial port
self.__ser = serial.Serial(port, 57600, timeout=0.1)
- def run(self):
- self.logger.info('SerialCommunicator started')
+ def run(self) -> None:
+ LOGGER.info('SerialCommunicator started')
while not self._stop_flag.is_set():
# If there's messages in transmit queue
# send them
@@ -34,10 +37,11 @@ def run(self):
try:
self._buffer.extend(bytearray(self.__ser.read(16)))
except serial.SerialException:
- self.logger.error('Serial port exception! (device disconnected or multiple access on port?)')
+ LOGGER.error('Serial port exception! (device disconnected or multiple access on port?)')
self.stop()
+ continue
self.parse()
time.sleep(0)
self.__ser.close()
- self.logger.info('SerialCommunicator stopped')
+ LOGGER.info('SerialCommunicator stopped')
diff --git a/enocean/communicators/tcpcommunicator.py b/enocean/communicators/tcpcommunicator.py
index 239012d..fecc846 100644
--- a/enocean/communicators/tcpcommunicator.py
+++ b/enocean/communicators/tcpcommunicator.py
@@ -1,22 +1,24 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import logging
import socket
-from enocean.communicators.communicator import Communicator
+from .communicator import Communicator
+
+LOGGER = logging.getLogger('enocean.communicators.TCPCommunicator')
class TCPCommunicator(Communicator):
- ''' Socket communicator class for EnOcean radio '''
- logger = logging.getLogger('enocean.communicators.TCPCommunicator')
+ """ Socket communicator class for EnOcean radio """
- def __init__(self, host='', port=9637):
- super(TCPCommunicator, self).__init__()
+ def __init__(self, host: str = '', port: int = 9637, loglevel=logging.NOTSET) -> None:
+ super().__init__(loglevel=loglevel)
+ LOGGER.setLevel(loglevel)
self.host = host
self.port = port
- def run(self):
- self.logger.info('TCPCommunicator started')
+ def run(self) -> None:
+ LOGGER.info('TCPCommunicator started')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((self.host, self.port))
sock.listen(5)
@@ -27,7 +29,7 @@ def run(self):
(client, addr) = sock.accept()
except socket.timeout:
continue
- self.logger.debug('Client "%s" connected' % (addr))
+ LOGGER.debug(f'Client "{addr}" connected')
client.settimeout(0.5)
while True and not self._stop_flag.is_set():
try:
@@ -39,6 +41,6 @@ def run(self):
self._buffer.extend(bytearray(data))
self.parse()
client.close()
- self.logger.debug('Client disconnected')
+ LOGGER.debug('Client disconnected')
sock.close()
- self.logger.info('TCPCommunicator stopped')
+ LOGGER.info('TCPCommunicator stopped')
diff --git a/enocean/communicators/utils.py b/enocean/communicators/utils.py
index 646841d..604aaad 100644
--- a/enocean/communicators/utils.py
+++ b/enocean/communicators/utils.py
@@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import socket
diff --git a/enocean/consolelogger.py b/enocean/consolelogger.py
index de3e01d..0557c15 100644
--- a/enocean/consolelogger.py
+++ b/enocean/consolelogger.py
@@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import logging
import logging.handlers
diff --git a/enocean/protocol/EEP.xml b/enocean/protocol/EEP.xml
index b7d79e2..c909268 100644
--- a/enocean/protocol/EEP.xml
+++ b/enocean/protocol/EEP.xml
@@ -496,6 +496,34 @@
+
+
+
+
+ 0
+ 250
+
+
+ 0
+ 100
+
+
+
+
+ 0
+ 250
+
+
+ -20
+ 60
+
+
+
+
+
+
+
+
@@ -524,6 +552,30 @@
+
+
+
+
+ 0
+ 199
+
+
+ 0
+ 100
+
+
+
+
+ 0
+ 1599
+
+
+ -40
+ +120
+
+
+
+
@@ -622,9 +674,37 @@
+
+
+
+
+ 0
+ 250
+
+
+ 0
+ 5.000000
+
+
+
+
+ 0
+ 1000
+
+
+ 0
+ 1000
+
+
+
+
+
+
+
+
-
+
@@ -666,6 +746,90 @@
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 5.100000
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 1020
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 51
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 5.100000
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 1530
+
+
+
+
+ 0
+ 255
+
+
+ -30
+ 50
+
+
+
+
+
+
+
+
+
+
+
+
@@ -745,6 +909,20 @@
+
+
+
+
+ 0
+ 255
+
+
+ 0
+ 2000
+
+
+
+
@@ -771,7 +949,7 @@
-
+
@@ -799,7 +977,7 @@
-
+
@@ -827,7 +1005,7 @@
-
+
@@ -865,7 +1043,7 @@
-
+
@@ -1394,6 +1572,133 @@
+
+
+
+
+ 0
+ 100
+
+
+ 0
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 100
+
+
+ 0
+ 100
+
+
+
+
+ 0
+ 80
+
+
+ 0
+ 40
+
+
+
+
+
+ 0
+ 160
+
+
+ 0
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1621,6 +1926,364 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 4294967295
+
+
+ 1
+ 4294967295
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 4294967295
+
+
+ 1
+ 4294967295
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 4294967295
+
+
+ 1
+ 4294967295
+
+
+
diff --git a/enocean/protocol/constants.py b/enocean/protocol/constants.py
index 8dd3e74..1f75624 100644
--- a/enocean/protocol/constants.py
+++ b/enocean/protocol/constants.py
@@ -1,5 +1,5 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
from enum import IntEnum
@@ -26,7 +26,15 @@ class PACKET(IntEnum):
COMMAND_2_4 = 0x11
+# EnOceanSerialProtocol3-1-1.pdf / 33
+# noinspection PyPep8Naming
+class COMMON_COMMAND(IntEnum):
+ CO_RD_VERSION = 0x03
+ CO_RD_IDBASE = 0x08
+
+
# EnOceanSerialProtocol3.pdf / 18
+# noinspection PyPep8Naming
class RETURN_CODE(IntEnum):
OK = 0x00
ERROR = 0x01
@@ -36,6 +44,7 @@ class RETURN_CODE(IntEnum):
# EnOceanSerialProtocol3.pdf / 20
+# noinspection PyPep8Naming
class EVENT_CODE(IntEnum):
SA_RECLAIM_NOT_SUCCESFUL = 0x01
SA_CONFIRM_LEARN = 0x02
@@ -45,6 +54,7 @@ class EVENT_CODE(IntEnum):
# EnOcean_Equipment_Profiles_EEP_V2.61_public.pdf / 8
+# noinspection PyPep8Naming
class RORG(IntEnum):
UNDEFINED = 0x00
RPS = 0xF6
@@ -59,10 +69,13 @@ class RORG(IntEnum):
SYS_EX = 0xC5
SEC = 0x30
SEC_ENCAPS = 0x31
+ SEC_MAN = 0x34
+ SIGNAL = 0xD0
UTE = 0xD4
# Results for message parsing
+# noinspection PyPep8Naming
class PARSE_RESULT(IntEnum):
OK = 0x00
INCOMPLETE = 0x01
@@ -71,7 +84,7 @@ class PARSE_RESULT(IntEnum):
# Data byte indexing
# Starts from the end, so works on messages of all length.
-class DB0(object):
+class DB0:
BIT_0 = -1
BIT_1 = -2
BIT_2 = -3
@@ -82,7 +95,7 @@ class DB0(object):
BIT_7 = -8
-class DB1(object):
+class DB1:
BIT_0 = -9
BIT_1 = -10
BIT_2 = -11
@@ -93,7 +106,7 @@ class DB1(object):
BIT_7 = -16
-class DB2(object):
+class DB2:
BIT_0 = -17
BIT_1 = -18
BIT_2 = -19
@@ -104,7 +117,7 @@ class DB2(object):
BIT_7 = -24
-class DB3(object):
+class DB3:
BIT_0 = -25
BIT_1 = -26
BIT_2 = -27
@@ -115,7 +128,7 @@ class DB3(object):
BIT_7 = -32
-class DB4(object):
+class DB4:
BIT_0 = -33
BIT_1 = -34
BIT_2 = -35
@@ -126,7 +139,7 @@ class DB4(object):
BIT_7 = -40
-class DB5(object):
+class DB5:
BIT_0 = -41
BIT_1 = -42
BIT_2 = -43
@@ -137,7 +150,7 @@ class DB5(object):
BIT_7 = -48
-class DB6(object):
+class DB6:
BIT_0 = -49
BIT_1 = -50
BIT_2 = -51
@@ -146,3 +159,7 @@ class DB6(object):
BIT_5 = -54
BIT_6 = -55
BIT_7 = -56
+
+class COMMON_COMMAND_CODE(IntEnum):
+ CO_RD_VERSION = 0x03
+ CO_RD_IDBASE = 0x08
\ No newline at end of file
diff --git a/enocean/protocol/crc8.py b/enocean/protocol/crc8.py
index 5dd9b9d..c4a061b 100644
--- a/enocean/protocol/crc8.py
+++ b/enocean/protocol/crc8.py
@@ -1,5 +1,4 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
# https://gist.github.com/hypebeast/3833758
CRC_TABLE = (
diff --git a/enocean/protocol/eep.py b/enocean/protocol/eep.py
index 8da3156..a74d8f7 100644
--- a/enocean/protocol/eep.py
+++ b/enocean/protocol/eep.py
@@ -1,46 +1,40 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-import os
+
import logging
-from sys import version_info
from collections import OrderedDict
-from bs4 import BeautifulSoup
+from importlib.resources import files
+from typing import Union
+
+from bs4 import BeautifulSoup, Tag
-import enocean.utils
-# Left as a helper
-from enocean.protocol.constants import RORG # noqa: F401
+from .. import utils
-class EEP(object):
+class EEP:
logger = logging.getLogger('enocean.protocol.eep')
- def __init__(self):
+ def __init__(self) -> None:
self.init_ok = False
self.telegrams = {}
- eep_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'EEP.xml')
try:
- if version_info[0] > 2:
- with open(eep_path, 'r', encoding='UTF-8') as xml_file:
- self.soup = BeautifulSoup(xml_file.read(), "html.parser")
- else:
- with open(eep_path, 'r') as xml_file:
- self.soup = BeautifulSoup(xml_file.read(), "html.parser")
+ xml_content = files('enocean.protocol').joinpath('EEP.xml').read_text(encoding='utf-8')
+ self.soup = BeautifulSoup(xml_content, features="lxml-xml")
+ self.__xml_to_dict()
self.init_ok = True
- self.__load_xml()
except IOError:
# Impossible to test with the current structure?
# To be honest, as the XML is included with the library,
# there should be no possibility of ever reaching this...
- self.logger.warn('Cannot load protocol file!')
+ self.logger.warning('Cannot load protocol file!')
self.init_ok = False
- def __load_xml(self):
+ def __xml_to_dict(self) -> None:
self.telegrams = {
- enocean.utils.from_hex_string(telegram['rorg']): {
- enocean.utils.from_hex_string(function['func']): {
- enocean.utils.from_hex_string(type['type'], ): type
- for type in function.find_all('profile')
+ utils.from_hex_string(telegram['rorg']): {
+ utils.from_hex_string(function['func']): {
+ utils.from_hex_string(typ['type'], ): typ
+ for typ in function.find_all('profile')
}
for function in telegram.find_all('profiles')
}
@@ -48,15 +42,15 @@ def __load_xml(self):
}
@staticmethod
- def _get_raw(source, bitarray):
- ''' Get raw data as integer, based on offset and size '''
+ def _get_raw(source, bitarray) -> int:
+ """ Get raw data as integer, based on offset and size """
offset = int(source['offset'])
size = int(source['size'])
return int(''.join(['1' if digit else '0' for digit in bitarray[offset:offset + size]]), 2)
@staticmethod
- def _set_raw(target, raw_value, bitarray):
- ''' put value into bit array '''
+ def _set_raw(target: Tag, raw_value: int, bitarray: list) -> list:
+ """ put value into bit array """
offset = int(target['offset'])
size = int(target['size'])
for digit in range(size):
@@ -64,13 +58,13 @@ def _set_raw(target, raw_value, bitarray):
return bitarray
@staticmethod
- def _get_rangeitem(source, raw_value):
+ def _get_rangeitem(source: Tag, raw_value: int) -> Tag:
for rangeitem in source.find_all('rangeitem'):
if raw_value in range(int(rangeitem.get('start', -1)), int(rangeitem.get('end', -1)) + 1):
return rangeitem
- def _get_value(self, source, bitarray):
- ''' Get value, based on the data in XML '''
+ def _get_value(self, source: Tag, bitarray: list) -> dict:
+ """ Get value, based on the data in XML """
raw_value = self._get_raw(source, bitarray)
rng = source.find('range')
@@ -84,14 +78,14 @@ def _get_value(self, source, bitarray):
return {
source['shortcut']: {
'description': source.get('description'),
- 'unit': source['unit'],
+ 'unit': source.get('unit'),
'value': (scl_max - scl_min) / (rng_max - rng_min) * (raw_value - rng_min) + scl_min,
'raw_value': raw_value,
}
}
- def _get_enum(self, source, bitarray):
- ''' Get enum value, based on the data in XML '''
+ def _get_enum(self, source: Tag, bitarray: list) -> dict:
+ """ Get enum value, based on the data in XML """
raw_value = self._get_raw(source, bitarray)
# Find value description.
@@ -106,8 +100,8 @@ def _get_enum(self, source, bitarray):
}
}
- def _get_boolean(self, source, bitarray):
- ''' Get boolean value, based on the data in XML '''
+ def _get_boolean(self, source: Tag, bitarray: list) -> dict:
+ """ Get boolean value, based on the data in XML """
raw_value = self._get_raw(source, bitarray)
return {
source['shortcut']: {
@@ -118,8 +112,8 @@ def _get_boolean(self, source, bitarray):
}
}
- def _set_value(self, target, value, bitarray):
- ''' set given numeric value to target field in bitarray '''
+ def _set_value(self, target: Tag, value: Union[int, float], bitarray: list) -> list:
+ """ set given numeric value to target field in bitarray """
# derive raw value
rng = target.find('range')
rng_min = float(rng.find('min').text)
@@ -131,8 +125,8 @@ def _set_value(self, target, value, bitarray):
# store value in bitfield
return self._set_raw(target, int(raw_value), bitarray)
- def _set_enum(self, target, value, bitarray):
- ''' set given enum value (by string or integer value) to target field in bitarray '''
+ def _set_enum(self, target: Tag, value: Union[int, str], bitarray: list) -> list:
+ """ set given enum value (by string or integer value) to target field in bitarray """
# derive raw value
if isinstance(value, int):
# check whether this value exists
@@ -140,39 +134,42 @@ def _set_enum(self, target, value, bitarray):
# set integer values directly
raw_value = value
else:
- raise ValueError('Enum value "%s" not found in EEP.' % (value))
+ raise ValueError(f'Enum value "{value}" not found in EEP.')
else:
value_item = target.find('item', {'description': value})
if value_item is None:
- raise ValueError('Enum description for value "%s" not found in EEP.' % (value))
+ raise ValueError(f'Enum description for value "{value}" not found in EEP.')
raw_value = int(value_item['value'])
return self._set_raw(target, raw_value, bitarray)
@staticmethod
- def _set_boolean(target, data, bitarray):
- ''' set given value to target bit in bitarray '''
+ def _set_boolean(target: Tag, data: bool, bitarray: list) -> list:
+ """ set given value to target bit in bitarray """
bitarray[int(target['offset'])] = data
return bitarray
- def find_profile(self, bitarray, eep_rorg, rorg_func, rorg_type, direction=None, command=None):
- ''' Find profile and data description, matching RORG, FUNC and TYPE '''
+ def find_profile(self, eep_rorg: int, rorg_func: int, rorg_type: int,
+ direction=None, command=None) -> Union[None, Tag]:
+ """ Find profile and data description, matching RORG, FUNC and TYPE """
if not self.init_ok:
- self.logger.warn('EEP.xml not loaded!')
+ self.logger.warning('EEP.xml not loaded!')
return None
if eep_rorg not in self.telegrams.keys():
- self.logger.warn('Cannot find rorg %s in EEP!', hex(eep_rorg))
+ self.logger.warning(f'Cannot find rorg {hex(eep_rorg)} in EEP!')
return None
if rorg_func not in self.telegrams[eep_rorg].keys():
- self.logger.warn('Cannot find rorg %s func %s in EEP!', hex(eep_rorg), hex(rorg_func))
+ self.logger.warning(f'Cannot find rorg {hex(eep_rorg)} func {hex(rorg_func)} in EEP!')
return None
if rorg_type not in self.telegrams[eep_rorg][rorg_func].keys():
- self.logger.warn('Cannot find rorg %s func %s type %s in EEP!', hex(eep_rorg), hex(rorg_func), hex(rorg_type))
+ self.logger.warning(
+ f'Cannot find rorg {hex(eep_rorg)} func {hex(rorg_func)} type {hex(rorg_type)} in EEP!'
+ )
return None
- profile = self.telegrams[eep_rorg][rorg_func][rorg_type]
+ profile: Tag = self.telegrams[eep_rorg][rorg_func][rorg_type]
if command:
# multiple commands can be defined, with the command id always in same location (per RORG-FUNC-TYPE).
@@ -191,8 +188,8 @@ def find_profile(self, bitarray, eep_rorg, rorg_func, rorg_type, direction=None,
return profile.find('data', recursive=False)
return profile.find('data', {'direction': direction}, recursive=False)
- def get_values(self, profile, bitarray, status):
- ''' Get keys and values from bitarray '''
+ def get_values(self, profile: Tag, bitarray: list, status: list) -> tuple:
+ """ Get keys and values from bitarray """
if not self.init_ok or profile is None:
return [], {}
@@ -200,16 +197,20 @@ def get_values(self, profile, bitarray, status):
for source in profile.contents:
if not source.name:
continue
- if source.name == 'value':
+ elif source.name == 'value':
output.update(self._get_value(source, bitarray))
- if source.name == 'enum':
- output.update(self._get_enum(source, bitarray))
- if source.name == 'status':
+ elif source.name == 'enum':
+ try:
+ output.update(self._get_enum(source, bitarray))
+ except (ValueError, TypeError):
+ pass
+ elif source.name == 'status':
output.update(self._get_boolean(source, status))
+
return output.keys(), output
- def set_values(self, profile, data, status, properties):
- ''' Update data based on data contained in properties '''
+ def set_values(self, profile: Tag, data: list, status: list, properties: dict) -> tuple:
+ """ Update data based on data contained in properties """
if not self.init_ok or profile is None:
return data, status
@@ -218,7 +219,7 @@ def set_values(self, profile, data, status, properties):
target = profile.find(shortcut=shortcut)
if not target:
# TODO: Should we raise an error?
- self.logger.warning('Cannot find data description for shortcut %s', shortcut)
+ self.logger.warning(f"Cannot find data description for shortcut '{shortcut}'")
continue
# update bit_data
diff --git a/enocean/protocol/packet.py b/enocean/protocol/packet.py
index 14fdc34..3c6d96b 100644
--- a/enocean/protocol/packet.py
+++ b/enocean/protocol/packet.py
@@ -1,25 +1,26 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import logging
from collections import OrderedDict
+from typing import Any, Union
-import enocean.utils
-from enocean.protocol import crc8
-from enocean.protocol.eep import EEP
-from enocean.protocol.constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6
+from .crc8 import calc
+from .constants import PACKET, RORG, PARSE_RESULT, DB0, DB2, DB3, DB4, DB6
+from .eep import EEP
+from ..utils import combine_hex, from_bitarray, to_hex_string, to_bitarray
-class Packet(object):
- '''
+class Packet:
+ """
Base class for Packet.
Mainly used for for packet generation and
Packet.parse_msg(buf) for parsing message.
parse_msg() returns subclass, if one is defined for the data type.
- '''
+ """
eep = EEP()
logger = logging.getLogger('enocean.protocol.packet')
- def __init__(self, packet_type, data=None, optional=None):
+ def __init__(self, packet_type: PACKET, data: Union[None, list] = None, optional: Union[None, list] = None) -> None:
self.packet_type = packet_type
self.rorg = RORG.UNDEFINED
self.rorg_func = None
@@ -47,35 +48,32 @@ def __init__(self, packet_type, data=None, optional=None):
self.parse()
- def __str__(self):
- return '0x%02X %s %s %s' % (
- self.packet_type,
- [hex(o) for o in self.data],
- [hex(o) for o in self.optional],
- self.parsed)
+ def __str__(self) -> str:
+ return (f'0x{self.packet_type:02X} {[hex(o) for o in self.data]}'
+ f' {[hex(o) for o in self.optional]} {self.parsed}')
- def __unicode__(self):
+ def __unicode__(self) -> str:
return self.__str__()
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
return self.packet_type == other.packet_type and self.rorg == other.rorg \
and self.data == other.data and self.optional == other.optional
@property
- def _bit_data(self):
+ def _bit_data(self) -> list[bool]:
# First and last 5 bits are always defined, so the data we're modifying is between them...
# TODO: This is valid for the packets we're currently manipulating.
# Needs the redefinition of Packet.data -> Packet.message.
# Packet.data would then only have the actual, documented data-bytes.
# Packet.message would contain the whole message.
- # See discussion in issue #14
- return enocean.utils.to_bitarray(self.data[1:len(self.data) - 5], (len(self.data) - 6) * 8)
+ # See discussion in issue https://github.com/kipe/enocean/issues/14
+ return to_bitarray(self.data[1:len(self.data) - 5], (len(self.data) - 6) * 8)
@_bit_data.setter
- def _bit_data(self, value):
+ def _bit_data(self, value: list[bool]) -> None:
# The same as getting the data, first and last 5 bits are ommitted, as they are defined...
for byte in range(len(self.data) - 6):
- self.data[byte+1] = enocean.utils.from_bitarray(value[byte*8:(byte+1)*8])
+ self.data[byte + 1] = from_bitarray(value[byte * 8:(byte + 1) * 8])
# # COMMENTED OUT, AS NOTHING TOUCHES _bit_optional FOR NOW.
# # Thus, this is also untested.
@@ -92,22 +90,24 @@ def _bit_data(self, value):
# self.data[byte+1] = enocean.utils.from_bitarray(value[byte*8:(byte+1)*8])
@property
- def _bit_status(self):
- return enocean.utils.to_bitarray(self.status)
+ def _bit_status(self) -> list[bool]:
+ return to_bitarray(self.status)
@_bit_status.setter
- def _bit_status(self, value):
- self.status = enocean.utils.from_bitarray(value)
+ def _bit_status(self, value: list[bool]) -> None:
+ self.status = from_bitarray(value)
@staticmethod
- def parse_msg(buf):
- '''
+ def parse_msg(buf: Union[list, bytearray]) -> (PARSE_RESULT, list, Any):
+ # 'Any' in return type should be Union[None | UTETeachInPacket | ResponsePacket | EventPacket | Packet]
+ # how to realize that?
+ """
Parses message from buffer.
returns:
- PARSE_RESULT
- remaining buffer
- Packet -object (if message was valid, else None)
- '''
+ """
# If the buffer doesn't contain 0x55 (start char)
# the message isn't needed -> ignore
if 0x55 not in buf:
@@ -137,12 +137,12 @@ def parse_msg(buf):
opt_data = msg[6 + data_len:6 + data_len + opt_len]
# Check CRCs for header and data
- if msg[5] != crc8.calc(msg[1:5]):
+ if msg[5] != calc(msg[1:5]):
# Fail if doesn't match message
Packet.logger.error('Header CRC error!')
# Return CRC_MISMATCH
return PARSE_RESULT.CRC_MISMATCH, buf, None
- if msg[6 + data_len + opt_len] != crc8.calc(msg[6:6 + data_len + opt_len]):
+ if msg[6 + data_len + opt_len] != calc(msg[6:6 + data_len + opt_len]):
# Fail if doesn't match message
Packet.logger.error('Data CRC error!')
# Return CRC_MISMATCH
@@ -165,11 +165,16 @@ def parse_msg(buf):
return PARSE_RESULT.OK, buf, packet
@staticmethod
- def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None,
- destination=None,
- sender=None,
- learn=False, **kwargs):
- '''
+ def create(
+ packet_type: PACKET, rorg: RORG, rorg_func: int, rorg_type: int,
+ direction=None,
+ command: Union[None, int] = None,
+ destination: Union[None, list] = None,
+ sender: Union[None, list] = None,
+ learn: bool = False,
+ **kwargs
+ ):
+ """
Creates an packet ready for sending.
Uses rorg, rorg_func and rorg_type to determine the values set based on EEP.
Additional arguments (**kwargs) are used for setting the values.
@@ -182,7 +187,7 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None
- Require sender to be set? Would force the "correct" sender to be set.
- Do we need to set telegram control bits?
Might be useful for acting as a repeater?
- '''
+ """
if packet_type != PACKET.RADIO_ERP1:
# At least for now, only support PACKET.RADIO_ERP1.
@@ -203,10 +208,10 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None
sender = [0xDE, 0xAD, 0xBE, 0xEF]
if not isinstance(destination, list) or len(destination) != 4:
- raise ValueError('Destination must a list containing 4 (numeric) values.')
+ raise ValueError('Destination must be a list containing 4 (numeric) values.')
if not isinstance(sender, list) or len(sender) != 4:
- raise ValueError('Sender must a list containing 4 (numeric) values.')
+ raise ValueError('Sender must be a list containing 4 (numeric) values.')
packet = Packet(packet_type, data=[], optional=[])
packet.rorg = rorg
@@ -243,11 +248,12 @@ def create(packet_type, rorg, rorg_func, rorg_type, direction=None, command=None
# For example, stuff like RadioPacket.learn should be set.
packet = Packet.parse_msg(packet.build())[2]
packet.rorg = rorg
+ # noinspection PyUnresolvedReferences
packet.parse_eep(rorg_func, rorg_type, direction, command)
return packet
- def parse(self):
- ''' Parse data from Packet '''
+ def parse(self) -> OrderedDict:
+ """ Parse data from Packet """
# Parse status from messages
if self.rorg in [RORG.RPS, RORG.BS1, RORG.BS4]:
self.status = self.data[-1]
@@ -256,19 +262,20 @@ def parse(self):
if self.rorg in [RORG.RPS, RORG.BS1, RORG.BS4]:
# These message types should have repeater count in the last for bits of status.
- self.repeater_count = enocean.utils.from_bitarray(self._bit_status[4:])
+ self.repeater_count = from_bitarray(self._bit_status[4:])
return self.parsed
- def select_eep(self, rorg_func, rorg_type, direction=None, command=None):
- ''' Set EEP based on FUNC and TYPE '''
+ def select_eep(self, rorg_func: int, rorg_type: int, direction=None, command: Union[None, int] = None) -> bool:
+ """ Set EEP based on FUNC and TYPE """
# set EEP profile
self.rorg_func = rorg_func
self.rorg_type = rorg_type
- self._profile = self.eep.find_profile(self._bit_data, self.rorg, rorg_func, rorg_type, direction, command)
+ self._profile = self.eep.find_profile(self.rorg, rorg_func, rorg_type, direction, command)
return self._profile is not None
- def parse_eep(self, rorg_func=None, rorg_type=None, direction=None, command=None):
- ''' Parse EEP based on FUNC and TYPE '''
+ def parse_eep(self, rorg_func: Union[None, int] = None, rorg_type: Union[None, int] = None,
+ direction=None, command: Union[None, int] = None) -> list:
+ """ Parse EEP based on FUNC and TYPE """
# set EEP profile, if demanded
if rorg_func is not None and rorg_type is not None:
self.select_eep(rorg_func, rorg_type, direction, command)
@@ -277,18 +284,19 @@ def parse_eep(self, rorg_func=None, rorg_type=None, direction=None, command=None
self.parsed.update(values)
return list(provides)
- def set_eep(self, data):
- ''' Update packet data based on EEP. Input data is a dictionary with keys corresponding to the EEP. '''
+ def set_eep(self, data: dict) -> None:
+ """ Update packet data based on EEP. Input data is a dictionary with keys corresponding to the EEP. """
self._bit_data, self._bit_status = self.eep.set_values(self._profile, self._bit_data, self._bit_status, data)
- def build(self):
- ''' Build Packet for sending to EnOcean controller '''
+ def build(self) -> list:
+ """ Build Packet for sending to EnOcean controller """
data_length = len(self.data)
+ # noinspection PyListCreation
ords = [0x55, (data_length >> 8) & 0xFF, data_length & 0xFF, len(self.optional), int(self.packet_type)]
- ords.append(crc8.calc(ords[1:5]))
+ ords.append(calc(ords[1:5]))
ords.extend(self.data)
ords.extend(self.optional)
- ords.append(crc8.calc(ords[6:]))
+ ords.append(calc(ords[6:]))
return ords
@@ -299,33 +307,34 @@ class RadioPacket(Packet):
learn = True
contains_eep = False
- def __str__(self):
+ def __str__(self) -> str:
packet_str = super(RadioPacket, self).__str__()
- return '%s->%s (%d dBm): %s' % (self.sender_hex, self.destination_hex, self.dBm, packet_str)
+ return f'{self.sender_hex}->{self.destination_hex} ({self.dBm} dBm): {packet_str}'
@staticmethod
- def create(rorg, rorg_func, rorg_type, direction=None, command=None,
- destination=None, sender=None, learn=False, **kwargs):
+ def create(rorg: RORG, rorg_func: int, rorg_type: int, direction=None,
+ command: Union[None, int] = None, destination: Union[None, list] = None,
+ sender: Union[None, list] = None, learn: bool = False, **kwargs) -> Packet:
return Packet.create(PACKET.RADIO_ERP1, rorg, rorg_func, rorg_type,
direction, command, destination, sender, learn, **kwargs)
@property
- def sender_int(self):
- return enocean.utils.combine_hex(self.sender)
+ def sender_int(self) -> int:
+ return combine_hex(self.sender)
@property
- def sender_hex(self):
- return enocean.utils.to_hex_string(self.sender)
+ def sender_hex(self) -> str:
+ return to_hex_string(self.sender)
@property
- def destination_int(self):
- return enocean.utils.combine_hex(self.destination)
+ def destination_int(self) -> int:
+ return combine_hex(self.destination)
@property
- def destination_hex(self):
- return enocean.utils.to_hex_string(self.destination)
+ def destination_hex(self) -> str:
+ return to_hex_string(self.destination)
- def parse(self):
+ def parse(self) -> OrderedDict:
self.destination = self.optional[1:5]
self.dBm = -self.optional[5]
self.sender = self.data[-5:-1]
@@ -343,11 +352,13 @@ def parse(self):
self.contains_eep = self._bit_data[DB0.BIT_7]
if self.contains_eep:
# Get rorg_func and rorg_type from an unidirectional learn packet
- self.rorg_func = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_7:DB3.BIT_1])
- self.rorg_type = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_1:DB2.BIT_2])
- self.rorg_manufacturer = enocean.utils.from_bitarray(self._bit_data[DB2.BIT_2:DB0.BIT_7])
- self.logger.debug('learn received, EEP detected, RORG: 0x%02X, FUNC: 0x%02X, TYPE: 0x%02X, Manufacturer: 0x%02X' % (self.rorg, self.rorg_func, self.rorg_type, self.rorg_manufacturer)) # noqa: E501
-
+ self.rorg_func = from_bitarray(self._bit_data[DB3.BIT_7:DB3.BIT_1])
+ self.rorg_type = from_bitarray(self._bit_data[DB3.BIT_1:DB2.BIT_2])
+ self.rorg_manufacturer = from_bitarray(self._bit_data[DB2.BIT_2:DB0.BIT_7])
+ self.logger.debug(
+ f'learn received, EEP detected, RORG: 0x{self.rorg:X}, FUNC: 0x{self.rorg_func:X}, '
+ f'TYPE: 0x{self.rorg_type:X}, Manufacturer: 0x{self.rorg_manufacturer:X}'
+ )
return super(RadioPacket, self).parse()
@@ -373,23 +384,24 @@ class UTETeachInPacket(RadioPacket):
contains_eep = True
@property
- def bidirectional(self):
+ def bidirectional(self) -> bool:
return not self.unidirectional
@property
- def teach_in(self):
+ def teach_in(self) -> bool:
return self.request_type != self.DELETE
@property
- def delete(self):
+ def delete(self) -> bool:
return self.request_type == self.DELETE
- def parse(self):
+ def parse(self) -> OrderedDict:
super(UTETeachInPacket, self).parse()
self.unidirectional = not self._bit_data[DB6.BIT_7]
self.response_expected = not self._bit_data[DB6.BIT_6]
- self.request_type = enocean.utils.from_bitarray(self._bit_data[DB6.BIT_5:DB6.BIT_3])
- self.rorg_manufacturer = enocean.utils.from_bitarray(self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501
+ self.request_type = from_bitarray(self._bit_data[DB6.BIT_5:DB6.BIT_3])
+ self.rorg_manufacturer = from_bitarray(
+ self._bit_data[DB3.BIT_2:DB2.BIT_7] + self._bit_data[DB4.BIT_7:DB3.BIT_7]) # noqa: E501
self.channel = self.data[2]
self.rorg_type = self.data[5]
self.rorg_func = self.data[6]
@@ -398,16 +410,21 @@ def parse(self):
self.learn = True
return self.parsed
- def create_response_packet(self, sender_id, response=TEACHIN_ACCEPTED):
+ def create_response_packet(self, sender_id: list, response: Union[None, list] = None) -> RadioPacket:
+ if response is None:
+ response = self.TEACHIN_ACCEPTED
+
# Create data:
# - Respond with same RORG (UTE Teach-in)
# - Always use bidirectional communication, set response code, set command identifier.
# - Databytes 5 to 0 are copied from the original message
# - Set sender id and status
- data = [self.rorg] + \
- [enocean.utils.from_bitarray([True, False] + response + [False, False, False, True])] + \
- self.data[2:8] + \
- sender_id + [0]
+ data = (
+ [self.rorg]
+ + [from_bitarray([True, False] + response + [False, False, False, True])]
+ + self.data[2:8]
+ + sender_id + [0]
+ )
# Always use 0x03 to indicate sending, attach sender ID, dBm, and security level
optional = [0x03] + self.sender + [0xFF, 0x00]
@@ -419,7 +436,7 @@ class ResponsePacket(Packet):
response = 0
response_data = []
- def parse(self):
+ def parse(self) -> OrderedDict:
self.response = self.data[0]
self.response_data = self.data[1:]
return super(ResponsePacket, self).parse()
@@ -429,7 +446,7 @@ class EventPacket(Packet):
event = 0
event_data = []
- def parse(self):
+ def parse(self) -> OrderedDict:
self.event = self.data[0]
self.event_data = self.data[1:]
return super(EventPacket, self).parse()
diff --git a/enocean/protocol/version_info.py b/enocean/protocol/version_info.py
new file mode 100644
index 0000000..af98c4b
--- /dev/null
+++ b/enocean/protocol/version_info.py
@@ -0,0 +1,15 @@
+# -*- encoding: utf-8 -*-
+from __future__ import print_function, unicode_literals, division, absolute_import
+
+class VersionIdentifier(object):
+ main = 0
+ beta = 0
+ alpha = 0
+ build = 0
+
+class VersionInfo(object):
+ app_version: VersionIdentifier = VersionIdentifier()
+ api_version: VersionIdentifier = VersionIdentifier()
+ chip_id: 0
+ chip_version = 0
+ app_description = ''
\ No newline at end of file
diff --git a/enocean/tests/test_utils.py b/enocean/tests/test_utils.py
deleted file mode 100644
index 9112d7d..0000000
--- a/enocean/tests/test_utils.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-import enocean.utils
-
-
-def test_get_bit():
- assert enocean.utils.get_bit(1, 0) == 1
- assert enocean.utils.get_bit(8, 3) == 1
- assert enocean.utils.get_bit(6, 2) == 1
- assert enocean.utils.get_bit(6, 1) == 1
-
-
-def test_to_hex_string():
- assert enocean.utils.to_hex_string(0) == '00'
- assert enocean.utils.to_hex_string(15) == '0F'
- assert enocean.utils.to_hex_string(16) == '10'
- assert enocean.utils.to_hex_string(22) == '16'
-
- assert enocean.utils.to_hex_string([0, 15, 16, 22]) == '00:0F:10:16'
- assert enocean.utils.to_hex_string([0x00, 0x0F, 0x10, 0x16]) == '00:0F:10:16'
-
-
-def test_from_hex_string():
- assert enocean.utils.from_hex_string('00') == 0
- assert enocean.utils.from_hex_string('0F') == 15
- assert enocean.utils.from_hex_string('10') == 16
- assert enocean.utils.from_hex_string('16') == 22
-
- assert enocean.utils.from_hex_string('00:0F:10:16') == [0, 15, 16, 22]
- assert enocean.utils.from_hex_string('00:0F:10:16') == [0x00, 0x0F, 0x10, 0x16]
diff --git a/enocean/utils.py b/enocean/utils.py
index 528b5af..e4edfb1 100644
--- a/enocean/utils.py
+++ b/enocean/utils.py
@@ -1,40 +1,40 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+from typing import Union
-def get_bit(byte, bit):
- ''' Get bit value from byte '''
+def get_bit(byte: int, bit: int) -> int:
+ """ Get bit value from byte """
return (byte >> bit) & 0x01
-def combine_hex(data):
- ''' Combine list of integer values to one big integer '''
+def combine_hex(data: Union[list, bytearray]) -> int:
+ """ Combine list of integer values to one big integer """
output = 0x00
for i, value in enumerate(reversed(data)):
output |= (value << i * 8)
return output
-def to_bitarray(data, width=8):
- ''' Convert data (list of integers, bytearray or integer) to bitarray '''
+def to_bitarray(data: Union[list, bytearray], width=8) -> list[bool]:
+ """ Convert data (list of integers, bytearray or integer) to bitarray """
if isinstance(data, list) or isinstance(data, bytearray):
data = combine_hex(data)
return [True if digit == '1' else False for digit in bin(data)[2:].zfill(width)]
-def from_bitarray(data):
- ''' Convert bit array back to integer '''
+def from_bitarray(data: list[bool]) -> int:
+ """ Convert bit array back to integer """
return int(''.join(['1' if x else '0' for x in data]), 2)
-def to_hex_string(data):
- ''' Convert list of integers to a hex string, separated by ":" '''
+def to_hex_string(data: Union[list, int]) -> str:
+ """ Convert list of integers to a hex string, separated by ":" """
if isinstance(data, int):
- return '%02X' % data
- return ':'.join([('%02X' % o) for o in data])
+ return f'{data:02X}'
+ return ':'.join([f'{o:02X}' for o in data])
-def from_hex_string(hex_string):
+def from_hex_string(hex_string: str) -> Union[int, list]:
reval = [int(x, 16) for x in hex_string.split(':')]
if len(reval) == 1:
return reval[0]
diff --git a/examples/co_rd_version_example.py b/examples/co_rd_version_example.py
index 7ff2d42..68014d5 100755
--- a/examples/co_rd_version_example.py
+++ b/examples/co_rd_version_example.py
@@ -10,18 +10,15 @@
in the ESP3 document.
"""
-from enocean.consolelogger import init_logging
-from enocean.communicators.serialcommunicator import SerialCommunicator
-from enocean.protocol.packet import Packet
-from enocean.protocol.constants import PACKET
-from enocean import utils
-import traceback
+import queue
import sys
+import traceback
-try:
- import queue
-except ImportError:
- import Queue as queue
+from enocean import utils
+from enocean.communicators.serialcommunicator import SerialCommunicator
+from enocean.consolelogger import init_logging
+from enocean.protocol.constants import PACKET
+from enocean.protocol.packet import Packet
init_logging()
"""
diff --git a/examples/enocean_example.py b/examples/enocean_example.py
index be9eb0c..f8729ed 100755
--- a/examples/enocean_example.py
+++ b/examples/enocean_example.py
@@ -1,17 +1,15 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
-from enocean.consolelogger import init_logging
-import enocean.utils
-from enocean.communicators.serialcommunicator import SerialCommunicator
-from enocean.protocol.packet import RadioPacket
-from enocean.protocol.constants import PACKET, RORG
+
+import queue
import sys
import traceback
-try:
- import queue
-except ImportError:
- import Queue as queue
+import enocean.utils
+from enocean.communicators.serialcommunicator import SerialCommunicator
+from enocean.consolelogger import init_logging
+from enocean.protocol.constants import PACKET, RORG
+from enocean.protocol.packet import RadioPacket
def assemble_radio_packet(transmitter_id):
@@ -25,6 +23,7 @@ def assemble_radio_packet(transmitter_id):
init_logging()
communicator = SerialCommunicator()
communicator.start()
+print('The Chip ID of your module is %s.' % enocean.utils.to_hex_string(communicator.chip_id))
print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id))
if communicator.base_id is not None:
diff --git a/examples/example_D2-05-00.py b/examples/example_D2-05-00.py
index 20f655d..5b87593 100644
--- a/examples/example_D2-05-00.py
+++ b/examples/example_D2-05-00.py
@@ -7,18 +7,14 @@
Waits for UTE Teach-ins, sends the response automatically and prints the ID of new device.
'''
+import queue
import sys
-import time
import traceback
+
import enocean.utils
from enocean.communicators import SerialCommunicator
-from enocean.protocol.packet import RadioPacket, UTETeachInPacket
from enocean.protocol.constants import RORG
-
-try:
- import queue
-except ImportError:
- import Queue as queue
+from enocean.protocol.packet import RadioPacket, UTETeachInPacket
def set_position(destination, percentage):
diff --git a/examples/example_DO21-11B-E.py b/examples/example_DO21-11B-E.py
index 52464ec..0015532 100644
--- a/examples/example_DO21-11B-E.py
+++ b/examples/example_DO21-11B-E.py
@@ -7,18 +7,15 @@
Waits for UTE Teach-ins, sends the response automatically and prints the ID of new device.
'''
+import queue
import sys
import time
import traceback
+
import enocean.utils
from enocean.communicators import SerialCommunicator
-from enocean.protocol.packet import RadioPacket, UTETeachInPacket
from enocean.protocol.constants import RORG
-
-try:
- import queue
-except ImportError:
- import Queue as queue
+from enocean.protocol.packet import RadioPacket, UTETeachInPacket
def send_command(destination, output_value):
diff --git a/examples/serial_to_tcp.py b/examples/serial_to_tcp.py
index e68d19c..899fda6 100755
--- a/examples/serial_to_tcp.py
+++ b/examples/serial_to_tcp.py
@@ -1,15 +1,13 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
-from enocean.consolelogger import init_logging
-from enocean.communicators.serialcommunicator import SerialCommunicator
-from enocean.communicators.utils import send_to_tcp_socket
+
+import queue
import sys
import traceback
-try:
- import queue
-except ImportError:
- import Queue as queue
+from enocean.communicators.serialcommunicator import SerialCommunicator
+from enocean.communicators.utils import send_to_tcp_socket
+from enocean.consolelogger import init_logging
init_logging()
communicator = SerialCommunicator()
diff --git a/examples/tcp_server.py b/examples/tcp_server.py
index d7fa94d..5d6b2e4 100755
--- a/examples/tcp_server.py
+++ b/examples/tcp_server.py
@@ -1,15 +1,13 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
-from enocean.consolelogger import init_logging
-from enocean.communicators.tcpcommunicator import TCPCommunicator
-from enocean.protocol.constants import PACKET, RORG
+
+import queue
import sys
import traceback
-try:
- import queue
-except ImportError:
- import Queue as queue
+from enocean.communicators.tcpcommunicator import TCPCommunicator
+from enocean.consolelogger import init_logging
+from enocean.protocol.constants import PACKET, RORG
init_logging()
communicator = TCPCommunicator()
diff --git a/generate_supported_profiles.py b/generate_supported_profiles.py
index 30ee887..962802b 100755
--- a/generate_supported_profiles.py
+++ b/generate_supported_profiles.py
@@ -1,38 +1,60 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+
import codecs
+
from enocean.protocol.eep import EEP
ROW_FORMAT = '|{:8s}|{:50s}|{:8s}|{:70s}|\n'
-
+BASE_URL = "https://github.com/topic2k/enocean4ha/blob/topix/SUPPORTED_PROFILES.md#"
eep = EEP()
with codecs.open('SUPPORTED_PROFILES.md', 'w', 'utf-8') as f_handle:
- f_handle.write('# Supported profiles\n')
- f_handle.write('All profiles (should) correspond to the official [EEP](http://www.enocean-alliance.org/eep/) by EnOcean.\n\n')
+ write = f_handle.write
+ write("# Supported profiles\n")
+ write(
+ "All profiles (should) correspond to the official"
+ " [EEP](https://www.enocean-alliance.org/eep/) by EnOcean.\n\n"
+ )
+ # table of contents
for telegram in eep.soup.find_all('telegram'):
- f_handle.write('### %s (%s)\n' % (telegram['description'], telegram['rorg']))
+ write("")
+ write(f" {telegram['description']} ({telegram['rorg']})
\n\n")
for func in telegram.find_all('profiles'):
- # f_handle.write('##### FUNC %s - %s\n' % (func['func'], func['description']))
for profile in func.find_all('profile'):
- f_handle.write('##### RORG %s - FUNC %s - TYPE %s - %s\n\n' % (telegram['rorg'], func['func'], profile['type'], profile['description']))
+ write(
+ f"- [FUNC {func['func']} - TYPE {profile['type']} - {profile['description']}]"
+ f"({BASE_URL}rorg-{telegram['rorg']}---func-{func['func']}---type-{profile['type']}-"
+ f"--{profile['description'].lower().replace(' ', '-')})\n"
+ )
+
+ write("\n \n\n")
+ write('\n\n---\n\n')
+
+ # contents
+ for telegram in eep.soup.find_all('telegram'):
+ write(f"### {telegram['description']} ({telegram['rorg']})\n\n")
+ for func in telegram.find_all('profiles'):
+ for profile in func.find_all('profile'):
+ write(
+ f"##### RORG {telegram['rorg']} - FUNC {func['func']}"
+ f" - TYPE {profile['type']} - {profile['description']}\n\n"
+ )
for data in profile.find_all('data'):
header = []
if data.get('direction'):
- header.append('direction: %s' % (data.get('direction')))
+ header.append(f"direction: {data.get('direction')}")
if data.get('command'):
- header.append('command: %s' % (data.get('command')))
-
+ header.append(f"command: {data.get('command')}")
if header:
- f_handle.write('###### %s\n' % ' '.join(header))
+ write(f"###### {' '.join(header)}\n")
- f_handle.write(ROW_FORMAT.format('shortcut', 'description', 'type', 'values'))
- f_handle.write(ROW_FORMAT.format('--------', '--------------------------------------------------', '--------', '----'))
+ write(ROW_FORMAT.format('shortcut', 'description', 'type', 'values'))
+ write(ROW_FORMAT.format('-'*8, '-'*50, '-'*8, '-'*4))
for child in data.children:
if child.name is None:
continue
@@ -43,27 +65,35 @@
continue
if item.name == 'rangeitem':
- values.append('%s-%s - %s' % (item['start'], item['end'], item['description']))
+ values.append(f"{item['start']}-{item['end']} - {item['description']}")
elif item.name == 'item':
- values.append('%s - %s' % (item['value'], item['description']))
+ values.append(f"{item['value']} - {item['description']}")
elif item.name == 'range':
parent = item.parent
range_min = float(item.find('min').text)
range_max = float(item.find('max').text)
scale = parent.find('scale')
- scale_min = float(scale.find('min').text)
- scale_max = float(scale.find('max').text)
+ if scale:
+ scale_min = float(scale.find('min').text)
+ scale_max = float(scale.find('max').text)
+ else:
+ scale_min = ''
+ scale_max = ''
+ unit = item.get('unit') or parent.get('unit')
+
+ values.append(f"{range_min}-{range_max} ↔ {scale_min}-{scale_max} {unit}")
- values.append('%s-%s ↔ %s-%s %s' % (range_min, range_max, scale_min, scale_max, parent['unit']))
if not values:
- f_handle.write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, ''))
+ write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, ''))
continue
- f_handle.write(ROW_FORMAT.format(child['shortcut'], child['description'], child.name, values[0]))
+ write(ROW_FORMAT.format(
+ child.get('shortcut', ''),
+ child.get('description', ''),
+ child.name, values[0]
+ ))
for i in range(1, len(values)):
- f_handle.write(ROW_FORMAT.format('', '', '', values[i]))
- f_handle.write('\n')
- f_handle.write('\n')
-
- f_handle.write('\n')
+ write(ROW_FORMAT.format('', '', '', values[i]))
+ write('\n')
+ write('\n')
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..0dc143e
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,46 @@
+[build-system]
+requires = [ "setuptools>=74.0", "build>=1.2"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "enocean4ha"
+dynamic = [ "version", "readme"]
+authors = [
+ { name="Kimmo Huoman", email="kipenroskaposti@gmail.com" },
+]
+maintainers = [
+ { name="Torsten Pieper", email="topic2k@atlogger.de" },
+]
+description = "EnOcean serial protocol implementation"
+keywords = [ "EnOcean", "HomeAssistant", ]
+requires-python = ">=3.9"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Intended Audience :: Developers",
+ "Topic :: Home Automation",
+]
+dependencies = [
+ 'pyserial>=3.5',
+ 'beautifulsoup4>=4.12',
+ 'lxml>=5.3',
+]
+
+[project.urls]
+Homepage = "https://github.com/topic2k/enocean4ha"
+Issues = "https://github.com/topic2k/enocean4ha/issues"
+
+[project.optional-dependencies]
+tests = [ "nose2>=0.15.1", ]
+
+[tool.setuptools.dynamic]
+version = { attr = "enocean.__version__" }
+readme = { file = [ "README.md", "SUPPORTED_PROFILES.md" ], content-type = "text/markdown" }
+
+[tool.setuptools.packages.find]
+where = [ "." ]
+include = [ "enocean*" ]
+
+[tool.setuptools.package-data]
+"*" = [ "*.xml", ]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index fb7ca9a..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-enum-compat>=0.0.2
-pyserial>=3.0
-beautifulsoup4>=4.3.2
diff --git a/run_tests_with_timing.sh b/run_tests_with_timing.sh
deleted file mode 100755
index 4cd310e..0000000
--- a/run_tests_with_timing.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-WITH_TIMINGS=1 nosetests -s -q
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 3fea09c..0000000
--- a/setup.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-try:
- from setuptools import setup
-except ImportError:
- from distutils.core import setup
-
-setup(
- name='enocean',
- version='0.60.1',
- description='EnOcean serial protocol implementation',
- author='Kimmo Huoman',
- author_email='kipenroskaposti@gmail.com',
- url='https://github.com/kipe/enocean',
- packages=[
- 'enocean',
- 'enocean.protocol',
- 'enocean.communicators',
- ],
- scripts=[
- 'examples/enocean_example.py',
- ],
- package_data={
- '': ['EEP.xml']
- },
- install_requires=[
- 'enum-compat>=0.0.2',
- 'pyserial>=3.0',
- 'beautifulsoup4>=4.3.2',
- ])
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/enocean/communicators/tests/test_communicator.py b/tests/test_communicator.py
similarity index 94%
rename from enocean/communicators/tests/test_communicator.py
rename to tests/test_communicator.py
index c4d1d54..05fe2a5 100644
--- a/enocean/communicators/tests/test_communicator.py
+++ b/tests/test_communicator.py
@@ -1,10 +1,9 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+from decorators import timing
from enocean.communicators.communicator import Communicator
-from enocean.protocol.packet import Packet, RadioPacket
from enocean.protocol.constants import PACKET
-from enocean.decorators import timing
+from enocean.protocol.packet import Packet, RadioPacket
@timing(1000)
diff --git a/enocean/protocol/tests/test_eep.py b/tests/test_protocol_eep.py
similarity index 91%
rename from enocean/protocol/tests/test_eep.py
rename to tests/test_protocol_eep.py
index acdecdf..3f08b1d 100644
--- a/enocean/protocol/tests/test_eep.py
+++ b/tests/test_protocol_eep.py
@@ -1,15 +1,14 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-from enocean.protocol.packet import Packet
-from enocean.protocol.eep import EEP
+from decorators import timing
from enocean.protocol.constants import RORG
-from enocean.decorators import timing
+from enocean.protocol.eep import EEP
+from enocean.protocol.packet import Packet
@timing(1000)
def test_temperature():
- ''' Tests RADIO message for EEP -profile 0xA5 0x02 0x05 '''
+ """ Tests RADIO message for EEP -profile 0xA5 0x02 0x05 """
status, buf, packet = Packet.parse_msg(bytearray([
0x55,
0x00, 0x0A, 0x07, 0x01,
@@ -35,7 +34,7 @@ def test_temperature():
@timing(1000)
def test_magnetic_switch():
- ''' Tests RADIO message for EEP -profile 0xD5 0x00 0x01 '''
+ """ Tests RADIO message for EEP -profile 0xD5 0x00 0x01 """
status, buf, packet = Packet.parse_msg(bytearray([
0x55,
0x00, 0x07, 0x07, 0x01,
@@ -245,16 +244,16 @@ def test_fails():
eep = EEP()
# Mock initialization failure
eep.init_ok = False
- assert eep.find_profile(packet._bit_data, 0xD5, 0x00, 0x01) is None
+ assert eep.find_profile(0xD5, 0x00, 0x01) is None
# TODO: Needs better test. A much better.
assert eep.set_values(profile=None, data=[True], status=[False, False], properties={'CV': False})
eep.init_ok = True
- profile = eep.find_profile(packet._bit_data, 0xD5, 0x00, 0x01)
+ profile = eep.find_profile(0xD5, 0x00, 0x01)
assert eep.set_values(profile, packet._bit_data, packet.status, {'ASD': 1})
- assert eep.find_profile(packet._bit_data, 0xFF, 0x00, 0x01) is None
- assert eep.find_profile(packet._bit_data, 0xD5, 0xFF, 0x01) is None
- assert eep.find_profile(packet._bit_data, 0xD5, 0x00, 0xFF) is None
+ assert eep.find_profile(0xFF, 0x00, 0x01) is None
+ assert eep.find_profile(0xD5, 0xFF, 0x01) is None
+ assert eep.find_profile(0xD5, 0x00, 0xFF) is None
status, buf, packet = Packet.parse_msg(bytearray([
0x55,
@@ -264,5 +263,5 @@ def test_fails():
0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x00,
0xBF
]))
- assert eep.find_profile(packet._bit_data, 0xD2, 0x01, 0x01) is not None
- assert eep.find_profile(packet._bit_data, 0xD2, 0x01, 0x01, command=-1) is None
+ assert eep.find_profile(0xD2, 0x01, 0x01) is not None
+ assert eep.find_profile(0xD2, 0x01, 0x01, command=-1) is None
diff --git a/enocean/protocol/tests/test_packet.py b/tests/test_protocol_packet.py
similarity index 98%
rename from enocean/protocol/tests/test_packet.py
rename to tests/test_protocol_packet.py
index 96fa4c3..43c5a1e 100644
--- a/enocean/protocol/tests/test_packet.py
+++ b/tests/test_protocol_packet.py
@@ -1,9 +1,8 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-from enocean.protocol.packet import Packet, EventPacket
+from decorators import timing
from enocean.protocol.constants import PACKET, PARSE_RESULT, EVENT_CODE
-from enocean.decorators import timing
+from enocean.protocol.packet import Packet, EventPacket
@timing(1000)
diff --git a/enocean/protocol/tests/test_packet_creation.py b/tests/test_protocol_packet_creation.py
similarity index 95%
rename from enocean/protocol/tests/test_packet_creation.py
rename to tests/test_protocol_packet_creation.py
index 03a6a8b..a097bd1 100644
--- a/enocean/protocol/tests/test_packet_creation.py
+++ b/tests/test_protocol_packet_creation.py
@@ -1,10 +1,10 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
-from nose.tools import raises
-from enocean.protocol.packet import Packet, RadioPacket
+from unittest.case import TestCase
+
+from decorators import timing
from enocean.protocol.constants import PACKET, RORG
-from enocean.decorators import timing
+from enocean.protocol.packet import Packet, RadioPacket
@timing(1000)
@@ -242,15 +242,17 @@ def test_switch():
@timing(1000)
-@raises(ValueError)
-def test_illegal_eep_enum1():
- RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB='inexisting')
+class TestIllegalEEPEnum1(TestCase):
+ def test_illegal_eep_enum1(self):
+ with self.assertRaises(ValueError):
+ RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB='inexisting')
-@raises(ValueError)
@timing(1000)
-def test_illegal_eep_enum2():
- RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB=2)
+class TestIllegalEEPEnum2(TestCase):
+ def test_illegal_eep_enum2(self):
+ with self.assertRaises(ValueError):
+ RadioPacket.create(rorg=RORG.RPS, rorg_func=0x02, rorg_type=0x02, sender=[0x00, 0x29, 0x89, 0x79], EB=2)
# Corresponds to the tests done in test_eep
diff --git a/enocean/protocol/tests/test_teachin.py b/tests/test_protocol_teachin.py
similarity index 92%
rename from enocean/protocol/tests/test_teachin.py
rename to tests/test_protocol_teachin.py
index d908a94..b197645 100644
--- a/enocean/protocol/tests/test_teachin.py
+++ b/tests/test_protocol_teachin.py
@@ -1,10 +1,9 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
+from decorators import timing
from enocean.communicators import Communicator
-from enocean.protocol.packet import Packet
from enocean.protocol.constants import RORG, DB6
-from enocean.decorators import timing
+from enocean.protocol.packet import Packet
@timing(rounds=100, limit=750)
diff --git a/enocean/protocol/tests/test_temperature_sensors.py b/tests/test_protocol_temperature_sensors.py
similarity index 80%
rename from enocean/protocol/tests/test_temperature_sensors.py
rename to tests/test_protocol_temperature_sensors.py
index b5bba06..70991d9 100644
--- a/enocean/protocol/tests/test_temperature_sensors.py
+++ b/tests/test_protocol_temperature_sensors.py
@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
-from __future__ import print_function, unicode_literals, division, absolute_import
from enocean.protocol.eep import EEP
+
eep = EEP()
# profiles = eep.
@@ -12,7 +12,7 @@ def test_first_range():
for i in range(len(values)):
minimum = float(i * 10 + offset)
maximum = minimum + 40
- profile = eep.find_profile([], 0xA5, 0x02, values[i])
+ profile = eep.find_profile(0xA5, 0x02, values[i])
assert minimum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text)
assert maximum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text)
@@ -24,17 +24,17 @@ def test_second_range():
for i in range(len(values)):
minimum = float(i * 10 + offset)
maximum = minimum + 80
- profile = eep.find_profile([], 0xA5, 0x02, values[i])
+ profile = eep.find_profile(0xA5, 0x02, values[i])
assert minimum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text)
assert maximum == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text)
def test_rest():
- profile = eep.find_profile([], 0xA5, 0x02, 0x20)
+ profile = eep.find_profile(0xA5, 0x02, 0x20)
assert -10 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text)
assert +41.2 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text)
- profile = eep.find_profile([], 0xA5, 0x02, 0x30)
+ profile = eep.find_profile(0xA5, 0x02, 0x30)
assert -40 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('min').text)
assert +62.3 == float(profile.find('value', {'shortcut': 'TMP'}).find('scale').find('max').text)
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..f49f531
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+
+from enocean.utils import from_hex_string, to_hex_string, get_bit
+
+
+def test_get_bit():
+ assert get_bit(1, 0) == 1
+ assert get_bit(8, 3) == 1
+ assert get_bit(6, 2) == 1
+ assert get_bit(6, 1) == 1
+
+
+def test_to_hex_string():
+ assert to_hex_string(0) == '00'
+ assert to_hex_string(15) == '0F'
+ assert to_hex_string(16) == '10'
+ assert to_hex_string(22) == '16'
+
+ assert to_hex_string([0, 15, 16, 22]) == '00:0F:10:16'
+ assert to_hex_string([0x00, 0x0F, 0x10, 0x16]) == '00:0F:10:16'
+
+
+def test_from_hex_string():
+ assert from_hex_string('00') == 0
+ assert from_hex_string('0F') == 15
+ assert from_hex_string('10') == 16
+ assert from_hex_string('16') == 22
+
+ assert from_hex_string('00:0F:10:16') == [0, 15, 16, 22]
+ assert from_hex_string('00:0F:10:16') == [0x00, 0x0F, 0x10, 0x16]