added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.ism8/.classpath
Normal file
32
bundles/org.openhab.binding.ism8/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.ism8/.project
Normal file
23
bundles/org.openhab.binding.ism8/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.ism8</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.ism8/NOTICE
Normal file
13
bundles/org.openhab.binding.ism8/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
193
bundles/org.openhab.binding.ism8/README.md
Normal file
193
bundles/org.openhab.binding.ism8/README.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# Ism8 Binding
|
||||
|
||||
_This binding can receive values of the Wolf heating system._
|
||||
|
||||
The ISM8 card can be placed into the Wolf heating system.
|
||||
The card is usually used in combination with an object server, where the object server does forward those messages into the KNX bus system.
|
||||
In case there is no need to handle the heating system values directly in the KNX system you can use this binding to monitor and control your heating system without the need to buy an object server.
|
||||
The system works in a way that the ISM8 connects to a partner and sends from time to time an update. The frequency depends on the change of the values.
|
||||
This binding is listening to those messages.
|
||||
After the first connection there is an active command send to the ISM8 in order to receive all available data points.
|
||||
The manual of the ISM8 can be downloaded from the supplier (https://www.wolf.eu/fileadmin/Wolf_Profi/Downloads/Montage-Bedienungsanleitungen/Regelungen/Zubehoer/3064356_201611_ISM8i_Montage-u.Bedienungsanleitung.pdf)
|
||||
|
||||
## Supported Things
|
||||
|
||||
_This binding does only support one Thing - the Ism8-Device._
|
||||
|
||||
## Discovery
|
||||
|
||||
_Auto-discovery is not supported._
|
||||
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The intention was to have a generic ISM8 binding in order to offer the full flexibilty for the different heating systems.
|
||||
For this reason you need to create a Thing configuration, where basically only the port is required next to the channel configuration.
|
||||
(`Thing ism8:device:heater "Wolf Heizung" [portNumber=12004]`)
|
||||
|
||||
## Channels
|
||||
|
||||
You can use any channel supported by the ISM8 as data point. Please have a look at the official manual from Wolf.
|
||||
Within this document you'll find a table containing all supported data points.
|
||||
The available data points are depending on your heating system configuration.
|
||||
The ISM8 does currently support 4 different devices at the same moment of time (e.g. CGB-2, CWL Excellent, Solar, ...).
|
||||
|
||||
Once you have an overview of your heating system set you can start to create the channels accordingly.
|
||||
Each channel should be created in the following way:
|
||||
|
||||
| Type | Name | Description | Configuration |
|
||||
|--------|---------|----------------------------|-----------------|
|
||||
| Number | DpId004 | "Kesseltemperatur" | id, type, write |
|
||||
|
||||
Type:
|
||||
|
||||
+ Switch use for boolean values
|
||||
+ Number use for any number
|
||||
+ Other types may work as well.
|
||||
|
||||
Name:
|
||||
|
||||
+ Put here any name you'd like. This name is used for creating the binding.
|
||||
|
||||
|
||||
Description:
|
||||
|
||||
+ Put here any description you'd like or the description for the data point ID from the Wolf manual.
|
||||
|
||||
|
||||
Configuration:
|
||||
|
||||
+ id=1 - Please enter here the ID of the data point you'd like to map to this channel.
|
||||
A list of the available IDs are available within the Wolf manual.
|
||||
The supported IDs are depending on the firmware version of the ISM8 and the connected systems.
|
||||
+ type="1.001" - Please enter here the knx type of the data point.
|
||||
You can find the data type in the Wolf ISM8 document as well.
|
||||
+ write=true - This parameter defines if the channel is bidirectional, but the parameter is optional and by default false.
|
||||
|
||||
Note:
|
||||
Not all available types of the ISM8 interface are fully supported, but this can be extended.
|
||||
For the moment the following data types are implemented:
|
||||
|
||||
+ DPT-Bool: `1.001`, `1.002`, `1.003`, `1.009`
|
||||
+ DPT-Scaling: `5.001`
|
||||
+ DPT-Value: `9.001`, `9.002`, `9.006`
|
||||
+ DPT-FlowRate: `13.002`
|
||||
+ DPT-Mode: `20.102`, `20.103`, `20.105`
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
_ism8.things_
|
||||
|
||||
Thing ism8:device:heater "Wolf Heizung" [portNumber=12004]
|
||||
{
|
||||
Type switch-readonly : DpId001 "Störung Heizgerät" [id=1, type="1.001"]
|
||||
Type number-readonly : DpId002 "Betriebsart" [id=2, type="20.105"]
|
||||
Type number-readonly : DpId003 "Brennerleistung" [id=3, type="5.001"]
|
||||
Type number-readonly : DpId004 "Kesseltemperatur" [id=4, type="9.001"]
|
||||
Type number-readonly : DpId006 "Rücklauftemperatur" [id=6, type="9.001"]
|
||||
Type number-readonly : DpId007 "Warmwassertemperatur" [id=7, type="9.001"]
|
||||
Type number-readonly : DpId008 "Außentemperatur" [id=8, type="9.001"]
|
||||
Type switch-readonly : DpId009 "Status Flamme" [id=9, type="1.001"]
|
||||
Type number-readonly : DpId013 "Anlagendruck" [id=13, type="9.006"]
|
||||
Type number-readonly : DpId053 "Störung Systemmodul" [id=53, type="1.001"]
|
||||
Type number-readonly : DpId054 "Außentemperatur Systemmodul" [id=54, type="9.001"]
|
||||
Type number : DpId056 "Sollwert Warmwasser" [id=56, type="9.001"]
|
||||
Type number : DpId057 "Betriebsart Heizkreis" [id=57, type="20.102"]
|
||||
Type number : DpId058 "Betriebsart Warmwasser" [id=58, type="20.103"]
|
||||
Type number : DpId065 "Sollwertverschiebung" [id=65, type="9.002"]
|
||||
Type number-readonly : DpId148 "CML Störung" [id=148, type="1.001"]
|
||||
Type number : DpId149 "CWL Betriebsart" [id=149, type="20.102"]
|
||||
Type number-readonly : DpId163 "CWL Lüftungsstufe" [id=163, type="5.001"]
|
||||
Type number-readonly : DpId164 "CWL Ablufttemperatur" [id=164, type="9.001"]
|
||||
Type number-readonly : DpId165 "CWL Zulufttemperatur" [id=165, type="9.001"]
|
||||
Type number-readonly : DpId166 "CWL Luftdurchsatz Zuluft" [id=166, type="13.002"]
|
||||
Type number-readonly : DpId167 "CWL Luftdurchsatz Abluft" [id=167, type="13.002"]
|
||||
Type number-readonly : DpId192 "CML Filterwarnung" [id=192, type="1.001"]
|
||||
}
|
||||
|
||||
_ism8.items_
|
||||
|
||||
Switch ISM_HeizungStoerung "Störung Heizgerät" { channel="ism8:device:heater:DpId001" }
|
||||
Number ISM_HeizungBetriebsart "Betriebsart" { channel="ism8:device:heater:DpId002" }
|
||||
Number ISM_HeizungBrennerleistung "Brennerleistung [%.1f %%]" { channel="ism8:device:heater:DpId003" }
|
||||
Number ISM_HeizungKesseltemperatur "Kesseltemperatur [%.1f °C]" { channel="ism8:device:heater:DpId004" }
|
||||
Number ISM_HeizungRuecklauftemperatur "Rücklauftemperatur [%.1f °C]" { channel="ism8:device:heater:DpId006" }
|
||||
Number ISM_HeizungWarmwassertemperatur "Warmwassertemperatur [%.1f °C]" { channel="ism8:device:heater:DpId007" }
|
||||
Number ISM_HeizungAussentemperatur "Außentemperatur [%.1f °C]" { channel="ism8:device:heater:DpId008" }
|
||||
Switch ISM_HeizungStatusFlamme "Status Flamme" { channel="ism8:device:heater:DpId009" }
|
||||
Number ISM_HeizungAnlagendruck "Anlagendruck [%.2f bar]" { channel="ism8:device:heater:DpId013" }
|
||||
Switch ISM_HeizungSysStoerung "Störung Systemmodul" { channel="ism8:device:heater:DpId053" }
|
||||
Number ISM_HeizungSysAussentemperatur "Außentemperatur Systemmodul [%.1f °C]" { channel="ism8:device:heater:DpId054" }
|
||||
Number ISM_HeizungSollwertWarmwasser "Sollwert Warmwasser [%.1f °C]" { channel="ism8:device:heater:DpId056" }
|
||||
Number ISM_HeizungBetriebsartHeizkreis "Betriebsart Heizkreis" { channel="ism8:device:heater:DpId057" }
|
||||
Number ISM_HeizungBetriebsartWarmwasser "Betriebsart Warmwasser" { channel="ism8:device:heater:DpId058" }
|
||||
Number ISM_HeizungSollwertverschiebung "Sollwertverschiebung [%.1f °C]" { channel="ism8:device:heater:DpId065" }
|
||||
Switch ISM_LueftungStoerung "CML Störung" { channel="ism8:device:heater:DpId148" }
|
||||
Number ISM_LueftungBetriebsart "CWL Betriebsart" { channel="ism8:device:heater:DpId149" }
|
||||
Number ISM_LueftungLueftungsstufe "CWL Lüftungsstufe [%.1f %%]" { channel="ism8:device:heater:DpId163" }
|
||||
Number ISM_LueftungAblufttemperatur "CWL Ablufttemperatur [%.1f °C]" { channel="ism8:device:heater:DpId164" }
|
||||
Number ISM_LueftungZulufttemperatur "CWL Zulufttemperatur [%.1f °C]" { channel="ism8:device:heater:DpId165" }
|
||||
Number ISM_LueftungLuftdurchsatzZuluft "CWL Luftdurchsatz Zuluft [%.1f m³/h]" { channel="ism8:device:heater:DpId166" }
|
||||
Number ISM_LueftungLuftdurchsatzAbluft "CWL Luftdurchsatz Abluft [%.1f m³/h]" { channel="ism8:device:heater:DpId167" }
|
||||
Switch ISM_LueftungFilterwarnung "CML Filterwarnung" { channel="ism8:device:heater:DpId192" }
|
||||
|
||||
_demo.sitemap_
|
||||
|
||||
Frame label="Heizung"
|
||||
{
|
||||
Text item=ISM_HeizungSysStoerung icon="siren"
|
||||
Text item=ISM_HeizungStoerung icon="siren"
|
||||
Text item=ISM_HeizungAussentemperatur icon="temperature"
|
||||
Text item=ISM_HeizungBetriebsart icon="radiator" label="Modus [MAP(HVACContrMode.map):%s]"
|
||||
Text item=ISM_HeizungAnlagendruck icon="pressure"
|
||||
Text item=ISM_HeizungBrennerleistung icon="chart"
|
||||
Selection item=ISM_HeizungBetriebsartHeizkreis icon="radiator" mappings=[0="Auto", 1="Komfort", 2="Stand By", 3="Eco", 4="Frost Schutz"]
|
||||
Text item=ISM_HeizungStatusFlamme icon="fire"
|
||||
Text item=ISM_HeizungKesseltemperatur icon="temperature"
|
||||
Text item=ISM_HeizungRuecklauftemperatur icon="temperature_cold"
|
||||
Setpoint item=ISM_HeizungSollwertverschiebung icon="radiator" minValue=-5 maxValue=5 step=1
|
||||
}
|
||||
Frame label="Wasser"
|
||||
{
|
||||
Text item=ISM_HeizungWarmwassertemperatur icon="temperature_hot"
|
||||
Setpoint item=ISM_HeizungSollwertWarmwasser icon="temperature" minValue=40 maxValue=60 step=1
|
||||
Selection item=ISM_HeizungBetriebsartWarmwasser icon="faucet" mappings=[0="Auto", 1="Legionellen Schutz", 2="Normal", 3="Eco", 4="Frost Schutz"]
|
||||
}
|
||||
Frame label="Lüftung"
|
||||
{
|
||||
Text item=ISM_LueftungStoerung icon="siren"
|
||||
Selection item=ISM_LueftungBetriebsart icon="fan" mappings=[0="Auto", 1="Minimum", 2="Reduziert", 3="Normal", 4="Intensiv"]
|
||||
Text item=ISM_LueftungLueftungsstufe icon="qualityofservice"
|
||||
Text item=ISM_LueftungFilterwarnung icon="siren"
|
||||
Text item=ISM_LueftungAblufttemperatur icon="temperature_hot"
|
||||
Text item=ISM_LueftungZulufttemperatur icon="temperature_cold"
|
||||
Text item=ISM_LueftungLuftdurchsatzZuluft icon="flow"
|
||||
Text item=ISM_LueftungLuftdurchsatzAbluft icon="flow"
|
||||
}
|
||||
|
||||
_HVACContrMode.map_
|
||||
|
||||
0=Auto
|
||||
1=Heizen
|
||||
2=Aufwärmen
|
||||
3=Abkühlen
|
||||
4=Nächtliche Reinigung
|
||||
5=Vorkühlen
|
||||
6=Aus
|
||||
7=Test
|
||||
8=Notfall Heizen
|
||||
9=Nur Lüften
|
||||
10=Freies Kühlen
|
||||
11=Eis
|
||||
12=Maximum Heizen
|
||||
13=Eco Heiz-/Kühlmodus
|
||||
14=Entfeuchten
|
||||
15=Kalibriermodus
|
||||
16=Notfall Kühlmodus
|
||||
17=Emergency Dampfmodus
|
||||
20=Reserviert
|
||||
NULL=Undefiniert
|
||||
|
||||
_Result_
|
||||
<img src="doc/Sitemap-Example.png" width="800" height="600">
|
||||
BIN
bundles/org.openhab.binding.ism8/doc/Sitemap-Example.png
Normal file
BIN
bundles/org.openhab.binding.ism8/doc/Sitemap-Example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
17
bundles/org.openhab.binding.ism8/pom.xml
Normal file
17
bundles/org.openhab.binding.ism8/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.ism8</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Ism8 Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.ism8-${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-ism8" description="ism8 Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ism8/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ism8BindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ism8BindingConstants {
|
||||
// Binding ID
|
||||
private static final String BINDING_ID = "ism8";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
|
||||
/**
|
||||
* Defines the thing type UID
|
||||
*
|
||||
*/
|
||||
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
|
||||
|
||||
// Thing Configuration parameters
|
||||
|
||||
/**
|
||||
* The port number configuration parameter
|
||||
*
|
||||
*/
|
||||
public static final String PORT_NUMBER = "portNumber";
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link ism8Configuration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ism8Configuration {
|
||||
private int portNumber;
|
||||
|
||||
/**
|
||||
* Gets the port number for the ISM8.
|
||||
*
|
||||
*/
|
||||
public int getPortNumber() {
|
||||
return portNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ism8.server.DataPointChangedEvent;
|
||||
import org.openhab.binding.ism8.server.IDataPoint;
|
||||
import org.openhab.binding.ism8.server.IDataPointChangeListener;
|
||||
import org.openhab.binding.ism8.server.Server;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
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.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link Ism8Handler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Ism8Handler extends BaseThingHandler implements IDataPointChangeListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(Ism8Handler.class);
|
||||
|
||||
private @Nullable Ism8Configuration config;
|
||||
private @Nullable Server server;
|
||||
|
||||
public Ism8Handler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
this.logger.debug("Ism8: Handle command = {} {}", channelUID.getId(), command);
|
||||
Channel channel = getThing().getChannel(channelUID);
|
||||
Server svr = this.server;
|
||||
if (channel != null && svr != null) {
|
||||
if (channel.getConfiguration().containsKey("id")) {
|
||||
IDataPoint dataPoint = null;
|
||||
try {
|
||||
int id = Integer.parseInt(channel.getConfiguration().get("id").toString());
|
||||
this.logger.debug("Channel '{}' writting into ID '{}'", channel.getUID().getId(), id);
|
||||
this.updateState(channelUID, new QuantityType<>(command.toString()));
|
||||
dataPoint = svr.getDataPoint(id);
|
||||
} catch (NumberFormatException e) {
|
||||
this.logger.debug("Updating State of ISM DataPoint '{}' failed. '{}'", channel.getConfiguration(),
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
if (dataPoint != null) {
|
||||
try {
|
||||
svr.sendData(dataPoint.createWriteData(command));
|
||||
} catch (IOException e) {
|
||||
this.logger.debug("Writting to ISM DataPoint '{}' failed. '{}'", dataPoint.getId(),
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Server svr = this.server;
|
||||
if (svr != null) {
|
||||
svr.stopServerThread();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
this.config = getConfigAs(Ism8Configuration.class);
|
||||
Ism8Configuration cfg = this.config;
|
||||
final String uid = this.getThing().getUID().getAsString();
|
||||
Server svr = new Server(cfg.getPortNumber(), uid);
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
if (channel.getConfiguration().containsKey("id") && channel.getConfiguration().containsKey("type")) {
|
||||
try {
|
||||
int id = Integer.parseInt(channel.getConfiguration().get("id").toString());
|
||||
String type = channel.getConfiguration().get("type").toString();
|
||||
String description = channel.getLabel();
|
||||
if (type != null && description != null) {
|
||||
svr.addDataPoint(id, type, description);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
this.logger.warn(
|
||||
"Ism8 initialize: ID couldn't be converted correctly. Check the configuration of channel {}. Cfg={}",
|
||||
channel.getLabel(), channel.getConfiguration());
|
||||
}
|
||||
} else {
|
||||
this.logger.debug("Ism8: ID or type missing - Channel={} Cfg={}", channel.getLabel(),
|
||||
channel.getConfiguration());
|
||||
}
|
||||
this.logger.debug("Ism8: Channel={}", channel.getConfiguration().toString());
|
||||
}
|
||||
|
||||
this.updateStatus(ThingStatus.UNKNOWN);
|
||||
svr.addDataPointChangeListener(this);
|
||||
scheduler.execute(svr::start);
|
||||
this.server = svr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataPointChanged(@Nullable DataPointChangedEvent e) {
|
||||
if (e != null) {
|
||||
IDataPoint dataPoint = e.getDataPoint();
|
||||
if (dataPoint != null) {
|
||||
this.logger.debug("Ism8: dataPointChanged {}", dataPoint.toString());
|
||||
this.updateDataPoint(dataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionStatusChanged(ThingStatus status) {
|
||||
this.updateStatus(status);
|
||||
}
|
||||
|
||||
private void updateDataPoint(IDataPoint dataPoint) {
|
||||
this.updateStatus(ThingStatus.ONLINE);
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
if (channel.getConfiguration().containsKey("id")) {
|
||||
try {
|
||||
int id = Integer.parseInt(channel.getConfiguration().get("id").toString());
|
||||
if (id == dataPoint.getId()) {
|
||||
this.logger.debug("Ism8 updateDataPoint ID:{} {}", dataPoint.getId(), dataPoint.getValueText());
|
||||
Object val = dataPoint.getValueObject();
|
||||
if (val != null) {
|
||||
updateState(channel.getUID(), new QuantityType<>(val.toString()));
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
this.logger.warn(
|
||||
"Ism8 updateDataPoint: ID couldn't be converted correctly. Check the configuration of channel {}. {}",
|
||||
channel.getLabel(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.internal;
|
||||
|
||||
import static org.openhab.binding.ism8.internal.Ism8BindingConstants.THING_TYPE_DEVICE;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link ism8HandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.ism8")
|
||||
public class Ism8HandlerFactory extends BaseThingHandlerFactory {
|
||||
public static final Collection<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Arrays.asList(THING_TYPE_DEVICE);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
if (THING_TYPE_DEVICE.equals(thingTypeUID)) {
|
||||
return new Ism8Handler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointBase} is the base class for all data points
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class DataPointBase<@Nullable T> implements IDataPoint {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointBase.class);
|
||||
|
||||
private final int id;
|
||||
private final String knxDataType;
|
||||
private final String description;
|
||||
private T value;
|
||||
private String unit = "";
|
||||
|
||||
protected DataPointBase(int id, String knxDataType, String description) {
|
||||
this.id = id;
|
||||
this.knxDataType = knxDataType;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKnxDataType() {
|
||||
return this.knxDataType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the data-point
|
||||
*
|
||||
*/
|
||||
public T getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the data-point
|
||||
*
|
||||
*/
|
||||
public void setValue(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getValueObject() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract String getValueText();
|
||||
|
||||
@Override
|
||||
public String getUnit() {
|
||||
return this.unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unit of the data-point.
|
||||
*
|
||||
*/
|
||||
public void setUnit(String value) {
|
||||
this.unit = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void processData(byte[] data);
|
||||
|
||||
@Override
|
||||
public byte[] createWriteData(Object value) {
|
||||
logger.debug("Convert into byte array '{}'", value);
|
||||
byte[] val = this.convertWriteValue(value);
|
||||
byte length = (byte) (val.length + 20);
|
||||
ByteBuffer list = ByteBuffer.allocate(length);
|
||||
list.put(KnxNetFrame.KNX_HEADER);
|
||||
list.put(KnxNetFrame.CONNECTION_HEADER);
|
||||
list.put((byte) 0xF0); // Main Service
|
||||
list.put(SubServiceType.DATAPOINT_VALUE_WRITE); // Sub Service
|
||||
byte low = (byte) (this.getId() & 0xFF);
|
||||
byte high = (byte) ((this.getId() & 0xFF) / 256);
|
||||
list.put(high);
|
||||
list.put(low); // Start DataPoint
|
||||
list.put((byte) 0x00); // Amount DataPoints (high-byte)
|
||||
list.put((byte) 0x01); // Amount DataPoints (low-byte)
|
||||
list.put(high);
|
||||
list.put(low); // Write: ID of DataPoint
|
||||
list.put((byte) 0x00); // State
|
||||
list.put((byte) val.length); // Length of Data
|
||||
list.put(val); // Data Value
|
||||
list.put(5, length);
|
||||
return list.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataPoint %d=%s", this.getId(), this.getValueText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the value to be written into a data array of bytes.
|
||||
*
|
||||
*/
|
||||
protected abstract byte[] convertWriteValue(Object value);
|
||||
|
||||
/**
|
||||
* Checks the data to be processed.
|
||||
*
|
||||
*/
|
||||
protected boolean checkProcessData(byte[] data) {
|
||||
if (data.length < 4) {
|
||||
logger.debug("DataPoint-ProcessData: Data size too small ({}).", data.length);
|
||||
return false;
|
||||
}
|
||||
|
||||
int dataPointId = Byte.toUnsignedInt(data[0]) * 256 + Byte.toUnsignedInt(data[1]);
|
||||
if (dataPointId != this.getId()) {
|
||||
logger.debug("DataPoint-ProcessData: Data contains the wrong ID ({}/{}).", dataPointId, this.getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = data[3];
|
||||
int expectedLength = length + 4;
|
||||
if (length <= 0 && expectedLength != data.length) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong ({}/{}).", data.length, expectedLength);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointBool} is the data points for boolean values
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointBool extends DataPointBase<@Nullable Boolean> {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointBool.class);
|
||||
|
||||
public DataPointBool(int id, String knxDataType, String description) {
|
||||
super(id, knxDataType, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueText() {
|
||||
return this.getValue() ? "True" : "False";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getValueObject() {
|
||||
return this.getValue() ? "1" : "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processData(byte[] data) {
|
||||
if (this.checkProcessData(data)) {
|
||||
if (data[3] != 1 && data.length <= 4) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong for this type({}/1).", data[3]);
|
||||
return;
|
||||
}
|
||||
this.setValue((data[4] & 0x1) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] convertWriteValue(Object value) {
|
||||
String valueText = value.toString().toLowerCase();
|
||||
if (valueText.equalsIgnoreCase("true") || valueText.equalsIgnoreCase("1")) {
|
||||
this.setValue(true);
|
||||
return new byte[] { 0x01 };
|
||||
}
|
||||
this.setValue(false);
|
||||
return new byte[] { 0x00 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointByteValue} is the data points for byte values
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointByteValue extends DataPointBase<@Nullable Byte> {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointByteValue.class);
|
||||
|
||||
public DataPointByteValue(int id, String knxDataType, String description) {
|
||||
super(id, knxDataType, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueText() {
|
||||
Object val = this.getValue();
|
||||
return val != null ? val.toString() : "0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processData(byte[] data) {
|
||||
if (this.checkProcessData(data)) {
|
||||
if (data[3] != 1 && data.length <= 4) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong for this type({}/1).", data[3]);
|
||||
return;
|
||||
}
|
||||
this.setValue(data[4]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] convertWriteValue(Object value) {
|
||||
this.setValue(Byte.parseByte(value.toString()));
|
||||
return new byte[] { this.getValue() };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link DataPointChangedEvent} is an event container for data point changes
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointChangedEvent {
|
||||
protected IDataPoint dataPoint;
|
||||
|
||||
public DataPointChangedEvent(Object source, IDataPoint dataPoint) {
|
||||
this.dataPoint = dataPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data-point of the event.
|
||||
*
|
||||
*/
|
||||
@Nullable
|
||||
public IDataPoint getDataPoint() {
|
||||
return this.dataPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link DataPointFactory} creates the data points depending on the types
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointFactory {
|
||||
|
||||
/**
|
||||
* Creates the concrete data-point based on the type.
|
||||
*
|
||||
*/
|
||||
@Nullable
|
||||
public static IDataPoint createDataPoint(int id, String knxType, String description) {
|
||||
IDataPoint dataPoint = null;
|
||||
switch (knxType) {
|
||||
case "1.001":
|
||||
case "1.002":
|
||||
case "1.003":
|
||||
case "1.009":
|
||||
dataPoint = new DataPointBool(id, knxType, description);
|
||||
break;
|
||||
case "5.001":
|
||||
dataPoint = new DataPointScaling(id, knxType, description);
|
||||
break;
|
||||
case "9.001":
|
||||
case "9.002":
|
||||
case "9.006":
|
||||
dataPoint = new DataPointValue(id, knxType, description);
|
||||
break;
|
||||
case "13.002":
|
||||
dataPoint = new DataPointLongValue(id, knxType, description);
|
||||
break;
|
||||
case "20.102":
|
||||
case "20.103":
|
||||
case "20.105":
|
||||
dataPoint = new DataPointByteValue(id, knxType, description);
|
||||
break;
|
||||
}
|
||||
return dataPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointLongValue} is the data points for long values
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointLongValue extends DataPointBase<@Nullable Double> {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointLongValue.class);
|
||||
private final float factor;
|
||||
private final String outputFormat;
|
||||
|
||||
public DataPointLongValue(int id, String knxDataType, String description) {
|
||||
super(id, knxDataType, description);
|
||||
|
||||
if (knxDataType.equals("13.002")) {
|
||||
this.setUnit("m³/h");
|
||||
this.factor = 0.0001f;
|
||||
this.outputFormat = "%.1f";
|
||||
} else {
|
||||
this.setUnit("");
|
||||
this.factor = 1.0f;
|
||||
this.outputFormat = "%.1f";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueText() {
|
||||
return String.format(this.outputFormat, this.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processData(byte[] data) {
|
||||
if (this.checkProcessData(data)) {
|
||||
if (data[3] != 4 && data.length <= 7) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong for this type({}/4).", data[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
int rawValue = Byte.toUnsignedInt(data[4]) * 0x1000000 + Byte.toUnsignedInt(data[5]) * 0x10000
|
||||
+ Byte.toUnsignedInt(data[6]) * 0x100 + Byte.toUnsignedInt(data[7]);
|
||||
this.setValue((double) rawValue * this.factor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] convertWriteValue(Object value) {
|
||||
ByteBuffer data = ByteBuffer.allocate(4);
|
||||
double dblVal;
|
||||
try {
|
||||
dblVal = Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
dblVal = 0.0;
|
||||
}
|
||||
|
||||
int val = (int) (dblVal / this.factor);
|
||||
data.put((byte) (val & 0xFF));
|
||||
val = (val & 0xFF) / 256;
|
||||
data.put((byte) (val & 0xFF));
|
||||
val = (val & 0xFF) / 256;
|
||||
data.put((byte) (val & 0xFF));
|
||||
val = (val & 0xFF) / 256;
|
||||
data.put((byte) (val & 0xFF));
|
||||
return data.array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointScaling} is the data points for scaling values
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointScaling extends DataPointBase<@Nullable Double> {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointScaling.class);
|
||||
private String outputFormat = "";
|
||||
|
||||
public DataPointScaling(int id, String knxDataType, String description) {
|
||||
super(id, knxDataType, description);
|
||||
this.setUnit("%");
|
||||
this.outputFormat = "%.1f";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueText() {
|
||||
return String.format(this.outputFormat, this.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processData(byte[] data) {
|
||||
if (this.checkProcessData(data)) {
|
||||
if (data[3] != 1 && data.length <= 4) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong for this type({}/1).", data[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setValue((Byte.toUnsignedInt(data[4]) * 100.0) / 255.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] convertWriteValue(Object value) {
|
||||
this.setValue(Math.max(Math.min(Double.parseDouble(value.toString()), 100), 0));
|
||||
Object rawVal = this.getValue();
|
||||
double rawValResult = rawVal != null ? (Double) rawVal : 0.0;
|
||||
byte val = (byte) (rawValResult / 100.0 * 255.0);
|
||||
return new byte[] { val };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DataPointValue} is the data points for double values
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DataPointValue extends DataPointBase<@Nullable Double> {
|
||||
private final Logger logger = LoggerFactory.getLogger(DataPointValue.class);
|
||||
private float factor;
|
||||
private String outputFormat = "";
|
||||
|
||||
public DataPointValue(int id, String knxDataType, String description) {
|
||||
super(id, knxDataType, description);
|
||||
this.factor = 0.0f;
|
||||
if (knxDataType.equals("9.001")) {
|
||||
this.setUnit("°C");
|
||||
this.factor = 0.01f;
|
||||
this.outputFormat = "%.1f";
|
||||
} else if (knxDataType.equals("9.002")) {
|
||||
this.setUnit("°K");
|
||||
this.factor = 0.01f;
|
||||
this.outputFormat = "%.1f";
|
||||
} else if (knxDataType.equals("9.006")) {
|
||||
this.setUnit("Bar");
|
||||
this.factor = 0.0000001f;
|
||||
this.outputFormat = "%.2f";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueText() {
|
||||
return String.format(this.outputFormat, this.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processData(byte[] data) {
|
||||
if (this.checkProcessData(data)) {
|
||||
if (data[3] != 2 && data.length <= 5) {
|
||||
logger.debug("DataPoint-ProcessData: Data size wrong for this type({}/2).", data[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
int rawValue = Byte.toUnsignedInt(data[4]) * 256 + Byte.toUnsignedInt(data[5]);
|
||||
boolean inverted = (rawValue & 0x8000) > 0;
|
||||
double exp = (rawValue & 0x7800) / 2048;
|
||||
rawValue = rawValue & 0x07FF;
|
||||
exp = Math.pow(2, exp);
|
||||
if (inverted) {
|
||||
rawValue = rawValue - 1;
|
||||
rawValue = rawValue ^ 0x7FF;
|
||||
this.setValue(rawValue * exp * this.factor * (-1.0));
|
||||
} else {
|
||||
this.setValue(rawValue * exp * this.factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] convertWriteValue(Object value) {
|
||||
ByteBuffer data = ByteBuffer.allocate(2);
|
||||
this.setValue(Double.parseDouble(value.toString()));
|
||||
Object rawVal = this.getValue();
|
||||
double rawValResult = rawVal != null ? (Double) rawVal : 0.0;
|
||||
double dblValue = rawValResult / this.factor;
|
||||
boolean inverted = dblValue < 0.0;
|
||||
int exp = 0;
|
||||
dblValue = Math.abs(dblValue);
|
||||
while (dblValue > 2047.0) {
|
||||
dblValue = dblValue / 2.0;
|
||||
exp++;
|
||||
}
|
||||
int val = (int) dblValue;
|
||||
if (inverted) {
|
||||
val = val ^ 0x7FF;
|
||||
val = val + 1;
|
||||
val |= 0x8000;
|
||||
}
|
||||
|
||||
val |= exp * 2048;
|
||||
byte low = (byte) (val & 0xFF);
|
||||
val = (val & 0xFF00) / 256;
|
||||
byte high = (byte) (val & 0xFF);
|
||||
data.put(high);
|
||||
data.put(low);
|
||||
return data.array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link IDataPoint} is the interface for all data points
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface IDataPoint {
|
||||
int getId();
|
||||
|
||||
/**
|
||||
* Gets the unit of the data-point.
|
||||
*
|
||||
*/
|
||||
String getUnit();
|
||||
|
||||
/**
|
||||
* Gets the type of the data-point.
|
||||
*
|
||||
*/
|
||||
String getKnxDataType();
|
||||
|
||||
/**
|
||||
* Gets the description of the data-point.
|
||||
*
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Gets the value as formated text.
|
||||
*
|
||||
*/
|
||||
String getValueText();
|
||||
|
||||
/**
|
||||
* Gets the value object.
|
||||
*
|
||||
*/
|
||||
@Nullable
|
||||
Object getValueObject();
|
||||
|
||||
/**
|
||||
* Processes the data received
|
||||
*
|
||||
*/
|
||||
void processData(byte[] data);
|
||||
|
||||
/**
|
||||
* Creates the data to be written
|
||||
*
|
||||
*/
|
||||
byte[] createWriteData(Object value);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
|
||||
/**
|
||||
* The {@link IDataPointChangeListener} is in interface for a data point changed consumer
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface IDataPointChangeListener {
|
||||
/**
|
||||
* This method will be called in case a data-point has changed.
|
||||
*
|
||||
*/
|
||||
void dataPointChanged(DataPointChangedEvent e);
|
||||
|
||||
/**
|
||||
* This method will be called in case the connection status has changed.
|
||||
*
|
||||
*/
|
||||
void connectionStatusChanged(ThingStatus status);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link KnxNetFrame} is used for handling the received KNX.Net frames
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class KnxNetFrame {
|
||||
public static byte[] KNX_HEADER = new byte[6];
|
||||
public static byte[] CONNECTION_HEADER = new byte[4];
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(KnxNetFrame.class);
|
||||
private ArrayList<SetDatapointValueMessage> valueMessages = new ArrayList<SetDatapointValueMessage>();
|
||||
private byte mainService;
|
||||
private byte subService = SubServiceType.SET_DATAPOINT_VALUE_REQUEST;
|
||||
private int startDataPoint;
|
||||
|
||||
static {
|
||||
KNX_HEADER[0] = (byte) 0x06; // Header size
|
||||
KNX_HEADER[1] = (byte) 0x20; // Version (2.0)
|
||||
KNX_HEADER[2] = (byte) 0xF0; // Object server request
|
||||
KNX_HEADER[3] = (byte) 0x80; // Object server request
|
||||
KNX_HEADER[4] = (byte) 0x00; // Frame size
|
||||
KNX_HEADER[5] = (byte) 0x00; // Frame size
|
||||
|
||||
CONNECTION_HEADER[0] = (byte) 0x04; // Structure length
|
||||
CONNECTION_HEADER[1] = (byte) 0x00; // Reserved
|
||||
CONNECTION_HEADER[2] = (byte) 0x00; // Reserved
|
||||
CONNECTION_HEADER[3] = (byte) 0x00; // Reserved
|
||||
}
|
||||
|
||||
public KnxNetFrame() {
|
||||
this.valueMessages = new ArrayList<SetDatapointValueMessage>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the main service of the KNX frame
|
||||
*
|
||||
*/
|
||||
public byte getMainService() {
|
||||
return this.mainService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the main service of the KNX frame
|
||||
*
|
||||
*/
|
||||
public void setMainService(byte value) {
|
||||
this.mainService = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sub service of the KNX frame
|
||||
*
|
||||
*/
|
||||
public byte getSubService() {
|
||||
return this.subService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sub service of the KNX frame
|
||||
*
|
||||
*/
|
||||
public void setSubService(byte value) {
|
||||
this.subService = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start data-point of the KNX frame
|
||||
*
|
||||
*/
|
||||
public int getStartDataPoint() {
|
||||
return this.startDataPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the start data-point of the KNX frame
|
||||
*
|
||||
*/
|
||||
public void setStartDataPoint(int value) {
|
||||
this.startDataPoint = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value messages of the KNX frame
|
||||
*
|
||||
*/
|
||||
public SetDatapointValueMessage[] getValueMessages() {
|
||||
SetDatapointValueMessage[] result = new SetDatapointValueMessage[this.valueMessages.size()];
|
||||
this.valueMessages.toArray(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a KNX frame based on the data-array
|
||||
*
|
||||
*/
|
||||
@Nullable
|
||||
public static KnxNetFrame createKnxNetPackage(byte[] data, int amount) {
|
||||
if (data.length < 16 || amount < 16 || data.length < amount) {
|
||||
LOGGER.debug("Length of the data too short for a KNXnet/IP package ({}).", data.length);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data[0] != KNX_HEADER[0] || data[1] != KNX_HEADER[1] || data[2] != KNX_HEADER[2]
|
||||
|| data[3] != KNX_HEADER[3]) {
|
||||
LOGGER.debug("Incorrect KNXnet/IP header.");
|
||||
return null;
|
||||
}
|
||||
|
||||
int frameSize = Byte.toUnsignedInt(data[4]) * 256 + Byte.toUnsignedInt(data[5]);
|
||||
if (frameSize != amount) {
|
||||
LOGGER.debug("CreateKnxNetPackage: Error TelegrammLength/FrameSize missmatch. ({}/{})", data.length,
|
||||
frameSize);
|
||||
return null;
|
||||
}
|
||||
|
||||
KnxNetFrame frame = new KnxNetFrame();
|
||||
frame.setMainService(data[10]);
|
||||
if (frame.getMainService() != (byte) 0xF0) {
|
||||
LOGGER.debug("CreateKnxNetPackage: Main-Service not supported. ({})", frame.getMainService());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data[11] == (byte) 0x06) {
|
||||
frame.setSubService(SubServiceType.SET_DATAPOINT_VALUE_REQUEST);
|
||||
} else if (data[11] == (byte) 0x86) {
|
||||
frame.setSubService(SubServiceType.SET_DATAPOINT_VALUE_RESULT);
|
||||
} else if (data[11] == (byte) 0xC1) {
|
||||
frame.setSubService(SubServiceType.DATAPOINT_VALUE_WRITE);
|
||||
} else if (data[11] == (byte) 0xD0) {
|
||||
frame.setSubService(SubServiceType.REQUEST_ALL_DATAPOINTS);
|
||||
} else {
|
||||
LOGGER.debug("CreateKnxNetPackage: Sub-Service not supported. ({})", frame.getSubService());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (frame.getSubService() == SubServiceType.SET_DATAPOINT_VALUE_REQUEST) {
|
||||
frame.setStartDataPoint(Byte.toUnsignedInt(data[12]) * 256 + Byte.toUnsignedInt(data[13]));
|
||||
int numberOfDatapoints = Byte.toUnsignedInt(data[14]) * 256 + Byte.toUnsignedInt(data[15]);
|
||||
int offset = 16;
|
||||
|
||||
ByteBuffer list = ByteBuffer.allocate(data.length);
|
||||
list.put(data);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < numberOfDatapoints; i++) {
|
||||
byte[] msgData = new byte[amount - offset];
|
||||
list.position(offset);
|
||||
list.get(msgData);
|
||||
SetDatapointValueMessage msg = new SetDatapointValueMessage(msgData);
|
||||
offset = offset + msg.getLength() + 4;
|
||||
frame.valueMessages.add(msg);
|
||||
}
|
||||
return frame;
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Error creating KnxNetPackage. {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the answer of the KNX frame
|
||||
*
|
||||
*/
|
||||
public byte[] createFrameAnswer() {
|
||||
ByteBuffer answer = ByteBuffer.allocate(17);
|
||||
if (this.getSubService() == SubServiceType.SET_DATAPOINT_VALUE_REQUEST) {
|
||||
answer.put(KNX_HEADER);
|
||||
answer.put(5, (byte) 0x11); // static size (17 bytes)
|
||||
answer.put(CONNECTION_HEADER);
|
||||
|
||||
answer.put(this.getMainService());
|
||||
answer.put(SubServiceType.SET_DATAPOINT_VALUE_RESULT);
|
||||
byte low = (byte) (this.getStartDataPoint() & (byte) 0xFF);
|
||||
byte high = (byte) ((this.getStartDataPoint() & (byte) 0xFF) / 256);
|
||||
answer.put(high);
|
||||
answer.put(low);
|
||||
answer.put((byte) 0);
|
||||
answer.put((byte) 0);
|
||||
answer.put((byte) 0);
|
||||
}
|
||||
return answer.array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ism8Server} is responsible for listening to the Ism8 information
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Server extends Thread {
|
||||
private final Logger logger = LoggerFactory.getLogger(Server.class);
|
||||
|
||||
private int port;
|
||||
private int startRetries;
|
||||
private boolean connected;
|
||||
private HashMap<Integer, IDataPoint> dataPoints = new HashMap<>();
|
||||
private @Nullable ServerSocket serverSocket = null;
|
||||
private @Nullable Socket client;
|
||||
private @Nullable IDataPointChangeListener changeListener;
|
||||
|
||||
public Server(int port, String uid) {
|
||||
super("OH-binding-" + uid);
|
||||
setDaemon(true);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port of the server
|
||||
*
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connection state of the server
|
||||
*
|
||||
*/
|
||||
public boolean getConnected() {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data points of the server
|
||||
*
|
||||
*/
|
||||
public @Nullable IDataPoint getDataPoint(int id) {
|
||||
IDataPoint dataPoint = null;
|
||||
if (this.dataPoints.containsKey(id)) {
|
||||
dataPoint = this.dataPoints.get(id);
|
||||
}
|
||||
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
this.startRetries = 0;
|
||||
while (!this.isInterrupted()) {
|
||||
try {
|
||||
this.handleCommunication();
|
||||
if (this.startRetries > 10) {
|
||||
Thread.sleep(6000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Thread interrupted");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
this.startRetries++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server
|
||||
*
|
||||
*/
|
||||
public void stopServerThread() {
|
||||
this.interrupt();
|
||||
this.stopServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data-point change listener to the server
|
||||
*
|
||||
*/
|
||||
public void addDataPointChangeListener(IDataPointChangeListener listener) {
|
||||
if (this.changeListener == null) {
|
||||
this.changeListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a data-point to the server
|
||||
*
|
||||
*/
|
||||
public void addDataPoint(int id, String knxType, String description) {
|
||||
if (this.dataPoints.containsKey(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IDataPoint dp = DataPointFactory.createDataPoint(id, knxType, description);
|
||||
if (dp != null) {
|
||||
this.dataPoints.put(new Integer(id), dp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the ISM8 partner
|
||||
*
|
||||
*/
|
||||
public void sendData(byte[] data) throws IOException {
|
||||
Socket clientSocket = this.client;
|
||||
if (clientSocket != null && clientSocket.isConnected() && data.length > 0) {
|
||||
OutputStream stream = clientSocket.getOutputStream();
|
||||
stream.write(data);
|
||||
stream.flush();
|
||||
logger.debug("Data sent: {}", this.printBytes(data));
|
||||
}
|
||||
}
|
||||
|
||||
private void stopServer() {
|
||||
logger.debug("Stop Ism8 server.");
|
||||
try {
|
||||
ServerSocket serverSock = this.serverSocket;
|
||||
if (serverSock != null) {
|
||||
serverSock.close();
|
||||
}
|
||||
|
||||
Socket clientSocket = this.client;
|
||||
if (clientSocket != null) {
|
||||
clientSocket.close();
|
||||
this.client = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error stopping Communication. {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommunication() {
|
||||
try {
|
||||
logger.debug("Waiting for connection in port {}.", this.getPort());
|
||||
IDataPointChangeListener listener = this.changeListener;
|
||||
if (listener != null) {
|
||||
listener.connectionStatusChanged(ThingStatus.OFFLINE);
|
||||
}
|
||||
ServerSocket serverSock = new ServerSocket(this.getPort());
|
||||
this.serverSocket = serverSock;
|
||||
Socket clientSocket = serverSock.accept();
|
||||
this.client = clientSocket;
|
||||
logger.debug("Connection from Partner established {}", clientSocket.getRemoteSocketAddress());
|
||||
if (listener != null) {
|
||||
listener.connectionStatusChanged(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
this.startRetries = 0;
|
||||
this.sendUpdateCommand();
|
||||
while (!this.isInterrupted()) {
|
||||
byte[] bytes = getBytesFromInputStream(clientSocket.getInputStream());
|
||||
ArrayList<byte[]> packages = this.getPackages(bytes);
|
||||
|
||||
for (byte[] pack : packages) {
|
||||
logger.debug("Data received: {}", this.printBytes(pack));
|
||||
KnxNetFrame frame = KnxNetFrame.createKnxNetPackage(pack, pack.length);
|
||||
if (frame != null) {
|
||||
byte[] answer = frame.createFrameAnswer();
|
||||
if (answer.length > 0) {
|
||||
this.sendData(answer);
|
||||
}
|
||||
|
||||
for (SetDatapointValueMessage message : frame.getValueMessages()) {
|
||||
logger.debug("Message received: {} {}", message.getId(),
|
||||
this.printBytes(message.getData()));
|
||||
|
||||
IDataPoint dataPoint = this.getDataPoint(message.getId());
|
||||
if (dataPoint != null) {
|
||||
dataPoint.processData(message.getData());
|
||||
logger.debug("{} {}", dataPoint.getDescription(), dataPoint.getValueText());
|
||||
if (listener != null) {
|
||||
listener.dataPointChanged(new DataPointChangedEvent(this, dataPoint));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error handle client data stream. {}", e.getMessage());
|
||||
this.stopServer();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendUpdateCommand() throws IOException {
|
||||
byte[] data = new byte[] { (byte) 0x06, (byte) 0x20, (byte) 0xF0, (byte) 0x80, (byte) 0x00, (byte) 0x16,
|
||||
(byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xF0, (byte) 0xD0 };
|
||||
this.sendData(data);
|
||||
}
|
||||
|
||||
private byte[] getBytesFromInputStream(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[0xFF];
|
||||
int len = is.read(buffer);
|
||||
os.write(buffer, 0, len);
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
private ArrayList<byte[]> getPackages(byte[] data) {
|
||||
ArrayList<byte[]> result = new ArrayList<byte[]>();
|
||||
if (data.length >= 0) {
|
||||
ByteBuffer list = ByteBuffer.allocate(data.length);
|
||||
list.put(data);
|
||||
int start = -1;
|
||||
for (int i = 0; i < data.length - 4; i++) {
|
||||
if (list.get(i + 0) == (byte) 0x06 && list.get(i + 1) == (byte) 0x20 && list.get(i + 2) == (byte) 0xF0
|
||||
&& list.get(i + 3) == (byte) 0x80) {
|
||||
if (start >= 0) {
|
||||
byte[] pkgData = new byte[i - start];
|
||||
list.position(start);
|
||||
list.get(pkgData);
|
||||
result.add(pkgData);
|
||||
}
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start >= 0) {
|
||||
byte[] pkgData = new byte[data.length - start];
|
||||
list.position(start);
|
||||
list.get(pkgData);
|
||||
result.add(pkgData);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String printBytes(byte[] bytes) {
|
||||
return HexUtils.bytesToHex(bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SetDatapointValueMessage} is a message within the KNX frame containing
|
||||
* the information of one data point
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SetDatapointValueMessage {
|
||||
private int id;
|
||||
private byte command;
|
||||
private byte[] data = new byte[0];
|
||||
private byte length;
|
||||
|
||||
public SetDatapointValueMessage() {
|
||||
}
|
||||
|
||||
public SetDatapointValueMessage(byte[] data) throws IllegalArgumentException {
|
||||
if (data.length < 5) {
|
||||
throw new IllegalArgumentException("Data size too small for a SetDatapointValueMessage.");
|
||||
}
|
||||
|
||||
this.setId(Byte.toUnsignedInt(data[0]) * 256 + Byte.toUnsignedInt(data[1]));
|
||||
this.setCommand(data[2]);
|
||||
this.setLength(data[3]);
|
||||
if (data.length < (this.getLength() + 4)) {
|
||||
throw new IllegalArgumentException("Data size incorrect (" + data.length + "/" + this.getLength() + ").");
|
||||
}
|
||||
|
||||
ByteBuffer list = ByteBuffer.allocate(this.getLength() + 4);
|
||||
list.put(data, 0, this.getLength() + 4);
|
||||
this.setData(list.array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the data-point message
|
||||
*
|
||||
*/
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the data-point message
|
||||
*
|
||||
*/
|
||||
public void setId(int value) {
|
||||
this.id = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command of the data-point message
|
||||
*
|
||||
*/
|
||||
public byte getCommand() {
|
||||
return this.command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command of the data-point message
|
||||
*
|
||||
*/
|
||||
public void setCommand(byte value) {
|
||||
this.command = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the data-point message
|
||||
*
|
||||
*/
|
||||
public byte getLength() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the data-point message
|
||||
*
|
||||
*/
|
||||
public void setLength(byte value) {
|
||||
this.length = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data array of the data-point message
|
||||
*
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data array of the data-point message
|
||||
*
|
||||
*/
|
||||
public void setData(byte[] value) {
|
||||
this.data = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
package org.openhab.binding.ism8.server;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SubServiceType} contains all supported sub-service types
|
||||
*
|
||||
* @author Hans-Reiner Hoffmann - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SubServiceType {
|
||||
/**
|
||||
* Sub-Service: Set data-point value request.
|
||||
*
|
||||
*/
|
||||
public static final byte SET_DATAPOINT_VALUE_REQUEST = (byte) 0x06;
|
||||
|
||||
/**
|
||||
* Sub-Service: Set data-point value result.
|
||||
*
|
||||
*/
|
||||
public static final byte SET_DATAPOINT_VALUE_RESULT = (byte) 0x86;
|
||||
|
||||
/**
|
||||
* Sub-Service: Write data-point value.
|
||||
*
|
||||
*/
|
||||
public static final byte DATAPOINT_VALUE_WRITE = (byte) 0xC1;
|
||||
|
||||
/**
|
||||
* Sub-Service: Request all data-points.
|
||||
*
|
||||
*/
|
||||
public static final byte REQUEST_ALL_DATAPOINTS = (byte) 0xD0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="ism8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>ISM8 Binding</name>
|
||||
<description>This is the binding for the ISM8 card used for Wolf heating systems or other Wolf eBus devices.</description>
|
||||
<author>Hans-Reiner Hoffmann</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ism8"
|
||||
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">
|
||||
|
||||
<thing-type id="device" extensible="switch, switch-readonly, number, number-readonly">
|
||||
<label>ISM8 Device</label>
|
||||
<description>ISM8 Interface</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="portNumber" type="integer" required="true" min="1" max="65535">
|
||||
<description>Port number of the object server</description>
|
||||
<label>Port</label>
|
||||
<default>12004</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="switch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Digital DataPoint</label>
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>DP ID</label>
|
||||
<description>Put the number of the DataPoint ID to be mapped from the heating sytem.</description>
|
||||
</parameter>
|
||||
<parameter name="type" type="text" required="true">
|
||||
<label>Type</label>
|
||||
<description>Put the KNX-type of the DataPoint (e.g. DPT_Switch / 1.001)</description>
|
||||
<options>
|
||||
<option value="1.001">DPT_Switch</option>
|
||||
<option value="1.002">DPT_Bool</option>
|
||||
<option value="1.003">DPT_Enable</option>
|
||||
<option value="1.009">DPT_OpenClose</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="switch-readonly">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Digital Readonly DataPoint</label>
|
||||
<state readOnly="true"/>
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>DP ID</label>
|
||||
<description>Put the number of the DataPoint ID to be mapped from the heating sytem.</description>
|
||||
</parameter>
|
||||
<parameter name="type" type="text" required="true">
|
||||
<label>Type</label>
|
||||
<description>Put the KNX-type of the DataPoint (e.g. DPT_Switch / 1.001)</description>
|
||||
<options>
|
||||
<option value="1.001">DPT_Switch</option>
|
||||
<option value="1.002">DPT_Bool</option>
|
||||
<option value="1.003">DPT_Enable</option>
|
||||
<option value="1.009">DPT_OpenClose</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="number-readonly">
|
||||
<item-type>number</item-type>
|
||||
<label>Value Readonly DataPoint</label>
|
||||
<state readOnly="true"/>
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>DP ID</label>
|
||||
<description>Put the number of the DataPoint ID to be mapped from the heating sytem.</description>
|
||||
</parameter>
|
||||
<parameter name="type" type="text" required="true">
|
||||
<label>Type</label>
|
||||
<description>Put the KNX-type of the DataPoint (e.g. DPT_Value_Temp / 9.001)</description>
|
||||
<options>
|
||||
<option value="5.001">DPT_Scaling</option>
|
||||
<option value="9.001">DPT_Value_Temp</option>
|
||||
<option value="9.002">DPT_Value_Tempd</option>
|
||||
<option value="9.006">DPT_Value_Pres</option>
|
||||
<option value="13.002">DPT_FlowRate</option>
|
||||
<option value="20.102">DPT_HVACMode</option>
|
||||
<option value="20.103">DPT_DHWMode</option>
|
||||
<option value="20.105">DPT_HVACContrMode</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="number">
|
||||
<item-type>number</item-type>
|
||||
<label>Value DataPoint</label>
|
||||
<config-description>
|
||||
<parameter name="id" type="integer" required="true">
|
||||
<label>DP ID</label>
|
||||
<description>Put the number of the DataPoint ID to be mapped from the heating sytem.</description>
|
||||
</parameter>
|
||||
<parameter name="type" type="text" required="true">
|
||||
<label>Type</label>
|
||||
<description>Put the KNX-type of the DataPoint (e.g. DPT_Value_Temp / 9.001)</description>
|
||||
<options>
|
||||
<option value="9.001">DPT_Value_Temp</option>
|
||||
<option value="20.102">DPT_HVACMode</option>
|
||||
<option value="20.103">DPT_DHWMode</option>
|
||||
<option value="20.105">DPT_HVACContrMode</option>
|
||||
</options>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user