mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-25 14:55:55 +01:00
[siemenshvac] Initial contribution (#14263)
Signed-off-by: Laurent ARNAL <laurent@clae.net>
This commit is contained in:
parent
0786bfde9a
commit
80bd5bfcb3
@ -1626,6 +1626,11 @@
|
|||||||
<artifactId>org.openhab.binding.shelly</artifactId>
|
<artifactId>org.openhab.binding.shelly</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.siemenshvac</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.siemensrds</artifactId>
|
<artifactId>org.openhab.binding.siemensrds</artifactId>
|
||||||
|
20
bundles/org.openhab.binding.siemenshvac/NOTICE
Normal file
20
bundles/org.openhab.binding.siemenshvac/NOTICE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
This content is produced and maintained by the openHAB project.
|
||||||
|
|
||||||
|
* Project home: https://www.openhab.org
|
||||||
|
|
||||||
|
== Declared Project Licenses
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the terms
|
||||||
|
of the Eclipse Public License 2.0 which is available at
|
||||||
|
https://www.eclipse.org/legal/epl-2.0/.
|
||||||
|
|
||||||
|
== Source Code
|
||||||
|
|
||||||
|
https://github.com/openhab/openhab-addons
|
||||||
|
|
||||||
|
== Third-party Content
|
||||||
|
|
||||||
|
RuntimeTypeAdapterFactory
|
||||||
|
* License: Apache License, Version 2.0
|
||||||
|
* Project: https://github.com/google/gson
|
||||||
|
* Source: https://github.com/google/gson/blob/main/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
|
98
bundles/org.openhab.binding.siemenshvac/README.md
Normal file
98
bundles/org.openhab.binding.siemenshvac/README.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# SiemensHVAC Binding
|
||||||
|
|
||||||
|
This binding provides support for the Siemens HVAC controller ecosystem, and the Web Gateway interface OZW672.
|
||||||
|
A typical system is composed of:
|
||||||
|
|
||||||
|
![Diagram](doc/Diagram.png)
|
||||||
|
|
||||||
|
There's a lot of different HVAC controllers depending on model in lot of different PAC constructors.
|
||||||
|
Siemens RVS41.813/327 inside a Atlantic Hybrid Duo was used for the development, and is fully supported and tested.
|
||||||
|
|
||||||
|
Siemens have a complete set of controller references under the name "Siemens Albatros".
|
||||||
|
Here is a picture of such device.
|
||||||
|
You can also find this device in other types of heating systems: boiler or solar based.
|
||||||
|
|
||||||
|
![](doc/Albatros.jpg)
|
||||||
|
|
||||||
|
You will find some information about the OZW672.01 gateway on the Siemens web site:
|
||||||
|
|
||||||
|
[OZW 672 Page](https://hit.sbt.siemens.com/RWD/app.aspx?rc=FR&lang=fr&module=Catalog&action=ShowProduct&key=BPZ:OZW672.01)
|
||||||
|
|
||||||
|
With this binding, you will be able to:
|
||||||
|
|
||||||
|
- Consult the different parameters of your system, like temperature, current heating mode, water temperature, and many more.
|
||||||
|
- Modify the functioning mode of your device: temperature set point, heating mode, and others.
|
||||||
|
|
||||||
|
The OZW672 gateway supports many different languages (about 16).
|
||||||
|
The binding should work with all language choices, but is currently tested more thoroughly with French and English as configured language.
|
||||||
|
If you use another language, and find some issues, you can report them on the openHAB forum.
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
Discovery of Gateway can be done using UPnP.
|
||||||
|
Just switch off/on your gateway to make it annonce itself on the network.
|
||||||
|
The gateway should appear in the Inbox a few minutes after.
|
||||||
|
Be aware what you will have to modify the password in Gateway parameters just after the discovery to make it work properly.
|
||||||
|
Be also aware that first initialization is a little long because the binding needs to read all the metadata from the device.
|
||||||
|
|
||||||
|
Currently test was done with the OZW672.x series.
|
||||||
|
No test was conducted using the OZW772.x series, the code will currently not handle initialization of an OZW772 gateway.
|
||||||
|
You can request support in the community forum, if you have the gateway model and want it to be supported.
|
||||||
|
|
||||||
|
Discovery of HVAC device inside your PAC (controller of type RVS...) have to be done through the Scan button inside the binding.
|
||||||
|
Go to the Thing page, click on the "+" button, select the SiemensHVAC binding, and then click Scan.
|
||||||
|
Your device should appear on the page after a few seconds.
|
||||||
|
Only test in real conditions with RVS41.813/327 have been done, but it should work with all other types as the API interface is standard.
|
||||||
|
|
||||||
|
## Bridge Configuration
|
||||||
|
|
||||||
|
Parameter | Required | Default | Description
|
||||||
|
----------------|----------------|----------------|------------------
|
||||||
|
baseUrl | yes | | The address of the OZW672 devices
|
||||||
|
userName | yes | Administrator | The user name to log into the OZW672
|
||||||
|
userPass | yes | | The user password to log into the OZW672
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
Channels are auto-discovered, you will find them on the RVS things.
|
||||||
|
They are organized the same way as the LCD screen of your PAC device, by top level menu functionality, and sub-functionalities.
|
||||||
|
Each channel is strongly typed, so for example, for heating mode, openHAB will provide you with a list of choices supported by the device.
|
||||||
|
|
||||||
|
Channel | Description | Type | Unit | Security Access Level | ReadOnly | Advanced
|
||||||
|
--------------------------------|---------------------------------------------------------------------------------------------------|-------------------------------|----------|-------------------------|-----------|----------
|
||||||
|
1724#1725-optgmode-hc1 | Set Operating mode heat circuit 1 (`Automatic`, `Comfort`, `Reduced`, `Protection`) | operating-mode-hc | | | R/W | true
|
||||||
|
1724#1726-roomtemp-comfsetp-hc1 | Romm temperature comfort setpoint HC1 | room-temp-comfort-setpoint-hc | | | R/W | true
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
Things file `.things`
|
||||||
|
|
||||||
|
```java
|
||||||
|
Bridge siemenshvac:ozw:ozw672_FF00F445 "Ozw672" [ baseUrl="https://192.168.254.42/", userName="Administrator", userPassword="mypass" ]
|
||||||
|
{
|
||||||
|
Thing rvs41-813-327 00770000756A "RVS41.813/327" [ ]
|
||||||
|
{
|
||||||
|
Type room-temp-comfort-setpoint-hc : testChannelTemperature "TestChannelTemperature" [ id="1726" ]
|
||||||
|
Type operating-mode-hc : testChannelCC1 "TestChannelCC1" [ id="1725" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Items file `.items`
|
||||||
|
|
||||||
|
```java
|
||||||
|
Contact Boiler_State_Pump_HWSb "HWS Pump State [%s]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2259-ppechargeecs" }
|
||||||
|
Number Boiler_State_HWS "HWS State [%s]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2032#2035-etat-ecs" }
|
||||||
|
Number:Temperature Flow_Temperature_Real "Flow Temparature Real [%.1f °C]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2248-valreelletempdep-cc1" }
|
||||||
|
Number:Temperature Flow_Temperature_Setpoint "Flow Temperature Setpoint [%.1f °C]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2249-constdepresultcc1" }
|
||||||
|
Number Hour_fct_HWS "HWS Hour function" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2263-heuresfoncpompeecs" }
|
||||||
|
Number Nb_Start_HWS "HWS Number of start [%.1f]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2266-comptdemarresel-ecs" }
|
||||||
|
Number:Temperature Thermostat_Temperature "Thermostat tempeature [%.1f °C]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:2237#2246-tambact-cc1" }
|
||||||
|
Number:Temperature Thermostat_Setpoint "Thermostat setpoint [%.1f °C]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:1724#1726-consconfort-ta-cc1" }
|
||||||
|
Number Heat_Mode "Heat mode [%s]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:1724#1725-regime-cc1" }
|
||||||
|
|
||||||
|
Number:Temperature Thermostat_Setpoint_bis "Temperature [%.1f °C]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:testChannelTemperature " }
|
||||||
|
Number Heat_Mode_bis "Heat mode [%s]" { channel = "siemenshvac:rvs41-813-327:ozw672_FF00F445:00770000756A:testChannelCC1" }
|
||||||
|
|
||||||
|
```
|
BIN
bundles/org.openhab.binding.siemenshvac/doc/Albatros.jpg
Normal file
BIN
bundles/org.openhab.binding.siemenshvac/doc/Albatros.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
bundles/org.openhab.binding.siemenshvac/doc/Diagram.png
Normal file
BIN
bundles/org.openhab.binding.siemenshvac/doc/Diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
38
bundles/org.openhab.binding.siemenshvac/pom.xml
Normal file
38
bundles/org.openhab.binding.siemenshvac/pom.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||||
|
<version>4.2.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.siemenshvac</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: SiemensHvac Binding</name>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>build-helper-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>add-source</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<configuration>
|
||||||
|
<sources>
|
||||||
|
<source>src/3rdparty/java</source>
|
||||||
|
</sources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,465 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copied from
|
||||||
|
* https://raw.githubusercontent.com/google/gson/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
|
||||||
|
* and repackaged here with additional content from
|
||||||
|
* com.google.gson.internal.{Streams,TypeAdapters,LazilyParsedNumber}
|
||||||
|
* to avoid using the internal package.
|
||||||
|
*/
|
||||||
|
package com.google.gson.typeadapters;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonIOException;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
import com.google.gson.stream.MalformedJsonException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts values whose runtime type may differ from their declaration type. This
|
||||||
|
* is necessary when a field's type is not the same type that GSON should create
|
||||||
|
* when deserializing that field. For example, consider these types:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* @code
|
||||||
|
* abstract class Shape {
|
||||||
|
* int x;
|
||||||
|
* int y;
|
||||||
|
* }
|
||||||
|
* class Circle extends Shape {
|
||||||
|
* int radius;
|
||||||
|
* }
|
||||||
|
* class Rectangle extends Shape {
|
||||||
|
* int width;
|
||||||
|
* int height;
|
||||||
|
* }
|
||||||
|
* class Diamond extends Shape {
|
||||||
|
* int width;
|
||||||
|
* int height;
|
||||||
|
* }
|
||||||
|
* class Drawing {
|
||||||
|
* Shape bottomShape;
|
||||||
|
* Shape topShape;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Without additional type information, the serialized JSON is ambiguous. Is
|
||||||
|
* the bottom shape in this drawing a rectangle or a diamond?
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* {
|
||||||
|
* "bottomShape": {
|
||||||
|
* "width": 10,
|
||||||
|
* "height": 5,
|
||||||
|
* "x": 0,
|
||||||
|
* "y": 0
|
||||||
|
* },
|
||||||
|
* "topShape": {
|
||||||
|
* "radius": 2,
|
||||||
|
* "x": 4,
|
||||||
|
* "y": 1
|
||||||
|
* }
|
||||||
|
* }}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* This class addresses this problem by adding type information to the
|
||||||
|
* serialized JSON and honoring that type information when the JSON is
|
||||||
|
* deserialized:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* {
|
||||||
|
* "bottomShape": {
|
||||||
|
* "type": "Diamond",
|
||||||
|
* "width": 10,
|
||||||
|
* "height": 5,
|
||||||
|
* "x": 0,
|
||||||
|
* "y": 0
|
||||||
|
* },
|
||||||
|
* "topShape": {
|
||||||
|
* "type": "Circle",
|
||||||
|
* "radius": 2,
|
||||||
|
* "x": 4,
|
||||||
|
* "y": 1
|
||||||
|
* }
|
||||||
|
* }}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Both the type field name ({@code "type"}) and the type labels ({@code
|
||||||
|
* "Rectangle"}) are configurable.
|
||||||
|
*
|
||||||
|
* <h3>Registering Types</h3>
|
||||||
|
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
|
||||||
|
* name to the {@link #of} factory method. If you don't supply an explicit type
|
||||||
|
* field name, {@code "type"} will be used.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* @code
|
||||||
|
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class, "type");
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Next register all of your subtypes. Every subtype must be explicitly
|
||||||
|
* registered. This protects your application from injection attacks. If you
|
||||||
|
* don't supply an explicit type label, the type's simple name will be used.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
|
||||||
|
* shapeAdapter.registerSubtype(Circle.class, "Circle");
|
||||||
|
* shapeAdapter.registerSubtype(Diamond.class, "Diamond");
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Finally, register the type adapter factory in your application's GSON builder:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* @code
|
||||||
|
* Gson gson = new GsonBuilder().registerTypeAdapterFactory(shapeAdapterFactory).create();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Like {@code GsonBuilder}, this API supports chaining:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* @code
|
||||||
|
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
|
||||||
|
* .registerSubtype(Rectangle.class).registerSubtype(Circle.class).registerSubtype(Diamond.class);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Jesse Wilson (swankjesse) - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||||
|
private final Class<?> baseType;
|
||||||
|
private final String typeFieldName;
|
||||||
|
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
|
||||||
|
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
|
||||||
|
|
||||||
|
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
|
||||||
|
this.baseType = baseType;
|
||||||
|
this.typeFieldName = typeFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||||
|
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||||
|
*/
|
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
|
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
|
||||||
|
* the type field name.
|
||||||
|
*/
|
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
|
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, "type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers {@code type} identified by {@code label}. Labels are case
|
||||||
|
* sensitive.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if either {@code type} or {@code label}
|
||||||
|
* have already been registered on this type adapter.
|
||||||
|
*/
|
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
|
||||||
|
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
|
||||||
|
throw new IllegalArgumentException("types and labels must be unique");
|
||||||
|
}
|
||||||
|
labelToSubtype.put(label, type);
|
||||||
|
subtypeToLabel.put(type, label);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers {@code type} identified by its {@link Class#getSimpleName simple
|
||||||
|
* name}. Labels are case sensitive.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if either {@code type} or its simple name
|
||||||
|
* have already been registered on this type adapter.
|
||||||
|
*/
|
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
|
||||||
|
return registerSubtype(type, type.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable <R> TypeAdapter<R> create(@Nullable Gson gson, @Nullable TypeToken<R> type) {
|
||||||
|
if (type == null || type.getRawType() != baseType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (gson == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<String, TypeAdapter<?>>();
|
||||||
|
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate = new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
||||||
|
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||||
|
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||||
|
labelToDelegate.put(entry.getKey(), delegate);
|
||||||
|
subtypeToDelegate.put(entry.getValue(), delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TypeAdapter<R>() {
|
||||||
|
@Override
|
||||||
|
public @Nullable R read(JsonReader in) throws IOException {
|
||||||
|
JsonElement jsonElement = RuntimeTypeAdapterFactory.parse(in);
|
||||||
|
if (jsonElement != null) {
|
||||||
|
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
|
||||||
|
if (labelJsonElement == null) {
|
||||||
|
throw new JsonParseException("cannot deserialize " + baseType
|
||||||
|
+ " because it does not define a field named " + typeFieldName);
|
||||||
|
}
|
||||||
|
String label = labelJsonElement.getAsString();
|
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label
|
||||||
|
+ "; did you forget to register a subtype?");
|
||||||
|
}
|
||||||
|
return delegate.fromJsonTree(jsonElement);
|
||||||
|
} else {
|
||||||
|
throw new JsonParseException("cannot deserialize " + baseType + " because jsonElement is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, @Nullable R value) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> srcType = value.getClass();
|
||||||
|
String label = subtypeToLabel.get(srcType);
|
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
|
||||||
|
if (delegate == null) {
|
||||||
|
throw new JsonParseException(
|
||||||
|
"cannot serialize " + srcType.getName() + "; did you forget to register a subtype?");
|
||||||
|
}
|
||||||
|
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||||
|
if (jsonObject.has(typeFieldName)) {
|
||||||
|
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||||
|
+ " because it already defines a field named " + typeFieldName);
|
||||||
|
}
|
||||||
|
JsonObject clone = new JsonObject();
|
||||||
|
clone.add(typeFieldName, new JsonPrimitive(label));
|
||||||
|
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
|
||||||
|
clone.add(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
RuntimeTypeAdapterFactory.write(clone, out);
|
||||||
|
}
|
||||||
|
}.nullSafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a reader in any state and returns the next value as a JsonElement.
|
||||||
|
*/
|
||||||
|
private static @Nullable JsonElement parse(JsonReader reader) throws JsonParseException {
|
||||||
|
boolean isEmpty = true;
|
||||||
|
try {
|
||||||
|
reader.peek();
|
||||||
|
isEmpty = false;
|
||||||
|
return RuntimeTypeAdapterFactory.JSON_ELEMENT.read(reader);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
/*
|
||||||
|
* For compatibility with JSON 1.5 and earlier, we return a JsonNull for
|
||||||
|
* empty documents instead of throwing.
|
||||||
|
*/
|
||||||
|
if (isEmpty) {
|
||||||
|
return JsonNull.INSTANCE;
|
||||||
|
}
|
||||||
|
// The stream ended prematurely so it is likely a syntax error.
|
||||||
|
throw new JsonSyntaxException(e);
|
||||||
|
} catch (MalformedJsonException e) {
|
||||||
|
throw new JsonSyntaxException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new JsonIOException(e);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new JsonSyntaxException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the JSON element to the writer, recursively.
|
||||||
|
*/
|
||||||
|
private static void write(JsonElement element, JsonWriter writer) throws IOException {
|
||||||
|
RuntimeTypeAdapterFactory.JSON_ELEMENT.write(writer, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
|
||||||
|
@Override
|
||||||
|
public @Nullable JsonElement read(JsonReader in) throws IOException {
|
||||||
|
switch (in.peek()) {
|
||||||
|
case STRING:
|
||||||
|
return new JsonPrimitive(in.nextString());
|
||||||
|
case NUMBER:
|
||||||
|
String number = in.nextString();
|
||||||
|
return new JsonPrimitive(new LazilyParsedNumber(number));
|
||||||
|
case BOOLEAN:
|
||||||
|
return new JsonPrimitive(in.nextBoolean());
|
||||||
|
case NULL:
|
||||||
|
in.nextNull();
|
||||||
|
return JsonNull.INSTANCE;
|
||||||
|
case BEGIN_ARRAY:
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
in.beginArray();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
array.add(read(in));
|
||||||
|
}
|
||||||
|
in.endArray();
|
||||||
|
return array;
|
||||||
|
case BEGIN_OBJECT:
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
in.beginObject();
|
||||||
|
while (in.hasNext()) {
|
||||||
|
object.add(in.nextName(), read(in));
|
||||||
|
}
|
||||||
|
in.endObject();
|
||||||
|
return object;
|
||||||
|
case END_DOCUMENT:
|
||||||
|
case NAME:
|
||||||
|
case END_OBJECT:
|
||||||
|
case END_ARRAY:
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter out, @Nullable JsonElement value) throws IOException {
|
||||||
|
if (value == null || value.isJsonNull()) {
|
||||||
|
out.nullValue();
|
||||||
|
} else if (value.isJsonPrimitive()) {
|
||||||
|
JsonPrimitive primitive = value.getAsJsonPrimitive();
|
||||||
|
if (primitive.isNumber()) {
|
||||||
|
out.value(primitive.getAsNumber());
|
||||||
|
} else if (primitive.isBoolean()) {
|
||||||
|
out.value(primitive.getAsBoolean());
|
||||||
|
} else {
|
||||||
|
out.value(primitive.getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value.isJsonArray()) {
|
||||||
|
out.beginArray();
|
||||||
|
for (JsonElement e : value.getAsJsonArray()) {
|
||||||
|
write(out, e);
|
||||||
|
}
|
||||||
|
out.endArray();
|
||||||
|
|
||||||
|
} else if (value.isJsonObject()) {
|
||||||
|
out.beginObject();
|
||||||
|
for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
|
||||||
|
out.name(e.getKey());
|
||||||
|
write(out, e.getValue());
|
||||||
|
}
|
||||||
|
out.endObject();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Couldn't write " + value.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds a number value that is lazily converted to a specific number type
|
||||||
|
*
|
||||||
|
* @author Inderjeet Singh
|
||||||
|
*/
|
||||||
|
public static final class LazilyParsedNumber extends Number {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public LazilyParsedNumber(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
try {
|
||||||
|
return (int) Long.parseLong(value);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return new BigDecimal(value).intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return new BigDecimal(value).longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float floatValue() {
|
||||||
|
return Float.parseFloat(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
return Double.parseDouble(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If somebody is unlucky enough to have to serialize one of these, serialize
|
||||||
|
* it as a BigDecimal so that they won't need Gson on the other side to
|
||||||
|
* deserialize it.
|
||||||
|
*/
|
||||||
|
private Object writeReplace() throws ObjectStreamException {
|
||||||
|
return new BigDecimal(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright (c) 2010-2020 Contributors to the openHAB project
|
||||||
|
|
||||||
|
See the NOTICE file(s) distributed with this work for additional
|
||||||
|
information.
|
||||||
|
|
||||||
|
This program and the accompanying materials are made available under the
|
||||||
|
terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
http://www.eclipse.org/legal/epl-2.0
|
||||||
|
|
||||||
|
SPDX-License-Identifier: EPL-2.0
|
||||||
|
|
||||||
|
-->
|
||||||
|
<features name="org.openhab.binding.siemenshvac-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||||
|
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||||
|
|
||||||
|
<feature name="openhab-binding-siemenshvac" description="SiemensHvac Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<feature>openhab-transport-upnp</feature>
|
||||||
|
<feature>openhab-transport-mdns</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.siemenshvac/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.constants;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacBindingConstants {
|
||||||
|
|
||||||
|
public static final String BINDING_ID = "siemenshvac";
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_OZW = new ThingTypeUID(BINDING_ID, "ozw");
|
||||||
|
|
||||||
|
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(SiemensHvacBindingConstants.THING_TYPE_OZW);
|
||||||
|
|
||||||
|
public static final String IP_ADDRESS = "ipAddress";
|
||||||
|
public static final String BASE_URL = "baseUrl";
|
||||||
|
|
||||||
|
public static final String PROPERTY_VENDOR_NAME = "Siemens";
|
||||||
|
|
||||||
|
public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing-type";
|
||||||
|
|
||||||
|
public static final String DPT_TYPE_ENUM = "Enumeration";
|
||||||
|
public static final String DPT_TYPE_NUMERIC = "Numeric";
|
||||||
|
public static final String DPT_TYPE_RADIO = "RadioButton";
|
||||||
|
public static final String DPT_TYPE_DATE_TIME = "DateTime";
|
||||||
|
public static final String DPT_TYPE_TIMEOFDAY = "TimeOfDay";
|
||||||
|
public static final String DPT_TYPE_STRING = "String";
|
||||||
|
|
||||||
|
public static final String DPT_TYPE_CHECKBOX = "CheckBox";
|
||||||
|
public static final String DPT_TYPE_SCHEDULER = "Scheduler";
|
||||||
|
public static final String DPT_TYPE_CALENDAR = "Calendar";
|
||||||
|
|
||||||
|
public static final String CATEGORY_THING_HVAC = "HVAC";
|
||||||
|
|
||||||
|
public static final String CATEGORY_CHANNEL_NUMBER = "Number";
|
||||||
|
public static final String CATEGORY_CHANNEL_SWITCH = "Switch";
|
||||||
|
public static final String CATEGORY_CHANNEL_TEMP = "Temperature";
|
||||||
|
public static final String CATEGORY_CHANNEL_TIME = "Time";
|
||||||
|
|
||||||
|
public static final String CATEGORY_CHANNEL_CONTROL_HEATING = "Heating";
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception if something goes wrong when converting values between openHAB and the binding.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ConverterException extends Exception {
|
||||||
|
private static final long serialVersionUID = 42567425458545L;
|
||||||
|
|
||||||
|
public ConverterException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.CalendarTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.CheckboxTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.DateTimeTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.EnumTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.NumericTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.RadioTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.SchedulerTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.StringTypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.type.TimeOfDayTypeConverter;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for creating converters based on the itemType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ConverterFactory {
|
||||||
|
private static Map<String, TypeConverter> converterCache = new HashMap<>();
|
||||||
|
|
||||||
|
public static void registerConverter(TimeZoneProvider timeZoneProvider) {
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_DATE_TIME, new DateTimeTypeConverter(timeZoneProvider));
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_ENUM, new EnumTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_NUMERIC, new NumericTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_RADIO, new RadioTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_STRING, new StringTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_TIMEOFDAY, new TimeOfDayTypeConverter(timeZoneProvider));
|
||||||
|
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_CHECKBOX, new CheckboxTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_SCHEDULER, new SchedulerTypeConverter());
|
||||||
|
registerConverter(SiemensHvacBindingConstants.DPT_TYPE_CALENDAR, new CalendarTypeConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerConverter(String key, TypeConverter tp) {
|
||||||
|
converterCache.put(key, tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the converter for an itemType.
|
||||||
|
*/
|
||||||
|
public static TypeConverter getConverter(String itemType) throws ConverterTypeException {
|
||||||
|
TypeConverter converter = converterCache.get(itemType);
|
||||||
|
if (converter == null) {
|
||||||
|
throw new ConverterTypeException("Can't find a converter for type '" + itemType + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception if converting between two types is not possible due wrong item type or command.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class ConverterTypeException extends ConverterException {
|
||||||
|
private static final long serialVersionUID = 2546248551752214152L;
|
||||||
|
|
||||||
|
public ConverterTypeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converter interface for converting between openHAB states/commands and siemensHvac values.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface TypeConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an openHAB type to a SiemensHVac value.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Object convertToBinding(Type type, ChannelType tp) throws ConverterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a siemensHvac value to an openHAB type.
|
||||||
|
*/
|
||||||
|
State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) throws ConverterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get underlying channel type to construct channel type UID
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
String getChannelType(SiemensHvacMetadataDataPoint dpt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get underlying item type on openhab side for this SiemensHvac type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
String getItemType(SiemensHvacMetadataDataPoint dpt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tell if this type have different subvariant or not
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
boolean hasVariant();
|
||||||
|
}
|
@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.TypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all Converters with common methods.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public abstract class AbstractTypeConverter implements TypeConverter {
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(AbstractTypeConverter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object convertToBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
if (type == UnDefType.NULL) {
|
||||||
|
return null;
|
||||||
|
} else if (type.getClass().isEnum()) {
|
||||||
|
return commandToBinding((Command) type, tp);
|
||||||
|
} else if (!toBindingValidation(type)) {
|
||||||
|
String errorMessage = String.format("Can't convert type %s with value '%s' to %s value",
|
||||||
|
type.getClass().getSimpleName(), type.toString(), this.getClass().getSimpleName());
|
||||||
|
throw new ConverterTypeException(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toBinding(type, tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State convertFromBinding(JsonObject dp, ChannelType tp, Locale locale) throws ConverterException {
|
||||||
|
String type = null;
|
||||||
|
String unit = "";
|
||||||
|
JsonElement value = null;
|
||||||
|
|
||||||
|
if (dp.has("Type")) {
|
||||||
|
type = dp.get("Type").getAsString().trim();
|
||||||
|
}
|
||||||
|
if (dp.has("Value")) {
|
||||||
|
value = dp.get("Value");
|
||||||
|
}
|
||||||
|
if (dp.has("EnumValue")) {
|
||||||
|
value = dp.get("EnumValue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dp.has("Unit")) {
|
||||||
|
unit = dp.get("Unit").getAsString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
return UnDefType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == null) {
|
||||||
|
logger.debug("siemensHvac:ReadDP:null type {}", dp);
|
||||||
|
return UnDefType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fromBindingValidation(value, unit, type)) {
|
||||||
|
logger.debug("Can't convert {} value '{}' with {} for '{}'", type, value, this.getClass().getSimpleName(),
|
||||||
|
dp);
|
||||||
|
return UnDefType.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromBinding(value, unit, type, tp, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an openHAB command to a SiemensHvacValue value.
|
||||||
|
*/
|
||||||
|
protected @Nullable Object commandToBinding(Command command, ChannelType tp) throws ConverterException {
|
||||||
|
throw new ConverterException("Unsupported command " + command.getClass().getSimpleName() + " for "
|
||||||
|
+ this.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if the conversion from openHAB to the binding is possible.
|
||||||
|
*/
|
||||||
|
protected abstract boolean toBindingValidation(Type type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the type to a datapoint value.
|
||||||
|
*/
|
||||||
|
protected abstract @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if the conversion from the binding to openHAB is possible.
|
||||||
|
*/
|
||||||
|
protected abstract boolean fromBindingValidation(JsonElement value, String unit, String type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the datapoint value to an openHAB type.
|
||||||
|
*/
|
||||||
|
protected abstract State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get underlying channel type to construct channel type UID
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract String getChannelType(SiemensHvacMetadataDataPoint dpt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get underlying item type on openhab side for this SiemensHvac type
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract String getItemType(SiemensHvacMetadataDataPoint dpt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tell if this type have different subvariant or not
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract boolean hasVariant();
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class CalendarTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DateTimeType dateTime) {
|
||||||
|
valUpdate = dateTime.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
throw new ConverterException("NIY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "datetime";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.DATETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class CheckboxTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
throw new ConverterException("NIY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
throw new ConverterException("NIY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "contact";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.CONTACT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
import java.time.temporal.ChronoField;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class DateTimeTypeConverter extends AbstractTypeConverter {
|
||||||
|
|
||||||
|
private final TimeZoneProvider timeZoneProvider;
|
||||||
|
|
||||||
|
public DateTimeTypeConverter(final TimeZoneProvider timeZoneProvider) {
|
||||||
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DateTimeType dateTime) {
|
||||||
|
valUpdate = dateTime.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DateTimeType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
if ("----".equals(value.getAsString())) {
|
||||||
|
return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone()));
|
||||||
|
} else {
|
||||||
|
String[] formats = { "EEEE, d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy HH:mm[:ss]", "d. LLLL yyyy",
|
||||||
|
"d. LLLL" };
|
||||||
|
|
||||||
|
for (int i = 0; i < formats.length; i++) {
|
||||||
|
try {
|
||||||
|
DateTimeFormatterBuilder formatterBuilder = new DateTimeFormatterBuilder().parseCaseInsensitive()
|
||||||
|
.appendPattern(formats[i]).parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
|
||||||
|
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
|
||||||
|
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
|
||||||
|
|
||||||
|
if (i == 3) {
|
||||||
|
formatterBuilder = formatterBuilder.parseDefaulting(ChronoField.YEAR,
|
||||||
|
ZonedDateTime.now(this.timeZoneProvider.getTimeZone()).getYear());
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDateTime parsedDate = LocalDateTime.parse(value.getAsString(),
|
||||||
|
formatterBuilder.toFormatter(locale));
|
||||||
|
|
||||||
|
ZonedDateTime zdt = parsedDate.atZone(this.timeZoneProvider.getTimeZone());
|
||||||
|
|
||||||
|
return new DateTimeType(zdt);
|
||||||
|
} catch (DateTimeParseException ex) {
|
||||||
|
// Silently ignore, we are proceeding to next format.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ConverterException("Can't parse the date for:" + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "datetime";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.DATETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class EnumTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DecimalType decimalValue) {
|
||||||
|
valUpdate = decimalValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
return new DecimalType(value.getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "number";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.measure.Unit;
|
||||||
|
import javax.measure.quantity.Dimensionless;
|
||||||
|
import javax.measure.quantity.ElectricPotential;
|
||||||
|
import javax.measure.quantity.Temperature;
|
||||||
|
import javax.measure.quantity.Time;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.library.types.QuantityType;
|
||||||
|
import org.openhab.core.library.unit.ImperialUnits;
|
||||||
|
import org.openhab.core.library.unit.SIUnits;
|
||||||
|
import org.openhab.core.library.unit.Units;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class NumericTypeConverter extends AbstractTypeConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof QuantityType quantityType) {
|
||||||
|
Number num = (quantityType);
|
||||||
|
valUpdate = num.doubleValue();
|
||||||
|
} else if (type instanceof DecimalType decimalValue) {
|
||||||
|
valUpdate = decimalValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
if ("----".equals(value.getAsString())) {
|
||||||
|
return new DecimalType(0);
|
||||||
|
} else {
|
||||||
|
double dValue = value.getAsDouble();
|
||||||
|
|
||||||
|
String itemType = tp.getItemType();
|
||||||
|
|
||||||
|
if (itemType != null) {
|
||||||
|
if ("Number:Temperature".equals(itemType)) {
|
||||||
|
Unit<Temperature> targetUnit = null;
|
||||||
|
|
||||||
|
if ("°C".equals(unit)) {
|
||||||
|
targetUnit = SIUnits.CELSIUS;
|
||||||
|
} else if ("°F".equals(unit)) {
|
||||||
|
targetUnit = ImperialUnits.FAHRENHEIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUnit != null) {
|
||||||
|
return new QuantityType<>(dValue, targetUnit);
|
||||||
|
}
|
||||||
|
} else if ("Number:ElectricPotential".equals(itemType)) {
|
||||||
|
Unit<ElectricPotential> targetUnit = null;
|
||||||
|
|
||||||
|
if ("V".equals(unit)) {
|
||||||
|
targetUnit = Units.VOLT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUnit != null) {
|
||||||
|
return new QuantityType<>(dValue, targetUnit);
|
||||||
|
}
|
||||||
|
} else if ("Number:Time".equals(itemType)) {
|
||||||
|
Unit<Time> targetUnit = null;
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "s":
|
||||||
|
case "sek":
|
||||||
|
targetUnit = Units.SECOND;
|
||||||
|
break;
|
||||||
|
case "m":
|
||||||
|
case "min":
|
||||||
|
case "perc":
|
||||||
|
case "dak":
|
||||||
|
case "мин":
|
||||||
|
targetUnit = Units.MINUTE;
|
||||||
|
break;
|
||||||
|
case "h":
|
||||||
|
case "sa":
|
||||||
|
targetUnit = Units.HOUR;
|
||||||
|
break;
|
||||||
|
case "Months":
|
||||||
|
case "Monate":
|
||||||
|
case "Mois":
|
||||||
|
case "Mesi":
|
||||||
|
case "Maanden":
|
||||||
|
case "mies.":
|
||||||
|
case "Měsíce":
|
||||||
|
case "hónap":
|
||||||
|
case "Meses":
|
||||||
|
case "mdr.":
|
||||||
|
case "Månader":
|
||||||
|
case "kk":
|
||||||
|
case "месяцы":
|
||||||
|
case "Aylar":
|
||||||
|
case "mesiac":
|
||||||
|
targetUnit = Units.MONTH;
|
||||||
|
break;
|
||||||
|
case "d":
|
||||||
|
case "Jours":
|
||||||
|
case "giorni":
|
||||||
|
case "Dny":
|
||||||
|
case "nap":
|
||||||
|
case "dage":
|
||||||
|
case "dag":
|
||||||
|
case "vrk":
|
||||||
|
case "д":
|
||||||
|
targetUnit = Units.DAY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUnit != null) {
|
||||||
|
return new QuantityType<>(dValue, targetUnit);
|
||||||
|
}
|
||||||
|
} else if ("Number:Dimensionless".equals(itemType)) {
|
||||||
|
Unit<Dimensionless> targetUnit = null;
|
||||||
|
|
||||||
|
if ("%".equals(unit)) {
|
||||||
|
targetUnit = Units.PERCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetUnit != null) {
|
||||||
|
return new QuantityType<>(dValue, targetUnit);
|
||||||
|
}
|
||||||
|
} else if ("Number".equals(itemType)) {
|
||||||
|
return new DecimalType(dValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new DecimalType(dValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DecimalType(dValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "number";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
String unit = dpt.getDptUnit();
|
||||||
|
|
||||||
|
if (unit == null) {
|
||||||
|
return CoreItemFactory.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("".equals(unit)) {
|
||||||
|
return CoreItemFactory.NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case "°F":
|
||||||
|
case "°C":
|
||||||
|
return CoreItemFactory.NUMBER + ":Temperature";
|
||||||
|
case "°F*min":
|
||||||
|
case "°C*min":
|
||||||
|
case "°Cmin":
|
||||||
|
case "°Cdak":
|
||||||
|
return CoreItemFactory.NUMBER;
|
||||||
|
case "V":
|
||||||
|
return CoreItemFactory.NUMBER + ":ElectricPotential";
|
||||||
|
case "%":
|
||||||
|
return CoreItemFactory.NUMBER + ":Dimensionless";
|
||||||
|
case "d":
|
||||||
|
case "Jours":
|
||||||
|
case "giorni":
|
||||||
|
case "Dny":
|
||||||
|
case "nap":
|
||||||
|
case "dage":
|
||||||
|
case "dag":
|
||||||
|
case "vrk":
|
||||||
|
case "д":
|
||||||
|
case "h":
|
||||||
|
case "sa":
|
||||||
|
case "m":
|
||||||
|
case "s":
|
||||||
|
case "сек":
|
||||||
|
case "min":
|
||||||
|
case "perc":
|
||||||
|
case "мин":
|
||||||
|
case "dak":
|
||||||
|
case "Months":
|
||||||
|
case "Monate":
|
||||||
|
case "Mois":
|
||||||
|
case "Mesi":
|
||||||
|
case "Maanden":
|
||||||
|
case "mies.":
|
||||||
|
case "Měsíce":
|
||||||
|
case "hónap":
|
||||||
|
case "Meses":
|
||||||
|
case "mdr.":
|
||||||
|
case "Månader":
|
||||||
|
case "kk":
|
||||||
|
case "месяцы":
|
||||||
|
case "Aylar":
|
||||||
|
case "mesiac":
|
||||||
|
return CoreItemFactory.NUMBER + ":Time";
|
||||||
|
default:
|
||||||
|
return CoreItemFactory.NUMBER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.library.types.OnOffType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
|
import org.openhab.core.types.StateOption;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class RadioTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DecimalType decimalValue) {
|
||||||
|
valUpdate = decimalValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object commandToBinding(Command command, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (command instanceof DecimalType decimalValue) {
|
||||||
|
valUpdate = decimalValue.toString();
|
||||||
|
} else if (command instanceof OnOffType onOffValue) {
|
||||||
|
if (onOffValue.equals(OnOffType.OFF)) {
|
||||||
|
valUpdate = 0;
|
||||||
|
} else if (onOffValue.equals(OnOffType.ON)) {
|
||||||
|
valUpdate = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
State updateVal = UnDefType.UNDEF;
|
||||||
|
String valueSt = value.getAsString();
|
||||||
|
|
||||||
|
StateDescription sd = tp.getState();
|
||||||
|
|
||||||
|
if (sd != null) {
|
||||||
|
List<StateOption> options = sd.getOptions();
|
||||||
|
StateOption offOpt = options.get(0);
|
||||||
|
StateOption onOpt = options.get(1);
|
||||||
|
|
||||||
|
if (valueSt.equals(onOpt.getLabel())) {
|
||||||
|
updateVal = new DecimalType(1);
|
||||||
|
} else if (valueSt.equals(offOpt.getLabel())) {
|
||||||
|
updateVal = new DecimalType(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
if (dpt.getWriteAccess()) {
|
||||||
|
return "switch";
|
||||||
|
} else {
|
||||||
|
return "contact";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
if (dpt.getWriteAccess()) {
|
||||||
|
return CoreItemFactory.SWITCH;
|
||||||
|
} else {
|
||||||
|
return CoreItemFactory.CONTACT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SchedulerTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DateTimeType dateTime) {
|
||||||
|
valUpdate = dateTime.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DecimalType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
throw new ConverterException("NIY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "datetime";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.DATETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.library.types.PercentType;
|
||||||
|
import org.openhab.core.library.types.StringType;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class StringTypeConverter extends AbstractTypeConverter {
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof PercentType percentValue) {
|
||||||
|
valUpdate = percentValue.toString();
|
||||||
|
} else if (type instanceof DecimalType decimalValue) {
|
||||||
|
valUpdate = decimalValue.toString();
|
||||||
|
} else if (type instanceof StringType stringValue) {
|
||||||
|
valUpdate = stringValue.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StringType fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
return new StringType(value.getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.converter.type;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.measure.Unit;
|
||||||
|
import javax.measure.quantity.Time;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.library.types.QuantityType;
|
||||||
|
import org.openhab.core.library.unit.Units;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts between a SiemensHvac datapoint value and an openHAB DecimalType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class TimeOfDayTypeConverter extends AbstractTypeConverter {
|
||||||
|
private final TimeZoneProvider timeZoneProvider;
|
||||||
|
|
||||||
|
public TimeOfDayTypeConverter(final TimeZoneProvider timeZoneProvider) {
|
||||||
|
this.timeZoneProvider = timeZoneProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean toBindingValidation(Type type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable Object toBinding(Type type, ChannelType tp) throws ConverterException {
|
||||||
|
Object valUpdate = null;
|
||||||
|
|
||||||
|
if (type instanceof DateTimeType dateTime) {
|
||||||
|
valUpdate = dateTime.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return valUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean fromBindingValidation(JsonElement value, String unit, String type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State fromBinding(JsonElement value, String unit, String type, ChannelType tp, Locale locale)
|
||||||
|
throws ConverterException {
|
||||||
|
if ("----".equals(value.getAsString())) {
|
||||||
|
return new DateTimeType(ZonedDateTime.now(this.timeZoneProvider.getTimeZone()));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (unit.equals("h:m")) {
|
||||||
|
String st = value.getAsString();
|
||||||
|
String[] parts = st.split(":");
|
||||||
|
int h = Integer.parseInt(parts[0]);
|
||||||
|
int m = Integer.parseInt(parts[1]);
|
||||||
|
|
||||||
|
Unit<Time> targetUnit = Units.MINUTE;
|
||||||
|
return new QuantityType<>(h * 60 + m, targetUnit);
|
||||||
|
|
||||||
|
} else if (unit.equals("m:s")) {
|
||||||
|
String st = value.getAsString();
|
||||||
|
String[] parts = st.split(":");
|
||||||
|
int m = Integer.parseInt(parts[0]);
|
||||||
|
int s = Integer.parseInt(parts[1]);
|
||||||
|
|
||||||
|
Unit<Time> targetUnit = Units.SECOND;
|
||||||
|
return new QuantityType<>(m * 60 + s, targetUnit);
|
||||||
|
|
||||||
|
} else if (unit.equals("h")) {
|
||||||
|
int val = Integer.parseInt(value.getAsString());
|
||||||
|
|
||||||
|
Unit<Time> targetUnit = Units.HOUR;
|
||||||
|
return new QuantityType<>(val, targetUnit);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new ConverterException("unsupported unit type:" + unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getChannelType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return "number";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getItemType(SiemensHvacMetadataDataPoint dpt) {
|
||||||
|
return CoreItemFactory.NUMBER + ":Time";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasVariant() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.discovery;
|
||||||
|
|
||||||
|
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.jupnp.model.meta.DeviceDetails;
|
||||||
|
import org.jupnp.model.meta.ModelDetails;
|
||||||
|
import org.jupnp.model.meta.RemoteDevice;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
|
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||||
|
import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Modified;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacDiscoveryParticipant} is responsible for discovering new and
|
||||||
|
* removed siemensHvac bridges. It uses the central {@link UpnpDiscoveryService}.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(configurationPid = "discovery.siemenshvac", immediate = true)
|
||||||
|
public class SiemenesHvacDiscoveryParticipant implements UpnpDiscoveryParticipant {
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public void activate(@Nullable Map<String, Object> configProperties) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Modified
|
||||||
|
public void modified(@Nullable Map<String, Object> configProperties) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||||
|
return SiemensHvacBindingConstants.SUPPORTED_THING_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable DiscoveryResult createResult(RemoteDevice device) {
|
||||||
|
ThingUID uid = getThingUID(device);
|
||||||
|
if (uid != null) {
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
String ipAddress = device.getDetails().getPresentationURI().getHost();
|
||||||
|
properties.put(SiemensHvacBindingConstants.BASE_URL, "https://" + ipAddress + "/");
|
||||||
|
|
||||||
|
String label = "";
|
||||||
|
|
||||||
|
if (uid.getAsString().contains("ozw672")) {
|
||||||
|
label = "OZW672 IP Gateway";
|
||||||
|
} else if (uid.getAsString().contains("ozw772")) {
|
||||||
|
label = "OZW772 IP Gateway";
|
||||||
|
}
|
||||||
|
|
||||||
|
String serialNumber = device.getDetails().getSerialNumber();
|
||||||
|
DiscoveryResult result;
|
||||||
|
if (serialNumber != null && !serialNumber.isBlank()) {
|
||||||
|
properties.put(PROPERTY_SERIAL_NUMBER, serialNumber.toLowerCase());
|
||||||
|
|
||||||
|
result = DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label)
|
||||||
|
.withRepresentationProperty(PROPERTY_SERIAL_NUMBER).build();
|
||||||
|
} else {
|
||||||
|
result = DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label).build();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingUID getThingUID(RemoteDevice device) {
|
||||||
|
DeviceDetails details = device.getDetails();
|
||||||
|
if (details != null) {
|
||||||
|
ModelDetails modelDetails = details.getModelDetails();
|
||||||
|
String serialNumber = details.getSerialNumber();
|
||||||
|
if (modelDetails != null && serialNumber != null && !serialNumber.isBlank()) {
|
||||||
|
String modelName = modelDetails.getModelName();
|
||||||
|
if (modelName != null) {
|
||||||
|
if (modelName.startsWith("Web Server OZW672")) {
|
||||||
|
return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW, "ozw672-" + serialNumber);
|
||||||
|
} else if (modelName.startsWith("Web Server OZW772")) {
|
||||||
|
return new ThingUID(SiemensHvacBindingConstants.THING_TYPE_OZW, "ozw772-" + serialNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.discovery;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.UidUtils;
|
||||||
|
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||||
|
import org.openhab.core.config.discovery.DiscoveryService;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacDeviceDiscoveryService} tracks for Siemens Hvac device connected to the bus.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacDeviceDiscoveryService extends AbstractDiscoveryService
|
||||||
|
implements DiscoveryService, ThingHandlerService {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SiemensHvacDeviceDiscoveryService.class);
|
||||||
|
|
||||||
|
private @Nullable SiemensHvacMetadataRegistry metadataRegistry;
|
||||||
|
private @Nullable SiemensHvacBridgeThingHandler siemensHvacBridgeHandler;
|
||||||
|
|
||||||
|
private static final int SEARCH_TIME = 10;
|
||||||
|
|
||||||
|
public SiemensHvacDeviceDiscoveryService() {
|
||||||
|
super(SiemensHvacBindingConstants.SUPPORTED_THING_TYPES, SEARCH_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Reference
|
||||||
|
public void setSiemensHvacMetadataRegistry(@Nullable SiemensHvacMetadataRegistry metadataRegistry) {
|
||||||
|
this.metadataRegistry = metadataRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsetSiemensHvacMetadataRegistry(SiemensHvacMetadataRegistry metadataRegistry) {
|
||||||
|
this.metadataRegistry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startBackgroundDiscovery() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stopBackgroundDiscovery() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ThingUID getThingUID(ThingTypeUID thingTypeUID, String serial) {
|
||||||
|
final SiemensHvacBridgeThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler;
|
||||||
|
if (lcSiemensHvacBridgeHandler != null) {
|
||||||
|
ThingUID localBridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID();
|
||||||
|
return new ThingUID(thingTypeUID, localBridgeUID, serial);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startScan() {
|
||||||
|
final SiemensHvacMetadataRegistry lcMetadataRegistry = metadataRegistry;
|
||||||
|
final SiemensHvacBridgeThingHandler lcSiemensHvacBridgeHandler = siemensHvacBridgeHandler;
|
||||||
|
logger.debug("call startScan()");
|
||||||
|
|
||||||
|
if (lcMetadataRegistry != null) {
|
||||||
|
try {
|
||||||
|
lcMetadataRegistry.readMeta();
|
||||||
|
} catch (SiemensHvacException ex) {
|
||||||
|
logger.debug("Exception occurred during execution: {}", ex.getMessage(), ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<SiemensHvacMetadataDevice> devices = lcMetadataRegistry.getDevices();
|
||||||
|
|
||||||
|
if (devices == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (SiemensHvacMetadataDevice device : devices) {
|
||||||
|
|
||||||
|
String name = device.getName();
|
||||||
|
String type = device.getType();
|
||||||
|
String addr = device.getAddr();
|
||||||
|
String serialNr = device.getSerialNr();
|
||||||
|
|
||||||
|
logger.debug("Find devices: {} / {} / {} / {}", name, type, addr, serialNr);
|
||||||
|
|
||||||
|
String typeSn = UidUtils.sanetizeId(type);
|
||||||
|
ThingTypeUID thingTypeUID = new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, typeSn);
|
||||||
|
|
||||||
|
ThingUID thingUID = getThingUID(thingTypeUID, serialNr);
|
||||||
|
|
||||||
|
if (lcSiemensHvacBridgeHandler != null) {
|
||||||
|
ThingUID bridgeUID = lcSiemensHvacBridgeHandler.getThing().getUID();
|
||||||
|
|
||||||
|
if (thingUID != null) {
|
||||||
|
Map<String, Object> properties = new HashMap<>(4);
|
||||||
|
properties.put(Thing.PROPERTY_MODEL_ID, name);
|
||||||
|
properties.put("type", type);
|
||||||
|
properties.put("addr", addr);
|
||||||
|
properties.put(Thing.PROPERTY_SERIAL_NUMBER, serialNr);
|
||||||
|
|
||||||
|
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||||
|
.withProperties(properties).withBridge(bridgeUID).withLabel(name).build();
|
||||||
|
|
||||||
|
thingDiscovered(discoveryResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void stopScan() {
|
||||||
|
super.stopScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setThingHandler(@Nullable ThingHandler handler) {
|
||||||
|
if (handler instanceof SiemensHvacBridgeThingHandler siemensHvacBridgeHandler) {
|
||||||
|
this.siemensHvacBridgeHandler = siemensHvacBridgeHandler;
|
||||||
|
this.siemensHvacBridgeHandler.registerDiscoveryListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingHandler getThingHandler() {
|
||||||
|
return siemensHvacBridgeHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deactivate() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.factory;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacHandlerImpl;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry;
|
||||||
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.i18n.TimeZoneProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.net.NetworkAddressService;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Laurent ARNAL - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.siemenshvac")
|
||||||
|
public class SiemensHvacHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
private final NetworkAddressService networkAddressService;
|
||||||
|
private final HttpClientFactory httpClientFactory;
|
||||||
|
private final SiemensHvacMetadataRegistry metaDataRegistry;
|
||||||
|
private final ChannelTypeRegistry channelTypeRegistry;
|
||||||
|
private final TranslationProvider translationProvider;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public SiemensHvacHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||||
|
final @Reference SiemensHvacMetadataRegistry metaDataRegistry,
|
||||||
|
final @Reference NetworkAddressService networkAddressService,
|
||||||
|
final @Reference ChannelTypeRegistry channelTypeRegistry,
|
||||||
|
final @Reference TimeZoneProvider timeZoneProvider,
|
||||||
|
final @Reference TranslationProvider translationProvider) {
|
||||||
|
this.httpClientFactory = httpClientFactory;
|
||||||
|
this.metaDataRegistry = metaDataRegistry;
|
||||||
|
this.networkAddressService = networkAddressService;
|
||||||
|
this.channelTypeRegistry = channelTypeRegistry;
|
||||||
|
this.translationProvider = translationProvider;
|
||||||
|
|
||||||
|
ConverterFactory.registerConverter(timeZoneProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
return SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
|
||||||
|
@Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
|
||||||
|
if (SiemensHvacBindingConstants.THING_TYPE_OZW.equals(thingTypeUID)) {
|
||||||
|
return super.createThing(thingTypeUID, configuration, thingUID, null);
|
||||||
|
} else if (SiemensHvacBindingConstants.BINDING_ID.equals(thingTypeUID.getBindingId())) {
|
||||||
|
return super.createThing(thingTypeUID, configuration, thingUID, bridgeUID);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The thing type " + thingTypeUID + " is not supported by the SiemensHvac binding.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
|
if (thing.getThingTypeUID().equals(SiemensHvacBindingConstants.THING_TYPE_OZW)) {
|
||||||
|
return new SiemensHvacBridgeThingHandler((Bridge) thing, networkAddressService, httpClientFactory,
|
||||||
|
metaDataRegistry, translationProvider);
|
||||||
|
} else if (SiemensHvacBindingConstants.BINDING_ID.equals(thing.getThingTypeUID().getBindingId())) {
|
||||||
|
SiemensHvacHandlerImpl handler = new SiemensHvacHandlerImpl(thing,
|
||||||
|
metaDataRegistry.getSiemensHvacConnector(), metaDataRegistry, channelTypeRegistry);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.handler;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacBridgeConfig {
|
||||||
|
|
||||||
|
public String baseUrl = "";
|
||||||
|
public String userName = "Administrator";
|
||||||
|
public String userPassword = "password";
|
||||||
|
}
|
@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.handler;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.discovery.SiemensHvacDeviceDiscoveryService;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.net.NetworkAddressService;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
import org.osgi.framework.FrameworkUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacBridgeBaseThingHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution and API
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacBridgeThingHandler extends BaseBridgeHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SiemensHvacBridgeThingHandler.class);
|
||||||
|
private @Nullable SiemensHvacDeviceDiscoveryService discoveryService;
|
||||||
|
private final @Nullable HttpClientFactory httpClientFactory;
|
||||||
|
private final SiemensHvacMetadataRegistry metaDataRegistry;
|
||||||
|
private @Nullable SiemensHvacBridgeConfig config;
|
||||||
|
private final TranslationProvider translationProvider;
|
||||||
|
|
||||||
|
public SiemensHvacBridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService,
|
||||||
|
@Nullable HttpClientFactory httpClientFactory, SiemensHvacMetadataRegistry metaDataRegistry,
|
||||||
|
TranslationProvider translationProvider) {
|
||||||
|
super(bridge);
|
||||||
|
SiemensHvacConnector lcConnector = null;
|
||||||
|
this.httpClientFactory = httpClientFactory;
|
||||||
|
this.metaDataRegistry = metaDataRegistry;
|
||||||
|
this.translationProvider = translationProvider;
|
||||||
|
|
||||||
|
lcConnector = this.metaDataRegistry.getSiemensHvacConnector();
|
||||||
|
if (lcConnector != null) {
|
||||||
|
lcConnector.setSiemensHvacBridgeBaseThingHandler(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
metaDataRegistry.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
SiemensHvacBridgeConfig lcConfig = getConfigAs(SiemensHvacBridgeConfig.class);
|
||||||
|
String baseUrl = null;
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Initialize() bridge: {}", getBuildDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
baseUrl = lcConfig.baseUrl;
|
||||||
|
|
||||||
|
if (baseUrl.isEmpty()) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||||
|
"@text/offline.error-gateway-init");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) {
|
||||||
|
baseUrl = "http://" + baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseUrl.endsWith("/")) {
|
||||||
|
baseUrl = baseUrl + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
config = lcConfig;
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/offline.waiting-bridge-initialization");
|
||||||
|
|
||||||
|
// Will read metadata in background to not block initialize for a long period !
|
||||||
|
scheduler.schedule(this::initializeCode, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getBuildDate() {
|
||||||
|
try {
|
||||||
|
ClassLoader cl = getClass().getClassLoader();
|
||||||
|
if (cl != null) {
|
||||||
|
URL res = cl.getResource(getClass().getCanonicalName().replace('.', '/') + ".class");
|
||||||
|
if (res != null) {
|
||||||
|
URLConnection cnx = res.openConnection();
|
||||||
|
LocalDate dt = LocalDate.ofEpochDay(cnx.getLastModified());
|
||||||
|
DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
|
||||||
|
return df.format(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStackTrace(final Throwable throwable) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
|
||||||
|
Throwable current = throwable;
|
||||||
|
while (current != null) {
|
||||||
|
sb.append(current.getLocalizedMessage());
|
||||||
|
sb.append(",\r\n");
|
||||||
|
|
||||||
|
Throwable cause = throwable.getCause();
|
||||||
|
if (cause != null) {
|
||||||
|
if (!cause.equals(throwable)) {
|
||||||
|
current = current.getCause();
|
||||||
|
} else {
|
||||||
|
current = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeCode() {
|
||||||
|
try {
|
||||||
|
metaDataRegistry.readMeta();
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
} catch (SiemensHvacException ex) {
|
||||||
|
Locale local = metaDataRegistry.getUserLocale();
|
||||||
|
BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
|
||||||
|
String text = translationProvider.getText(bundleContext.getBundle(), "offline.error-gateway-init",
|
||||||
|
"DefaultValue", local);
|
||||||
|
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
MessageFormat.format(text, ex.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
|
||||||
|
super.updateStatus(status, statusDetail, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean registerDiscoveryListener(SiemensHvacDeviceDiscoveryService listener) {
|
||||||
|
SiemensHvacDeviceDiscoveryService lcDiscoveryService = discoveryService;
|
||||||
|
if (lcDiscoveryService == null) {
|
||||||
|
lcDiscoveryService = listener;
|
||||||
|
lcDiscoveryService.setSiemensHvacMetadataRegistry(metaDataRegistry);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean unregisterDiscoveryListener() {
|
||||||
|
SiemensHvacDeviceDiscoveryService lcDiscoveryService = discoveryService;
|
||||||
|
if (lcDiscoveryService != null) {
|
||||||
|
discoveryService = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable HttpClientFactory getHttpClientFactory() {
|
||||||
|
return this.httpClientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||||
|
return Set.of(SiemensHvacDeviceDiscoveryService.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,451 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.handler;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.TypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataRegistry;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.network.SiemensHvacCallback;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.network.SiemensHvacRequestListener.ErrorSource;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
import org.openhab.core.thing.Bridge;
|
||||||
|
import org.openhab.core.thing.Channel;
|
||||||
|
import org.openhab.core.thing.ChannelUID;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
import org.openhab.core.thing.ThingStatusDetail;
|
||||||
|
import org.openhab.core.thing.ThingStatusInfo;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerCallback;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeRegistry;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.StateDescription;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SiemensHvacHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Laurent ARNAL - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacHandlerImpl extends BaseThingHandler {
|
||||||
|
|
||||||
|
private Lock lockObj = new ReentrantLock();
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SiemensHvacHandlerImpl.class);
|
||||||
|
|
||||||
|
private @Nullable ScheduledFuture<?> pollingJob = null;
|
||||||
|
|
||||||
|
private final @Nullable SiemensHvacConnector hvacConnector;
|
||||||
|
private final @Nullable SiemensHvacMetadataRegistry metaDataRegistry;
|
||||||
|
private final ChannelTypeRegistry channelTypeRegistry;
|
||||||
|
|
||||||
|
private long lastWrite = 0;
|
||||||
|
|
||||||
|
public SiemensHvacHandlerImpl(Thing thing, @Nullable SiemensHvacConnector hvacConnector,
|
||||||
|
@Nullable SiemensHvacMetadataRegistry metaDataRegistry, ChannelTypeRegistry channelTypeRegistry) {
|
||||||
|
super(thing);
|
||||||
|
|
||||||
|
this.hvacConnector = hvacConnector;
|
||||||
|
this.metaDataRegistry = metaDataRegistry;
|
||||||
|
this.channelTypeRegistry = channelTypeRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
|
pollingJob = scheduler.scheduleWithFixedDelay(this::pollingCode, 0, 5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
ScheduledFuture<?> lcPollingJob = pollingJob;
|
||||||
|
if (lcPollingJob != null) {
|
||||||
|
lcPollingJob.cancel(true);
|
||||||
|
pollingJob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pollingCode() {
|
||||||
|
Bridge lcBridge = getBridge();
|
||||||
|
|
||||||
|
if (lcBridge == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcBridge.getStatus() == ThingStatus.OFFLINE) {
|
||||||
|
if (!ThingStatusDetail.COMMUNICATION_ERROR.equals(lcBridge.getStatusInfo().getStatusDetail())) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcBridge.getStatus() != ThingStatus.ONLINE) {
|
||||||
|
if (!ThingStatusDetail.COMMUNICATION_ERROR.equals(lcBridge.getStatusInfo().getStatusDetail())) {
|
||||||
|
logger.debug("Bridge is not ready, don't enter polling for now!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
var chList = this.getThing().getChannels();
|
||||||
|
|
||||||
|
SiemensHvacConnector lcHvacConnector = hvacConnector;
|
||||||
|
if (lcHvacConnector != null) {
|
||||||
|
int previousRequestCount = lcHvacConnector.getRequestCount();
|
||||||
|
int previousErrorCount = lcHvacConnector.getErrorCount();
|
||||||
|
|
||||||
|
logger.debug("readChannels:");
|
||||||
|
for (Channel channel : chList) {
|
||||||
|
readChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("WaitAllPendingRequest:Start waiting()");
|
||||||
|
lcHvacConnector.waitAllPendingRequest();
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
logger.debug("WaitAllPendingRequest:All request done(): {}", (end - start) / 1000.00);
|
||||||
|
|
||||||
|
int newRequestCount = lcHvacConnector.getRequestCount();
|
||||||
|
int newErrorCount = lcHvacConnector.getErrorCount();
|
||||||
|
|
||||||
|
int requestCount = newRequestCount - previousRequestCount;
|
||||||
|
int errorCount = newErrorCount - previousErrorCount;
|
||||||
|
|
||||||
|
double errorRate = (double) errorCount / requestCount * 100.00;
|
||||||
|
|
||||||
|
if (errorRate > 50) {
|
||||||
|
SiemensHvacBridgeThingHandler bridgeHandler = (SiemensHvacBridgeThingHandler) lcBridge.getHandler();
|
||||||
|
|
||||||
|
if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorBridge) {
|
||||||
|
if (bridgeHandler != null) {
|
||||||
|
bridgeHandler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
String.format("Communication ErrorRate to gateway is too high: %f", errorRate));
|
||||||
|
}
|
||||||
|
} else if (lcHvacConnector.getErrorSource() == ErrorSource.ErrorThings) {
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
String.format("Communication ErrorRate to thing is too high: %f", errorRate));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
|
||||||
|
SiemensHvacBridgeThingHandler bridgeHandler = (SiemensHvacBridgeThingHandler) lcBridge.getHandler();
|
||||||
|
|
||||||
|
// Automatically recover from communication errors if errorRate is ok.
|
||||||
|
if (bridgeHandler != null) {
|
||||||
|
if (ThingStatusDetail.COMMUNICATION_ERROR
|
||||||
|
.equals(bridgeHandler.getThing().getStatusInfo().getStatusDetail())) {
|
||||||
|
bridgeHandler.updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lcHvacConnector.displayRequestStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readChannel(Channel channel) {
|
||||||
|
ThingHandlerCallback cb = this.getCallback();
|
||||||
|
boolean isLink = false;
|
||||||
|
|
||||||
|
if (cb != null) {
|
||||||
|
isLink = cb.isChannelLinked(channel.getUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("readChannel: {}", channel.getDescription());
|
||||||
|
|
||||||
|
ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID());
|
||||||
|
|
||||||
|
if (tp == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = channel.getProperties().get("id");
|
||||||
|
String uid = channel.getUID().getId();
|
||||||
|
String type = tp.getItemType();
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
id = (String) channel.getConfiguration().getProperties().get("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
logger.debug("pollingCode: Id is null {} ", channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type == null) {
|
||||||
|
logger.debug("pollingCode: type is null {} ", channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
readDp(id, uid, tp, type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeReadDp(@Nullable JsonObject response, @Nullable String uid, @Nullable String dp, ChannelType tp,
|
||||||
|
@Nullable String type) {
|
||||||
|
SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry;
|
||||||
|
if (lcMetaDataRegistry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != null && response.has("Data")) {
|
||||||
|
JsonObject subResult = (JsonObject) response.get("Data");
|
||||||
|
|
||||||
|
String updateKey = "" + uid;
|
||||||
|
|
||||||
|
String typer = null;
|
||||||
|
|
||||||
|
if (subResult.has("Type")) {
|
||||||
|
typer = subResult.get("Type").getAsString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typer != null) {
|
||||||
|
TypeConverter converter = ConverterFactory.getConverter(typer);
|
||||||
|
|
||||||
|
Locale local = lcMetaDataRegistry.getUserLocale();
|
||||||
|
if (local == null) {
|
||||||
|
local = Locale.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
State state = converter.convertFromBinding(subResult, tp, local);
|
||||||
|
updateState(updateKey, state);
|
||||||
|
}
|
||||||
|
} catch (ConverterTypeException ex) {
|
||||||
|
logger.warn("{}, for uid : {}, please check the item type", ex.getMessage(), uid);
|
||||||
|
} catch (ConverterException ex) {
|
||||||
|
logger.warn("{}, for uid: {}, please check the item type", ex.getMessage(), uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readDp(String dp, String uid, ChannelType tp, String type, boolean async) {
|
||||||
|
SiemensHvacConnector lcHvacConnector = hvacConnector;
|
||||||
|
|
||||||
|
if ("-1".equals(dp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
lockObj.lock();
|
||||||
|
|
||||||
|
logger.trace("Start read: {}", dp);
|
||||||
|
String request = "api/menutree/read_datapoint.json?Id=" + dp;
|
||||||
|
|
||||||
|
logger.debug("siemensHvac:ReadDp:DoRequest(): {}", request);
|
||||||
|
|
||||||
|
if (async) {
|
||||||
|
if (lcHvacConnector != null) {
|
||||||
|
lcHvacConnector.doRequest(request, new SiemensHvacCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(java.net.URI uri, int status, @Nullable Object response) {
|
||||||
|
// prevent async read if we just write so we have no overlaps
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - lastWrite < 5000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("End read: {}", dp);
|
||||||
|
|
||||||
|
if (response instanceof JsonObject jsonResponse) {
|
||||||
|
decodeReadDp(jsonResponse, uid, dp, tp, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (lcHvacConnector != null) {
|
||||||
|
JsonObject js = lcHvacConnector.doRequest(request);
|
||||||
|
decodeReadDp(js, uid, dp, tp, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
logger.trace("End read: {}", dp);
|
||||||
|
lockObj.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeDp(String dp, Type dpVal, ChannelType tp, String type) {
|
||||||
|
SiemensHvacConnector lcHvacConnector = hvacConnector;
|
||||||
|
|
||||||
|
if (lcHvacConnector != null) {
|
||||||
|
lcHvacConnector.displayRequestStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("-1".equals(dp)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
lockObj.lock();
|
||||||
|
logger.trace("Start write: {}", dp);
|
||||||
|
lastWrite = System.currentTimeMillis();
|
||||||
|
|
||||||
|
Object valUpdate = "0";
|
||||||
|
|
||||||
|
try {
|
||||||
|
TypeConverter converter = ConverterFactory.getConverter(type);
|
||||||
|
|
||||||
|
valUpdate = converter.convertToBinding(dpVal, tp);
|
||||||
|
if (valUpdate != null) {
|
||||||
|
String request = String.format("api/menutree/write_datapoint.json?Id=%s&Value=%s&Type=%s", dp,
|
||||||
|
valUpdate, type);
|
||||||
|
|
||||||
|
if (lcHvacConnector != null) {
|
||||||
|
logger.trace("Write request for: {} ", valUpdate);
|
||||||
|
JsonObject response = lcHvacConnector.doRequest(request);
|
||||||
|
|
||||||
|
logger.trace("Write request response: {} ", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.debug("Failed to get converted state from datapoint '{}'", dp);
|
||||||
|
}
|
||||||
|
} catch (ConverterTypeException ex) {
|
||||||
|
logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage());
|
||||||
|
} catch (ConverterException ex) {
|
||||||
|
logger.warn("{}, please check the item type and the commands in your scripts", ex.getMessage());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
logger.debug("End write: {}", dp);
|
||||||
|
lockObj.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Command applyState(ChannelType tp, Command command) {
|
||||||
|
StateDescription sd = tp.getState();
|
||||||
|
Command result = command;
|
||||||
|
|
||||||
|
if (sd != null) {
|
||||||
|
BigDecimal maxb = sd.getMaximum();
|
||||||
|
BigDecimal minb = sd.getMinimum();
|
||||||
|
BigDecimal step = sd.getStep();
|
||||||
|
boolean doMods = false;
|
||||||
|
|
||||||
|
if (command instanceof DecimalType decimalCommand) {
|
||||||
|
double v1 = decimalCommand.doubleValue();
|
||||||
|
|
||||||
|
if (step != null) {
|
||||||
|
doMods = true;
|
||||||
|
int divider = 1;
|
||||||
|
|
||||||
|
if (step.doubleValue() == 0.5) {
|
||||||
|
divider = 2;
|
||||||
|
} else if (step.doubleValue() == 0.1) {
|
||||||
|
divider = 10;
|
||||||
|
} else if (step.doubleValue() == 0.02) {
|
||||||
|
divider = 50;
|
||||||
|
} else if (step.doubleValue() == 0.01) {
|
||||||
|
divider = 100;
|
||||||
|
}
|
||||||
|
v1 = v1 * divider;
|
||||||
|
v1 = Math.floor(v1);
|
||||||
|
v1 = v1 / divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minb != null && v1 < minb.floatValue()) {
|
||||||
|
doMods = true;
|
||||||
|
v1 = minb.floatValue();
|
||||||
|
}
|
||||||
|
if (maxb != null && v1 > maxb.floatValue()) {
|
||||||
|
doMods = true;
|
||||||
|
v1 = maxb.floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doMods) {
|
||||||
|
result = new DecimalType(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
SiemensHvacMetadataRegistry lcMetaDataRegistry = metaDataRegistry;
|
||||||
|
logger.debug("handleCommand");
|
||||||
|
if (command instanceof RefreshType) {
|
||||||
|
var channel = this.getThing().getChannel(channelUID);
|
||||||
|
if (channel != null) {
|
||||||
|
readChannel(channel);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Channel channel = getThing().getChannel(channelUID);
|
||||||
|
if (channel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command commandVar = command;
|
||||||
|
ChannelType tp = channelTypeRegistry.getChannelType(channel.getChannelTypeUID());
|
||||||
|
|
||||||
|
if (tp == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = tp.getItemType();
|
||||||
|
String dptType = "";
|
||||||
|
String id = channel.getProperties().get("id");
|
||||||
|
SiemensHvacMetadataDataPoint md = null;
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
id = (String) channel.getConfiguration().getProperties().get("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lcMetaDataRegistry != null) {
|
||||||
|
md = (SiemensHvacMetadataDataPoint) lcMetaDataRegistry.getDptMap(id);
|
||||||
|
if (md != null) {
|
||||||
|
id = "" + md.getId();
|
||||||
|
dptType = md.getDptType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command instanceof State commandState) {
|
||||||
|
commandVar = applyState(tp, commandVar);
|
||||||
|
this.updateState(channelUID, commandState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != null && type != null) {
|
||||||
|
writeDp(id, commandVar, tp, dptType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadata {
|
||||||
|
private int id = -1;
|
||||||
|
private int subId = -1;
|
||||||
|
private int groupId = -1;
|
||||||
|
private int catId = -1;
|
||||||
|
private String shortDescEn = "";
|
||||||
|
private String longDescEn = "";
|
||||||
|
private String shortDesc = "";
|
||||||
|
private String longDesc = "";
|
||||||
|
@Nullable
|
||||||
|
private transient SiemensHvacMetadata parent;
|
||||||
|
|
||||||
|
public SiemensHvacMetadata() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int Id) {
|
||||||
|
this.id = Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSubId() {
|
||||||
|
return subId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubId(int subId) {
|
||||||
|
this.subId = subId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroupId(int groupId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCatId() {
|
||||||
|
return catId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCatId(int catId) {
|
||||||
|
this.catId = catId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShortDescEn() {
|
||||||
|
return shortDescEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortDescEn(String shortDesc) {
|
||||||
|
this.shortDescEn = shortDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLongDescEn() {
|
||||||
|
return longDescEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLongDescEn(String longDesc) {
|
||||||
|
this.longDescEn = longDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShortDesc() {
|
||||||
|
return shortDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortDesc(String shortDesc) {
|
||||||
|
this.shortDesc = shortDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLongDesc() {
|
||||||
|
return longDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLongDesc(String longDesc) {
|
||||||
|
this.longDesc = longDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable SiemensHvacMetadata getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(@Nullable SiemensHvacMetadata parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataDataPoint extends SiemensHvacMetadata {
|
||||||
|
private String dptId = "-1";
|
||||||
|
private String dptType = "";
|
||||||
|
private @Nullable String dptUnit = null;
|
||||||
|
private @Nullable String min = null;
|
||||||
|
private @Nullable String max = null;
|
||||||
|
private @Nullable String resolution = null;
|
||||||
|
private @Nullable String fieldWitdh = null;
|
||||||
|
private @Nullable String decimalDigits = null;
|
||||||
|
private boolean detailsResolved = false;
|
||||||
|
private @Nullable String dialogType = null;
|
||||||
|
private @Nullable String maxLength = null;
|
||||||
|
private @Nullable String address = null;
|
||||||
|
private int dptSubKey = -1;
|
||||||
|
private boolean writeAccess = false;
|
||||||
|
|
||||||
|
private @NotNull List<SiemensHvacMetadataPointChild> child = List.of();
|
||||||
|
|
||||||
|
public SiemensHvacMetadataDataPoint() {
|
||||||
|
child = new ArrayList<SiemensHvacMetadataPointChild>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDptType() {
|
||||||
|
return dptType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDptType(String dptType) {
|
||||||
|
this.dptType = dptType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SiemensHvacMetadataPointChild> getChild() {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChild(List<SiemensHvacMetadataPointChild> child) {
|
||||||
|
this.child = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDptId() {
|
||||||
|
return dptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDptId(String dptId) {
|
||||||
|
this.dptId = dptId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDptSubKey() {
|
||||||
|
return dptSubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDptSubKey(int dptSubKey) {
|
||||||
|
this.dptSubKey = dptSubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWriteAccess(boolean writeAccess) {
|
||||||
|
this.writeAccess = writeAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getWriteAccess() {
|
||||||
|
return writeAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getDptUnit() {
|
||||||
|
return dptUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDptUnit(String dptUnit) {
|
||||||
|
this.dptUnit = dptUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getMaxLength() {
|
||||||
|
return maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxLength(String maxLength) {
|
||||||
|
this.maxLength = maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getDialogType() {
|
||||||
|
return dialogType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDialogType(String dialogType) {
|
||||||
|
this.dialogType = dialogType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getMin() {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMin(String min) {
|
||||||
|
this.min = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getMax() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(String max) {
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getResolution() {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResolution(String resolution) {
|
||||||
|
this.resolution = resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getFieldWitdh() {
|
||||||
|
return fieldWitdh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFieldWitdh(String fieldWitdh) {
|
||||||
|
this.fieldWitdh = fieldWitdh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getDecimalDigits() {
|
||||||
|
return decimalDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecimalDigits(String decimalDigits) {
|
||||||
|
this.decimalDigits = decimalDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getDetailsResolved() {
|
||||||
|
return detailsResolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetailsResolved(Boolean detailsResolved) {
|
||||||
|
this.detailsResolved = detailsResolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resolveDptDetails(JsonObject result) {
|
||||||
|
JsonObject subResultObj = result.getAsJsonObject("Result");
|
||||||
|
JsonObject desc = result.getAsJsonObject("Description");
|
||||||
|
|
||||||
|
if (subResultObj.has("Success")) {
|
||||||
|
JsonObject error = subResultObj.getAsJsonObject("Error");
|
||||||
|
String errorMsg = "";
|
||||||
|
if (error != null) {
|
||||||
|
errorMsg = error.get("Txt").getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (("datatype not supported").equals(errorMsg)) {
|
||||||
|
detailsResolved = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc != null) {
|
||||||
|
this.dptType = desc.get("Type").getAsString();
|
||||||
|
|
||||||
|
if (SiemensHvacBindingConstants.DPT_TYPE_ENUM.equals(dptType)) {
|
||||||
|
JsonArray enums = desc.getAsJsonArray("Enums");
|
||||||
|
|
||||||
|
for (Object obj : enums) {
|
||||||
|
JsonObject entry = (JsonObject) obj;
|
||||||
|
|
||||||
|
SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild();
|
||||||
|
ch.setText(entry.get("Text").getAsString());
|
||||||
|
ch.setValue(entry.get("Value").getAsString());
|
||||||
|
ch.setIsActive(entry.get("IsCurrentValue").getAsString());
|
||||||
|
child.add(ch);
|
||||||
|
}
|
||||||
|
} else if (SiemensHvacBindingConstants.DPT_TYPE_NUMERIC.equals(dptType)) {
|
||||||
|
this.dptUnit = desc.get("Unit").getAsString();
|
||||||
|
this.min = desc.get("Min").getAsString();
|
||||||
|
this.max = desc.get("Max").getAsString();
|
||||||
|
this.resolution = desc.get("Resolution").getAsString();
|
||||||
|
this.fieldWitdh = desc.get("FieldWitdh").getAsString();
|
||||||
|
this.decimalDigits = desc.get("DecimalDigits").getAsString();
|
||||||
|
} else if (SiemensHvacBindingConstants.DPT_TYPE_STRING.equals(dptType)) {
|
||||||
|
this.dialogType = desc.get("DialogType").getAsString();
|
||||||
|
this.maxLength = desc.get("MaxLength").getAsString();
|
||||||
|
} else if (SiemensHvacBindingConstants.DPT_TYPE_RADIO.equals(dptType)) {
|
||||||
|
JsonArray buttons = desc.getAsJsonArray("Buttons");
|
||||||
|
|
||||||
|
child = new ArrayList<SiemensHvacMetadataPointChild>();
|
||||||
|
|
||||||
|
for (Object obj : buttons) {
|
||||||
|
JsonObject button = (JsonObject) obj;
|
||||||
|
|
||||||
|
SiemensHvacMetadataPointChild ch = new SiemensHvacMetadataPointChild();
|
||||||
|
ch.setOpt0(button.get("TextOpt0").getAsString());
|
||||||
|
ch.setOpt1(button.get("TextOpt1").getAsString());
|
||||||
|
ch.setIsActive(button.get("IsActive").getAsString());
|
||||||
|
child.add(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detailsResolved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataDevice {
|
||||||
|
|
||||||
|
private String name = "";
|
||||||
|
|
||||||
|
private String addr = "";
|
||||||
|
|
||||||
|
private String type = "unknown";
|
||||||
|
|
||||||
|
private String serialNr = "";
|
||||||
|
|
||||||
|
private @Nullable String treeDate;
|
||||||
|
|
||||||
|
private @Nullable String treeTime;
|
||||||
|
|
||||||
|
private boolean treeGenerated;
|
||||||
|
private int treeId;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddr() {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddr(String addr) {
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerialNr() {
|
||||||
|
return serialNr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSerialNr(String serialNr) {
|
||||||
|
this.serialNr = serialNr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getTreeDate() {
|
||||||
|
return treeDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTreeDate(String treeDate) {
|
||||||
|
this.treeDate = treeDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getTreeTime() {
|
||||||
|
return treeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTreeTime(String treeTime) {
|
||||||
|
this.treeTime = treeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getTreeGenerated() {
|
||||||
|
return treeGenerated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTreeGenerated(boolean treeGenerated) {
|
||||||
|
this.treeGenerated = treeGenerated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTreeId() {
|
||||||
|
return treeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTreeId(int treeId) {
|
||||||
|
this.treeId = treeId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataLanguage {
|
||||||
|
private String name;
|
||||||
|
private int id;
|
||||||
|
private String language;
|
||||||
|
private int languageId;
|
||||||
|
|
||||||
|
public SiemensHvacMetadataLanguage() {
|
||||||
|
name = "";
|
||||||
|
language = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(String language) {
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLanguageId() {
|
||||||
|
return languageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguageId(int languageId) {
|
||||||
|
this.languageId = languageId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataMenu extends SiemensHvacMetadata {
|
||||||
|
private LinkedHashMap<Integer, SiemensHvacMetadata> childList;
|
||||||
|
|
||||||
|
public SiemensHvacMetadataMenu() {
|
||||||
|
childList = new LinkedHashMap<Integer, SiemensHvacMetadata>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(SiemensHvacMetadata information) {
|
||||||
|
childList.put(information.getId(), information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<Integer, SiemensHvacMetadata> getChilds() {
|
||||||
|
return this.childList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasChild(int Id) {
|
||||||
|
return this.childList.containsKey(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SiemensHvacMetadata getChild(int Id) {
|
||||||
|
return this.childList.get(Id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataPointChild {
|
||||||
|
|
||||||
|
private String text = "";
|
||||||
|
private String value = "";
|
||||||
|
private String opt0 = "";
|
||||||
|
private String opt1 = "";
|
||||||
|
private String isActive = "";
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return this.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOpt0() {
|
||||||
|
return this.opt0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpt0(String opt0) {
|
||||||
|
this.opt0 = opt0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOpt1() {
|
||||||
|
return this.opt1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpt1(String opt1) {
|
||||||
|
this.opt1 = opt1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIsActive() {
|
||||||
|
return this.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsActive(String isActive) {
|
||||||
|
this.isActive = isActive;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.network.SiemensHvacConnector;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacChannelTypeProvider;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacMetadataRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the type generator.
|
||||||
|
*/
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
void readMeta() throws SiemensHvacException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacMetadataMenu getRoot();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ArrayList<SiemensHvacMetadataDevice> getDevices();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacMetadata getDptMap(@Nullable String key);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacChannelTypeProvider getChannelTypeProvider();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacConnector getSiemensHvacConnector();
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacMetadataUser getUser();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Locale getUserLocale();
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.metadata;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacMetadataUser {
|
||||||
|
private String name;
|
||||||
|
private int id;
|
||||||
|
private String language;
|
||||||
|
private int languageId;
|
||||||
|
|
||||||
|
public SiemensHvacMetadataUser() {
|
||||||
|
name = "";
|
||||||
|
language = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguage(String language) {
|
||||||
|
this.language = language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLanguageId() {
|
||||||
|
return languageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLanguageId(int languageId) {
|
||||||
|
this.languageId = languageId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.network;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacCallback {
|
||||||
|
/**
|
||||||
|
* Runs callback code after response completion.
|
||||||
|
*/
|
||||||
|
void execute(URI uri, int status, @Nullable Object response);
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.network;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacConnector {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String doBasicRequest(String uri) throws SiemensHvacException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
JsonObject doRequest(String req);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback);
|
||||||
|
|
||||||
|
void waitAllPendingRequest();
|
||||||
|
|
||||||
|
void waitNoNewRequest();
|
||||||
|
|
||||||
|
void onComplete(Request request, SiemensHvacRequestHandler reqListener) throws SiemensHvacException;
|
||||||
|
|
||||||
|
void onError(Request request, SiemensHvacRequestHandler reqListener,
|
||||||
|
SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws SiemensHvacException;
|
||||||
|
|
||||||
|
void setSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
SiemensHvacBridgeConfig getBridgeConfiguration();
|
||||||
|
|
||||||
|
void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web);
|
||||||
|
|
||||||
|
void displayRequestStats();
|
||||||
|
|
||||||
|
Gson getGson();
|
||||||
|
|
||||||
|
Gson getGsonWithAdapter();
|
||||||
|
|
||||||
|
int getRequestCount();
|
||||||
|
|
||||||
|
int getErrorCount();
|
||||||
|
|
||||||
|
SiemensHvacRequestListener.ErrorSource getErrorSource();
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
|
||||||
|
void setTimeOut(int timeout);
|
||||||
|
}
|
@ -0,0 +1,669 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.network;
|
||||||
|
|
||||||
|
import java.net.CookieStore;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeConfig;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.handler.SiemensHvacBridgeThingHandler;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadata;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.types.Type;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.typeadapters.RuntimeTypeAdapterFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(immediate = true)
|
||||||
|
public class SiemensHvacConnectorImpl implements SiemensHvacConnector {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SiemensHvacConnectorImpl.class);
|
||||||
|
|
||||||
|
private Map<SiemensHvacRequestHandler, SiemensHvacRequestHandler> currentHandlerRegistry = new ConcurrentHashMap<>();
|
||||||
|
private Map<SiemensHvacRequestHandler, SiemensHvacRequestHandler> handlerInErrorRegistry = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Map<String, Boolean> oldSessionId = new HashMap<>();
|
||||||
|
|
||||||
|
private final Gson gson;
|
||||||
|
private final Gson gsonWithAdapter;
|
||||||
|
|
||||||
|
private @Nullable String sessionId = null;
|
||||||
|
private @Nullable String sessionIdHttp = null;
|
||||||
|
private @Nullable SiemensHvacBridgeConfig config = null;
|
||||||
|
|
||||||
|
protected final HttpClientFactory httpClientFactory;
|
||||||
|
|
||||||
|
protected HttpClient httpClient;
|
||||||
|
|
||||||
|
private Map<String, Type> updateCommand;
|
||||||
|
|
||||||
|
private int requestCount = 0;
|
||||||
|
private int errorCount = 0;
|
||||||
|
private int timeout = 10;
|
||||||
|
private SiemensHvacRequestListener.ErrorSource errorSource = SiemensHvacRequestListener.ErrorSource.ErrorBridge;
|
||||||
|
|
||||||
|
private @Nullable SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public SiemensHvacConnectorImpl(@Reference HttpClientFactory httpClientFactory) {
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
gson = builder.setPrettyPrinting().create();
|
||||||
|
|
||||||
|
RuntimeTypeAdapterFactory<SiemensHvacMetadata> adapter = RuntimeTypeAdapterFactory
|
||||||
|
.of(SiemensHvacMetadata.class);
|
||||||
|
adapter.registerSubtype(SiemensHvacMetadataMenu.class);
|
||||||
|
adapter.registerSubtype(SiemensHvacMetadataDataPoint.class);
|
||||||
|
|
||||||
|
gsonWithAdapter = new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create();
|
||||||
|
|
||||||
|
this.updateCommand = new Hashtable<String, Type>();
|
||||||
|
this.httpClientFactory = httpClientFactory;
|
||||||
|
|
||||||
|
SslContextFactory ctxFactory = new SslContextFactory.Client(true);
|
||||||
|
ctxFactory.setRenegotiationAllowed(false);
|
||||||
|
ctxFactory.setEnableCRLDP(false);
|
||||||
|
ctxFactory.setEnableOCSP(false);
|
||||||
|
ctxFactory.setTrustAll(true);
|
||||||
|
ctxFactory.setValidateCerts(false);
|
||||||
|
ctxFactory.setValidatePeerCerts(false);
|
||||||
|
ctxFactory.setEndpointIdentificationAlgorithm(null);
|
||||||
|
|
||||||
|
this.httpClient = new HttpClient(ctxFactory);
|
||||||
|
this.httpClient.setMaxConnectionsPerDestination(10);
|
||||||
|
this.httpClient.setMaxRequestsQueuedPerDestination(10000);
|
||||||
|
this.httpClient.setConnectTimeout(10000);
|
||||||
|
this.httpClient.setFollowRedirects(false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.httpClient.start();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to start http client: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSiemensHvacBridgeBaseThingHandler(
|
||||||
|
@Nullable SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler) {
|
||||||
|
this.hvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsetSiemensHvacBridgeBaseThingHandler(SiemensHvacBridgeThingHandler hvacBridgeBaseThingHandler) {
|
||||||
|
this.hvacBridgeBaseThingHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(@Nullable Request request, SiemensHvacRequestHandler reqHandler)
|
||||||
|
throws SiemensHvacException {
|
||||||
|
unregisterRequestHandler(reqHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String extractSessionId(String query) {
|
||||||
|
int idx1 = query.indexOf("SessionId=");
|
||||||
|
int idx2 = query.indexOf("&", idx1 + 1);
|
||||||
|
if (idx2 < 0) {
|
||||||
|
idx2 = query.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
String sessionId = query.substring(idx1 + 10, idx2);
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(@Nullable Request request, @Nullable SiemensHvacRequestHandler reqHandler,
|
||||||
|
SiemensHvacRequestListener.ErrorSource errorSource, boolean mayRetry) throws SiemensHvacException {
|
||||||
|
if (reqHandler == null || request == null) {
|
||||||
|
throw new SiemensHvacException("internalError: onError call with reqHandler == null");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean doRetry = mayRetry;
|
||||||
|
// Don't retry if we have do it multiple time
|
||||||
|
if (reqHandler.getRetryCount() >= 5) {
|
||||||
|
doRetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry if we lost session, just abort the request, and wait next loop
|
||||||
|
if (sessionIdHttp == null || sessionId == null) {
|
||||||
|
doRetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doRetry) {
|
||||||
|
logger.debug("unable to handle request, doRetry = false, cancel it");
|
||||||
|
unregisterRequestHandler(reqHandler);
|
||||||
|
registerHandlerError(reqHandler);
|
||||||
|
errorCount++;
|
||||||
|
this.errorSource = errorSource;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wait one second before retrying the request to avoid flooding the gateway
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// We can silently ignore this one
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionIdHttp == null) {
|
||||||
|
doAuth(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionId == null) {
|
||||||
|
doAuth(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
URI uri = request.getURI();
|
||||||
|
String query = uri.toString();
|
||||||
|
|
||||||
|
String sessionIdInQuery = extractSessionId(query);
|
||||||
|
if (query.indexOf("main.app") >= 0) {
|
||||||
|
String sessionIdHttpLc = sessionIdHttp;
|
||||||
|
|
||||||
|
if (sessionIdHttpLc != null && !sessionIdHttpLc.equals(sessionIdInQuery)) {
|
||||||
|
uri = new URI(query.replace(sessionIdInQuery, sessionIdHttpLc));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String sessionIdLc = sessionId;
|
||||||
|
|
||||||
|
if (sessionIdLc != null && !sessionIdLc.equals(sessionIdInQuery)) {
|
||||||
|
uri = new URI(query.replace(sessionIdInQuery, sessionIdLc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Request retryRequest = httpClient.newRequest(uri);
|
||||||
|
request.method(HttpMethod.GET);
|
||||||
|
reqHandler.setRequest(retryRequest);
|
||||||
|
reqHandler.incrementRetryCount();
|
||||||
|
|
||||||
|
if (retryRequest != null) {
|
||||||
|
executeRequest(retryRequest, reqHandler);
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new SiemensHvacException("Error during gateway request", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ContentResponse executeRequest(final Request request) throws SiemensHvacException {
|
||||||
|
return executeRequest(request, (SiemensHvacCallback) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ContentResponse executeRequest(final Request request, @Nullable SiemensHvacCallback callback)
|
||||||
|
throws SiemensHvacException {
|
||||||
|
requestCount++;
|
||||||
|
|
||||||
|
// For asynchronous request, we create a RequestHandler that will enable us to follow request state
|
||||||
|
SiemensHvacRequestHandler requestHandler = null;
|
||||||
|
if (callback != null) {
|
||||||
|
requestHandler = new SiemensHvacRequestHandler(callback, this);
|
||||||
|
requestHandler.setRequest(request);
|
||||||
|
currentHandlerRegistry.put(requestHandler, requestHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeRequest(request, requestHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterRequestHandler(SiemensHvacRequestHandler handler) throws SiemensHvacException {
|
||||||
|
synchronized (currentHandlerRegistry) {
|
||||||
|
if (currentHandlerRegistry.containsKey(handler)) {
|
||||||
|
currentHandlerRegistry.remove(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerHandlerError(SiemensHvacRequestHandler handler) {
|
||||||
|
synchronized (handlerInErrorRegistry) {
|
||||||
|
handlerInErrorRegistry.put(handler, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ContentResponse executeRequest(final Request request,
|
||||||
|
@Nullable SiemensHvacRequestHandler requestHandler) throws SiemensHvacException {
|
||||||
|
// Give a high timeout because we queue a lot of async request,
|
||||||
|
// so enqueued them will take some times ...
|
||||||
|
request.timeout(timeout, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
ContentResponse response = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (requestHandler != null) {
|
||||||
|
SiemensHvacRequestListener requestListener = new SiemensHvacRequestListener(requestHandler);
|
||||||
|
request.send(requestListener);
|
||||||
|
} else {
|
||||||
|
response = request.send();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||||
|
throw new SiemensHvacException("siemensHvac:Exception by executing request: "
|
||||||
|
+ anominized(request.getURI().toString()) + " ; " + e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConfig() throws SiemensHvacException {
|
||||||
|
SiemensHvacBridgeThingHandler lcHvacBridgeBaseThingHandler = hvacBridgeBaseThingHandler;
|
||||||
|
|
||||||
|
if (lcHvacBridgeBaseThingHandler != null) {
|
||||||
|
config = lcHvacBridgeBaseThingHandler.getBridgeConfiguration();
|
||||||
|
} else {
|
||||||
|
throw new SiemensHvacException(
|
||||||
|
"siemensHvac:Exception unable to get config because hvacBridgeBaseThingHandler is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable SiemensHvacBridgeConfig getBridgeConfiguration() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAuth(boolean http) throws SiemensHvacException {
|
||||||
|
synchronized (this) {
|
||||||
|
logger.debug("siemensHvac:doAuth()");
|
||||||
|
|
||||||
|
initConfig();
|
||||||
|
|
||||||
|
SiemensHvacBridgeConfig config = this.config;
|
||||||
|
if (config == null) {
|
||||||
|
throw new SiemensHvacException("Missing SiemensHvacOZW Bridge configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
String baseUri = config.baseUrl;
|
||||||
|
String uri = "";
|
||||||
|
|
||||||
|
if (http) {
|
||||||
|
uri = "main.app";
|
||||||
|
} else {
|
||||||
|
uri = String.format("api/auth/login.json?user=%s&pwd=%s", config.userName, config.userPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Request request = httpClient.newRequest(baseUri + uri);
|
||||||
|
if (http) {
|
||||||
|
request.method(HttpMethod.POST).param("user", config.userName).param("pwd", config.userPassword);
|
||||||
|
} else {
|
||||||
|
request.method(HttpMethod.GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("siemensHvac:doAuth:connect()");
|
||||||
|
|
||||||
|
ContentResponse response = executeRequest(request);
|
||||||
|
if (response != null) {
|
||||||
|
int statusCode = response.getStatus();
|
||||||
|
|
||||||
|
if (statusCode == HttpStatus.OK_200) {
|
||||||
|
String result = response.getContentAsString();
|
||||||
|
|
||||||
|
if (http) {
|
||||||
|
CookieStore cookieStore = httpClient.getCookieStore();
|
||||||
|
List<HttpCookie> cookies = cookieStore.getCookies();
|
||||||
|
|
||||||
|
for (HttpCookie httpCookie : cookies) {
|
||||||
|
if (httpCookie.getName().equals("SessionId")) {
|
||||||
|
sessionIdHttp = httpCookie.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionIdHttp == null) {
|
||||||
|
logger.debug("Session request auth was unsuccessful in _doAuth()");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result != null) {
|
||||||
|
JsonObject resultObj = getGson().fromJson(result, JsonObject.class);
|
||||||
|
|
||||||
|
if (resultObj != null && resultObj.has("Result")) {
|
||||||
|
JsonElement resultVal = resultObj.get("Result");
|
||||||
|
JsonObject resultObj2 = resultVal.getAsJsonObject();
|
||||||
|
|
||||||
|
if (resultObj2.has("Success")) {
|
||||||
|
boolean successVal = resultObj2.get("Success").getAsBoolean();
|
||||||
|
|
||||||
|
if (successVal) {
|
||||||
|
if (resultObj.has("SessionId")) {
|
||||||
|
sessionId = resultObj.get("SessionId").getAsString();
|
||||||
|
logger.debug("Have new SessionId: {} ", sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("siemensHvac:doAuth:decodeResponse:()");
|
||||||
|
|
||||||
|
if (sessionId == null) {
|
||||||
|
throw new SiemensHvacException(
|
||||||
|
"Session request auth was unsuccessful in _doAuth(), please verify login parameters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("siemensHvac:doAuth:connect()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String doBasicRequest(String uri) throws SiemensHvacException {
|
||||||
|
return doBasicRequest(uri, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String doBasicRequestAsync(String uri, @Nullable SiemensHvacCallback callback)
|
||||||
|
throws SiemensHvacException {
|
||||||
|
return doBasicRequest(uri, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String doBasicRequest(String uri, @Nullable SiemensHvacCallback callback)
|
||||||
|
throws SiemensHvacException {
|
||||||
|
if (sessionIdHttp == null) {
|
||||||
|
doAuth(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionId == null) {
|
||||||
|
doAuth(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SiemensHvacBridgeConfig config = this.config;
|
||||||
|
if (config == null) {
|
||||||
|
throw new SiemensHvacException("Missing SiemensHvac OZW Bridge configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
String baseUri = config.baseUrl;
|
||||||
|
|
||||||
|
String mUri = uri;
|
||||||
|
if (!mUri.endsWith("?")) {
|
||||||
|
mUri = mUri + "&";
|
||||||
|
}
|
||||||
|
if (mUri.indexOf("main.app") >= 0) {
|
||||||
|
mUri = mUri + "SessionId=" + sessionIdHttp;
|
||||||
|
} else {
|
||||||
|
mUri = mUri + "SessionId=" + sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
CookieStore c = httpClient.getCookieStore();
|
||||||
|
java.net.HttpCookie cookie = new HttpCookie("SessionId", sessionIdHttp);
|
||||||
|
cookie.setPath("/");
|
||||||
|
cookie.setVersion(0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
c.add(new URI(baseUri), cookie);
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new SiemensHvacException(String.format("URI is not correctly formatted: %s", baseUri), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Execute request: {}", uri);
|
||||||
|
final Request request = httpClient.newRequest(baseUri + mUri);
|
||||||
|
request.method(HttpMethod.GET);
|
||||||
|
|
||||||
|
ContentResponse response = executeRequest(request, callback);
|
||||||
|
if (callback == null && response != null) {
|
||||||
|
int statusCode = response.getStatus();
|
||||||
|
|
||||||
|
if (statusCode == HttpStatus.OK_200) {
|
||||||
|
return response.getContentAsString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable JsonObject doRequest(String req) {
|
||||||
|
return doRequest(req, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable JsonObject doRequest(String req, @Nullable SiemensHvacCallback callback) {
|
||||||
|
try {
|
||||||
|
String response = doBasicRequest(req, callback);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
JsonObject resultObj = getGson().fromJson(response, JsonObject.class);
|
||||||
|
|
||||||
|
if (resultObj != null && resultObj.has("Result")) {
|
||||||
|
JsonObject subResultObj = resultObj.getAsJsonObject("Result");
|
||||||
|
|
||||||
|
if (subResultObj.has("Success")) {
|
||||||
|
boolean result = subResultObj.get("Success").getAsBoolean();
|
||||||
|
if (result) {
|
||||||
|
return resultObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (SiemensHvacException e) {
|
||||||
|
logger.warn("siemensHvac:DoRequest:Exception by executing jsonRequest: {} ; {} ", req,
|
||||||
|
e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayRequestStats() {
|
||||||
|
logger.debug("DisplayRequestStats: ");
|
||||||
|
logger.debug(" currentRuning : {}", getCurrentHandlerRegistryCount());
|
||||||
|
logger.debug(" errors : {}", getHandlerInErrorRegistryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitAllPendingRequest() {
|
||||||
|
logger.debug("WaitAllPendingRequest:start");
|
||||||
|
try {
|
||||||
|
boolean allRequestDone = false;
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (!allRequestDone) {
|
||||||
|
allRequestDone = false;
|
||||||
|
int currentRequestCount = getCurrentHandlerRegistryCount();
|
||||||
|
|
||||||
|
logger.debug("WaitAllPendingRequest:waitAllRequestDone {}: {}", idx, currentRequestCount);
|
||||||
|
|
||||||
|
if (currentRequestCount == 0) {
|
||||||
|
allRequestDone = true;
|
||||||
|
}
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
if ((idx % 50) == 0) {
|
||||||
|
checkStaleRequest();
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("WaitAllPendingRequest:end WaitAllPendingRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkStaleRequest() {
|
||||||
|
synchronized (currentHandlerRegistry) {
|
||||||
|
logger.debug("check stale request::begin");
|
||||||
|
int staleRequest = 0;
|
||||||
|
|
||||||
|
for (SiemensHvacRequestHandler handler : currentHandlerRegistry.keySet()) {
|
||||||
|
long elapseTime = handler.getElapsedTime();
|
||||||
|
if (elapseTime > 150) {
|
||||||
|
String uri = "";
|
||||||
|
Request request = handler.getRequest();
|
||||||
|
if (request != null) {
|
||||||
|
uri = request.getURI().toString();
|
||||||
|
}
|
||||||
|
logger.debug("find stale request: {} {}", elapseTime, anominized(uri));
|
||||||
|
staleRequest++;
|
||||||
|
|
||||||
|
try {
|
||||||
|
unregisterRequestHandler(handler);
|
||||||
|
registerHandlerError(handler);
|
||||||
|
} catch (SiemensHvacException ex) {
|
||||||
|
logger.debug("error unregistring handler: {}", handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("check stale request::end: {}", staleRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String anominized(String uri) {
|
||||||
|
int p0 = uri.indexOf("pwd=");
|
||||||
|
if (p0 > 0) {
|
||||||
|
return uri.substring(0, p0) + "pwd=xxxxx";
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCurrentHandlerRegistryCount() {
|
||||||
|
synchronized (currentHandlerRegistry) {
|
||||||
|
return currentHandlerRegistry.keySet().size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getHandlerInErrorRegistryCount() {
|
||||||
|
synchronized (handlerInErrorRegistry) {
|
||||||
|
return handlerInErrorRegistry.keySet().size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitNoNewRequest() {
|
||||||
|
logger.debug("WaitNoNewRequest:start");
|
||||||
|
try {
|
||||||
|
int lastRequestCount = getCurrentHandlerRegistryCount();
|
||||||
|
boolean newRequest = true;
|
||||||
|
while (newRequest) {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
int newRequestCount = getCurrentHandlerRegistryCount();
|
||||||
|
if (newRequestCount != lastRequestCount) {
|
||||||
|
logger.debug("waitNoNewRequest {}/{})", newRequestCount, lastRequestCount);
|
||||||
|
lastRequestCount = newRequestCount;
|
||||||
|
} else {
|
||||||
|
newRequest = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
logger.debug("WaitAllPendingRequest:interrupted in WaitAllRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("WaitNoNewRequest:end WaitAllStartingRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Gson getGson() {
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Gson getGsonWithAdapter() {
|
||||||
|
return gsonWithAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDpUpdate(String itemName, Type dp) {
|
||||||
|
synchronized (updateCommand) {
|
||||||
|
updateCommand.put(itemName, dp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetSessionId(@Nullable String sessionIdToInvalidate, boolean web) {
|
||||||
|
if (web) {
|
||||||
|
if (sessionIdToInvalidate == null) {
|
||||||
|
sessionIdHttp = null;
|
||||||
|
} else {
|
||||||
|
if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionIdHttp)) {
|
||||||
|
oldSessionId.put(sessionIdToInvalidate, true);
|
||||||
|
|
||||||
|
logger.debug("Invalidate sessionIdHttp: {}", sessionIdToInvalidate);
|
||||||
|
sessionIdHttp = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sessionIdToInvalidate == null) {
|
||||||
|
sessionId = null;
|
||||||
|
} else {
|
||||||
|
if (!oldSessionId.containsKey(sessionIdToInvalidate) && sessionIdToInvalidate.equals(sessionId)) {
|
||||||
|
oldSessionId.put(sessionIdToInvalidate, true);
|
||||||
|
|
||||||
|
logger.debug("Invalidate sessionId: {}", sessionIdToInvalidate);
|
||||||
|
sessionId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRequestCount() {
|
||||||
|
return requestCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getErrorCount() {
|
||||||
|
return errorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SiemensHvacRequestListener.ErrorSource getErrorSource() {
|
||||||
|
return errorSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
sessionId = null;
|
||||||
|
sessionIdHttp = null;
|
||||||
|
|
||||||
|
synchronized (currentHandlerRegistry) {
|
||||||
|
currentHandlerRegistry.clear();
|
||||||
|
handlerInErrorRegistry.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTimeOut(int timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.network;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacRequestHandler {
|
||||||
|
private SiemensHvacConnector hvacConnector;
|
||||||
|
|
||||||
|
private int retryCount = 0;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Request request = null;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Response response = null;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Result result = null;
|
||||||
|
|
||||||
|
private Instant startRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to execute on complete response
|
||||||
|
*/
|
||||||
|
private final SiemensHvacCallback callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param callback Callback which execute method has to be called.
|
||||||
|
*/
|
||||||
|
public SiemensHvacRequestHandler(SiemensHvacCallback callback, SiemensHvacConnector hvacConnector) {
|
||||||
|
this.callback = callback;
|
||||||
|
this.hvacConnector = hvacConnector;
|
||||||
|
startRequest = Instant.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SiemensHvacConnector getHvacConnector() {
|
||||||
|
return hvacConnector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SiemensHvacCallback getCallback() {
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementRetryCount() {
|
||||||
|
retryCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRetryCount() {
|
||||||
|
return retryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Response getResponse() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Request getRequest() {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Result getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponse(@Nullable Response response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequest(@Nullable Request request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(@Nullable Result result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getElapsedTime() {
|
||||||
|
Instant finish = Instant.now();
|
||||||
|
return Duration.between(startRequest, finish).toSeconds();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,247 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.network;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Request.BeginListener;
|
||||||
|
import org.eclipse.jetty.client.api.Request.QueuedListener;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Response.CompleteListener;
|
||||||
|
import org.eclipse.jetty.client.api.Response.ContentListener;
|
||||||
|
import org.eclipse.jetty.client.api.Response.FailureListener;
|
||||||
|
import org.eclipse.jetty.client.api.Response.SuccessListener;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.type.SiemensHvacException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacRequestListener extends BufferingResponseListener
|
||||||
|
implements SuccessListener, FailureListener, ContentListener, CompleteListener, QueuedListener, BeginListener {
|
||||||
|
|
||||||
|
public enum ErrorSource {
|
||||||
|
ErrorBridge,
|
||||||
|
ErrorThings
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int onSuccessCount = 0;
|
||||||
|
private static int onBeginCount = 0;
|
||||||
|
private static int onQueuedCount = 0;
|
||||||
|
private static int onCompleteCount = 0;
|
||||||
|
private static int onFailureCount = 0;
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SiemensHvacRequestListener.class);
|
||||||
|
|
||||||
|
private SiemensHvacRequestHandler requestHandler;
|
||||||
|
private SiemensHvacConnector hvacConnector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to execute on complete response
|
||||||
|
*/
|
||||||
|
private final SiemensHvacCallback callback;
|
||||||
|
|
||||||
|
public static int getQueuedCount() {
|
||||||
|
return onQueuedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getStartedCount() {
|
||||||
|
return onBeginCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCompleteCount() {
|
||||||
|
return onCompleteCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFailureCount() {
|
||||||
|
return onFailureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getSuccessCount() {
|
||||||
|
return onSuccessCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param callback Callback which execute method has to be called.
|
||||||
|
*/
|
||||||
|
public SiemensHvacRequestListener(SiemensHvacRequestHandler requestHandler) {
|
||||||
|
this.requestHandler = requestHandler;
|
||||||
|
this.hvacConnector = requestHandler.getHvacConnector();
|
||||||
|
this.callback = requestHandler.getCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@Nullable Response response) {
|
||||||
|
onSuccessCount++;
|
||||||
|
requestHandler.setResponse(response);
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
logger.debug("{} response: {}", response.getRequest().getURI(), response.getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@Nullable Response response, @Nullable Throwable failure) {
|
||||||
|
onFailureCount++;
|
||||||
|
requestHandler.setResponse(response);
|
||||||
|
|
||||||
|
if (response != null && failure != null) {
|
||||||
|
Throwable cause = failure.getCause();
|
||||||
|
if (cause == null) {
|
||||||
|
cause = failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
String msg = cause.getLocalizedMessage();
|
||||||
|
|
||||||
|
if (cause instanceof ConnectException e) {
|
||||||
|
logger.debug("ConnectException during request: {} {}", response.getRequest().getURI(), msg, e);
|
||||||
|
} else if (cause instanceof SocketException e) {
|
||||||
|
logger.debug("SocketException during request: {} {}", response.getRequest().getURI(), msg, e);
|
||||||
|
} else if (cause instanceof SocketTimeoutException e) {
|
||||||
|
logger.debug("SocketTimeoutException during request: {} {}", response.getRequest().getURI(), msg, e);
|
||||||
|
} else if (cause instanceof EOFException e) {
|
||||||
|
logger.debug("EOFException during request: {} {}", response.getRequest().getURI(), msg, e);
|
||||||
|
} else if (cause instanceof TimeoutException e) {
|
||||||
|
logger.debug("TimeoutException during request: {} {}", response.getRequest().getURI(), msg, e);
|
||||||
|
} else {
|
||||||
|
logger.debug("Response failed: {} {}", response.getRequest().getURI(), msg, failure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueued(@Nullable Request request) {
|
||||||
|
onQueuedCount++;
|
||||||
|
requestHandler.setRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(@Nullable Request request) {
|
||||||
|
onBeginCount++;
|
||||||
|
requestHandler.setRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(@Nullable Result result) {
|
||||||
|
onCompleteCount++;
|
||||||
|
requestHandler.setResult(result);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String content = getContentAsString();
|
||||||
|
logger.trace("response complete: {}", content);
|
||||||
|
boolean mayRetry = true;
|
||||||
|
|
||||||
|
if (result.getResponse().getStatus() != 200) {
|
||||||
|
logger.debug("Error requesting gateway, non success code: {}", result.getResponse().getStatus());
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content != null) {
|
||||||
|
if (content.indexOf("<!DOCTYPE html>") >= 0) {
|
||||||
|
hvacConnector.onComplete(result.getRequest(), requestHandler);
|
||||||
|
callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(), content);
|
||||||
|
} else {
|
||||||
|
JsonObject resultObj = null;
|
||||||
|
try {
|
||||||
|
Gson gson = hvacConnector.getGson();
|
||||||
|
resultObj = gson.fromJson(content, JsonObject.class);
|
||||||
|
} catch (JsonSyntaxException ex) {
|
||||||
|
logger.debug("error(1): {}", ex.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultObj != null && resultObj.has("Result")) {
|
||||||
|
JsonObject subResultObj = resultObj.getAsJsonObject("Result");
|
||||||
|
|
||||||
|
if (subResultObj.has("Success")) {
|
||||||
|
boolean resultVal = subResultObj.get("Success").getAsBoolean();
|
||||||
|
JsonObject error = subResultObj.getAsJsonObject("Error");
|
||||||
|
String errorMsg = "";
|
||||||
|
if (error != null) {
|
||||||
|
errorMsg = error.get("Txt").getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMsg.indexOf("session") >= 0) {
|
||||||
|
String query = result.getRequest().getURI().getQuery();
|
||||||
|
String sessionId = SiemensHvacConnectorImpl.extractSessionId(query);
|
||||||
|
|
||||||
|
hvacConnector.resetSessionId(sessionId, false);
|
||||||
|
hvacConnector.resetSessionId(sessionId, true);
|
||||||
|
mayRetry = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultVal) {
|
||||||
|
hvacConnector.onComplete(result.getRequest(), requestHandler);
|
||||||
|
callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(),
|
||||||
|
resultObj);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (("datatype not supported").equals(errorMsg)) {
|
||||||
|
hvacConnector.onComplete(result.getRequest(), requestHandler);
|
||||||
|
callback.execute(result.getRequest().getURI(), result.getResponse().getStatus(),
|
||||||
|
resultObj);
|
||||||
|
return;
|
||||||
|
} else if (("read failed").equals(errorMsg)) {
|
||||||
|
logger.debug("error(2): {}", subResultObj);
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorThings,
|
||||||
|
mayRetry);
|
||||||
|
} else {
|
||||||
|
logger.debug("error(3): {}", subResultObj);
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge,
|
||||||
|
mayRetry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("error(4): invalid response from gateway, missing subResultObj:Success entry");
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge,
|
||||||
|
mayRetry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.debug("error(5): invalid response from gateway, missing Result entry");
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("error: content == null");
|
||||||
|
hvacConnector.onError(result.getRequest(), requestHandler, ErrorSource.ErrorBridge, mayRetry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (SiemensHvacException ex) {
|
||||||
|
logger.debug("An error occurred", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupType;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the ChannelGroupTypeProvider to manually add a ChannelGroupType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacChannelGroupTypeProvider extends ChannelGroupTypeProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the ChannelGroupType to this provider.
|
||||||
|
*/
|
||||||
|
void addChannelGroupType(ChannelGroupType channelGroupType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to lookup a ChannelGroupType which was generated by the siemensHvac binding.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID);
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupType;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides all ChannelGroupTypes from all SiemensHvac bridges.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = { SiemensHvacChannelGroupTypeProvider.class, ChannelGroupTypeProvider.class })
|
||||||
|
public class SiemensHvacChannelGroupTypeProviderImpl implements SiemensHvacChannelGroupTypeProvider {
|
||||||
|
|
||||||
|
private final Map<ChannelGroupTypeUID, ChannelGroupType> channelGroupTypesByUID = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ChannelGroupType getInternalChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID) {
|
||||||
|
return channelGroupTypesByUID.get(channelGroupTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChannelGroupType(ChannelGroupType channelGroupType) {
|
||||||
|
channelGroupTypesByUID.put(channelGroupType.getUID(), channelGroupType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ChannelGroupType getChannelGroupType(ChannelGroupTypeUID channelGroupTypeUID,
|
||||||
|
@Nullable Locale locale) {
|
||||||
|
return channelGroupTypesByUID.get(channelGroupTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @see ChannelTypeRegistr#getChannelGroupTypes(Locale)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<ChannelGroupType> getChannelGroupTypes(@Nullable Locale locale) {
|
||||||
|
Collection<ChannelGroupType> result = new ArrayList<>();
|
||||||
|
for (ChannelGroupTypeUID uid : channelGroupTypesByUID.keySet()) {
|
||||||
|
ChannelGroupType groupType = channelGroupTypesByUID.get(uid);
|
||||||
|
if (groupType != null) {
|
||||||
|
result.add(groupType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
channelGroupTypesByUID.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the ChannelTypeProvider to manually add a ChannelType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacChannelTypeProvider extends ChannelTypeProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the ChannelType to this provider.
|
||||||
|
*/
|
||||||
|
void addChannelType(ChannelType channelType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to lookup a ChannelType which was generated by the siemensHvac binding.
|
||||||
|
*
|
||||||
|
* @param channelTypeUID
|
||||||
|
* @return ChannelType that was added to SiemensHvacChannelTypeProvider, identified by its
|
||||||
|
* config-description-uri<br>
|
||||||
|
* <i>null</i> if no ChannelType with the given UID was added
|
||||||
|
* before
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID);
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.type.ChannelType;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides all ChannelTypes from SiemensHvac bridges.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = { SiemensHvacChannelTypeProvider.class, ChannelTypeProvider.class })
|
||||||
|
public class SiemensHvacChannelTypeProviderImpl implements SiemensHvacChannelTypeProvider {
|
||||||
|
private final Map<ChannelTypeUID, ChannelType> channelTypesByUID = new HashMap<>();
|
||||||
|
|
||||||
|
public SiemensHvacChannelTypeProviderImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChannelType(ChannelType channelType) {
|
||||||
|
channelTypesByUID.put(channelType.getUID(), channelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ChannelType> getChannelTypes(@Nullable Locale locale) {
|
||||||
|
Collection<ChannelType> result = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ChannelTypeUID uid : channelTypesByUID.keySet()) {
|
||||||
|
ChannelType tp = channelTypesByUID.get(uid);
|
||||||
|
if (tp != null) {
|
||||||
|
result.add(tp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see ChannelTypeRegistr#getChannelType(ChannelTypeUID, Locale)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public @Nullable ChannelType getChannelType(ChannelTypeUID channelTypeUID, @Nullable Locale locale) {
|
||||||
|
return channelTypesByUID.get(channelTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ChannelType getInternalChannelType(@Nullable ChannelTypeUID channelTypeUID) {
|
||||||
|
return channelTypesByUID.get(channelTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
channelTypesByUID.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.config.core.ConfigDescription;
|
||||||
|
import org.openhab.core.config.core.ConfigDescriptionProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the ConfigDescriptionProvider to manually add a ConfigDescription.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacConfigDescriptionProvider extends ConfigDescriptionProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the ConfigDescription to this provider.
|
||||||
|
*/
|
||||||
|
void addConfigDescription(ConfigDescription configDescription);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a {@link ConfigDescription} for the given URI.
|
||||||
|
*
|
||||||
|
* @param uri uri of the config description
|
||||||
|
* @param locale locale
|
||||||
|
*
|
||||||
|
* @return config description or null if no config description could be found
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to lookup a ConfigDescription which was generated by the
|
||||||
|
* siemenshvac binding.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ConfigDescription getInternalConfigDescription(URI uri);
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.config.core.ConfigDescription;
|
||||||
|
import org.openhab.core.config.core.ConfigDescriptionProvider;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = { SiemensHvacConfigDescriptionProvider.class, ConfigDescriptionProvider.class })
|
||||||
|
public class SiemensHvacConfigDescriptionProviderImpl implements SiemensHvacConfigDescriptionProvider {
|
||||||
|
private Map<URI, ConfigDescription> configDescriptionsByURI = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigDescription> getConfigDescriptions(@Nullable Locale locale) {
|
||||||
|
Collection<ConfigDescription> result = new ArrayList<>();
|
||||||
|
for (URI configDescriptionURI : configDescriptionsByURI.keySet()) {
|
||||||
|
ConfigDescription desc = configDescriptionsByURI.get(configDescriptionURI);
|
||||||
|
if (desc != null) {
|
||||||
|
result.add(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale) {
|
||||||
|
return configDescriptionsByURI.get(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ConfigDescription getInternalConfigDescription(URI uri) {
|
||||||
|
return configDescriptionsByURI.get(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addConfigDescription(ConfigDescription configDescription) {
|
||||||
|
configDescriptionsByURI.put(configDescription.getUID(), configDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
configDescriptionsByURI.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* An exception that occurred while operating the binding
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SiemensHvacException extends Exception {
|
||||||
|
private static final long serialVersionUID = -3398100220952729816L;
|
||||||
|
|
||||||
|
public SiemensHvacException(String message, Exception e) {
|
||||||
|
super(message, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SiemensHvacException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ThingType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the ThingTypeProvider to manually add a ThingType.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface SiemensHvacThingTypeProvider extends ThingTypeProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the ThingType to this provider.
|
||||||
|
*/
|
||||||
|
void addThingType(ThingType thingType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to lookup a ThingType which was generated by the
|
||||||
|
* binding. Other than {@link #getThingType(ThingTypeUID)}
|
||||||
|
* of this provider, it will return also those {@link ThingType}s which are
|
||||||
|
* excluded by {@link ThingTypeExcluder}
|
||||||
|
*
|
||||||
|
* @param thingTypeUID
|
||||||
|
* @return ThingType that was added to SiemensHvacThingTypeProvider, identified
|
||||||
|
* by its thingTypeUID<br>
|
||||||
|
* <i>null</i> if no ThingType with the given thingTypeUID was added
|
||||||
|
* before
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ThingType getInternalThingType(ThingTypeUID thingTypeUID);
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.ThingTypeProvider;
|
||||||
|
import org.openhab.core.thing.type.ThingType;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides all ThingTypes from SiemensHvac bridges.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(service = { SiemensHvacThingTypeProvider.class, ThingTypeProvider.class }, immediate = true)
|
||||||
|
public class SiemensHvacThingTypeProviderImpl implements SiemensHvacThingTypeProvider {
|
||||||
|
|
||||||
|
private Map<ThingTypeUID, ThingType> thingTypesByUID = new HashMap<>();
|
||||||
|
|
||||||
|
public SiemensHvacThingTypeProviderImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addThingType(ThingType thingType) {
|
||||||
|
thingTypesByUID.put(thingType.getUID(), thingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingType getInternalThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
return thingTypesByUID.get(thingTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ThingType> getThingTypes(@Nullable Locale locale) {
|
||||||
|
Map<ThingTypeUID, ThingType> copy = new HashMap<>(thingTypesByUID);
|
||||||
|
return copy.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ThingType getThingType(ThingTypeUID thingTypeUID, @Nullable Locale locale) {
|
||||||
|
return thingTypesByUID.get(thingTypeUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
thingTypesByUID.clear();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import java.text.Normalizer;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.constants.SiemensHvacBindingConstants;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterFactory;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.ConverterTypeException;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.converter.TypeConverter;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDataPoint;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataDevice;
|
||||||
|
import org.openhab.binding.siemenshvac.internal.metadata.SiemensHvacMetadataMenu;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for generating some UIDs.
|
||||||
|
*
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class UidUtils {
|
||||||
|
/**
|
||||||
|
* The methods remove specific local character (like 'é'/'ê','â') so we have a correctly formated UID from a
|
||||||
|
* localize item label
|
||||||
|
*
|
||||||
|
* @param label
|
||||||
|
* @return the label without invalid character
|
||||||
|
*/
|
||||||
|
public static String sanetizeId(String label) {
|
||||||
|
String result = label;
|
||||||
|
|
||||||
|
if (!Normalizer.isNormalized(label, Normalizer.Form.NFKD)) {
|
||||||
|
result = Normalizer.normalize(label, Normalizer.Form.NFKD);
|
||||||
|
result = result.replaceAll("\\p{M}", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.replaceAll("[^a-zA-Z0-9_]", "-").toLowerCase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ThingTypeUID for the given device. If it's a Homegear device, add a prefix because a Homegear
|
||||||
|
* device has more datapoints.
|
||||||
|
*/
|
||||||
|
public static ThingTypeUID generateThingTypeUID(SiemensHvacMetadataDevice device) {
|
||||||
|
String type = sanetizeId(device.getType());
|
||||||
|
return new ThingTypeUID(SiemensHvacBindingConstants.BINDING_ID, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a more user friendly description from English short descriptor
|
||||||
|
*
|
||||||
|
* @param descriptor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static String normalizeDescriptor(String descriptor) {
|
||||||
|
String result = descriptor.trim();
|
||||||
|
|
||||||
|
if (result.indexOf("CC") >= 0 || result.indexOf("HC") >= 0) {
|
||||||
|
for (int idx = 0; idx < 4; idx++) {
|
||||||
|
result = result.replace("CC" + idx, "CC");
|
||||||
|
result = result.replace("HC" + idx, "HC");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.toLowerCase();
|
||||||
|
|
||||||
|
if (result.indexOf("history") >= 0) {
|
||||||
|
for (int idx = 0; idx < 20; idx++) {
|
||||||
|
result = result.replace("history " + idx, "history");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.replace(" mon", "");
|
||||||
|
result = result.replace(" tue", "");
|
||||||
|
result = result.replace(" wed", "");
|
||||||
|
result = result.replace(" thu", "");
|
||||||
|
result = result.replace(" fri", "");
|
||||||
|
result = result.replace(" sat", "");
|
||||||
|
result = result.replace(" sun", "");
|
||||||
|
result = result.replace(" mo", "");
|
||||||
|
result = result.replace(" tu", "");
|
||||||
|
result = result.replace(" we", "");
|
||||||
|
result = result.replace(" th", "");
|
||||||
|
result = result.replace(" fr", "");
|
||||||
|
result = result.replace(" sa", "");
|
||||||
|
result = result.replace(" su", "");
|
||||||
|
|
||||||
|
if (result.indexOf("holidays") >= 0) {
|
||||||
|
if (result.indexOf("firstd") >= 0) {
|
||||||
|
result = "holidays-hc-firstd";
|
||||||
|
}
|
||||||
|
if (result.indexOf("lastd") >= 0) {
|
||||||
|
result = "holidays-hc-lastd";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.replace("---", "-");
|
||||||
|
result = result.replace("--", "-");
|
||||||
|
result = result.replace('\'', '-');
|
||||||
|
result = result.replace('/', '-');
|
||||||
|
result = result.replace(' ', '-');
|
||||||
|
result = result.replace("+", "-");
|
||||||
|
|
||||||
|
result = result.replace("standard-tsp-hc", "time-switch-program-standard");
|
||||||
|
result = result.replace("standard-tsp-4", "time-switch-program-standard");
|
||||||
|
result = result.replace("tsp-3", "time-switch-program-day");
|
||||||
|
result = result.replace("tsp-4", "time-switch-program-day");
|
||||||
|
result = result.replace("setpointtemp", "setpoint-temp-");
|
||||||
|
result = result.replace("rmtmp", "roomtemp");
|
||||||
|
result = result.replace("roomtempfrostprot", "room-temp-frostprot-");
|
||||||
|
result = result.replace("-setp", "-setpoint");
|
||||||
|
result = result.replace("optg", "operating-");
|
||||||
|
result = result.replace("-comf", "-comfort");
|
||||||
|
result = result.replace("-red", "-reduce");
|
||||||
|
result = result.replace("setp-", "-setpoint");
|
||||||
|
result = result.replace("roomtemp-", "room-temp-");
|
||||||
|
result = result.replace("-setpointhc", "-setpoint-hc");
|
||||||
|
result = result.replace("setphc", "-setpoint-hc");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ChannelTypeUID for the given datapoint with deviceType, channelNumber and datapointName.
|
||||||
|
*/
|
||||||
|
public static ChannelTypeUID generateChannelTypeUID(SiemensHvacMetadataDataPoint dpt) throws SiemensHvacException {
|
||||||
|
String type = dpt.getDptType();
|
||||||
|
String shortDesc = dpt.getShortDescEn();
|
||||||
|
String result = normalizeDescriptor(shortDesc);
|
||||||
|
|
||||||
|
try {
|
||||||
|
TypeConverter tp = ConverterFactory.getConverter(type);
|
||||||
|
if (!tp.hasVariant()) {
|
||||||
|
result = tp.getChannelType(dpt);
|
||||||
|
}
|
||||||
|
} catch (ConverterTypeException ex) {
|
||||||
|
throw new SiemensHvacException(String.format("Can't find converter for type: %s", type), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChannelTypeUID(SiemensHvacBindingConstants.BINDING_ID, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the ChannelTypeUID for the given datapoint with deviceType and channelNumber.
|
||||||
|
*/
|
||||||
|
public static ChannelGroupTypeUID generateChannelGroupTypeUID(SiemensHvacMetadataMenu menu) {
|
||||||
|
return new ChannelGroupTypeUID(SiemensHvacBindingConstants.BINDING_ID, String.valueOf(menu.getId()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon:addon xmlns:addon="https://openhab.org/schemas/addon/v1.0.0" id="siemenshvac"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<type>binding</type>
|
||||||
|
<name>SiemensHvac Binding</name>
|
||||||
|
<description>This is the binding for SiemensHvac.</description>
|
||||||
|
<connection>local</connection>
|
||||||
|
<discovery-methods>
|
||||||
|
<discovery-method>
|
||||||
|
<service-type>upnp</service-type>
|
||||||
|
<match-properties>
|
||||||
|
<match-property>
|
||||||
|
<name>manufacturer</name>
|
||||||
|
<regex>Siemens.*</regex>
|
||||||
|
</match-property>
|
||||||
|
<match-property>
|
||||||
|
<name>modelName</name>
|
||||||
|
<regex>Web Server OZW.*</regex>
|
||||||
|
</match-property>
|
||||||
|
</match-properties>
|
||||||
|
</discovery-method>
|
||||||
|
</discovery-methods>
|
||||||
|
</addon:addon>
|
@ -0,0 +1,26 @@
|
|||||||
|
# add-on
|
||||||
|
|
||||||
|
addon.siemenshvac.name = SiemensHvac Binding
|
||||||
|
addon.siemenshvac.description = This is the binding for SiemensHvac.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.siemenshvac.ozw.label = OZW IP Gateway
|
||||||
|
thing-type.siemenshvac.ozw.description = This is a OZW IP interface
|
||||||
|
|
||||||
|
# thing types config
|
||||||
|
|
||||||
|
thing-type.config.siemenshvac.ozw.baseUrl.label = Base URL
|
||||||
|
thing-type.config.siemenshvac.ozw.baseUrl.description = The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't forget the trailing '/'
|
||||||
|
thing-type.config.siemenshvac.ozw.userName.label = User Name
|
||||||
|
thing-type.config.siemenshvac.ozw.userName.description = User name of the Siemens Hvac gateway
|
||||||
|
thing-type.config.siemenshvac.ozw.userPassword.label = User Password
|
||||||
|
thing-type.config.siemenshvac.ozw.userPassword.description = User password of the Siemens Hvac gateway
|
||||||
|
|
||||||
|
# offline message
|
||||||
|
|
||||||
|
offline.baseurl-mandatory = baseUrl is mandatory on configuration.
|
||||||
|
offline.error-gateway-init = Error occurred during gateway initialization [{0}]
|
||||||
|
offline.config-not-init = Config not initialize during reading metadata, aborting.
|
||||||
|
offline.user-not-find = Cannot find user during reading metadata, aborting.
|
||||||
|
offline.waiting-bridge-initialization = Waiting bridge initialization, reading metadata in background
|
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="siemenshvac"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||||
|
|
||||||
|
<!-- Bridge Thing Type -->
|
||||||
|
<bridge-type id="ozw">
|
||||||
|
<label>OZW IP Gateway</label>
|
||||||
|
<description>This is a OZW IP interface</description>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="baseUrl" type="text" pattern="(http|https):\/\/(.+)\/">
|
||||||
|
<label>Base URL</label>
|
||||||
|
<context>url</context>
|
||||||
|
<description>The URL of the Siemens Hvac IP gateway. Must be in format http://hostname/ or https://hostname/. Don't
|
||||||
|
forget the trailing '/'</description>
|
||||||
|
<required>true</required>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="userName" type="text">
|
||||||
|
<description>User name of the Siemens Hvac gateway</description>
|
||||||
|
<required>false</required>
|
||||||
|
<label>User Name</label>
|
||||||
|
<default>Administrator</default>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="userPassword" type="text">
|
||||||
|
<context>password</context>
|
||||||
|
<description>User password of the Siemens Hvac gateway</description>
|
||||||
|
<required>false</required>
|
||||||
|
<label>User Password</label>
|
||||||
|
<default>password</default>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</bridge-type>
|
||||||
|
</thing:thing-descriptions>
|
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.siemenshvac.internal.type;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Laurent Arnal - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class UidUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSanetizeId() throws Exception {
|
||||||
|
assertEquals(UidUtils.sanetizeId("Début heure été"), "debut-heure-ete");
|
||||||
|
assertEquals(UidUtils.sanetizeId("App.Ambiance 1"), "app-ambiance-1");
|
||||||
|
assertEquals(UidUtils.sanetizeId("Appareil d'ambiance P"), "appareil-d-ambiance-p");
|
||||||
|
}
|
||||||
|
}
|
@ -362,6 +362,7 @@
|
|||||||
<module>org.openhab.binding.serialbutton</module>
|
<module>org.openhab.binding.serialbutton</module>
|
||||||
<module>org.openhab.binding.shelly</module>
|
<module>org.openhab.binding.shelly</module>
|
||||||
<module>org.openhab.binding.silvercrestwifisocket</module>
|
<module>org.openhab.binding.silvercrestwifisocket</module>
|
||||||
|
<module>org.openhab.binding.siemenshvac</module>
|
||||||
<module>org.openhab.binding.siemensrds</module>
|
<module>org.openhab.binding.siemensrds</module>
|
||||||
<module>org.openhab.binding.sinope</module>
|
<module>org.openhab.binding.sinope</module>
|
||||||
<module>org.openhab.binding.sleepiq</module>
|
<module>org.openhab.binding.sleepiq</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user