[Qbus] Initial contribution (#9191)

Signed-off-by: Koen Schockaert <ks@qbus.be>
This commit is contained in:
Koen Schockaert 2021-04-11 19:22:37 +02:00 committed by GitHub
parent dae4743fe0
commit 16ffeecb90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 4642 additions and 0 deletions

View File

@ -222,6 +222,7 @@
/bundles/org.openhab.binding.pulseaudio/ @peuter
/bundles/org.openhab.binding.pushbullet/ @hakan42
/bundles/org.openhab.binding.pushover/ @cweitkamp
/bundles/org.openhab.binding.qbus/ @QbusKoen
/bundles/org.openhab.binding.radiothermostat/ @mlobstein
/bundles/org.openhab.binding.regoheatpump/ @crnjan
/bundles/org.openhab.binding.revogi/ @andibraeu

View 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

View File

@ -0,0 +1,110 @@
# Qbus Binding
![Qbus Logo](doc/Logo.JPG)
This binding for [Qbus](https://qbus.be) communicates with all controllers of the Qbus home automation system.
We also host a site which contains a [manual](https://manualoh.schockaert.tk/) where you can find lots of information to set up openHAB with Qbus client and server (for the moment only in Dutch).
The controllers can not communicate directly with openHAB, therefore we developed a client/server application which you must install prior to enable this binding.
More information can be found here:
[Qbus Client/Server](https://github.com/QbusKoen/QbusClientServer-Installer)
With this binding you can control and read almost every output from the Qbus system.
## Supported Things
The following things are supported by the Qbus binding:
- `dimmer`: Dimmer 1 button, 2 button and clc
- `onOff`: Bistabiel, Timer1-3, Interval
- `thermostats`: Thermostats - normal and PID
- `scene`: Scenes
- `co2`: CO2
- `rollershutter`: Rollershutter
- `rollershutter_slats`: Rollerhutter with slats
For now the following Qbus things are not yet supported but will come:
- DMX
- Timer 4 & 5
- HVAC
- Humidity
- Renson
- Duco
- Kinetura
- Energy monitor
- Weather station
## Discovery
The discovery service is not yet implemented but the System Manager III software of Qbus generates things and item files from the programming, which you can use directly in openHAB.
## Bridge configuration
```
Bridge qbus:bridge:CTD001122 [ addr="localhost", sn="001122", port=8447, serverCheck=10 ] {
...
}
```
| Property | Default | Required | Description |
| ------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `addr` | localhost | YES | The ip address of the machine where the Qbus Server runs |
| `sn` | | YES | The serial number of your controller |
| `port` | 8447 | YES | The communication port of the client/server |
| `serverCheck` | 10 | NO | Refresh time - After x minutes there will be a check if server is still running and if client is still connected. If not - reconnect |
## Things configuration
| Thing Type ID | Channel Name | Read only | description |
| --------------------- | ------------- | --------- | ------------------------------------------------------ |
| `onOff` | switch | No | This is the channel for Bistable, Timers and Intervals |
| `dimmer` | brightness | No | This is the channel for Dimmers 1&2 buttons and CLC |
| `scene` | Switch | No | This is the channel for scenes |
| `co2` | co2 | Yes | This is the channel for CO2 sensors |
| `rollershutter` | rollershutter | No | This is the channel for rollershutters |
| `rollershutter_slats` | rollershutter | No | This is the channel for rollershutters with slats |
| `thermostat` | setpoint | No | This is the channel for thermostats setpoint |
| `thermostat` | measured | Yes | This is the channel for thermostats currenttemp |
| `thermostat` | mode | No | This is the channel for thermostats mode |
## Full Example
### Things
```
Bridge qbus:bridge:CTD001122 [ addr="localhost", sn="001122", port=8447, serverCheck=10 ] {
dimmer 1 "ToonzaalLED" [ dimmerId=100 ]
onOff 30 "Toonzaal230V" [ bistabielId=76 ]
thermostat 50 "Service" [ thermostatId=99 ]
scene 70 "Disco" [ sceneId=36 ]
co2 100 "Productie" [ co2Id=26 ]
rollershutter 120 "Roller1" [ rolId=268 ]
rollershutter_slats 121 "Roller2" [ rolId=264 ]
}
```
### Items
```
Dimmer ToonzaalLED <light> [ "Lighting" ] {channel="qbus:dimmer:CTD007841:1:brightness"}
Switch Toonzaal230V <light> {channel="qbus:onOff:CTD007841:30:switch"}
Number:Temperature ServiceSP"[%.1f %unit%]" (GroepThermostaten) {channel="qbus:thermostat:CTD007841:50:setpoint"}
Number:Temperature ServiceCT"[%.1f %unit%]" (GroepThermostaten) {channel="qbus:thermostat:CTD007841:50:measured"}
Number ServiceMode (GroepThermostaten) {channel="qbus:thermostat:CTD007841:50:mode",ihc="0x33c311" , autoupdate="true"}
Switch Disco <light> {channel="qbus:scene:CTD007841:36:scene"}
Number ProductieCO2 {channel="qbus:co2:CTD007841:100:co2"}
Rollershutter Roller1 {channel="qbus:rollershutter:CTD007841:120:rollershutter"}
Rollershutter Roller2 {channel="qbus:rollershutter_slats:CTD007841:121:rollershutter"}
Dimmer Roller2_slats {channel="qbus:rollershutter_slats:CTD007841:121:slats"}
```
This is the link to the [Qbus forum](https://qbusforum.be). This forum is mainly in dutch and you can find a lot of information about the pre testings of this binding and offers a way to communicate with other users.

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.qbus</artifactId>
<name>openHAB Add-ons :: Bundles :: Qbus Binding</name>
</project>

View File

@ -0,0 +1,23 @@
<?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.qbus-${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-qbus" description="Qbus Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.qbus/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link QbusBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Koen Schockaert - Initial contribution
*/
@NonNullByDefault
public class QbusBindingConstants {
private static final String BINDING_ID = "qbus";
// bridge
public static final ThingTypeUID BRIDGE_THING_TYPE = new ThingTypeUID(BINDING_ID, "bridge");
public static final Set<ThingTypeUID> BRIDGE_THING_TYPES_UIDS = Collections.singleton(BRIDGE_THING_TYPE);
// Bridge config properties
public static final String CONFIG_HOST_NAME = "addr";
public static final String CONFIG_PORT = "port";
public static final String CONFIG_SN = "sn";
public static final String CONFIG_SERVERCHECK = "serverCheck";
// generic thing types
public static final ThingTypeUID THING_TYPE_CO2 = new ThingTypeUID(BINDING_ID, "co2");
public static final ThingTypeUID THING_TYPE_SCENE = new ThingTypeUID(BINDING_ID, "scene");
public static final ThingTypeUID THING_TYPE_ON_OFF_LIGHT = new ThingTypeUID(BINDING_ID, "onOff");
public static final ThingTypeUID THING_TYPE_DIMMABLE_LIGHT = new ThingTypeUID(BINDING_ID, "dimmer");
public static final ThingTypeUID THING_TYPE_ROLLERSHUTTER = new ThingTypeUID(BINDING_ID, "rollershutter");
public static final ThingTypeUID THING_TYPE_ROLLERSHUTTER_SLATS = new ThingTypeUID(BINDING_ID,
"rollershutter_slats");
public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");
// List of all Thing Type UIDs
public static final Set<ThingTypeUID> SCENE_THING_TYPES_UIDS = Set.of(THING_TYPE_SCENE);
public static final Set<ThingTypeUID> CO2_THING_TYPES_UIDS = Set.of(THING_TYPE_CO2);
public static final Set<ThingTypeUID> ROLLERSHUTTER_THING_TYPES_UIDS = Set.of(THING_TYPE_ROLLERSHUTTER);
public static final Set<ThingTypeUID> ROLLERSHUTTER_SLATS_THING_TYPES_UIDS = Set.of(THING_TYPE_ROLLERSHUTTER_SLATS);
public static final Set<ThingTypeUID> BISTABIEL_THING_TYPES_UIDS = Set.of(THING_TYPE_ON_OFF_LIGHT);
public static final Set<ThingTypeUID> THERMOSTAT_THING_TYPES_UIDS = Set.of(THING_TYPE_THERMOSTAT);
public static final Set<ThingTypeUID> DIMMER_THING_TYPES_UIDS = Set.of(THING_TYPE_ON_OFF_LIGHT,
THING_TYPE_DIMMABLE_LIGHT);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ON_OFF_LIGHT,
THING_TYPE_DIMMABLE_LIGHT, THING_TYPE_THERMOSTAT, THING_TYPE_SCENE, THING_TYPE_CO2,
THING_TYPE_ROLLERSHUTTER, THING_TYPE_ROLLERSHUTTER_SLATS);
// List of all Channel ids
public static final String CHANNEL_SWITCH = "switch";
public static final String CHANNEL_SCENE = "scene";
public static final String CHANNEL_BRIGHTNESS = "brightness";
public static final String CHANNEL_MEASURED = "measured";
public static final String CHANNEL_SETPOINT = "setpoint";
public static final String CHANNEL_MODE = "mode";
public static final String CHANNEL_CO2 = "co2";
public static final String CHANNEL_ROLLERSHUTTER = "rollershutter";
public static final String CHANNEL_SLATS = "slats";
// Thing config properties
public static final String CONFIG_BISTABIEL_ID = "bistabielId";
public static final String CONFIG_DIMMER_ID = "dimmerId";
public static final String CONFIG_THERMOSTAT_ID = "thermostatId";
public static final String CONFIG_SCENE_ID = "sceneId";
public static final String CONFIG_CO2_ID = "co2Id";
public static final String CONFIG_ROLLERSHUTTER_ID = "rolId";
public static final String CONFIG_STEP_VALUE = "step";
}

View File

@ -0,0 +1,359 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal;
import java.io.IOException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
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.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link QbusBridgeHandler} is the handler for a Qbus controller
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusBridgeHandler extends BaseBridgeHandler {
private @Nullable QbusCommunication qbusComm;
protected @Nullable QbusConfiguration bridgeConfig = new QbusConfiguration();
private @Nullable ScheduledFuture<?> refreshTimer;
private final Logger logger = LoggerFactory.getLogger(QbusBridgeHandler.class);
public QbusBridgeHandler(Bridge Bridge) {
super(Bridge);
}
/**
* Initialize the bridge
*/
@Override
public void initialize() {
Integer serverCheck = getServerCheck();
readConfig();
createCommunicationObject();
if (serverCheck != null) {
this.setupRefreshTimer(serverCheck);
}
}
/**
* Sets the Bridge call back
*/
private void setBridgeCallBack() {
QbusCommunication qbusCommunication = getQbusCommunication();
if (qbusCommunication != null) {
qbusCommunication.setBridgeCallBack(this);
}
}
/**
* Create communication object to Qbus server and start communication.
*
* @param addr : IP address of Qbus server
* @param port : Communication port of QbusServer
*/
private void createCommunicationObject() {
scheduler.submit(() -> {
setQbusCommunication(new QbusCommunication(thing));
QbusCommunication qbusCommunication = getQbusCommunication();
setBridgeCallBack();
Integer serverCheck = getServerCheck();
String sn = getSn();
if (serverCheck != null) {
if (sn != null) {
if (qbusCommunication != null) {
try {
qbusCommunication.startCommunication();
} catch (InterruptedException e) {
String msg = e.getMessage();
bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Communication wit Qbus server could not be established, will try to reconnect every "
+ serverCheck + " minutes. InterruptedException: " + msg);
return;
} catch (IOException e) {
String msg = e.getMessage();
bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Communication wit Qbus server could not be established, will try to reconnect every "
+ serverCheck + " minutes. IOException: " + msg);
return;
}
if (!qbusCommunication.communicationActive()) {
bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Server, will try to reconnect every " + serverCheck
+ " minutes");
return;
}
if (!qbusCommunication.clientConnected()) {
bridgePending("Waiting for Qbus client to come online");
return;
}
}
}
}
});
}
/**
* Updates offline status off the Bridge when an error occurs.
*
* @param status
* @param detail
* @param message
*/
public void bridgeOffline(ThingStatusDetail detail, String message) {
updateStatus(ThingStatus.OFFLINE, detail, message);
}
/**
* Updates pending status off the Bridge (usualay when Qbus client id not connected)
*
* @param message
*/
public void bridgePending(String message) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_CONFIGURATION_PENDING, message);
}
/**
* Put bridge online when error in communication resolved.
*/
public void bridgeOnline() {
updateStatus(ThingStatus.ONLINE);
}
/**
* Initializes a timer that check the communication with Qbus server/client and tries to re-establish communication.
*
* @param refreshInterval Time before refresh in minutes.
*/
private void setupRefreshTimer(int refreshInterval) {
ScheduledFuture<?> timer = refreshTimer;
if (timer != null) {
timer.cancel(true);
refreshTimer = null;
}
if (refreshInterval == 0) {
return;
}
refreshTimer = scheduler.scheduleWithFixedDelay(() -> {
QbusCommunication comm = getCommunication();
Integer serverCheck = getServerCheck();
if (comm != null) {
if (serverCheck != null) {
if (!comm.communicationActive()) {
// Disconnected from Qbus Server, restart communication
try {
comm.startCommunication();
} catch (InterruptedException e) {
String msg = e.getMessage();
bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Communication wit Qbus server could not be established, will try to reconnect every "
+ serverCheck + " minutes. InterruptedException: " + msg);
} catch (IOException e) {
String msg = e.getMessage();
bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Communication wit Qbus server could not be established, will try to reconnect every "
+ serverCheck + " minutes. IOException: " + msg);
}
}
}
}
}, refreshInterval, refreshInterval, TimeUnit.MINUTES);
}
/**
* Disposes the Bridge and stops communication with the Qbus server
*/
@Override
public void dispose() {
ScheduledFuture<?> timer = refreshTimer;
if (timer != null) {
timer.cancel(true);
}
refreshTimer = null;
QbusCommunication comm = getCommunication();
if (comm != null) {
try {
comm.stopCommunication();
} catch (IOException e) {
String message = e.toString();
logger.debug("Error on stopping communication.{} ", message);
}
}
comm = null;
}
/**
* Reconnect to Qbus server if controller is offline
*/
public void ctdOffline() {
bridgePending("Waiting for CTD connection");
}
/**
* Get BridgeCommunication
*
* @return BridgeCommunication
*/
public @Nullable QbusCommunication getQbusCommunication() {
if (this.qbusComm != null) {
return this.qbusComm;
} else {
return null;
}
}
/**
* Sets BridgeCommunication
*
* @param BridgeCommunication
*/
void setQbusCommunication(QbusCommunication comm) {
this.qbusComm = comm;
}
/**
* Gets the status off the Bridge
*
* @return
*/
public ThingStatus getStatus() {
return thing.getStatus();
}
/**
* Gets the status off the Bridge
*
* @return
*/
public ThingStatusDetail getStatusDetails() {
ThingStatusInfo status = thing.getStatusInfo();
ThingStatusDetail detail = status.getStatusDetail();
return detail;
}
/**
* Sets the configuration parameters
*/
protected void readConfig() {
bridgeConfig = getConfig().as(QbusConfiguration.class);
}
/**
* Get the Qbus communication object.
*
* @return Qbus communication object
*/
public @Nullable QbusCommunication getCommunication() {
return this.qbusComm;
}
/**
* Get the ip address of the Qbus server.
*
* @return the ip address
*/
public @Nullable String getAddress() {
QbusConfiguration localConfig = this.bridgeConfig;
if (localConfig != null) {
return localConfig.addr;
} else {
return null;
}
}
/**
* Get the listening port of the Qbus server.
*
* @return
*/
public @Nullable Integer getPort() {
QbusConfiguration localConfig = this.bridgeConfig;
if (localConfig != null) {
return localConfig.port;
} else {
return null;
}
}
/**
* Get the serial nr of the Qbus server.
*
* @return the serial nr of the controller
*/
public @Nullable String getSn() {
QbusConfiguration localConfig = this.bridgeConfig;
if (localConfig != null) {
return localConfig.sn;
} else {
return null;
}
}
/**
* Get the refresh interval.
*
* @return the refresh interval
*/
public @Nullable Integer getServerCheck() {
QbusConfiguration localConfig = this.bridgeConfig;
if (localConfig != null) {
return localConfig.serverCheck;
} else {
return null;
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Class {@link QbusConfiguration} Configuration Class
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusConfiguration {
public @Nullable String addr;
public @Nullable Integer port;
public @Nullable String sn;
public @Nullable Integer serverCheck;
}

View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusBistabielHandler;
import org.openhab.binding.qbus.internal.handler.QbusCO2Handler;
import org.openhab.binding.qbus.internal.handler.QbusDimmerHandler;
import org.openhab.binding.qbus.internal.handler.QbusRolHandler;
import org.openhab.binding.qbus.internal.handler.QbusSceneHandler;
import org.openhab.binding.qbus.internal.handler.QbusThermostatHandler;
import org.openhab.core.thing.Bridge;
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 qbusHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Koen Schockaert - Initial Contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.qbus")
@NonNullByDefault
public class QbusHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID) || BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
if (BRIDGE_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
QbusBridgeHandler handler = new QbusBridgeHandler((Bridge) thing);
return handler;
} else if (SCENE_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusSceneHandler(thing);
} else if (BISTABIEL_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusBistabielHandler(thing);
} else if (THERMOSTAT_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusThermostatHandler(thing);
} else if (DIMMER_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusDimmerHandler(thing);
} else if (CO2_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusCO2Handler(thing);
} else if (ROLLERSHUTTER_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusRolHandler(thing);
} else if (ROLLERSHUTTER_SLATS_THING_TYPES_UIDS.contains(thing.getThingTypeUID())) {
return new QbusRolHandler(thing);
}
return null;
}
}

