python-openhab-rest-client 21.4.0__tar.gz → 21.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_openhab_rest_client-21.4.0/python_openhab_rest_client.egg-info → python_openhab_rest_client-21.4.1}/PKG-INFO +92 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/README.md +91 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncLinks.py +2 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncPersistence.py +2 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncRules.py +1 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncSitemaps.py +29 -66
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncVoice.py +4 -2
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Links.py +2 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Persistence.py +2 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Rules.py +1 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Sitemaps.py +29 -66
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Voice.py +4 -2
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/__init__.py +1 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1/python_openhab_rest_client.egg-info}/PKG-INFO +92 -1
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/python_openhab_rest_client.egg-info/SOURCES.txt +1 -35
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/setup.py +1 -1
- python_openhab_rest_client-21.4.0/tests/Actions.py +0 -39
- python_openhab_rest_client-21.4.0/tests/Addons.py +0 -35
- python_openhab_rest_client-21.4.0/tests/Audio.py +0 -18
- python_openhab_rest_client-21.4.0/tests/Auth.py +0 -33
- python_openhab_rest_client-21.4.0/tests/ChannelEvents.py +0 -16
- python_openhab_rest_client-21.4.0/tests/ChannelTypes.py +0 -23
- python_openhab_rest_client-21.4.0/tests/ConfigDescriptions.py +0 -20
- python_openhab_rest_client-21.4.0/tests/Discovery.py +0 -20
- python_openhab_rest_client-21.4.0/tests/Events.py +0 -22
- python_openhab_rest_client-21.4.0/tests/Iconsets.py +0 -18
- python_openhab_rest_client-21.4.0/tests/Inbox.py +0 -26
- python_openhab_rest_client-21.4.0/tests/InboxEvents.py +0 -19
- python_openhab_rest_client-21.4.0/tests/ItemEvents.py +0 -28
- python_openhab_rest_client-21.4.0/tests/Items.py +0 -46
- python_openhab_rest_client-21.4.0/tests/LinkEvents.py +0 -20
- python_openhab_rest_client-21.4.0/tests/Links.py +0 -26
- python_openhab_rest_client-21.4.0/tests/Logging.py +0 -23
- python_openhab_rest_client-21.4.0/tests/ModuleTypes.py +0 -20
- python_openhab_rest_client-21.4.0/tests/Persistence.py +0 -32
- python_openhab_rest_client-21.4.0/tests/ProfileTypes.py +0 -16
- python_openhab_rest_client-21.4.0/tests/Rules.py +0 -38
- python_openhab_rest_client-21.4.0/tests/Services.py +0 -30
- python_openhab_rest_client-21.4.0/tests/Sitemaps.py +0 -25
- python_openhab_rest_client-21.4.0/tests/Systeminfo.py +0 -17
- python_openhab_rest_client-21.4.0/tests/Tags.py +0 -33
- python_openhab_rest_client-21.4.0/tests/Templates.py +0 -20
- python_openhab_rest_client-21.4.0/tests/ThingEvents.py +0 -23
- python_openhab_rest_client-21.4.0/tests/ThingTypes.py +0 -20
- python_openhab_rest_client-21.4.0/tests/Things.py +0 -34
- python_openhab_rest_client-21.4.0/tests/Transformations.py +0 -30
- python_openhab_rest_client-21.4.0/tests/UI.py +0 -45
- python_openhab_rest_client-21.4.0/tests/UUID.py +0 -16
- python_openhab_rest_client-21.4.0/tests/Voice.py +0 -45
- python_openhab_rest_client-21.4.0/tests/__init__.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/LICENSE +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Actions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Addons.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncActions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncAddons.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncAudio.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncAuth.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncChannelTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncClient.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncConfigDescriptions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncDiscovery.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncIconsets.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncInbox.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncItems.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncLogging.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncModuleTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncProfileTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncServices.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncSysteminfo.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncTags.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncTemplates.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncThingTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncThings.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncTransformations.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncUI.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncUUID.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Audio.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Auth.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/ChannelTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Client.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/ConfigDescriptions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Discovery.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Events.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Iconsets.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Inbox.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Items.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Logging.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/ModuleTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/ProfileTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/SSE.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Services.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Systeminfo.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Tags.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Templates.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/ThingTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Things.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Transformations.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/UI.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/UUID.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Actions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Addons.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Audio.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Auth.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ChannelEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ChannelTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ConfigDescriptions.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Discovery.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Events.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Iconsets.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Inbox.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/InboxEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ItemEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Items.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/LinkEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Links.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Logging.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ModuleTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Persistence.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ProfileTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Rules.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Services.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Sitemaps.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Systeminfo.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Tags.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Templates.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ThingEvents.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/ThingTypes.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Things.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Transformations.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/UI.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/UUID.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/Voice.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/tests/__init__.py +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/pyproject.toml +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/python_openhab_rest_client.egg-info/dependency_links.txt +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/python_openhab_rest_client.egg-info/requires.txt +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/python_openhab_rest_client.egg-info/top_level.txt +0 -0
- {python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-openhab-rest-client
|
|
3
|
-
Version: 21.4.
|
|
3
|
+
Version: 21.4.1
|
|
4
4
|
Summary: A Python client for the openHAB REST API. This library enables easy interaction with the openHAB REST API to control smart home devices, retrieve status information, and process events.
|
|
5
5
|
Home-page: https://github.com/Michdo93/python-openhab-rest-client
|
|
6
6
|
Author: Michael Christian Dörflinger
|
|
@@ -84,6 +84,76 @@ Install via pip:
|
|
|
84
84
|
pip install python-openhab-rest-client
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
Here is the complete section translated into English, formatted to match your README:
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### Secure HTTPS & Self-Signed Certificates (TLS)
|
|
92
|
+
|
|
93
|
+
When running openHAB over HTTPS with a custom or self-signed certificate, you must ensure that Python (via the underlying `requests` library) trusts the certificate and successfully validates the hostname.
|
|
94
|
+
|
|
95
|
+
#### 1. Switch openHAB to HTTPS
|
|
96
|
+
|
|
97
|
+
1. **Disable HTTP:**
|
|
98
|
+
Open the configuration file `/etc/openhab/services/org.ops4j.pax.web.cfg` (if it is not existing, create it) and set:
|
|
99
|
+
```properties
|
|
100
|
+
org.osgi.service.http.enabled=false
|
|
101
|
+
org.osgi.service.http.secure.enabled=true
|
|
102
|
+
org.ops4j.pax.web.ssl.port=8443
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
It could also help to edit the `/etc/default/openhab` file and make this changes:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
#########################
|
|
109
|
+
## PORTS
|
|
110
|
+
## The ports openHAB will bind its HTTP/HTTPS web server to.
|
|
111
|
+
|
|
112
|
+
#OPENHAB_HTTP_PORT=8080
|
|
113
|
+
OPENHAB_HTTPS_PORT=8443
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This ensures openHAB runs exclusively on the secure HTTPS port (default `8443`).
|
|
117
|
+
2. **Generate Keystore with a Proper SAN (Subject Alternative Name):**
|
|
118
|
+
Modern OpenSSL and Python versions strictly require a SAN entry in the certificate to prevent hostname mismatches. Create a new keystore using Java's `keytool` (replace `<your-hostname>` with your actual hostname):
|
|
119
|
+
```bash
|
|
120
|
+
sudo rm /var/lib/openhab/etc/keystore
|
|
121
|
+
sudo keytool -genkeypair -alias openhab -keyalg RSA -keysize 2048 -validity 365 \
|
|
122
|
+
-keystore /var/lib/openhab/etc/keystore -storepass openhab -keypass openhab \
|
|
123
|
+
-dname "CN=openhab" -ext "san=dns:<your-hostname>,ip:127.0.0.1,dns:localhost"
|
|
124
|
+
sudo chown openhab:openhab /var/lib/openhab/etc/keystore
|
|
125
|
+
sudo systemctl restart openhab
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. **Export the Certificate in PEM Format:**
|
|
129
|
+
Export the public certificate so your Python client can read and trust it:
|
|
130
|
+
```bash
|
|
131
|
+
sudo keytool -exportcert -rfc -alias openhab -keystore /var/lib/openhab/etc/keystore \
|
|
132
|
+
-storepass openhab -file /home/$USER/openhab-exported.crt
|
|
133
|
+
sudo chown $USER:$USER /home/$USER/openhab-exported.crt
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### 2. Usage in the Python Client
|
|
137
|
+
|
|
138
|
+
Since self-signed certificates are rejected by default, you can pass the absolute path to the exported `.crt` file via the `verify` parameter of the client. The library will cryptographically verify the connection against your certificate:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from openhab import OpenHABClient, Items
|
|
142
|
+
|
|
143
|
+
client = OpenHABClient(
|
|
144
|
+
url="https://<your-hostname>:8443",
|
|
145
|
+
username="openhab",
|
|
146
|
+
password="habopen",
|
|
147
|
+
verify="/home/smarthome/openhab-exported.crt"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
itemsAPI = Items(client)
|
|
151
|
+
allItems = itemsAPI.getItems()
|
|
152
|
+
print("All Items:", allItems)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
*(Note: For the asynchronous client `AsyncOpenHABClient`, the `verify` parameter works the exact same way via `aiohttp`.)*
|
|
156
|
+
|
|
87
157
|
## Usage
|
|
88
158
|
|
|
89
159
|
Basically, you always need the `OpenHABClient` (or `AsyncOpenHABClient` for async usage). Regardless of whether you carry out a normal `REST request` or one via `SSE`.
|
|
@@ -3009,6 +3079,27 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
|
|
|
3009
3079
|
|
|
3010
3080
|
## Changelog
|
|
3011
3081
|
|
|
3082
|
+
### 21.4.1
|
|
3083
|
+
|
|
3084
|
+
Bug fixes (found while writing the examples of the book »openHAB & Python-Entwicklung«
|
|
3085
|
+
against openHAB 5.2.3):
|
|
3086
|
+
|
|
3087
|
+
- **`Sitemaps.getSitemapEvents()` / `getFullSitemapEvents()`** read the SSE endpoints like a
|
|
3088
|
+
normal response and failed after 10 s with "Read timed out". They now open the stream via
|
|
3089
|
+
`executeSSE()` and return it (read it with `iterSSELines()` and `parseSSEData()` – sitemap
|
|
3090
|
+
events have no `topic`). Note: bind the subscription to a page first with
|
|
3091
|
+
`getSitemapPage(sitemap, page, subscriptionID=...)`; openHAB answers the stream request only
|
|
3092
|
+
when the first event arrives.
|
|
3093
|
+
- **`Rules.setModuleConfigParam()`** sent the value with `json.dumps()`, so `10` was stored as
|
|
3094
|
+
`"10"` (with quotes) and e.g. an `ItemCommandAction` sent an invalid command. The value is now
|
|
3095
|
+
sent as plain text.
|
|
3096
|
+
- **`Links.deleteAllLinks()`** failed with `500 No message body writer` because of the missing
|
|
3097
|
+
`Accept: application/json` header. It now returns openHAB's answer, e.g. `{"count": 1}`.
|
|
3098
|
+
- **`Persistence.deleteItemData()`** reported every `400` as "Invalid filter parameters.";
|
|
3099
|
+
openHAB also uses `400` for read-only services such as rrd4j. openHAB's reason is appended now.
|
|
3100
|
+
- **`Voice.interpretText()` / `interpretTextBatch()`** dropped the interpreter's answer on `400`
|
|
3101
|
+
(e.g. "Entschuldigung, aber das habe ich nicht verstanden."). It is appended now.
|
|
3102
|
+
|
|
3012
3103
|
### 21.4.0
|
|
3013
3104
|
|
|
3014
3105
|
Bug fixes (all found against openHAB 5.2.3):
|
|
@@ -57,6 +57,76 @@ Install via pip:
|
|
|
57
57
|
pip install python-openhab-rest-client
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
Here is the complete section translated into English, formatted to match your README:
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Secure HTTPS & Self-Signed Certificates (TLS)
|
|
65
|
+
|
|
66
|
+
When running openHAB over HTTPS with a custom or self-signed certificate, you must ensure that Python (via the underlying `requests` library) trusts the certificate and successfully validates the hostname.
|
|
67
|
+
|
|
68
|
+
#### 1. Switch openHAB to HTTPS
|
|
69
|
+
|
|
70
|
+
1. **Disable HTTP:**
|
|
71
|
+
Open the configuration file `/etc/openhab/services/org.ops4j.pax.web.cfg` (if it is not existing, create it) and set:
|
|
72
|
+
```properties
|
|
73
|
+
org.osgi.service.http.enabled=false
|
|
74
|
+
org.osgi.service.http.secure.enabled=true
|
|
75
|
+
org.ops4j.pax.web.ssl.port=8443
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
It could also help to edit the `/etc/default/openhab` file and make this changes:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
#########################
|
|
82
|
+
## PORTS
|
|
83
|
+
## The ports openHAB will bind its HTTP/HTTPS web server to.
|
|
84
|
+
|
|
85
|
+
#OPENHAB_HTTP_PORT=8080
|
|
86
|
+
OPENHAB_HTTPS_PORT=8443
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This ensures openHAB runs exclusively on the secure HTTPS port (default `8443`).
|
|
90
|
+
2. **Generate Keystore with a Proper SAN (Subject Alternative Name):**
|
|
91
|
+
Modern OpenSSL and Python versions strictly require a SAN entry in the certificate to prevent hostname mismatches. Create a new keystore using Java's `keytool` (replace `<your-hostname>` with your actual hostname):
|
|
92
|
+
```bash
|
|
93
|
+
sudo rm /var/lib/openhab/etc/keystore
|
|
94
|
+
sudo keytool -genkeypair -alias openhab -keyalg RSA -keysize 2048 -validity 365 \
|
|
95
|
+
-keystore /var/lib/openhab/etc/keystore -storepass openhab -keypass openhab \
|
|
96
|
+
-dname "CN=openhab" -ext "san=dns:<your-hostname>,ip:127.0.0.1,dns:localhost"
|
|
97
|
+
sudo chown openhab:openhab /var/lib/openhab/etc/keystore
|
|
98
|
+
sudo systemctl restart openhab
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
3. **Export the Certificate in PEM Format:**
|
|
102
|
+
Export the public certificate so your Python client can read and trust it:
|
|
103
|
+
```bash
|
|
104
|
+
sudo keytool -exportcert -rfc -alias openhab -keystore /var/lib/openhab/etc/keystore \
|
|
105
|
+
-storepass openhab -file /home/$USER/openhab-exported.crt
|
|
106
|
+
sudo chown $USER:$USER /home/$USER/openhab-exported.crt
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### 2. Usage in the Python Client
|
|
110
|
+
|
|
111
|
+
Since self-signed certificates are rejected by default, you can pass the absolute path to the exported `.crt` file via the `verify` parameter of the client. The library will cryptographically verify the connection against your certificate:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from openhab import OpenHABClient, Items
|
|
115
|
+
|
|
116
|
+
client = OpenHABClient(
|
|
117
|
+
url="https://<your-hostname>:8443",
|
|
118
|
+
username="openhab",
|
|
119
|
+
password="habopen",
|
|
120
|
+
verify="/home/smarthome/openhab-exported.crt"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
itemsAPI = Items(client)
|
|
124
|
+
allItems = itemsAPI.getItems()
|
|
125
|
+
print("All Items:", allItems)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
*(Note: For the asynchronous client `AsyncOpenHABClient`, the `verify` parameter works the exact same way via `aiohttp`.)*
|
|
129
|
+
|
|
60
130
|
## Usage
|
|
61
131
|
|
|
62
132
|
Basically, you always need the `OpenHABClient` (or `AsyncOpenHABClient` for async usage). Regardless of whether you carry out a normal `REST request` or one via `SSE`.
|
|
@@ -2982,6 +3052,27 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
|
|
|
2982
3052
|
|
|
2983
3053
|
## Changelog
|
|
2984
3054
|
|
|
3055
|
+
### 21.4.1
|
|
3056
|
+
|
|
3057
|
+
Bug fixes (found while writing the examples of the book »openHAB & Python-Entwicklung«
|
|
3058
|
+
against openHAB 5.2.3):
|
|
3059
|
+
|
|
3060
|
+
- **`Sitemaps.getSitemapEvents()` / `getFullSitemapEvents()`** read the SSE endpoints like a
|
|
3061
|
+
normal response and failed after 10 s with "Read timed out". They now open the stream via
|
|
3062
|
+
`executeSSE()` and return it (read it with `iterSSELines()` and `parseSSEData()` – sitemap
|
|
3063
|
+
events have no `topic`). Note: bind the subscription to a page first with
|
|
3064
|
+
`getSitemapPage(sitemap, page, subscriptionID=...)`; openHAB answers the stream request only
|
|
3065
|
+
when the first event arrives.
|
|
3066
|
+
- **`Rules.setModuleConfigParam()`** sent the value with `json.dumps()`, so `10` was stored as
|
|
3067
|
+
`"10"` (with quotes) and e.g. an `ItemCommandAction` sent an invalid command. The value is now
|
|
3068
|
+
sent as plain text.
|
|
3069
|
+
- **`Links.deleteAllLinks()`** failed with `500 No message body writer` because of the missing
|
|
3070
|
+
`Accept: application/json` header. It now returns openHAB's answer, e.g. `{"count": 1}`.
|
|
3071
|
+
- **`Persistence.deleteItemData()`** reported every `400` as "Invalid filter parameters.";
|
|
3072
|
+
openHAB also uses `400` for read-only services such as rrd4j. openHAB's reason is appended now.
|
|
3073
|
+
- **`Voice.interpretText()` / `interpretTextBatch()`** dropped the interpreter's answer on `400`
|
|
3074
|
+
(e.g. "Entschuldigung, aber das habe ich nicht verstanden."). It is appended now.
|
|
3075
|
+
|
|
2985
3076
|
### 21.4.0
|
|
2986
3077
|
|
|
2987
3078
|
Bug fixes (all found against openHAB 5.2.3):
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncLinks.py
RENAMED
|
@@ -185,7 +185,8 @@ class AsyncLinks:
|
|
|
185
185
|
:return: The API response when all links are successfully deleted.
|
|
186
186
|
"""
|
|
187
187
|
try:
|
|
188
|
-
response = await self.client.delete(f"/links/{object}"
|
|
188
|
+
response = await self.client.delete(f"/links/{object}",
|
|
189
|
+
headers={"Accept": "application/json"})
|
|
189
190
|
|
|
190
191
|
if isinstance(response, StatusResponse):
|
|
191
192
|
status_code = response["status"]
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncPersistence.py
RENAMED
|
@@ -308,7 +308,8 @@ class AsyncPersistence:
|
|
|
308
308
|
except aiohttp.ClientResponseError as err:
|
|
309
309
|
status_code = err.status
|
|
310
310
|
if status_code == 400:
|
|
311
|
-
|
|
311
|
+
# openHAB uses 400 for invalid filters and for read-only services (e.g. rrd4j)
|
|
312
|
+
return {"error": f"Invalid filter parameters or service not modifiable: {str(err)}"}
|
|
312
313
|
elif status_code == 404:
|
|
313
314
|
return {"error": "Unknown persistence service."}
|
|
314
315
|
else:
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncRules.py
RENAMED
|
@@ -315,7 +315,7 @@ class AsyncRules:
|
|
|
315
315
|
"""
|
|
316
316
|
try:
|
|
317
317
|
response = await self.client.put(f"/rules/{ruleUID}/{moduleCategory}/{moduleID}/config/{param}",
|
|
318
|
-
data=
|
|
318
|
+
data=str(value), headers={"Content-Type": "text/plain"})
|
|
319
319
|
|
|
320
320
|
if isinstance(response, StatusResponse):
|
|
321
321
|
status_code = response["status"]
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncSitemaps.py
RENAMED
|
@@ -14,6 +14,18 @@ class AsyncSitemaps:
|
|
|
14
14
|
"""
|
|
15
15
|
self.client = client
|
|
16
16
|
|
|
17
|
+
async def _openEventStream(self, path: str, params: dict, errors: dict):
|
|
18
|
+
"""Opens a sitemap SSE stream; returns the streaming response or {"error": ...}."""
|
|
19
|
+
try:
|
|
20
|
+
return await self.client.executeSSE(path, params=params)
|
|
21
|
+
except aiohttp.ClientResponseError as err:
|
|
22
|
+
statusCode = err.status
|
|
23
|
+
if statusCode in errors:
|
|
24
|
+
return {"error": errors[statusCode]}
|
|
25
|
+
return {"error": f"HTTP error {statusCode}: {str(err)}"}
|
|
26
|
+
except (aiohttp.ClientError, asyncio.TimeoutError) as err:
|
|
27
|
+
return {"error": f"Request error: {str(err)}"}
|
|
28
|
+
|
|
17
29
|
async def getSitemaps(self):
|
|
18
30
|
"""
|
|
19
31
|
Get all available sitemaps.
|
|
@@ -175,82 +187,33 @@ class AsyncSitemaps:
|
|
|
175
187
|
|
|
176
188
|
async def getSitemapEvents(self, subscriptionID: str, sitemapName: str = None, pageID: str = None):
|
|
177
189
|
"""
|
|
178
|
-
|
|
190
|
+
Opens the event stream (SSE) of a sitemap subscription for one page.
|
|
179
191
|
|
|
180
|
-
:param subscriptionID: The ID of the subscription.
|
|
181
|
-
:param
|
|
182
|
-
:param pageID: The ID of the page (
|
|
192
|
+
:param subscriptionID: The ID of the subscription (see subscribeToSitemapEvents()).
|
|
193
|
+
:param sitemapName: The name of the sitemap.
|
|
194
|
+
:param pageID: The ID of the page (the sitemap name for the home page).
|
|
183
195
|
|
|
184
|
-
:return:
|
|
196
|
+
:return: A SSE stream (aiohttp.ClientResponse, read it with iterSSEEvents()) or {"error": ...}.
|
|
185
197
|
"""
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if isinstance(response, StatusResponse):
|
|
191
|
-
status_code = response["status"]
|
|
192
|
-
else:
|
|
193
|
-
return response
|
|
194
|
-
|
|
195
|
-
except aiohttp.ClientResponseError as err:
|
|
196
|
-
status_code = err.status
|
|
197
|
-
if status_code == 400:
|
|
198
|
-
return {"error": "Missing sitemap or page parameter, or page not linked successfully to the subscription."}
|
|
199
|
-
elif status_code == 404:
|
|
200
|
-
return {"error": "Subscription not found."}
|
|
201
|
-
else:
|
|
202
|
-
return {"error": f"HTTP error {status_code}: {str(err)}"}
|
|
203
|
-
|
|
204
|
-
except (aiohttp.ClientError, asyncio.TimeoutError) as err:
|
|
205
|
-
return {"error": f"Request error: {str(err)}"}
|
|
206
|
-
|
|
207
|
-
if status_code == 200:
|
|
208
|
-
return {"message": "OK"}
|
|
209
|
-
elif status_code == 404:
|
|
210
|
-
return {"error": "Subscription not found."}
|
|
211
|
-
elif status_code == 400:
|
|
212
|
-
return {"error": "Missing sitemap or page parameter, or page not linked successfully to the subscription."}
|
|
213
|
-
|
|
214
|
-
return {"error": f"Unexpected response: {status_code}"}
|
|
198
|
+
return await self._openEventStream(
|
|
199
|
+
f"/rest/sitemaps/events/{subscriptionID}", {"sitemap": sitemapName, "pageid": pageID},
|
|
200
|
+
{400: "Missing sitemap or page parameter, or page not linked successfully to the subscription.",
|
|
201
|
+
404: "Subscription not found."})
|
|
215
202
|
|
|
216
203
|
async def getFullSitemapEvents(self, subscriptionID: str, sitemapName: str = None):
|
|
217
204
|
"""
|
|
218
|
-
|
|
205
|
+
Opens the event stream (SSE) of a sitemap subscription for the whole sitemap.
|
|
206
|
+
Not recommended due to potentially high traffic.
|
|
219
207
|
|
|
220
208
|
:param subscriptionID: The ID of the subscription.
|
|
221
|
-
:param
|
|
209
|
+
:param sitemapName: The name of the sitemap.
|
|
222
210
|
|
|
223
|
-
:return:
|
|
211
|
+
:return: A SSE stream (aiohttp.ClientResponse) or {"error": ...}.
|
|
224
212
|
"""
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if isinstance(response, StatusResponse):
|
|
230
|
-
status_code = response["status"]
|
|
231
|
-
else:
|
|
232
|
-
return response
|
|
233
|
-
|
|
234
|
-
except aiohttp.ClientResponseError as err:
|
|
235
|
-
status_code = err.status
|
|
236
|
-
if status_code == 400:
|
|
237
|
-
return {"error": "Missing sitemap parameter, or sitemap not linked successfully to the subscription."}
|
|
238
|
-
elif status_code == 404:
|
|
239
|
-
return {"error": "Subscription not found."}
|
|
240
|
-
else:
|
|
241
|
-
return {"error": f"HTTP error {status_code}: {str(err)}"}
|
|
242
|
-
|
|
243
|
-
except (aiohttp.ClientError, asyncio.TimeoutError) as err:
|
|
244
|
-
return {"error": f"Request error: {str(err)}"}
|
|
245
|
-
|
|
246
|
-
if status_code == 200:
|
|
247
|
-
return {"message": "OK"}
|
|
248
|
-
elif status_code == 404:
|
|
249
|
-
return {"error": "Subscription not found."}
|
|
250
|
-
elif status_code == 400:
|
|
251
|
-
return {"error": "Missing sitemap parameter, or sitemap not linked successfully to the subscription."}
|
|
252
|
-
|
|
253
|
-
return {"error": f"Unexpected response: {status_code}"}
|
|
213
|
+
return await self._openEventStream(
|
|
214
|
+
f"/rest/sitemaps/events/{subscriptionID}/*", {"sitemap": sitemapName},
|
|
215
|
+
{400: "Missing sitemap parameter, or sitemap not linked successfully to the subscription.",
|
|
216
|
+
404: "Subscription not found."})
|
|
254
217
|
|
|
255
218
|
async def subscribeToSitemapEvents(self):
|
|
256
219
|
"""
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/AsyncVoice.py
RENAMED
|
@@ -190,7 +190,8 @@ class AsyncVoice:
|
|
|
190
190
|
except aiohttp.ClientResponseError as err:
|
|
191
191
|
status_code = err.status
|
|
192
192
|
if status_code == 400:
|
|
193
|
-
|
|
193
|
+
# openHAB explains in its error message why the text was not understood
|
|
194
|
+
return {"error": f"Interpretation exception occurs: {str(err)}"}
|
|
194
195
|
elif status_code == 404:
|
|
195
196
|
return {"error": "No human language interpreter was found."}
|
|
196
197
|
else:
|
|
@@ -272,7 +273,8 @@ class AsyncVoice:
|
|
|
272
273
|
except aiohttp.ClientResponseError as err:
|
|
273
274
|
status_code = err.status
|
|
274
275
|
if status_code == 400:
|
|
275
|
-
|
|
276
|
+
# openHAB explains in its error message why the text was not understood
|
|
277
|
+
return {"error": f"Interpretation exception occurs: {str(err)}"}
|
|
276
278
|
elif status_code == 404:
|
|
277
279
|
return {"error": "No human language interpreter was found."}
|
|
278
280
|
else:
|
|
@@ -182,7 +182,8 @@ class Links:
|
|
|
182
182
|
:return: The API response when all links are successfully deleted.
|
|
183
183
|
"""
|
|
184
184
|
try:
|
|
185
|
-
response = self.client.delete(f"/links/{object}"
|
|
185
|
+
response = self.client.delete(f"/links/{object}",
|
|
186
|
+
headers={"Accept": "application/json"})
|
|
186
187
|
|
|
187
188
|
if isinstance(response, StatusResponse):
|
|
188
189
|
status_code = response["status"]
|
{python_openhab_rest_client-21.4.0 → python_openhab_rest_client-21.4.1}/openhab/Persistence.py
RENAMED
|
@@ -305,7 +305,8 @@ class Persistence:
|
|
|
305
305
|
except requests.exceptions.HTTPError as err:
|
|
306
306
|
status_code = err.response.status_code
|
|
307
307
|
if status_code == 400:
|
|
308
|
-
|
|
308
|
+
# openHAB uses 400 for invalid filters and for read-only services (e.g. rrd4j)
|
|
309
|
+
return {"error": f"Invalid filter parameters or service not modifiable: {str(err)}"}
|
|
309
310
|
elif status_code == 404:
|
|
310
311
|
return {"error": "Unknown persistence service."}
|
|
311
312
|
else:
|
|
@@ -312,7 +312,7 @@ class Rules:
|
|
|
312
312
|
"""
|
|
313
313
|
try:
|
|
314
314
|
response = self.client.put(f"/rules/{ruleUID}/{moduleCategory}/{moduleID}/config/{param}",
|
|
315
|
-
data=
|
|
315
|
+
data=str(value), headers={"Content-Type": "text/plain"})
|
|
316
316
|
|
|
317
317
|
if isinstance(response, StatusResponse):
|
|
318
318
|
status_code = response["status"]
|
|
@@ -11,6 +11,18 @@ class Sitemaps:
|
|
|
11
11
|
"""
|
|
12
12
|
self.client = client
|
|
13
13
|
|
|
14
|
+
def _openEventStream(self, path: str, params: dict, errors: dict):
|
|
15
|
+
"""Opens a sitemap SSE stream; returns the streaming response or {"error": ...}."""
|
|
16
|
+
try:
|
|
17
|
+
return self.client.executeSSE(path, params=params)
|
|
18
|
+
except requests.exceptions.HTTPError as err:
|
|
19
|
+
statusCode = err.response.status_code
|
|
20
|
+
if statusCode in errors:
|
|
21
|
+
return {"error": errors[statusCode]}
|
|
22
|
+
return {"error": f"HTTP error {statusCode}: {str(err)}"}
|
|
23
|
+
except requests.exceptions.RequestException as err:
|
|
24
|
+
return {"error": f"Request error: {str(err)}"}
|
|
25
|
+
|
|
14
26
|
def getSitemaps(self):
|
|
15
27
|
"""
|
|
16
28
|
Get all available sitemaps.
|
|
@@ -172,82 +184,33 @@ class Sitemaps:
|
|
|
172
184
|
|
|
173
185
|
def getSitemapEvents(self, subscriptionID: str, sitemapName: str = None, pageID: str = None):
|
|
174
186
|
"""
|
|
175
|
-
|
|
187
|
+
Opens the event stream (SSE) of a sitemap subscription for one page.
|
|
176
188
|
|
|
177
|
-
:param subscriptionID: The ID of the subscription.
|
|
178
|
-
:param
|
|
179
|
-
:param pageID: The ID of the page (
|
|
189
|
+
:param subscriptionID: The ID of the subscription (see subscribeToSitemapEvents()).
|
|
190
|
+
:param sitemapName: The name of the sitemap.
|
|
191
|
+
:param pageID: The ID of the page (the sitemap name for the home page).
|
|
180
192
|
|
|
181
|
-
:return:
|
|
193
|
+
:return: A SSE stream (requests.Response, read it with iterSSEEvents()) or {"error": ...}.
|
|
182
194
|
"""
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if isinstance(response, StatusResponse):
|
|
188
|
-
status_code = response["status"]
|
|
189
|
-
else:
|
|
190
|
-
return response
|
|
191
|
-
|
|
192
|
-
except requests.exceptions.HTTPError as err:
|
|
193
|
-
status_code = err.response.status_code
|
|
194
|
-
if status_code == 400:
|
|
195
|
-
return {"error": "Missing sitemap or page parameter, or page not linked successfully to the subscription."}
|
|
196
|
-
elif status_code == 404:
|
|
197
|
-
return {"error": "Subscription not found."}
|
|
198
|
-
else:
|
|
199
|
-
return {"error": f"HTTP error {status_code}: {str(err)}"}
|
|
200
|
-
|
|
201
|
-
except requests.exceptions.RequestException as err:
|
|
202
|
-
return {"error": f"Request error: {str(err)}"}
|
|
203
|
-
|
|
204
|
-
if status_code == 200:
|
|
205
|
-
return {"message": "OK"}
|
|
206
|
-
elif status_code == 404:
|
|
207
|
-
return {"error": "Subscription not found."}
|
|
208
|
-
elif status_code == 400:
|
|
209
|
-
return {"error": "Missing sitemap or page parameter, or page not linked successfully to the subscription."}
|
|
210
|
-
|
|
211
|
-
return {"error": f"Unexpected response: {status_code}"}
|
|
195
|
+
return self._openEventStream(
|
|
196
|
+
f"/rest/sitemaps/events/{subscriptionID}", {"sitemap": sitemapName, "pageid": pageID},
|
|
197
|
+
{400: "Missing sitemap or page parameter, or page not linked successfully to the subscription.",
|
|
198
|
+
404: "Subscription not found."})
|
|
212
199
|
|
|
213
200
|
def getFullSitemapEvents(self, subscriptionID: str, sitemapName: str = None):
|
|
214
201
|
"""
|
|
215
|
-
|
|
202
|
+
Opens the event stream (SSE) of a sitemap subscription for the whole sitemap.
|
|
203
|
+
Not recommended due to potentially high traffic.
|
|
216
204
|
|
|
217
205
|
:param subscriptionID: The ID of the subscription.
|
|
218
|
-
:param
|
|
206
|
+
:param sitemapName: The name of the sitemap.
|
|
219
207
|
|
|
220
|
-
:return:
|
|
208
|
+
:return: A SSE stream (requests.Response) or {"error": ...}.
|
|
221
209
|
"""
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if isinstance(response, StatusResponse):
|
|
227
|
-
status_code = response["status"]
|
|
228
|
-
else:
|
|
229
|
-
return response
|
|
230
|
-
|
|
231
|
-
except requests.exceptions.HTTPError as err:
|
|
232
|
-
status_code = err.response.status_code
|
|
233
|
-
if status_code == 400:
|
|
234
|
-
return {"error": "Missing sitemap parameter, or sitemap not linked successfully to the subscription."}
|
|
235
|
-
elif status_code == 404:
|
|
236
|
-
return {"error": "Subscription not found."}
|
|
237
|
-
else:
|
|
238
|
-
return {"error": f"HTTP error {status_code}: {str(err)}"}
|
|
239
|
-
|
|
240
|
-
except requests.exceptions.RequestException as err:
|
|
241
|
-
return {"error": f"Request error: {str(err)}"}
|
|
242
|
-
|
|
243
|
-
if status_code == 200:
|
|
244
|
-
return {"message": "OK"}
|
|
245
|
-
elif status_code == 404:
|
|
246
|
-
return {"error": "Subscription not found."}
|
|
247
|
-
elif status_code == 400:
|
|
248
|
-
return {"error": "Missing sitemap parameter, or sitemap not linked successfully to the subscription."}
|
|
249
|
-
|
|
250
|
-
return {"error": f"Unexpected response: {status_code}"}
|
|
210
|
+
return self._openEventStream(
|
|
211
|
+
f"/rest/sitemaps/events/{subscriptionID}/*", {"sitemap": sitemapName},
|
|
212
|
+
{400: "Missing sitemap parameter, or sitemap not linked successfully to the subscription.",
|
|
213
|
+
404: "Subscription not found."})
|
|
251
214
|
|
|
252
215
|
def subscribeToSitemapEvents(self):
|
|
253
216
|
"""
|
|
@@ -187,7 +187,8 @@ class Voice:
|
|
|
187
187
|
except requests.exceptions.HTTPError as err:
|
|
188
188
|
status_code = err.response.status_code
|
|
189
189
|
if status_code == 400:
|
|
190
|
-
|
|
190
|
+
# openHAB explains in its error message why the text was not understood
|
|
191
|
+
return {"error": f"Interpretation exception occurs: {str(err)}"}
|
|
191
192
|
elif status_code == 404:
|
|
192
193
|
return {"error": "No human language interpreter was found."}
|
|
193
194
|
else:
|
|
@@ -269,7 +270,8 @@ class Voice:
|
|
|
269
270
|
except requests.exceptions.HTTPError as err:
|
|
270
271
|
status_code = err.response.status_code
|
|
271
272
|
if status_code == 400:
|
|
272
|
-
|
|
273
|
+
# openHAB explains in its error message why the text was not understood
|
|
274
|
+
return {"error": f"Interpretation exception occurs: {str(err)}"}
|
|
273
275
|
elif status_code == 404:
|
|
274
276
|
return {"error": "No human language interpreter was found."}
|
|
275
277
|
else:
|