View File

@ -0,0 +1,244 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.CHANNEL_SWITCH;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusBistabiel;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.core.library.types.OnOffType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link QbusBistabielHandler} is responsible for handling the Bistable outputs of Qbus
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusBistabielHandler extends QbusGlobalHandler {
private final Logger logger = LoggerFactory.getLogger(QbusBistabielHandler.class);
protected @Nullable QbusThingsConfig bistabielConfig = new QbusThingsConfig();
private @Nullable Integer bistabielId;
private @Nullable String sn;
public QbusBistabielHandler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.bistabielId = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.bistabielId != null) {
controllerComm = getCommunication("Bistabiel", this.bistabielId);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for BISTABIEL no set! " + this.bistabielId);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for BISTABIEL not known in controller " + this.bistabielId);
return;
}
Map<Integer, QbusBistabiel> bistabielCommLocal = controllerComm.getBistabiel();
QbusBistabiel outputLocal = bistabielCommLocal.get(this.bistabielId);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"Bridge could not initialize BISTABIEL ID " + this.bistabielId);
return;
}
outputLocal.setThingHandler(this);
handleStateUpdate(outputLocal);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Bistabiel", this.bistabielId);
if (qBridgeHandler != null) {
if (qBridgeHandler.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
"Bridge offline for BISTABIEL ID " + this.bistabielId);
}
}
});
}
/**
* Handle the status update from the bistabiel
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("Bistabiel", this.bistabielId);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for BISTABIEL not known in controller " + this.bistabielId);
return;
} else {
Map<Integer, QbusBistabiel> bistabielComm = qComm.getBistabiel();
QbusBistabiel qBistabiel = bistabielComm.get(this.bistabielId);
if (qBistabiel == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for BISTABIEL not known in controller " + this.bistabielId);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "Bistabiel", this.bistabielId);
}
if (qComm.communicationActive()) {
if (command == REFRESH) {
handleStateUpdate(qBistabiel);
return;
}
switch (channelUID.getId()) {
case CHANNEL_SWITCH:
try {
handleSwitchCommand(qBistabiel, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Switch for bistabiel ID {}. IOException: {}",
this.bistabielId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn(
"Error on executing Switch for bistabiel ID {}. Interruptedexception {}",
this.bistabielId, message);
}
break;
default:
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown Channel " + channelUID.getId());
}
}
});
}
}
}
/**
* Executes the switch command
*
* @throws IOException
* @throws InterruptedException
*/
private void handleSwitchCommand(QbusBistabiel qBistabiel, Command command)
throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof OnOffType) {
if (command == OnOffType.OFF) {
qBistabiel.execute(0, snr);
} else {
qBistabiel.execute(100, snr);
}
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"No serial number configured for BISTABIEL " + this.bistabielId);
}
}
}
/**
* Method to update state of channel, called from Qbus Bistabiel.
*
* @param qBistabiel
*/
public void handleStateUpdate(QbusBistabiel qBistabiel) {
Integer bistabielState = qBistabiel.getState();
if (bistabielState != null) {
updateState(CHANNEL_SWITCH, (bistabielState == 0) ? OnOffType.OFF : OnOffType.ON);
}
}
/**
* Returns the serial number of the controller
*
* @return the serial nr
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Bistabiel", this.bistabielId);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for BISTABIEL " + this.bistabielId);
return;
}
sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
bistabielConfig = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = bistabielConfig;
if (localConfig != null) {
return localConfig.bistabielId;
} else {
return null;
}
}
}

View File

@ -0,0 +1,197 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.CHANNEL_CO2;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCO2;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.core.library.types.DecimalType;
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.types.Command;
/**
* The {@link QbusCO2Handler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusCO2Handler extends QbusGlobalHandler {
protected @Nullable QbusThingsConfig config;
protected @Nullable QbusThingsConfig co2Config = new QbusThingsConfig();
private @Nullable Integer co2Id;
private @Nullable String sn;
public QbusCO2Handler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.co2Id = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.co2Id != null) {
controllerComm = getCommunication("CO2", this.co2Id);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for CO2 no set! " + this.co2Id);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for CO2 not known in controller " + this.co2Id);
return;
}
Map<Integer, QbusCO2> co2CommLocal = controllerComm.getCo2();
QbusCO2 outputLocal = co2CommLocal.get(this.co2Id);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "Bridge could not initialize CO2 ID " + this.co2Id);
return;
}
outputLocal.setThingHandler(this);
handleStateUpdate(outputLocal);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("CO2", this.co2Id);
if (qBridgeHandler != null) {
if (qBridgeHandler.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
"Bridge offline for CO2 ID " + this.co2Id);
}
}
});
}
/**
* Handle the status update from the thing
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("CO2", this.co2Id);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for CO2 not known in controller " + this.co2Id);
return;
} else {
Map<Integer, QbusCO2> co2Comm = qComm.getCo2();
QbusCO2 qCo2 = co2Comm.get(this.co2Id);
if (qCo2 == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for CO2 not known in controller " + this.co2Id);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "CO2", this.co2Id);
}
if (qComm.communicationActive()) {
if (command == REFRESH) {
handleStateUpdate(qCo2);
return;
}
switch (channelUID.getId()) {
default:
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown Channel " + channelUID.getId());
}
}
});
}
}
}
/**
* Method to update state of channel, called from Qbus CO2.
*/
public void handleStateUpdate(QbusCO2 qCo2) {
Integer co2State = qCo2.getState();
if (co2State != null) {
updateState(CHANNEL_CO2, new DecimalType(co2State));
}
}
/**
* Returns the serial number of the controller
*
* @return the serial nr
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("CO2", this.co2Id);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for CO2 " + this.co2Id);
return;
}
sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
co2Config = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = this.co2Config;
if (localConfig != null) {
return localConfig.co2Id;
} else {
return null;
}
}
}

View File

@ -0,0 +1,310 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.*;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.binding.qbus.internal.protocol.QbusDimmer;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link QbusDimmerHandler} is responsible for handling the dimmable outputs of Qbus
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusDimmerHandler extends QbusGlobalHandler {
private final Logger logger = LoggerFactory.getLogger(QbusDimmerHandler.class);
protected @Nullable QbusThingsConfig dimmerConfig = new QbusThingsConfig();
private @Nullable Integer dimmerId;
private @Nullable String sn;
public QbusDimmerHandler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.dimmerId = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.dimmerId != null) {
controllerComm = getCommunication("Dimmer", this.dimmerId);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for DIMMER no set! " + this.dimmerId);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for DIMMER not known in controller " + this.dimmerId);
return;
}
Map<Integer, QbusDimmer> dimmerCommLocal = controllerComm.getDimmer();
QbusDimmer outputLocal = dimmerCommLocal.get(this.dimmerId);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"Bridge could not initialize DIMMER ID " + this.dimmerId);
return;
}
outputLocal.setThingHandler(this);
handleStateUpdate(outputLocal);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Dimmer", this.dimmerId);
if (qBridgeHandler != null) {
if (qBridgeHandler.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
"Bridge offline for DIMMER ID " + this.dimmerId);
}
}
});
}
/**
* Handle the status update from the dimmer
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("Dimmer", this.dimmerId);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for DIMMER not known in controller " + this.dimmerId);
return;
} else {
Map<Integer, QbusDimmer> dimmerComm = qComm.getDimmer();
QbusDimmer qDimmer = dimmerComm.get(this.dimmerId);
if (qDimmer == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for DIMMER not known in controller " + this.dimmerId);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "Dimmer", this.dimmerId);
}
if (qComm.communicationActive()) {
if (command == REFRESH) {
handleStateUpdate(qDimmer);
return;
}
switch (channelUID.getId()) {
case CHANNEL_SWITCH:
try {
handleSwitchCommand(qDimmer, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Switch for dimmer ID {}. IOException: {}",
this.dimmerId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn("Error on executing Switch for dimmer ID {}. Interruptedexception {}",
this.dimmerId, message);
}
break;
case CHANNEL_BRIGHTNESS:
try {
handleBrightnessCommand(qDimmer, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Brightness for dimmer ID {}. IOException: {}",
this.dimmerId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn(
"Error on executing Brightness for dimmer ID {}. Interruptedexception {}",
this.dimmerId, message);
}
break;
default:
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown Channel " + channelUID.getId());
}
}
});
}
}
}
/**
* Executes the switch command
*
* @throws IOException
* @throws InterruptedException
*/
private void handleSwitchCommand(QbusDimmer qDimmer, Command command) throws InterruptedException, IOException {
if (command instanceof OnOffType) {
String snr = getSN();
if (snr != null) {
if (command == OnOffType.OFF) {
qDimmer.execute(0, snr);
} else {
qDimmer.execute(1000, snr);
}
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"No serial number configured for DIMMER " + this.dimmerId);
}
}
}
/**
* Executes the brightness command
*
* @throws IOException
* @throws InterruptedException
*/
private void handleBrightnessCommand(QbusDimmer qDimmer, Command command) throws InterruptedException, IOException {
String snr = getSN();
if (snr == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"No serial number configured for DIMMER " + this.dimmerId);
return;
} else {
if (command instanceof OnOffType) {
if (command == OnOffType.OFF) {
qDimmer.execute(0, snr);
} else {
qDimmer.execute(100, snr);
}
} else if (command instanceof IncreaseDecreaseType) {
int stepValue = ((Number) getConfig().get(CONFIG_STEP_VALUE)).intValue();
Integer currentValue = qDimmer.getState();
Integer newValue;
Integer sendvalue;
if (currentValue != null) {
if (command == IncreaseDecreaseType.INCREASE) {
newValue = currentValue + stepValue;
// round down to step multiple
newValue = newValue - newValue % stepValue;
sendvalue = newValue > 100 ? 100 : newValue;
qDimmer.execute(sendvalue, snr);
} else {
newValue = currentValue - stepValue;
// round up to step multiple
newValue = newValue + newValue % stepValue;
sendvalue = newValue < 0 ? 0 : newValue;
qDimmer.execute(sendvalue, snr);
}
}
} else if (command instanceof PercentType) {
int percentToInt = ((PercentType) command).intValue();
if (command == PercentType.ZERO) {
qDimmer.execute(0, snr);
} else {
qDimmer.execute(percentToInt, snr);
}
}
}
}
/**
* Method to update state of channel, called from Qbus Dimmer.
*
* @param qDimmer
*/
public void handleStateUpdate(QbusDimmer qDimmer) {
Integer dimmerState = qDimmer.getState();
if (dimmerState != null) {
updateState(CHANNEL_BRIGHTNESS, new PercentType(dimmerState));
}
}
/**
* Returns the serial number of the controller
*
* @return the serial number
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Dimmer", this.dimmerId);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for DIMMER " + this.dimmerId);
return;
}
this.sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
dimmerConfig = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = dimmerConfig;
if (localConfig != null) {
return localConfig.dimmerId;
} else {
return null;
}
}
}

View File

@ -0,0 +1,114 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
/**
* The {@link QbusGlobalHandler} is used in other handlers, to share the functions.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public abstract class QbusGlobalHandler extends BaseThingHandler {
public QbusGlobalHandler(Thing thing) {
super(thing);
}
/**
* Get Bridge communication
*
* @param type
* @param globalId
* @return
*/
public @Nullable QbusCommunication getCommunication(String type, @Nullable Integer globalId) {
QbusBridgeHandler qBridgeHandler = null;
if (globalId != null) {
qBridgeHandler = getBridgeHandler(type, globalId);
}
if (qBridgeHandler == null) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"No bridge handler initialized for " + type + " with id " + globalId + ".");
return null;
}
QbusCommunication qComm = qBridgeHandler.getCommunication();
return qComm;
}
/**
* Get the Bridge handler
*
* @param type
* @param globalId
* @return
*/
public @Nullable QbusBridgeHandler getBridgeHandler(String type, @Nullable Integer globalId) {
Bridge qBridge = getBridge();
if (qBridge == null) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"No bridge initialized for " + type + " with ID " + globalId);
return null;
}
QbusBridgeHandler qBridgeHandler = (QbusBridgeHandler) qBridge.getHandler();
return qBridgeHandler;
}
/**
*
* @param qComm
* @param type
* @param globalId
*/
public void restartCommunication(QbusCommunication qComm, String type, @Nullable Integer globalId) {
try {
qComm.restartCommunication();
} catch (InterruptedException e) {
String message = e.toString();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
} catch (IOException e) {
String message = e.toString();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
}
QbusBridgeHandler qBridgeHandler = getBridgeHandler(type, globalId);
if (qBridgeHandler != null && qComm.communicationActive()) {
qBridgeHandler.bridgeOnline();
} else {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Communication socket error");
}
}
/**
* Put thing offline
*
* @param message
*/
public void thingOffline(ThingStatusDetail detail, String message) {
updateStatus(ThingStatus.OFFLINE, detail, message);
}
}

View File

@ -0,0 +1,333 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.*;
import static org.openhab.core.library.types.UpDownType.DOWN;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.binding.qbus.internal.protocol.QbusRol;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.UpDownType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link QbusRolHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusRolHandler extends QbusGlobalHandler {
private final Logger logger = LoggerFactory.getLogger(QbusRolHandler.class);
protected @Nullable QbusThingsConfig rolConfig = new QbusThingsConfig();
private @Nullable Integer rolId;
private @Nullable String sn;
public QbusRolHandler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.rolId = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.rolId != null) {
controllerComm = getCommunication("Screen/Store", this.rolId);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for Screen/Store no set! " + this.rolId);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for Screen/Store not known in controller " + this.rolId);
return;
}
Map<Integer, QbusRol> rolCommLocal = controllerComm.getRol();
QbusRol outputLocal = rolCommLocal.get(this.rolId);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"Bridge could not initialize Screen/Store ID " + this.rolId);
return;
}
outputLocal.setThingHandler(this);
handleStateUpdate(outputLocal);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Screen/Store", this.rolId);
if (qBridgeHandler != null) {
if (qBridgeHandler.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
"Bridge offline for SCREEN/STORE ID " + this.rolId);
}
}
});
}
/**
* Handle the status update from the thing
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("Screen/Store", this.rolId);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for ROLLERSHUTTER/SCREEN not known in controller " + this.rolId);
return;
} else {
Map<Integer, QbusRol> rolComm = qComm.getRol();
QbusRol qRol = rolComm.get(this.rolId);
if (qRol == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for ROLLERSHUTTER/SCREEN not known in controller " + this.rolId);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "Screen/Store", this.rolId);
}
if (qComm.communicationActive()) {
if (command == REFRESH) {
handleStateUpdate(qRol);
return;
}
switch (channelUID.getId()) {
case CHANNEL_ROLLERSHUTTER:
try {
handleScreenposCommand(qRol, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Rollershutter for screen ID {}. IOException: {}",
this.rolId, message);
} catch (InterruptedException e) {
String message = e.toString();
logger.warn(
"Error on executing Rollershutter for screen ID {}. Interruptedexception {}",
this.rolId, message);
}
break;
case CHANNEL_SLATS:
try {
handleSlatsposCommand(qRol, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Slats for screen ID {}. IOException: {}",
this.rolId, message);
} catch (InterruptedException e) {
String message = e.toString();
logger.warn("Error on executing Slats for screen ID {}. Interruptedexception {}",
this.rolId, message);
}
break;
}
}
});
}
}
}
/**
* Executes the command for screen up/down position
*
* @throws IOException
* @throws InterruptedException
*/
private void handleScreenposCommand(QbusRol qRol, Command command) throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof UpDownType) {
UpDownType upDown = (UpDownType) command;
if (upDown == DOWN) {
qRol.execute(0, snr);
} else {
qRol.execute(100, snr);
}
} else if (command instanceof IncreaseDecreaseType) {
IncreaseDecreaseType inc = (IncreaseDecreaseType) command;
int stepValue = ((Number) getConfig().get(CONFIG_STEP_VALUE)).intValue();
Integer currentValue = qRol.getState();
int newValue;
int sendValue;
if (currentValue != null) {
if (inc == IncreaseDecreaseType.INCREASE) {
newValue = currentValue + stepValue;
// round down to step multiple
newValue = newValue - newValue % stepValue;
sendValue = newValue > 100 ? 100 : newValue;
qRol.execute(sendValue, snr);
} else {
newValue = currentValue - stepValue;
// round up to step multiple
newValue = newValue + newValue % stepValue;
sendValue = newValue > 100 ? 100 : newValue;
qRol.execute(sendValue, snr);
}
}
} else if (command instanceof PercentType) {
PercentType p = (PercentType) command;
int pp = p.intValue();
if (p == PercentType.ZERO) {
qRol.execute(0, snr);
} else {
qRol.execute(pp, snr);
}
}
}
}
/**
* Executes the command for screen slats position
*
* @throws IOException
* @throws InterruptedException
*/
private void handleSlatsposCommand(QbusRol qRol, Command command) throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof UpDownType) {
if (command == DOWN) {
qRol.executeSlats(0, snr);
} else {
qRol.executeSlats(100, snr);
}
} else if (command instanceof IncreaseDecreaseType) {
int stepValue = ((Number) getConfig().get(CONFIG_STEP_VALUE)).intValue();
Integer currentValue = qRol.getState();
int newValue;
int sendValue;
if (currentValue != null) {
if (command == IncreaseDecreaseType.INCREASE) {
newValue = currentValue + stepValue;
// round down to step multiple
newValue = newValue - newValue % stepValue;
sendValue = newValue > 100 ? 100 : newValue;
qRol.executeSlats(sendValue, snr);
} else {
newValue = currentValue - stepValue;
// round up to step multiple
newValue = newValue + newValue % stepValue;
sendValue = newValue > 100 ? 100 : newValue;
qRol.executeSlats(sendValue, snr);
}
}
} else if (command instanceof PercentType) {
int percentToInt = ((PercentType) command).intValue();
if (command == PercentType.ZERO) {
qRol.executeSlats(0, snr);
} else {
qRol.executeSlats(percentToInt, snr);
}
}
}
}
/**
* Method to update state of channel, called from Qbus Screen/Store.
*/
public void handleStateUpdate(QbusRol qRol) {
Integer rolState = qRol.getState();
Integer slatState = qRol.getStateSlats();
if (rolState != null) {
updateState(CHANNEL_ROLLERSHUTTER, new PercentType(rolState));
}
if (slatState != null) {
updateState(CHANNEL_SLATS, new PercentType(slatState));
}
}
/**
* Returns the serial number of the controller
*
* @return the serial nr
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Screen/Store", this.rolId);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for ROLLERSHUTTER/SCREEN " + this.rolId);
return;
}
sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
rolConfig = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = rolConfig;
if (localConfig != null) {
return localConfig.rolId;
} else {
return null;
}
}
}

View File

@ -0,0 +1,217 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.CHANNEL_SCENE;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.binding.qbus.internal.protocol.QbusScene;
import org.openhab.core.library.types.OnOffType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link QbusSceneHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusSceneHandler extends QbusGlobalHandler {
private final Logger logger = LoggerFactory.getLogger(QbusSceneHandler.class);
protected @Nullable QbusThingsConfig sceneConfig = new QbusThingsConfig();
private @Nullable Integer sceneId;
private @Nullable String sn;
public QbusSceneHandler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.sceneId = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.sceneId != null) {
controllerComm = getCommunication("Scene", this.sceneId);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for SCENE no set! " + this.sceneId);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for SCENE not known in controller " + this.sceneId);
return;
}
Map<Integer, QbusScene> sceneCommLocal = controllerComm.getScene();
QbusScene outputLocal = sceneCommLocal.get(this.sceneId);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"Bridge could not initialize SCENE ID " + this.sceneId);
return;
}
outputLocal.setThingHandler(this);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Scene", this.sceneId);
if ((qBridgeHandler != null) && (qBridgeHandler.getStatus() == ThingStatus.ONLINE)) {
updateStatus(ThingStatus.ONLINE);
} else {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Bridge offline for SCENE ID " + this.sceneId);
}
});
}
/**
* Handle the status update from the thing
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("Scene", this.sceneId);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for SCENE not known in controller " + this.sceneId);
return;
} else {
Map<Integer, QbusScene> sceneComm = qComm.getScene();
QbusScene qScene = sceneComm.get(this.sceneId);
if (qScene == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for SCENE not known in controller " + this.sceneId);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "Scene", this.sceneId);
}
if (qComm.communicationActive()) {
switch (channelUID.getId()) {
case CHANNEL_SCENE:
try {
handleSwitchCommand(qScene, channelUID, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Scene for scene ID {}. IOException: {}",
this.sceneId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn("Error on executing Scene for scene ID {}. Interruptedexception {}",
this.sceneId, message);
}
break;
default:
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown Channel " + channelUID.getId());
}
}
});
}
}
}
/**
* Executes the scene command
*
* @throws IOException
* @throws InterruptedException
*/
void handleSwitchCommand(QbusScene qScene, ChannelUID channelUID, Command command)
throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof OnOffType) {
if (command == OnOffType.OFF) {
qScene.execute(0, snr);
} else {
qScene.execute(100, snr);
}
}
}
}
/**
* Returns the serial number of the controller
*
* @return the serial nr
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Scene", this.sceneId);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for SCENE " + this.sceneId);
return;
}
sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
sceneConfig = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = sceneConfig;
if (localConfig != null) {
return localConfig.sceneId;
} else {
return null;
}
}
}

View File

@ -0,0 +1,295 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import static org.openhab.binding.qbus.internal.QbusBindingConstants.*;
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
import static org.openhab.core.types.RefreshType.REFRESH;
import java.io.IOException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.binding.qbus.internal.protocol.QbusCommunication;
import org.openhab.binding.qbus.internal.protocol.QbusThermostat;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
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.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link QbusThermostatHandler} is responsible for handling the Thermostat outputs of Qbus
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusThermostatHandler extends QbusGlobalHandler {
private final Logger logger = LoggerFactory.getLogger(QbusThermostatHandler.class);
protected @Nullable QbusThingsConfig thermostatConfig = new QbusThingsConfig();
private @Nullable Integer thermostatId;
private @Nullable String sn;
public QbusThermostatHandler(Thing thing) {
super(thing);
}
/**
* Main initialization
*/
@Override
public void initialize() {
readConfig();
this.thermostatId = getId();
setSN();
scheduler.submit(() -> {
QbusCommunication controllerComm;
if (this.thermostatId != null) {
controllerComm = getCommunication("Thermostat", this.thermostatId);
} else {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "ID for THERMOSTAT no set! " + this.thermostatId);
return;
}
if (controllerComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for THERMOSTAT not known in controller " + this.thermostatId);
return;
}
Map<Integer, QbusThermostat> thermostatlCommLocal = controllerComm.getThermostat();
QbusThermostat outputLocal = thermostatlCommLocal.get(this.thermostatId);
if (outputLocal == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"Bridge could not initialize THERMOSTAT ID " + this.thermostatId);
return;
}
outputLocal.setThingHandler(this);
handleStateUpdate(outputLocal);
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Thermostat", this.thermostatId);
if (qBridgeHandler != null) {
if (qBridgeHandler.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
"Bridge offline for THERMOSTAT ID " + this.thermostatId);
}
}
});
}
/**
* Handle the status update from the thermostat
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
QbusCommunication qComm = getCommunication("Thermostat", this.thermostatId);
if (qComm == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for THERMOSTAT not known in controller " + this.thermostatId);
return;
} else {
Map<Integer, QbusThermostat> thermostatComm = qComm.getThermostat();
QbusThermostat qThermostat = thermostatComm.get(this.thermostatId);
if (qThermostat == null) {
thingOffline(ThingStatusDetail.CONFIGURATION_ERROR,
"ID for THERMOSTAT not known in controller " + this.thermostatId);
return;
} else {
scheduler.submit(() -> {
if (!qComm.communicationActive()) {
restartCommunication(qComm, "Thermostat", this.thermostatId);
}
if (qComm.communicationActive()) {
if (command == REFRESH) {
handleStateUpdate(qThermostat);
return;
}
switch (channelUID.getId()) {
case CHANNEL_MODE:
try {
handleModeCommand(qThermostat, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Mode for thermostat ID {}. IOException: {} ",
this.thermostatId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn(
"Error on executing Mode for thermostat ID {}. Interruptedexception {} ",
this.thermostatId, message);
}
break;
case CHANNEL_SETPOINT:
try {
handleSetpointCommand(qThermostat, command);
} catch (IOException e) {
String message = e.getMessage();
logger.warn("Error on executing Setpoint for thermostat ID {}. IOException: {} ",
this.thermostatId, message);
} catch (InterruptedException e) {
String message = e.getMessage();
logger.warn(
"Error on executing Setpoint for thermostat ID {}. Interruptedexception {} ",
this.thermostatId, message);
}
break;
default:
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"Unknown Channel " + channelUID.getId());
}
}
});
}
}
}
/**
* Executes the Mode command
*
* @param qThermostat
* @param command
* @param snr
* @throws InterruptedException
* @throws IOException
*/
private void handleModeCommand(QbusThermostat qThermostat, Command command)
throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof DecimalType) {
int mode = ((DecimalType) command).intValue();
qThermostat.executeMode(mode, snr);
}
}
}
/**
* Executes the Setpoint command
*
* @param qThermostat
* @param command
* @param snr
* @throws InterruptedException
* @throws IOException
*/
private void handleSetpointCommand(QbusThermostat qThermostat, Command command)
throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
if (command instanceof QuantityType<?>) {
QuantityType<?> s = (QuantityType<?>) command;
double sp = s.doubleValue();
QuantityType<?> spCelcius = s.toUnit(CELSIUS);
if (spCelcius != null) {
qThermostat.executeSetpoint(sp, snr);
} else {
logger.warn("Could not set setpoint for thermostat (conversion failed) {}", this.thermostatId);
}
}
}
}
/**
* Method to update state of all channels, called from Qbus thermostat.
*
* @param qThermostat
*/
public void handleStateUpdate(QbusThermostat qThermostat) {
Double measured = qThermostat.getMeasured();
if (measured != null) {
updateState(CHANNEL_MEASURED, new QuantityType<>(measured, CELSIUS));
}
Double setpoint = qThermostat.getSetpoint();
if (setpoint != null) {
updateState(CHANNEL_SETPOINT, new QuantityType<>(setpoint, CELSIUS));
}
Integer mode = qThermostat.getMode();
if (mode != null) {
updateState(CHANNEL_MODE, new DecimalType(mode));
}
}
/**
* Returns the serial number of the controller
*
* @return the serial nr
*/
public @Nullable String getSN() {
return sn;
}
/**
* Sets the serial number of the controller
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = getBridgeHandler("Thermostsat", this.thermostatId);
if (qBridgeHandler == null) {
thingOffline(ThingStatusDetail.COMMUNICATION_ERROR,
"No communication with Qbus Bridge for THERMOSTAT " + this.thermostatId);
return;
}
sn = qBridgeHandler.getSn();
}
/**
* Read the configuration
*/
protected synchronized void readConfig() {
thermostatConfig = getConfig().as(QbusThingsConfig.class);
}
/**
* Returns the Id from the configuration
*
* @return outputId
*/
public @Nullable Integer getId() {
QbusThingsConfig localConfig = thermostatConfig;
if (localConfig != null) {
return localConfig.thermostatId;
} else {
return null;
}
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link QbusThingsConfig} is responible for handling configurations for all things
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public class QbusThingsConfig {
public @Nullable Integer bistabielId;
public @Nullable Integer dimmerId;
public @Nullable Integer co2Id;
public @Nullable Integer rolId;
public @Nullable Integer sceneId;
public @Nullable Integer thermostatId;
}

View File

@ -0,0 +1,101 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusBistabielHandler;
/**
* The {@link QbusBistabiel} class represents the Qbus BISTABIEL output.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusBistabiel {
private @Nullable QbusCommunication qComm;
private Integer id;
private @Nullable Integer state;
private @Nullable QbusBistabielHandler thingHandler;
QbusBistabiel(Integer id) {
this.id = id;
}
/**
* This method should be called if the ThingHandler for the thing corresponding to this bistabiel is initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the bistable output receives an update from the Qbus client.
*
* @param handler
*/
public void setThingHandler(QbusBistabielHandler handler) {
this.thingHandler = handler;
}
/**
* This method sets a pointer to the qComm BISTABIEL of class {@link QbusCommuncation}.
* This is then used to be able to call back the sendCommand method in this class to send a command to the
* Qbus client.
*
* @param qComm
*/
public void setQComm(QbusCommunication qComm) {
this.qComm = qComm;
}
/**
* Update the value of the Bistabiel.
*
* @param state
*/
void updateState(@Nullable Integer state) {
this.state = state;
QbusBistabielHandler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Get the value of the Bistabiel.
*
* @return
*/
public @Nullable Integer getState() {
return this.state;
}
/**
* Sends Bistabiel state to Qbus.
*
* @param value
* @param sn
* @throws InterruptedException
* @throws IOException
*/
public void execute(int value, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeBistabiel").withId(this.id).withState(value);
QbusCommunication comm = this.qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
}

View File

@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusCO2Handler;
/**
* The {@link QbusCO2} class represents the action Qbus CO2 output.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusCO2 {
private @Nullable Integer state;
private @Nullable QbusCO2Handler thingHandler;
/**
* This method should be called if the ThingHandler for the thing corresponding to this CO2 is initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the CO2 output receives an update from the Qbus IP-interface.
*
* @param handler
*/
public void setThingHandler(QbusCO2Handler handler) {
this.thingHandler = handler;
}
/**
* Get state of CO2.
*
* @return CO2 state
*/
public @Nullable Integer getState() {
return this.state;
}
/**
* Update the value of the CO2.
*
* @param CO2 value
*/
void updateState(@Nullable Integer state) {
this.state = state;
QbusCO2Handler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
}

View File

@ -0,0 +1,796 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.QbusBridgeHandler;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
/**
* The {@link QbusCommunication} class is able to do the following tasks with Qbus
* CTD controllers:
* <ul>
* <li>Start and stop TCP socket connection with Qbus Server.
* <li>Read all the outputs and their status from the Qbus Controller.
* <li>Execute Qbus commands.
* <li>Listen to events from Qbus.
* </ul>
*
* A class instance is instantiated from the {@link QbusBridgeHandler} class initialization.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusCommunication extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(QbusCommunication.class);
private @Nullable Socket qSocket;
private @Nullable PrintWriter qOut;
private @Nullable BufferedReader qIn;
private boolean listenerStopped;
private boolean qbusListenerRunning;
private Gson gsonOut = new Gson();
private Gson gsonIn;
private @Nullable String ctd;
private boolean ctdConnected;
private List<Map<String, String>> outputs = new ArrayList<>();
private final Map<Integer, QbusBistabiel> bistabiel = new HashMap<>();
private final Map<Integer, QbusScene> scene = new HashMap<>();
private final Map<Integer, QbusDimmer> dimmer = new HashMap<>();
private final Map<Integer, QbusRol> rol = new HashMap<>();
private final Map<Integer, QbusThermostat> thermostat = new HashMap<>();
private final Map<Integer, QbusCO2> co2 = new HashMap<>();
private final ExecutorService threadExecutor = Executors
.newSingleThreadExecutor(new NamedThreadFactory(getThing().getUID().getAsString(), true));
private @Nullable QbusBridgeHandler bridgeCallBack;
public QbusCommunication(Thing thing) {
super(thing);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(QbusMessageBase.class, new QbusMessageDeserializer());
gsonIn = gsonBuilder.create();
}
/**
* Starts main communication thread.
* <ul>
* <li>Connect to Qbus server
* <li>Requests outputs
* <li>Start listener
* </ul>
*
* @throws IOException
* @throws InterruptedException
*/
public synchronized void startCommunication() throws IOException, InterruptedException {
QbusBridgeHandler handler = bridgeCallBack;
ctdConnected = false;
if (qbusListenerRunning) {
throw new IOException("Previous listening thread is still active.");
}
if (handler == null) {
throw new IOException("No Bridge handler initialised.");
}
InetAddress addr = InetAddress.getByName(handler.getAddress());
Integer port = handler.getPort();
if (port != null) {
Socket socket = new Socket(addr, port);
qSocket = socket;
qOut = new PrintWriter(socket.getOutputStream(), true);
qIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} else {
return;
}
setSN();
getSN();
// Connect to Qbus server
connect();
// Then start thread to listen to incoming updates from Qbus.
threadExecutor.execute(() -> {
try {
qbusListener();
} catch (IOException e) {
String msg = e.getMessage();
logger.warn("Could not start listening thread, IOException: {}", msg);
} catch (InterruptedException e) {
String msg = e.getMessage();
logger.warn("Could not start listening thread, InterruptedException: {}", msg);
}
});
if (!ctdConnected) {
handler.bridgePending("Waiting for CTD to come online...");
}
}
/**
* Cleanup socket when the communication with Qbus Server is closed.
*
* @throws IOException
*
*/
public synchronized void stopCommunication() throws IOException {
listenerStopped = true;
Socket socket = qSocket;
if (socket != null) {
try {
socket.close();
} catch (IOException ignore) {
// ignore IO Error when trying to close the socket if the intention is to close it anyway
}
}
BufferedReader reader = this.qIn;
if (reader != null) {
reader.close();
}
PrintWriter writer = this.qOut;
if (writer != null) {
writer.close();
}
qSocket = null;
qbusListenerRunning = false;
ctdConnected = false;
logger.trace("Communication stopped from thread {}", Thread.currentThread().getId());
}
/**
* Close and restart communication with Qbus Server.
*
* @throws InterruptedException
* @throws IOException
*/
public synchronized void restartCommunication() throws InterruptedException, IOException {
stopCommunication();
startCommunication();
}
/**
* Thread that handles incoming messages from Qbus client.
* <p>
* The thread listens to the TCP socket opened at instantiation of the {@link QbusCommunication} class
* and interprets all incomming json messages. It triggers state updates for active channels linked to the
* Qbus outputs. It is started after initialization of the communication.
*
* @return
* @throws IOException
* @throws InterruptedException
*
*
*/
private void qbusListener() throws IOException, InterruptedException {
String qMessage;
listenerStopped = false;
qbusListenerRunning = true;
BufferedReader reader = this.qIn;
if (reader == null) {
throw new IOException("Bufferreader for incoming messages not initialized.");
}
try {
while (!Thread.currentThread().isInterrupted() && ((qMessage = reader.readLine()) != null)) {
readMessage(qMessage);
}
} catch (IOException e) {
if (!listenerStopped) {
qbusListenerRunning = false;
// the IO has stopped working, so we need to close cleanly and try to restart
restartCommunication();
return;
}
} finally {
qbusListenerRunning = false;
}
if (!listenerStopped) {
qbusListenerRunning = false;
QbusBridgeHandler handler = bridgeCallBack;
if (handler != null) {
ctdConnected = false;
handler.bridgeOffline(ThingStatusDetail.COMMUNICATION_ERROR, "No communication with Qbus server");
}
}
qbusListenerRunning = false;
logger.trace("Event listener thread stopped on thread {}", Thread.currentThread().getId());
};
/**
* Called by other methods to send json data to Qbus.
*
* @param qMessage
* @throws InterruptedException
* @throws IOException
*/
synchronized void sendMessage(Object qMessage) throws InterruptedException, IOException {
PrintWriter writer = qOut;
String json = gsonOut.toJson(qMessage);
if (writer != null) {
writer.println(json);
// Delay after sending data to improve scene execution
TimeUnit.MILLISECONDS.sleep(250);
}
if ((writer == null) || (writer.checkError())) {
logger.warn("Error sending message, trying to restart communication");
restartCommunication();
// retry sending after restart
writer = qOut;
if (writer != null) {
writer.println(json);
}
if ((writer == null) || (writer.checkError())) {
logger.warn("Error resending message");
}
}
}
/**
* Method that interprets all feedback from Qbus Server application and calls appropriate handling methods.
* <ul>
* <li>Get request & update states for Bistabiel/Timers/Intervals/Mono outputs
* <li>Get request & update states for the Scenes
* <li>Get request & update states for Dimmers 1T and 2T
* <li>Get request & update states for Shutters
* <li>Get request & update states for Thermostats
* <li>Get request & update states for CO2
* </ul>
*
* @param qMessage message read from Qbus.
* @throws InterruptedException
* @throws IOException
*
*/
private void readMessage(String qMessage) {
String sn = null;
String cmd = "";
String ctd = null;
Integer id = null;
Integer state = null;
Integer mode = null;
Double setpoint = null;
Double measured = null;
Integer slats = null;
QbusMessageBase qMessageGson;
try {
qMessageGson = gsonIn.fromJson(qMessage, QbusMessageBase.class);
if (qMessageGson != null) {
ctd = qMessageGson.getSn();
cmd = qMessageGson.getCmd();
id = qMessageGson.getId();
state = qMessageGson.getState();
mode = qMessageGson.getMode();
setpoint = qMessageGson.getSetPoint();
measured = qMessageGson.getMeasured();
slats = qMessageGson.getSlatState();
}
} catch (JsonParseException e) {
String msg = e.getMessage();
logger.trace("Not acted on unsupported json {} : {}", qMessage, msg);
return;
}
QbusBridgeHandler handler = bridgeCallBack;
if (handler != null) {
sn = handler.getSn();
}
if (sn != null && ctd != null) {
try {
if (sn.equals(ctd) && qMessageGson != null) { // Check if commands are for this Bridge
// Handle all outputs from Qbus
if ("returnOutputs".equals(cmd)) {
outputs = ((QbusMessageListMap) qMessageGson).getOutputs();
for (Map<String, String> ctdOutputs : outputs) {
String ctdType = ctdOutputs.get("type");
String ctdIdStr = ctdOutputs.get("id");
Integer ctdId = null;
if (ctdIdStr != null) {
ctdId = Integer.parseInt(ctdIdStr);
} else {
return;
}
if (ctdType != null) {
String ctdState = ctdOutputs.get("state");
String ctdMmode = ctdOutputs.get("regime");
String ctdSetpoint = ctdOutputs.get("setpoint");
String ctdMeasured = ctdOutputs.get("measured");
String ctdSlats = ctdOutputs.get("slats");
Integer ctdStateI = null;
if (ctdState != null) {
ctdStateI = Integer.parseInt(ctdState);
}
Integer ctdSlatsI = null;
if (ctdSlats != null) {
ctdSlatsI = Integer.parseInt(ctdSlats);
}
Integer ctdMmodeI = null;
if (ctdMmode != null) {
ctdMmodeI = Integer.parseInt(ctdMmode);
}
Double ctdSetpointD = null;
if (ctdSetpoint != null) {
ctdSetpointD = Double.parseDouble(ctdSetpoint);
}
Double ctdMeasuredD = null;
if (ctdMeasured != null) {
ctdMeasuredD = Double.parseDouble(ctdMeasured);
}
if (ctdState != null) {
if (ctdType.equals("bistabiel")) {
QbusBistabiel output = new QbusBistabiel(ctdId);
if (!bistabiel.containsKey(ctdId)) {
output.setQComm(this);
output.updateState(ctdStateI);
bistabiel.put(ctdId, output);
} else {
output.updateState(ctdStateI);
}
} else if (ctdType.equals("dimmer")) {
QbusDimmer output = new QbusDimmer(ctdId);
if (!dimmer.containsKey(ctdId)) {
output.setQComm(this);
output.updateState(ctdStateI);
dimmer.put(ctdId, output);
} else {
output.updateState(ctdStateI);
}
} else if (ctdType.equals("CO2")) {
QbusCO2 output = new QbusCO2();
if (!co2.containsKey(ctdId)) {
output.updateState(ctdStateI);
co2.put(ctdId, output);
} else {
output.updateState(ctdStateI);
}
} else if (ctdType.equals("scene")) {
QbusScene output = new QbusScene(ctdId);
if (!scene.containsKey(ctdId)) {
output.setQComm(this);
scene.put(ctdId, output);
}
} else if (ctdType.equals("rol")) {
QbusRol output = new QbusRol(ctdId);
if (!rol.containsKey(ctdId)) {
output.setQComm(this);
output.updateState(ctdStateI);
if (ctdSlats != null) {
output.updateSlats(ctdSlatsI);
}
rol.put(ctdId, output);
} else {
output.updateState(ctdStateI);
if (ctdSlats != null) {
output.updateSlats(ctdSlatsI);
}
}
}
} else if (ctdMeasuredD != null && ctdSetpointD != null && ctdMmodeI != null) {
if (ctdType.equals("thermostat")) {
QbusThermostat output = new QbusThermostat(ctdId);
if (!thermostat.containsKey(ctdId)) {
output.setQComm(this);
output.updateState(ctdMeasuredD, ctdSetpointD, ctdMmodeI);
thermostat.put(ctdId, output);
} else {
output.updateState(ctdMeasuredD, ctdSetpointD, ctdMmodeI);
}
}
}
}
}
// Handle update commands from Qbus
} else if ("updateBistabiel".equals(cmd)) {
if (id != null && state != null) {
updateBistabiel(id, state);
}
} else if ("updateDimmer".equals(cmd)) {
if (id != null && state != null) {
updateDimmer(id, state);
}
} else if ("updateDimmer".equals(cmd)) {
if (id != null && state != null) {
updateDimmer(id, state);
}
} else if ("updateCo2".equals(cmd)) {
if (id != null && state != null) {
updateCO2(id, state);
}
} else if ("updateThermostat".equals(cmd)) {
if (id != null && measured != null && setpoint != null && mode != null) {
updateThermostat(id, mode, setpoint, measured);
}
} else if ("updateRol02p".equals(cmd)) {
if (id != null && state != null) {
updateRol(id, state);
}
} else if ("updateRol02pSlat".equals(cmd)) {
if (id != null && state != null && slats != null) {
updateRolSlats(id, state, slats);
}
// Incomming commands from Qbus server to verify the client connection
} else if ("noconnection".equals(cmd)) {
eventDisconnect();
} else if ("connected".equals(cmd)) {
// threadExecutor.execute(() -> {
try {
requestOutputs();
} catch (InterruptedException e) {
String msg = e.getMessage();
logger.warn("Could not request outputs. InterruptedException: {}", msg);
} catch (IOException e) {
String msg = e.getMessage();
logger.warn("Could not request outputs. IOException: {}", msg);
}
}
}
} catch (JsonParseException e) {
String msg = e.getMessage();
logger.warn("Not acted on unsupported json {}, {}", qMessage, msg);
}
}
}
/**
* Initialize the communication object
*/
@Override
public void initialize() {
}
/**
* Initial connection to Qbus Server to open a communication channel
*
* @throws InterruptedException
* @throws IOException
*/
private void connect() throws InterruptedException, IOException {
String snr = getSN();
if (snr != null) {
QbusMessageCmd qCmd = new QbusMessageCmd(snr, "openHAB");
sendMessage(qCmd);
BufferedReader reader = qIn;
if (reader == null) {
throw new IOException("Cannot read from socket, reader not connected.");
}
readMessage(reader.readLine());
} else {
QbusBridgeHandler handler = bridgeCallBack;
if (handler != null) {
handler.bridgeOffline(ThingStatusDetail.CONFIGURATION_ERROR, "No serial nr defined");
}
}
}
/**
* Send a request for all available outputs and initializes them via readMessage
*
* @throws InterruptedException
* @throws IOException
*/
private void requestOutputs() throws InterruptedException, IOException {
String snr = getSN();
QbusBridgeHandler handler = bridgeCallBack;
if (snr != null) {
QbusMessageCmd qCmd = new QbusMessageCmd(snr, "all");
sendMessage(qCmd);
BufferedReader reader = qIn;
if (reader == null) {
throw new IOException("Cannot read from socket, reader not connected.");
}
readMessage(reader.readLine());
ctdConnected = true;
if (handler != null) {
handler.bridgeOnline();
}
} else {
if (handler != null) {
handler.bridgeOffline(ThingStatusDetail.CONFIGURATION_ERROR, "No serial nr defined");
}
}
}
/**
* Event on incoming Bistabiel/Timer/Mono/Interval updates
*
* @param id
* @param state
*/
private void updateBistabiel(Integer id, Integer state) {
QbusBistabiel qBistabiel = this.bistabiel.get(id);
if (qBistabiel != null) {
qBistabiel.updateState(state);
} else {
logger.trace("Bistabiel in controller not known {}", id);
}
}
/**
* Event on incoming Dimmer updates
*
* @param id
* @param state
*/
private void updateDimmer(Integer id, Integer state) {
QbusDimmer qDimmer = this.dimmer.get(id);
if (qDimmer != null) {
qDimmer.updateState(state);
} else {
logger.trace("Dimmer in controller not known {}", id);
}
}
/**
* Event on incoming thermostat updates
*
* @param id
* @param mode
* @param sp
* @param ct
*/
private void updateThermostat(Integer id, int mode, double sp, double ct) {
QbusThermostat qThermostat = this.thermostat.get(id);
if (qThermostat != null) {
qThermostat.updateState(ct, sp, mode);
} else {
logger.trace("Thermostat in controller not known {}", id);
}
}
/**
* Event on incoming CO2 updates
*
* @param id
* @param state
*/
private void updateCO2(Integer id, Integer state) {
QbusCO2 qCO2 = this.co2.get(id);
if (qCO2 != null) {
qCO2.updateState(state);
} else {
logger.trace("CO2 in controller not known {}", id);
}
}
/**
* Event on incoming screen updates
*
* @param id
* @param state
*/
private void updateRol(Integer id, Integer state) {
QbusRol qRol = this.rol.get(id);
if (qRol != null) {
qRol.updateState(state);
} else {
logger.trace("ROL02P in controller not known {}", id);
}
}
/**
* Event on incoming screen with slats updates
*
* @param id
* @param state
* @param slats
*/
private void updateRolSlats(Integer id, Integer state, Integer slats) {
QbusRol qRol = this.rol.get(id);
if (qRol != null) {
qRol.updateState(state);
qRol.updateSlats(slats);
} else {
logger.trace("ROL02P with slats in controller not known {}", id);
}
}
/**
* Put Bridge offline when there is no connection from the QbusClient
*
*/
private void eventDisconnect() {
QbusBridgeHandler handler = bridgeCallBack;
if (handler != null) {
handler.bridgePending("Waiting for CTD connection");
}
}
/**
* Return all Bistabiel/Timers/Mono/Intervals in the Qbus Controller.
*
* @return
*/
public Map<Integer, QbusBistabiel> getBistabiel() {
return this.bistabiel;
}
/**
* Return all Scenes in the Qbus Controller
*
* @return
*/
public Map<Integer, QbusScene> getScene() {
return this.scene;
}
/**
* Return all Dimmers outputs in the Qbus Controller.
*
* @return
*/
public Map<Integer, QbusDimmer> getDimmer() {
return this.dimmer;
}
/**
* Return all rollershutter/screen outputs in the Qbus Controller.
*
* @return
*/
public Map<Integer, QbusRol> getRol() {
return this.rol;
}
/**
* Return all Thermostats outputs in the Qbus Controller.
*
* @return
*/
public Map<Integer, QbusThermostat> getThermostat() {
return this.thermostat;
}
/**
* Return all CO2 outputs in the Qbus Controller.
*
* @return
*/
public Map<Integer, QbusCO2> getCo2() {
return this.co2;
}
/**
* Method to check if communication with Qbus Server is active
*
* @return True if active
*/
public boolean communicationActive() {
return qSocket != null;
}
/**
* Method to check if communication with Qbus Client is active
*
* @return True if active
*/
public boolean clientConnected() {
return ctdConnected;
}
/**
* @param bridgeCallBack the bridgeCallBack to set
*/
public void setBridgeCallBack(QbusBridgeHandler bridgeCallBack) {
this.bridgeCallBack = bridgeCallBack;
}
/**
* Get the serial number of the CTD as configured in the Bridge.
*
* @return serial number of controller
*/
public @Nullable String getSN() {
return this.ctd;
}
/**
* Sets the serial number of the CTD, as configured in the Bridge.
*/
public void setSN() {
QbusBridgeHandler qBridgeHandler = bridgeCallBack;
if (qBridgeHandler != null) {
this.ctd = qBridgeHandler.getSn();
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
}

View File

@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusDimmerHandler;
/**
* The {@link QbusDimmer} class represents the action Qbus Dimmer output.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusDimmer {
private @Nullable QbusCommunication qComm;
private Integer id;
private @Nullable Integer state;
private @Nullable QbusDimmerHandler thingHandler;
QbusDimmer(Integer id) {
this.id = id;
}
/**
* This method should be called if the ThingHandler for the thing corresponding to this dimmer is initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the dimmer receives an update from the Qbus client.
*
* @param handler
*/
public void setThingHandler(QbusDimmerHandler handler) {
this.thingHandler = handler;
}
/**
* This method sets a pointer to the qComm Dimmer of class {@link QbusCommuncation}.
* This is then used to be able to call back the sendCommand method in this class to send a command to the
* Qbus client.
*
* @param qComm
*/
public void setQComm(QbusCommunication qComm) {
this.qComm = qComm;
}
/**
* Update the value of the dimmer
*
* @param state
*/
public void updateState(@Nullable Integer state) {
this.state = state;
QbusDimmerHandler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Get the state of dimmer.
*
* @return dimmer state
*/
public @Nullable Integer getState() {
return this.state;
}
/**
* Sets the state of Dimmer.
*
* @param dimmer state
*/
void setState(int state) {
this.state = state;
QbusDimmerHandler handler = thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Sends the dimmer state to Qbus.
*
* @throws IOException
* @throws InterruptedException
*/
public void execute(int percent, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeDimmer").withId(this.id).withState(percent);
QbusCommunication comm = this.qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Class {@link QbusMessageBase} used as base class for output from gson for cmd or event feedback from the Qbus server.
* This class only contains the common base fields required for the deserializer
* {@link QbusMessageDeserializer} to select the specific formats implemented in {@link QbusMessageMap},
* {@link QbusMessageListMap}, {@link QbusMessageCmd}.
* <p>
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
abstract class QbusMessageBase {
private @Nullable String ctd;
protected @Nullable String cmd;
protected @Nullable String type;
protected @Nullable Integer id;
protected @Nullable Integer state;
protected @Nullable Integer mode;
protected @Nullable Double setpoint;
protected @Nullable Double measured;
protected @Nullable Integer slatState;
@Nullable
String getSn() {
return this.ctd;
}
void setSn(String ctd) {
this.ctd = ctd;
}
@Nullable
String getCmd() {
return this.cmd;
}
void setCmd(String cmd) {
this.cmd = cmd;
}
@Nullable
public Integer getId() {
return id;
}
public void setType(String type) {
this.type = type;
}
@Nullable
public String getType() {
return type;
}
public void setId(Integer id) {
this.id = id;
}
@Nullable
public Integer getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
@Nullable
public Integer getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
@Nullable
public Double getSetPoint() {
return setpoint;
}
public void setSetPoint(Double setpoint) {
this.setpoint = setpoint;
}
@Nullable
public Double getMeasured() {
return measured;
}
public void setMeasured(Double measured) {
this.measured = measured;
}
@Nullable
public Integer getSlatState() {
return slatState;
}
public void setSlatState(int slatState) {
this.slatState = slatState;
}
}

View File

@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Class {@link QbusMessageCmd} used as input to gson to send commands to Qbus. Extends
* {@link QbusMessageBase}.
* <p>
* Example: <code>{"cmd":"executebistabiel","id":1,"value1":0}</code>
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
class QbusMessageCmd extends QbusMessageBase {
QbusMessageCmd(String CTD) {
super.setSn(CTD);
}
QbusMessageCmd(String CTD, String cmd) {
this(CTD);
this.cmd = cmd;
}
QbusMessageCmd withId(Integer id) {
this.setId(id);
return this;
}
QbusMessageCmd withState(int state) {
this.setState(state);
return this;
}
QbusMessageCmd withMode(int mode) {
this.setMode(mode);
return this;
}
QbusMessageCmd withSetPoint(Double setpoint) {
this.setSetPoint(setpoint);
return this;
}
QbusMessageCmd withSlatState(int slatState) {
this.setSlatState(slatState);
return this;
}
}

View File

@ -0,0 +1,157 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
/**
* Class {@link QbusMessageDeserializer} deserializes all json messages from Qbus. Various json
* message formats are supported. The format is selected based on the content of the cmd and event json objects.
*
* @author Koen Schockaert - Initial Contribution
*
*/
@NonNullByDefault
class QbusMessageDeserializer implements JsonDeserializer<QbusMessageBase> {
@Override
public @Nullable QbusMessageBase deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
String ctd = null;
String cmd = null;
Integer id = null;
Integer state = null;
Integer mode = null;
Double measured = null;
Double setpoint = null;
Integer slats = null;
QbusMessageBase message = null;
JsonElement jsonOutputs = null;
try {
if (jsonObject.has("CTD")) {
ctd = jsonObject.get("CTD").getAsString();
}
if (jsonObject.has("cmd")) {
cmd = jsonObject.get("cmd").getAsString();
}
if (jsonObject.has("id")) {
id = jsonObject.get("id").getAsInt();
}
if (jsonObject.has("state")) {
state = jsonObject.get("state").getAsInt();
}
if (jsonObject.has("mode")) {
mode = jsonObject.get("mode").getAsInt();
}
if (jsonObject.has("measured")) {
measured = jsonObject.get("measured").getAsDouble();
}
if (jsonObject.has("setpoint")) {
setpoint = jsonObject.get("setpoint").getAsDouble();
}
if (jsonObject.has("slats")) {
slats = jsonObject.get("slats").getAsInt();
}
if (jsonObject.has("outputs")) {
jsonOutputs = jsonObject.get("outputs");
}
if (ctd != null && cmd != null) {
if (jsonOutputs != null) {
if (jsonOutputs.isJsonArray()) {
JsonArray jsonOutputsArray = jsonOutputs.getAsJsonArray();
message = new QbusMessageListMap();
message.setCmd(cmd);
message.setSn(ctd);
List<Map<String, String>> outputsList = new ArrayList<>();
for (int i = 0; i < jsonOutputsArray.size(); i++) {
JsonObject jsonOutputsObject = jsonOutputsArray.get(i).getAsJsonObject();
Map<String, String> outputs = new HashMap<>();
for (Entry<String, JsonElement> entry : jsonOutputsObject.entrySet()) {
outputs.put(entry.getKey(), entry.getValue().getAsString());
}
outputsList.add(outputs);
}
((QbusMessageListMap) message).setOutputs(outputsList);
}
} else {
message = new QbusMessageMap();
message.setCmd(cmd);
message.setSn(ctd);
if (id != null) {
message.setId(id);
}
if (state != null) {
message.setState(state);
}
if (slats != null) {
message.setSlatState(slats);
}
if (mode != null) {
message.setMode(mode);
}
if (measured != null) {
message.setMeasured(measured);
}
if (setpoint != null) {
message.setSetPoint(setpoint);
}
}
}
return message;
} catch (IllegalStateException e) {
String mess = e.getMessage();
throw new JsonParseException("Unexpected Json format " + mess + " for " + jsonObject.toString());
}
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Class {@link QbusMessageListMap} used as output from gson for cmd or event feedback from Qbus where the
* data part is enclosed by [] and contains a list of json strings. Extends {@link QbusMessageBase}.
* <p>
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
class QbusMessageListMap extends QbusMessageBase {
private List<Map<String, String>> outputs = new ArrayList<>();
List<Map<String, String>> getOutputs() {
return this.outputs;
}
void setOutputs(List<Map<String, String>> outputs) {
this.outputs = outputs;
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Class {@link QbusMessageMap} used as output from gson for cmd or event feedback from Qbus where the
* data part is a simple json string. Extends {@link QbusMessageBase}.
* <p>
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
class QbusMessageMap extends QbusMessageBase {
private Map<String, String> outputs = new HashMap<>();
Map<String, String> getData() {
return this.outputs;
}
void setOutputs(Map<String, String> outputs) {
this.outputs = outputs;
}
}

View File

@ -0,0 +1,138 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusRolHandler;
/**
* The {@link QbusRol} class represents the action Qbus Shutter/Slats output.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusRol {
private @Nullable QbusCommunication qComm;
private Integer id;
private @Nullable Integer state;
private @Nullable Integer slats;
private @Nullable QbusRolHandler thingHandler;
QbusRol(Integer id) {
this.id = id;
}
/**
* This method should be called if the ThingHandler for the thing corresponding to this Shutter/Slats is
* initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the shutter/slat receives an update from the Qbus client.
*
* @param qbusRolHandler
*/
public void setThingHandler(QbusRolHandler qbusRolHandler) {
this.thingHandler = qbusRolHandler;
}
/**
* This method sets a pointer to the qComm Shutter/Slats of class {@link QbusCommuncation}.
* This is then used to be able to call back the sendCommand method in this class to send a command to the
* Qbus IP-interface when..
*
* @param qComm
*/
public void setQComm(QbusCommunication qComm) {
this.qComm = qComm;
}
/**
* Update the value of the Shutter.
*
* @param Shutter value
*/
public void updateState(@Nullable Integer state) {
this.state = state;
QbusRolHandler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Update the value of the Slats.
*
* @param Slat value
*/
public void updateSlats(@Nullable Integer Slats) {
this.slats = Slats;
QbusRolHandler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Get the value of the Shutter.
*
* @return shutter value
*/
public @Nullable Integer getState() {
return this.state;
}
/**
* Get the value of the Slats.
*
* @return slats value
*/
public @Nullable Integer getStateSlats() {
return this.slats;
}
/**
* Sends shutter state to Qbus.
*
* @throws IOException
* @throws InterruptedException
*/
public void execute(int value, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeStore").withId(this.id).withState(value);
QbusCommunication comm = qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
/**
* Sends slats state to Qbus.
*
* @throws IOException
* @throws InterruptedException
*/
public void executeSlats(int value, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeSlats").withId(this.id).withState(value);
QbusCommunication comm = qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
}

View File

@ -0,0 +1,88 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusSceneHandler;
/**
* The {@link QbusScene} class represents the action Qbus Scene output.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusScene {
private @Nullable QbusCommunication qComm;
public @Nullable QbusSceneHandler thingHandler;
private @Nullable Integer state;
private Integer id;
QbusScene(Integer id) {
this.id = id;
}
/**
* This method should be called if the ThingHandler for the thing corresponding to this scene is initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the scene output receives an update from the Qbus client.
*
* @param handler
*/
public void setThingHandler(QbusSceneHandler handler) {
this.thingHandler = handler;
}
/**
* This method sets a pointer to the qComm SCENE of class {@link QbusCommuncation}.
* This is then used to be able to call back the sendCommand method in this class to send a command to the
* Qbus client.
*
* @param qComm
*/
public void setQComm(QbusCommunication qComm) {
this.qComm = qComm;
}
/**
* Get the value of the Scene.
*
* @return Scene value
*/
public @Nullable Integer getState() {
return this.state;
}
/**
* Sends Scene state to Qbus.
*
* @param value
* @param sn
* @throws InterruptedException
* @throws IOException
*/
public void execute(int value, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeScene").withId(this.id).withState(value);
QbusCommunication comm = qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
}

View File

@ -0,0 +1,142 @@
/**
* Copyright (c) 2010-2021 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.qbus.internal.protocol;
import java.io.IOException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.qbus.internal.handler.QbusThermostatHandler;
/**
* The {@link QbusThermostat} class represents the thermostat Qbus communication object. It contains all
* fields representing a Qbus thermostat and has methods to set the thermostat mode and setpoint in Qbus and
* receive thermostat updates.
*
* @author Koen Schockaert - Initial Contribution
*/
@NonNullByDefault
public final class QbusThermostat {
private @Nullable QbusCommunication qComm;
private Integer id;
private double measured = 0.0;
private double setpoint = 0.0;
private @Nullable Integer mode;
private @Nullable QbusThermostatHandler thingHandler;
QbusThermostat(Integer id) {
this.id = id;
}
/**
* This method should be called if the ThingHandler for the thing corresponding to the termostat is initialized.
* It keeps a record of the thing handler in this object so the thing can be updated when
* the thermostat receives an update from the Qbus client.
*
* @param handler
*/
public void setThingHandler(QbusThermostatHandler handler) {
this.thingHandler = handler;
}
/**
* This method sets a pointer to the qComm THERMOSTAT of class {@link QbusCommuncation}.
* This is then used to be able to call back the sendCommand method in this class to send a command to the
* Qbus client.
*
* @param qComm
*/
public void setQComm(QbusCommunication qComm) {
this.qComm = qComm;
}
/**
* Update all values of the Thermostat
*
* @param measured current temperature in 1°C multiples
* @param setpoint the setpoint temperature in 1°C multiples
* @param mode 0="Manual", 1="Freeze", 2="Economic", 3="Comfort", 4="Night"
*/
public void updateState(Double measured, Double setpoint, Integer mode) {
this.measured = measured;
this.setpoint = setpoint;
this.mode = mode;
QbusThermostatHandler handler = this.thingHandler;
if (handler != null) {
handler.handleStateUpdate(this);
}
}
/**
* Get measured temperature of the Thermostat.
*
* @return measured temperature in 0.5°C multiples
*/
public @Nullable Double getMeasured() {
return this.measured;
}
/**
* Get setpoint temperature of the Thermostat.
*
* @return the setpoint temperature in 0.5°C multiples
*/
public @Nullable Double getSetpoint() {
return this.setpoint;
}
/**
* Get the Thermostat mode.
*
* @return the mode: 0="Manual", 1="Freeze", 2="Economic", 3="Comfort", 4="Night"
*/
public @Nullable Integer getMode() {
return this.mode;
}
/**
* Sends Thermostat mode to Qbus.
*
* @param mode
* @param sn
* @throws InterruptedException
* @throws IOException
*/
public void executeMode(int mode, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeThermostat").withId(this.id).withMode(mode);
QbusCommunication comm = this.qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
/**
* Sends Thermostat setpoint to Qbus.
*
* @param setpoint
* @throws IOException
* @throws InterruptedException
*/
public void executeSetpoint(double setpoint, String sn) throws InterruptedException, IOException {
QbusMessageCmd qCmd = new QbusMessageCmd(sn, "executeThermostat").withId(this.id).withSetPoint(setpoint);
QbusCommunication comm = this.qComm;
if (comm != null) {
comm.sendMessage(qCmd);
}
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="qbus" 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>Qbus Binding</name>
<description>This is the binding for the Qbus home automation system. Qbus is a system made and developed in Belgium
(https://www.qbus.be/nl-nl)</description>
</binding:binding>

View File

@ -0,0 +1,85 @@
# binding
binding.qbus.name = Qbus Binding
binding.qbus.description = Deze binding maakt via een server applicate verbinding met de Qbus controller.
# thing types
thing-type.qbus.bridge.label = Qbus Bridge
thing-type.qbus.bridge.description = De Qbus Bridge Maakt Verbinding Met de Qbus Server.
thing-type.config.qbus.bridge.addr.label = IP Adres of Host Naam
thing-type.config.qbus.bridge.addr.description = IP adres van de Qbus server, meestal 'localhost'
thing-type.config.qbus.bridge.sn.label = Serienummer van de Controller
thing-type.config.qbus.bridge.sn.description = Serienummer van de CTD controller
thing-type.config.qbus.bridge.port.label = Poort
thing-type.config.qbus.bridge.port.description = Communicatiepoort van de Qbus server (standaard: 8447)
thing-type.config.qbus.bridge.serverCheck.label = Server Connectie
thing-type.config.qbus.bridge.serverCheck.description = Ingestelde timer, bij het verlopen van de timer zal de communicatie met de Qbus server gecontroleerd worden en indien nodig herstart.
thing-type.qbus.onOff.label = Aan/uit
thing-type.qbus.onOff.description = Alle Bistabiel-Mono-Timer-Interval uitgangen
thing-type.config.qbus.onOff.bistabielId.label = Qbus ID
thing-type.config.qbus.onOff.bistabielId.description = Identificatienummer van de uitgang (zie SMIII)
thing-type.qbus.scene.label = Sfeer
thing-type.qbus.scene.description = Alle sferen
thing-type.config.qbus.scene.sceneId.label = Qbus ID
thing-type.config.qbus.scene.sceneId.description = Identificatienummer van de sfeer (zie SMIII)
thing-type.qbus.co2.label = CO2
thing-type.qbus.co2.description = Alle CO2 Uitgangen
thing-type.config.qbus.co2.co2Id.label = Qbus ID
thing-type.config.qbus.co2.co2Id.description = Identificatienummer van de uitgang (zie SMIII)
thing-type.qbus.dimmer.label = Dimmer
thing-type.qbus.dimmer.description = Alle Dimbare Uitgangen
thing-type.config.qbus.dimmer.dimmerId.label = Qbus ID
thing-type.config.qbus.dimmer.dimmerId.description = Identificatienummer van de uitgang (zie SMIII)
thing-type.config.qbus.dimmer.step.label = Stappenwaarde
thing-type.config.qbus.dimmer.step.description = Waarde gebruikt voor het dimmen in stappen (standaard 10%)
thing-type.qbus.rollershutter.label = Rolluik
thing-type.qbus.rollershutter.description = Alle Rolluik (ROL02P) Uitgangen
thing-type.config.qbus.rollershutter.rolId.label = Qbus ID
thing-type.config.qbus.rollershutter.rolId.description = Identificatienummer van de uitgang (zie SMIII)
thing-type.qbus.rollershutter_slats.label = Rolluik (met lamellen)
thing-type.qbus.rollershutter_slats.description = Alle schermen met lamellen (ROL02P) uitgang
thing-type.config.qbus.rollershutter_slats.rolId.label = Qbus ID
thing-type.config.qbus.rollershutter_slats.rolId.description = Identificatienummer van de uitgang (zie SMIII)
thing-type.qbus.thermostat.label = Thermostaat
thing-type.qbus.thermostat.description = Alle thermostaten
thing-type.config.qbus.thermostat.thermostatId.label = Qbus ID
thing-type.config.qbus.thermostat.thermostatId.description = Identificatienummer van de uitgang (zie SMIII)
channel-type.qbus.scene.label = Sfeer
channel-type.qbus.scene.description = Bediening van de sfeer
channel-type.qbus.co2.label = CO2
channel-type.qbus.co2.description = Uitlezing van de CO2 waarde
channel-type.qbus.switch.label = Schakelaar
channel-type.qbus.switch.description = Schakelaar bediening van de uitgangen
channel-type.qbus.brightness.label = Helderheid
channel-type.qbus.brightness.description = Helderheid bediening van de uitgangen
channel-type.qbus.measured.label = Gemeten Temperatuur
channel-type.qbus.measured.description = Uitlezing van de gemeten Temperatuur
channel-type.qbus.setpoint.label = Ingestelde Temperatuur
channel-type.qbus.setpoint.description = Ingestelde temperatuur bediening van de uitgangen
channel-type.qbus.mode.label = Ingesteld Regime
channel-type.qbus.mode.description = Regime bediening van de uitgangen
channel-type.qbus.mode.state.option.0 = Manueel
channel-type.qbus.mode.state.option.1 = Vorst
channel-type.qbus.mode.state.option.2 = Economisch
channel-type.qbus.mode.state.option.3 = Comfort
channel-type.qbus.mode.state.option.4 = Nacht
channel-type.qbus.rollershutter.label = Rolluik Bediening
channel-type.qbus.rollershutter.description = Rolluik bediening van de uitgangen
channel-type.qbus.slats.label = Lamellen Bediening
channel-type.qbus.slats.description = Lamellen bediening van de uitgangen

View File

@ -0,0 +1,231 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="qbus"
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-type id="bridge">
<label>Qbus Bridge</label>
<description>This bridge represents a Qbus client</description>
<config-description>
<parameter name="addr" type="text" required="true">
<label>Hostname</label>
<description>IP address or hostname of Qbus server, usually 'localhost'</description>
<default>localhost</default>
<context>network-address</context>
</parameter>
<parameter name="sn" type="text" required="true">
<label>Serial Number</label>
<description>Serial number of the CTD controller</description>
</parameter>
<parameter name="port" type="integer" required="false">
<label>Bridge Port</label>
<description>Port to communicate with Qbus server, default 8447</description>
<default>8447</default>
<advanced>true</advanced>
</parameter>
<parameter name="serverCheck" type="integer" required="false" unit="min" min="1">
<label>Server Check</label>
<description>Time to check communication with Qbus Server (min), default 10. If set to 0 or left empty, no refresh
will be scheduled</description>
<default>10</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<thing-type id="onOff">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Switch</label>
<description>Bistabiel-Mono-Timer-Interval Output</description>
<channels>
<channel id="switch" typeId="system.power"/>
</channels>
<config-description>
<parameter name="bistabielId" type="integer" required="true">
<label>Qbus ID</label>
<description>Qbus Bistabiel ID</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="scene">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Scene</label>
<description>Qbus Scene</description>
<channels>
<channel id="scene" typeId="scene"/>
</channels>
<config-description>
<parameter name="sceneId" type="integer" required="true">
<label>Qbus Scene ID</label>
<description>Qbus Scene ID</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="co2">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>CO2</label>
<description>Qbus CO2</description>
<channels>
<channel id="co2" typeId="co2"/>
</channels>
<config-description>
<parameter name="co2Id" type="integer" required="true">
<label>Qbus CO2 ID</label>
<description>Qbus CO2 ID</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="dimmer">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Dimmer</label>
<description>Qbus Dimmer Output</description>
<channels>
<channel id="brightness" typeId="system.brightness"/>
</channels>
<config-description>
<parameter name="dimmerId" type="integer" required="true">
<label>Output ID</label>
<description>Qbus Dimmer ID</description>
</parameter>
<parameter name="step" type="integer" required="true">
<label>Step Value</label>
<description>Step value used for increase/decrease of dimmer brightness, default 10%</description>
<default>10</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<thing-type id="rollershutter">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>RollerShutter</label>
<description>Qbus shutter (ROL02P) control</description>
<channels>
<channel id="rollershutter" typeId="rollershutter"/>
</channels>
<config-description>
<parameter name="rolId" type="integer" required="true">
<label>Rol ID</label>
<description>Qbus Rol Id</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="rollershutter_slats">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>RollerShutter (With Slats)</label>
<description>Qbus shutter with slats control</description>
<channels>
<channel id="rollershutter" typeId="rollershutter"/>
<channel id="slats" typeId="slats"/>
</channels>
<config-description>
<parameter name="rolId" type="integer" required="true">
<label>Rol ID</label>
<description>Qbus Rol Id</description>
</parameter>
</config-description>
</thing-type>
<thing-type id="thermostat">
<supported-bridge-type-refs>
<bridge-type-ref id="bridge"/>
</supported-bridge-type-refs>
<label>Thermostat</label>
<description>Qbus Thermostat</description>
<channels>
<channel id="measured" typeId="measured"/>
<channel id="mode" typeId="mode"/>
<channel id="setpoint" typeId="setpoint"/>
</channels>
<config-description>
<parameter name="thermostatId" type="integer" required="true">
<label>Thermostat ID</label>
<description>Qbus Thermostat ID</description>
</parameter>
</config-description>
</thing-type>
<channel-type id="scene">
<item-type>Switch</item-type>
<label>Scene</label>
<description>Scene Control for Qbus</description>
<category>Scene</category>
</channel-type>
<channel-type id="measured">
<item-type>Number:Temperature</item-type>
<label>Measured</label>
<description>Temperature Measured by Thermostat</description>
<category>Temperature</category>
<tags>
<tag>CurrentTemperature</tag>
</tags>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="setpoint">
<item-type>Number:Temperature</item-type>
<label>Setpoint</label>
<description>Setpoint Temperature of Thermostat</description>
<category>Temperature</category>
<tags>
<tag>TargetTemperature</tag>
</tags>
<state min="0" max="100" step="0.5" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="mode">
<item-type>Number</item-type>
<label>Mode</label>
<description>Thermostat Mode</description>
<category>Number</category>
<state>
<options>
<option value="0">Manual</option>
<option value="1">Freeze</option>
<option value="2">Economic</option>
<option value="3">Comfort</option>
<option value="4">Night</option>
</options>
</state>
</channel-type>
<channel-type id="co2">
<item-type>Number</item-type>
<label>CO2</label>
<description>CO2 value for Qbus</description>
<category>CO2</category>
</channel-type>
<channel-type id="rollershutter">
<item-type>Rollershutter</item-type>
<label>Rollershutter</label>
<description>Rollershutter Control for Qbus</description>
<category>Blinds</category>
</channel-type>
<channel-type id="slats">
<item-type>Dimmer</item-type>
<label>Slatcontrol</label>
<description>Slatcontrol for Qbus</description>
<category>Blinds</category>
</channel-type>
</thing:thing-descriptions>

View File

@ -253,6 +253,7 @@
<module>org.openhab.binding.pulseaudio</module>
<module>org.openhab.binding.pushbullet</module>
<module>org.openhab.binding.pushover</module>
<module>org.openhab.binding.qbus</module>
<module>org.openhab.binding.radiothermostat</module>
<module>org.openhab.binding.regoheatpump</module>
<module>org.openhab.binding.revogi</module>