added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/cgateinterface-1.1.0-JH-dev.jar"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.cbus</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,19 @@
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/openhab2-addons
== Third-party Content
* License: Apache 2.0 License
* Project: https://github.com/jpharvey/cgateinterface
* Source: https://github.com/jpharvey/cgateinterface

View File

@@ -0,0 +1,112 @@
# C-Bus Binding
This is the binding for the [Clipsal C-Bus System](http://www2.clipsal.com/cis/technical/product_groups/cbus).
This binding allows you to view and control groups on C-Bus networks from openHAB.
## Configuration
This binding connects to C-Gate software which can be downloaded from the [Clipsal Downloads Site](https://updates.clipsal.com/ClipsalSoftwareDownload/mainsite/cis/technical/index.html). There is information about setting up the C-Gate software in the [CBus Forums](https://www.cbusforums.com/forums/c-bus-toolkit-and-c-gate-software.4). Make sure that the config/access.txt file allows a connection from computer running openHAB.
Whilst all versions of C-Gate should work 2.11.2 contained a fix for handling Indicator Kill messages for trigger groups. Without that they will remain on the last value set and wont match what is shown on CBus devices.
First the CGate Connection bridge needs to be configured with the ip address of the computer running the C-Gate software.
After this a Bridge is creaed for each network configured on the CBus Network. The CBus Project Name and the network Id for that network
## Supported Things
This binding support 6 different things types
| Thing | Type | Description |
|----------------|---------|-----------------------------------|
| cgate | Bridge | This connects to a C-Bus CGate instance to |
| network | Bridge | This connects to a C-Bus Network via a CGate bridge |
| light | Thing | This is for C-Bus lighting groups |
| temperature | Thing | This is for C-Bus temperature groups |
| trigger | Thing | This is for C-Bus trigger groups |
| dali | Thing | This is for C-Bus DALI dimming groups |
The scan within Paper UI will find all the groups on the CBus network and allow Things to be creaed for them.
##Channels
At startup the binding will scan the network for the values of all the groups and set those on the appropriate channels. It is not possible to fetch the value of a Trigger Group so those values will only be updated when a trigger is set on the CBus network.
### Lights
Light things have 2 channels which show the current state of the group on the cbus network and can also set the state of the group:-
* **state** - On/Off state of the light
* **level** - The level of the channel between 0 and 100
### Temperature
Temperature things have 1 channel which shows the current value. This is read-only and will not set the value on the CBus Network.
* **temp** - Temperature value
### Trigger
Trigger things have 1 channel which shows the current trigger value on the cbus network and can be used to set a trigger value on the CBus Network.
* **value** - CBus Trigger value
### Dali
Dali things have 1 channel which shows the current value on the cbus network and can be used to set a value on the CBus Network.
* **level** - Value from the DALI node
## Example
### cbus.things
```
/* Need a cgate bridge to connect to cgate and then 1 network bridge for each network on that system */
Bridge cbus:cgate:cgatenetwork "file - cgate" [ ipAddress="127.0.0.1"] {
Bridge network cbusnetwork "file - network" [ id=254, project="OURHOME" ] {
/* Things can be configured within each network bridge */
Thing light light27 "light 27" [group=27]
}
}
/* Things can be configured seperatly and associated with the network bridge */
Thing cbus:light:cgatenetwork:cbusnetwork:light31 "light 31" (cbus:network:cgatenetwork:cbusnetwork) [ group=31 ]
Thing cbus:trigger:cgatenetwork:cbusnetwork:trigger1 "trigger 1" (cbus:network:cgatenetwork:cbusnetwork) [ group=1 ]
Thing cbus:temperature:cgatenetwork:cbusnetwork:temp2 "temp 2" (cbus:network:cgatenetwork:cbusnetwork) [ group=2 ]
Thing cbus:dali:cgatenetwork:cbusnetwork:dali3 "dali 3 value" (cbus:network:cgatenetwork:cbusnetwork) [ group=3 ]
```
### cbus.items
```
Dimmer light31Dimmer { channel="cbus:light:cgatenetwork:cbusnetwork:light31:level"}
Switch light31Switch { channel="cbus:light:cgatenetwork:cbusnetwork:light31:state"}
Number trigger1Value { channel="cbus:trigger:cgatenetwork:cbusnetwork:trigger1:value"}
Number temp2 { channel="cbus:temperature:cgatenetwork:cbusnetwork:temp2:temp"}
Dimmer dali3 { channel="cbus:dali:cgatenetwork:cbusnetwork:dali3:level"}
```
### cbusdemo.sitemap
```
sitemap cbusdemo label="CBus Binding Demo"
{
Frame label="light" {
Slider item=light31Dimmer label="dimmer"
Switch item=light31Switch label="switch"
}
Frame label="trigger" {
Switch item=trigger1Value label="trigger Value" mappings=[0="light 1", 1="light 2", 2="both lights", 3="off"]
}
Frame label="temperature" {
Default item=temp2 label="Temperature" icon="temperature"
}
Frame label="dali" {
Default item=dali3 label="Dali Level"
}
}
```

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.cbus</artifactId>
<name>openHAB Add-ons :: Bundles :: CBUs Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.cbus-${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-cbus" description="CBus Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.cbus/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link CBusBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusBindingConstants {
public static final String BINDING_ID = "cbus";
// List of main things
public static final String BRIDGE_CGATE = "cgate";
public static final String BRIDGE_NETWORK = "network";
public static final String THING_GROUP = "group";
public static final String THING_LIGHT = "light";
public static final String THING_TEMPERATURE = "temperature";
public static final String THING_TRIGGER = "trigger";
public static final String THING_DALI = "dali";
// List of all Thing Type UIDs
public static final ThingTypeUID BRIDGE_TYPE_CGATE = new ThingTypeUID(BINDING_ID, BRIDGE_CGATE);
public static final ThingTypeUID BRIDGE_TYPE_NETWORK = new ThingTypeUID(BINDING_ID, BRIDGE_NETWORK);
public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, THING_GROUP);
public static final ThingTypeUID THING_TYPE_LIGHT = new ThingTypeUID(BINDING_ID, THING_LIGHT);
public static final ThingTypeUID THING_TYPE_TEMPERATURE = new ThingTypeUID(BINDING_ID, THING_TEMPERATURE);
public static final ThingTypeUID THING_TYPE_TRIGGER = new ThingTypeUID(BINDING_ID, THING_TRIGGER);
public static final ThingTypeUID THING_TYPE_DALI = new ThingTypeUID(BINDING_ID, THING_DALI);
// List of all Channel ids
public static final String CHANNEL_LEVEL = "level";
public static final String CHANNEL_STATE = "state";
public static final String CHANNEL_TEMP = "temp";
public static final String CHANNEL_VALUE = "value";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<ThingTypeUID>(
Arrays.asList(BRIDGE_TYPE_CGATE, BRIDGE_TYPE_NETWORK, THING_TYPE_GROUP, THING_TYPE_LIGHT,
THING_TYPE_TEMPERATURE, THING_TYPE_TRIGGER, THING_TYPE_DALI));
public static final Set<ThingTypeUID> NETWORK_DISCOVERY_THING_TYPES_UIDS = new HashSet<ThingTypeUID>(
Arrays.asList(BRIDGE_TYPE_NETWORK));
public static final String CONFIG_NETWORK_ID = "id";
public static final String CONFIG_NETWORK_PROJECT = "project";
public static final String CONFIG_NETWORK_SYNC = "syncInterval";
public static final String PROPERTY_NETWORK_NAME = "name";
public static final String CONFIG_CGATE_IP_ADDRESS = "ipAddress";
public static final String CONFIG_GROUP_ID = "group";
public static final String PROPERTY_NETWORK_ID = "CBUS Network Id";
public static final String PROPERTY_APPLICATION_ID = "CBUS Application Id";
public static final String PROPERTY_GROUP_NAME = "CBUS Group Name";
public static final int CBUS_APPLICATION_LIGHTING = 56;
public static final int CBUS_APPLICATION_TEMPERATURE = 25;
public static final int CBUS_APPLICATION_TRIGGER = 202;
public static final int CBUS_APPLICATION_DALI = 95;
}

View File

@@ -0,0 +1,379 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.GregorianCalendar;
import java.util.LinkedList;
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.cbus.CBusBindingConstants;
import org.openhab.binding.cbus.internal.CBusCGateConfiguration;
import org.openhab.binding.cbus.internal.CBusThreadPool;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateConnectException;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.CGateInterface;
import com.daveoxley.cbus.CGateSession;
import com.daveoxley.cbus.events.EventCallback;
import com.daveoxley.cbus.status.StatusChangeCallback;
/**
* The {@link CBusCGateHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusCGateHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(CBusCGateHandler.class);
private @Nullable InetAddress ipAddress;
public @Nullable CGateSession cGateSession;
private @Nullable ScheduledFuture<?> keepAliveFuture;
public CBusCGateHandler(Bridge br) {
super(br);
}
// This is abstract in base class so have to implement it.
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
updateStatus(ThingStatus.OFFLINE);
logger.debug("Initializing CGate Bridge handler. {} {}", getThing().getThingTypeUID(), getThing().getUID());
CBusCGateConfiguration configuration = getConfigAs(CBusCGateConfiguration.class);
logger.debug("Using configuration {}", configuration);
try {
this.ipAddress = InetAddress.getByName(configuration.ipAddress);
} catch (UnknownHostException e1) {
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
"IP Address not resolvable");
return;
}
InetAddress address = this.ipAddress;
if (address != null) {
logger.debug("CGate IP {}.", address.getHostAddress());
}
keepAliveFuture = scheduler.scheduleWithFixedDelay(this::keepAlive, 0, 100, TimeUnit.SECONDS);
}
private void keepAlive() {
CGateSession session = cGateSession;
if (session == null || !session.isConnected()) {
if (!getThing().getStatus().equals(ThingStatus.ONLINE)) {
connect();
} else {
updateStatus();
}
}
}
private void connect() {
CGateSession cGateSession = this.cGateSession;
if (cGateSession == null) {
cGateSession = CGateInterface.connect(this.ipAddress, 20023, 20024, 20025, new CBusThreadPool());
cGateSession.registerEventCallback(new EventMonitor());
cGateSession.registerStatusChangeCallback(new StatusChangeMonitor());
this.cGateSession = cGateSession;
}
if (cGateSession.isConnected()) {
logger.debug("CGate session reports online");
updateStatus(ThingStatus.ONLINE);
} else {
try {
cGateSession.connect();
updateStatus();
} catch (CGateConnectException e) {
updateStatus();
logger.debug("Failed to connect to CGate:", e);
try {
cGateSession.close();
} catch (CGateException ignore) {
// We dont really care if an exception is thrown when clossing the connection after a failure
// connecting.
}
}
}
}
private void updateStatus() {
ThingStatus lastStatus = getThing().getStatus();
CGateSession cGateSession = this.cGateSession;
if (cGateSession == null) {
return;
}
if (cGateSession.isConnected()) {
updateStatus(ThingStatus.ONLINE);
} else {
if (lastStatus != ThingStatus.OFFLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
}
if (!getThing().getStatus().equals(lastStatus)) {
boolean isOnline = getThing().getStatus().equals(ThingStatus.ONLINE);
updateChildThings(isOnline);
}
}
private void updateChildThings(boolean isOnline) {
scheduler.execute(() -> {
// now also re-initialize all network handlers
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler instanceof CBusNetworkHandler) {
((CBusNetworkHandler) handler).cgateStateChanged(isOnline);
}
}
});
}
private void updateGroup(@Nullable String address, @Nullable String value) {
if (address == null || value == null) {
return;
}
logger.debug("updateGroup address {}", address);
// Address should be of the form //Project/network/application/group
if (!address.startsWith("//")) {
logger.debug("Address does not start with // so ignoring this update");
return;
}
String[] addressParts = address.substring(2).split("/");
if (addressParts.length != 4) {
logger.debug("Address is badly formed so ignoring this update length of parts is {} not 4",
addressParts.length);
return;
}
updateGroup(Integer.parseInt(addressParts[1]), Integer.parseInt(addressParts[2]),
Integer.parseInt(addressParts[3]), value);
}
private void updateGroup(int network, int application, int group, String value) {
for (Thing networkThing : getThing().getThings()) {
// Is this networkThing from the network we are looking for...
if (networkThing.getThingTypeUID().equals(CBusBindingConstants.BRIDGE_TYPE_NETWORK)) {
CBusNetworkHandler netThingHandler = (CBusNetworkHandler) networkThing.getHandler();
if (netThingHandler == null || netThingHandler.getNetworkId() != network) {
continue;
}
// Loop through all the things on this network and see if they match the application / group
for (Thing thing : netThingHandler.getThing().getThings()) {
ThingHandler thingThingHandler = thing.getHandler();
if (thingThingHandler == null) {
continue;
}
if (thingThingHandler instanceof CBusGroupHandler) {
((CBusGroupHandler) thingThingHandler).updateGroup(application, group, value);
}
}
}
}
}
public @Nullable CGateSession getCGateSession() {
return cGateSession;
}
@Override
public void dispose() {
ScheduledFuture<?> keepAliveFuture = this.keepAliveFuture;
if (keepAliveFuture != null) {
keepAliveFuture.cancel(true);
}
CGateSession cGateSession = this.cGateSession;
if (cGateSession != null && cGateSession.isConnected()) {
try {
cGateSession.close();
} catch (CGateException e) {
logger.warn("Cannot close CGate session", e);
}
} else {
logger.debug("no session or it is disconnected");
}
super.dispose();
}
@NonNullByDefault
private class EventMonitor extends EventCallback {
@Override
public boolean acceptEvent(int eventCode) {
return true;
}
@Override
public void processEvent(@Nullable CGateSession cgate_session, int eventCode,
@Nullable GregorianCalendar event_time, @Nullable String event) {
if (event == null) {
return;
}
if (eventCode == 701) {
// By Marking this as Nullable it fools the static analyser into understanding that poll can return Null
LinkedList<@Nullable String> tokenizer = new LinkedList<>(Arrays.asList(event.trim().split("\\s+")));
@Nullable
String address = tokenizer.poll();
tokenizer.poll();
@Nullable
String value = tokenizer.poll();
if (value != null && value.startsWith("level=")) {
String level = value.replace("level=", "");
updateGroup(address, level);
}
}
}
}
@NonNullByDefault
private class StatusChangeMonitor extends StatusChangeCallback {
@Override
public boolean isActive() {
return true;
}
@Override
public void processStatusChange(@Nullable CGateSession cGateSession, @Nullable String status) {
if (cGateSession == null || status == null) {
return;
}
if (status.startsWith("# ")) {
status = status.substring(2);
// Shouldnt need to check for null but this silences a warning
if (status == null || status.isEmpty()) {
return;
}
}
logger.debug("ProcessStatusChange {}", status);
String[] contents = status.split("#");
if (cGateSession.getSessionID() != null
&& contents[1].contains("sessionId=" + cGateSession.getSessionID())) {
// We created this event - don't worry about processing it again...
return;
}
// By Marking this as Nullable it fools the static analyser into understanding that poll can return Null
LinkedList<@Nullable String> tokenizer = new LinkedList<>(Arrays.asList(contents[0].split("\\s+")));
@Nullable
String firstToken = tokenizer.poll();
if (firstToken == null) {
logger.debug("ProcessStateChange: Cant tokenize status {}", status);
return;
}
switch (firstToken) {
case "lighting": {
@Nullable
String state = tokenizer.poll();
@Nullable
String address = tokenizer.poll();
if ("ramp".equals(state)) {
state = tokenizer.poll();
}
updateGroup(address, state);
break;
}
case "temperature": {
// For temperature we ignore the state
tokenizer.poll();
@Nullable
String address = tokenizer.poll();
@Nullable
String temp = tokenizer.poll();
updateGroup(address, temp);
break;
}
case "trigger": {
@Nullable
String command = tokenizer.poll();
@Nullable
String address = tokenizer.poll();
if ("event".equals(command)) {
@Nullable
String level = tokenizer.poll();
updateGroup(address, level);
} else if ("indicatorkill".equals(command)) {
updateGroup(address, "-1");
} else {
logger.warn("Unhandled trigger command {} - status {}", command, status);
}
break;
}
case "clock": {
@Nullable
String address = "";
@Nullable
String value = "";
@Nullable
String type = tokenizer.poll();
if ("date".equals(type)) {
address = tokenizer.poll() + "/1";
value = tokenizer.poll();
} else if ("time".equals(type)) {
address = tokenizer.poll() + "/0";
value = tokenizer.poll();
} else if (!"request_refresh".equals(type)) {
// We dont handle request_refresh as we are not a clock master
logger.debug("Received unknown clock event: {}", status);
}
if (value != null && !value.isEmpty()) {
updateGroup(address, value);
}
break;
}
default: {
LinkedList<String> commentTokenizer = new LinkedList<>(Arrays.asList(contents[1].split("\\s+")));
if ("lighting".equals(commentTokenizer.peek())) {
commentTokenizer.poll();
@Nullable
String commentToken = commentTokenizer.peek();
if ("SyncUpdate".equals(commentToken)) {
commentTokenizer.poll();
@Nullable
String address = commentTokenizer.poll();
@Nullable
String level = commentTokenizer.poll();
level = level.replace("level=", "");
updateGroup(address, level);
}
} else {
logger.debug("Received unparsed event: '{}'", status);
}
break;
}
}
}
}
}

View File

@@ -0,0 +1,126 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
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.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
/**
* The {@link CBusDaliHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusDaliHandler extends CBusGroupHandler {
private final Logger logger = LoggerFactory.getLogger(CBusDaliHandler.class);
public CBusDaliHandler(Thing thing) {
super(thing, CBusBindingConstants.CBUS_APPLICATION_DALI);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
Group group = this.group;
if (group == null) {
return;
}
if (command instanceof RefreshType) {
try {
int level = group.getLevel();
logger.debug("handle RefreshType Command for Chanell {} Group {} level {}", channelUID, groupId, level);
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_LEVEL)) {
updateState(channelUID, new PercentType((int) (level * 100 / 255.0)));
}
} catch (CGateException e) {
logger.debug("Failed to getLevel for group {}", groupId, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
} else {
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_LEVEL)) {
logger.debug("Channel Level command for {}: {}", channelUID, command);
try {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
group.on();
} else if (command == OnOffType.OFF) {
group.off();
}
} else if (command instanceof PercentType) {
PercentType value = (PercentType) command;
group.ramp((int) Math.round(value.doubleValue() / 100 * 255), 0);
} else if (command instanceof IncreaseDecreaseType) {
int level = group.getLevel();
if (command == IncreaseDecreaseType.DECREASE) {
level = Math.max(level - 1, 0);
} else if (command == IncreaseDecreaseType.INCREASE) {
level = Math.min(level + 1, 255);
}
group.ramp(level, 0);
logger.debug("Change group level to {}", level);
}
} catch (CGateException e) {
logger.debug("Cannot send command {} to {}", command, group, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
}
}
}
public void updateGroup(int updateApplicationId, int updateGroupId, String value) {
if (updateGroupId == groupId && updateApplicationId == applicationId) {
Thing thing = getThing();
Channel channel = thing.getChannel(CBusBindingConstants.CHANNEL_LEVEL);
if (channel != null) {
ChannelUID channelUID = channel.getUID();
if ("on".equalsIgnoreCase(value) || "255".equalsIgnoreCase(value)) {
updateState(channelUID, OnOffType.ON);
updateState(channelUID, new PercentType(100));
} else if ("off".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value)) {
updateState(channelUID, OnOffType.OFF);
updateState(channelUID, new PercentType(0));
} else {
try {
int v = Integer.parseInt(value);
PercentType perc = new PercentType(Math.round(v * 100 / 255));
updateState(channelUID, perc);
} catch (NumberFormatException e) {
logger.warn(
"Invalid value presented to channel {}. Received {}, expected On/Off or decimal value",
channelUID, value);
}
}
logger.debug("Updating CBus Lighting Group {} with value {}", thing.getUID(), value);
} else {
logger.debug("Failed to Updat CBus Lighting Group {} with value {}: No Channel", thing.getUID(), value);
}
}
}
}

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.binding.cbus.internal.CBusGroupConfiguration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.Application;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
import com.daveoxley.cbus.Network;
/**
* The {@link CBusGroupHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public abstract class CBusGroupHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(CBusGroupHandler.class);
protected @Nullable CBusNetworkHandler cBusNetworkHandler;
protected @Nullable Group group;
protected int applicationId = -1;
protected int groupId = -1;
public CBusGroupHandler(Thing thing, int applicationId) {
super(thing);
this.applicationId = applicationId;
}
@Override
public abstract void handleCommand(ChannelUID channelUID, Command command);
@Override
public void initialize() {
/*
* Cast to Nullable in map to avoid compiler warnings
*/
CBusGroupConfiguration configuration = getConfigAs(CBusGroupConfiguration.class);
logger.debug("Using configuration {}", configuration);
groupId = configuration.group;
CBusNetworkHandler cBusNetworkHandler = getCBusNetworkHandler();
this.cBusNetworkHandler = cBusNetworkHandler;
if (cBusNetworkHandler == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No CBusNetworkHandler Bridge available");
return;
}
updateStatus();
Map<String, String> updatedProperties = editProperties();
updatedProperties.put(CBusBindingConstants.PROPERTY_APPLICATION_ID, Integer.toString(applicationId));
updatedProperties.put(CBusBindingConstants.PROPERTY_NETWORK_ID,
Integer.toString(cBusNetworkHandler.getNetworkId()));
updateProperties(updatedProperties);
}
public void updateStatus() {
try {
logger.debug("updateStatus UID: {} applicaton: {} group: {}", getThing().getUID(), applicationId, groupId);
CBusNetworkHandler networkHandler = cBusNetworkHandler;
if (networkHandler == null || !networkHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
} else {
Group group = this.group;
if (group == null) {
group = getGroup();
this.group = group;
}
if (group == null) {
logger.debug("Set state to configuration error -no group");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No Group object available");
} else if (group.getNetwork().isOnline()) {
updateStatus(ThingStatus.ONLINE);
try {
Map<String, String> updatedProperties = editProperties();
updatedProperties.put(CBusBindingConstants.PROPERTY_GROUP_NAME, group.getName());
updateProperties(updatedProperties);
} catch (CGateException ignore) {
// Cant get name so properties wont be updated
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Network is not reporting online");
}
}
} catch (CGateException e) {
logger.debug("Problem checking network state for network {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
}
public abstract void updateGroup(int application, int group, String value);
private @Nullable Group getGroup() {
try {
CBusNetworkHandler networkHandler = cBusNetworkHandler;
if (networkHandler == null) {
return null;
}
Network network = networkHandler.getNetwork();
if (network != null) {
Application application = network.getApplication(applicationId);
if (application == null) {
logger.debug("getGroup() Cant get application for id {}", applicationId);
return null;
}
logger.debug("GetGroup for {}/id {}", applicationId, groupId);
return application.getGroup(groupId);
}
} catch (CGateException e) {
logger.debug("GetGroup for id {}/{} failed {}", applicationId, groupId, e.getMessage());
}
return null;
}
private @Nullable CBusNetworkHandler getCBusNetworkHandler() {
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Required bridge not defined for device .");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof CBusNetworkHandler) {
return (CBusNetworkHandler) handler;
}
logger.debug("No available bridge handler found for bridge: {}", bridge.getUID());
return null;
}
}

View File

@@ -0,0 +1,144 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
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.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
/**
* The {@link CBusLightHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusLightHandler extends CBusGroupHandler {
private final Logger logger = LoggerFactory.getLogger(CBusLightHandler.class);
public CBusLightHandler(Thing thing) {
super(thing, CBusBindingConstants.CBUS_APPLICATION_LIGHTING);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
Group group = this.group;
if (group == null) {
return;
}
if (command instanceof RefreshType) {
try {
int level = group.getLevel();
logger.debug("handle RefreshType Command for Chanell {} Group {} level {}", channelUID, groupId, level);
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_STATE)) {
updateState(channelUID, (level > 0) ? OnOffType.ON : OnOffType.OFF);
} else if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_LEVEL)) {
updateState(channelUID, new PercentType((int) (level * 100 / 255.0)));
}
} catch (CGateException e) {
logger.debug("Failed to getLevel for group {}", groupId, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
} else {
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_STATE)) {
logger.debug("Channel State command for {}: {}", channelUID, command);
if (command instanceof OnOffType) {
try {
if (command == OnOffType.ON) {
group.on();
} else if (command == OnOffType.OFF) {
group.off();
}
} catch (CGateException e) {
logger.debug("Failed to send command {} to {}", command, group, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
}
} else if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_LEVEL)) {
logger.debug("Channel Level command for {}: {}", channelUID, command);
try {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
group.on();
} else if (command == OnOffType.OFF) {
group.off();
}
} else if (command instanceof PercentType) {
PercentType value = (PercentType) command;
group.ramp((int) Math.round(value.doubleValue() / 100 * 255), 0);
} else if (command instanceof IncreaseDecreaseType) {
int level = group.getLevel();
if (command == IncreaseDecreaseType.DECREASE) {
level = Math.max(level - 1, 0);
} else if (command == IncreaseDecreaseType.INCREASE) {
level = Math.min(level + 1, 255);
}
group.ramp(level, 0);
logger.debug("Change group level to {}", level);
}
} catch (CGateException e) {
logger.debug("Failed to send command {} to {}", command, group, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
}
}
}
public void updateGroup(int updateApplicationId, int updateGroupId, String value) {
if (updateGroupId == groupId && updateApplicationId == applicationId) {
Thing thing = getThing();
Channel channel = thing.getChannel(CBusBindingConstants.CHANNEL_STATE);
Channel channelLevel = thing.getChannel(CBusBindingConstants.CHANNEL_LEVEL);
if (channel != null && channelLevel != null) {
ChannelUID channelUID = channel.getUID();
ChannelUID channelLevelUID = channelLevel.getUID();
logger.debug("channel UID {} level UID {}", channelUID, channelLevelUID);
if ("on".equalsIgnoreCase(value) || "255".equalsIgnoreCase(value)) {
updateState(channelUID, OnOffType.ON);
updateState(channelLevelUID, new PercentType(100));
} else if ("off".equalsIgnoreCase(value) || "0".equalsIgnoreCase(value)) {
updateState(channelUID, OnOffType.OFF);
updateState(channelLevelUID, new PercentType(0));
} else {
try {
int v = Integer.parseInt(value);
updateState(channelUID, v > 0 ? OnOffType.ON : OnOffType.OFF);
updateState(channelLevelUID, new PercentType((int) (v * 100 / 255.0)));
} catch (NumberFormatException e) {
logger.warn("Invalid value presented to channel {}. Received {}, expected On/Off", channelUID,
value);
}
}
logger.debug("Updating CBus Lighting Group {} with value {}", thing.getUID(), value);
} else {
logger.debug("Failed to Update CBus Lighting Group {} with value {}: No Channel", thing.getUID(),
value);
}
}
}
}

View File

@@ -0,0 +1,289 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
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.cbus.internal.CBusNetworkConfiguration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.CGateSession;
import com.daveoxley.cbus.Network;
import com.daveoxley.cbus.Project;
/**
* The {@link CBusNetworkHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusNetworkHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(CBusNetworkHandler.class);
private @Nullable CBusNetworkConfiguration configuration;
private @Nullable Network network;
private @Nullable Project projectObject;
private @Nullable ScheduledFuture<?> initNetwork;
private @Nullable ScheduledFuture<?> networkSync;
public CBusNetworkHandler(Bridge thing) {
super(thing);
}
// This is abstract in base class so have to implement it.
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
logger.debug("initialize ");
configuration = getConfigAs(CBusNetworkConfiguration.class);
logger.debug("Using configuration {}", configuration);
CBusCGateHandler bridgeHandler = getCBusCGateHandler();
if (bridgeHandler == null || !bridgeHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
logger.debug("bridge not online");
updateStatus(ThingStatus.OFFLINE);
return;
}
logger.debug("Bridge online so init properly");
scheduler.execute(this::cgateOnline);
}
@Override
public void dispose() {
ScheduledFuture<?> networkSync = this.networkSync;
if (networkSync != null) {
networkSync.cancel(false);
}
ScheduledFuture<?> initNetwork = this.initNetwork;
if (initNetwork != null) {
initNetwork.cancel(false);
}
super.dispose();
}
public void cgateStateChanged(boolean isOnline) {
logger.debug("CgateStateChanged {}", isOnline);
if (!isOnline) {
network = null;
projectObject = null;
updateStatus();
} else {
cgateOnline();
}
}
private void cgateOnline() {
CBusNetworkConfiguration configuration = this.configuration;
if (configuration == null) {
logger.debug("cgateOnline - NetworkHandler not initialised");
return;
}
ThingStatus lastStatus = getThing().getStatus();
logger.debug("cgateOnline {}", lastStatus);
Integer networkID = configuration.id;
String project = configuration.project;
logger.debug("cgateOnline netid {} project {}", networkID, project);
Project projectObject = getProjectObject();
Network network = getNetwork();
logger.debug("network {}", network);
CBusCGateHandler cbusCGateHandler = getCBusCGateHandler();
if (cbusCGateHandler == null) {
logger.debug("NoCGateHandler");
return;
}
try {
if (projectObject == null) {
CGateSession session = cbusCGateHandler.getCGateSession();
if (session != null) {
try {
projectObject = (Project) session.getCGateObject("//" + project);
this.projectObject = projectObject;
} catch (CGateException ignore) {
// We dont need to do anything other than stop this propagating
}
}
if (projectObject == null) {
logger.debug("Cant get projectobject");
return;
}
}
if (network == null) {
CGateSession session = cbusCGateHandler.getCGateSession();
if (session != null) {
try {
network = (Network) session.getCGateObject("//" + project + "/" + networkID);
this.network = network;
} catch (CGateException ignore) {
// We dont need to do anything other than stop this propagating
}
}
if (network == null) {
logger.debug("cgateOnline: Cant get network");
return;
}
}
String state = network.getState();
logger.debug("Network state is {}", state);
if ("new".equals(state)) {
projectObject.start();
logger.debug("Need to wait for it to be synced");
} else if ("sync".equals(state)) {
logger.debug("Network is syncing so wait for it to be ok");
}
if (!"ok".equals(state)) {
ScheduledFuture<?> initNetwork = this.initNetwork;
if (initNetwork == null || initNetwork.isCancelled()) {
this.initNetwork = scheduler.scheduleWithFixedDelay(this::checkNetworkOnline, 30, 30,
TimeUnit.SECONDS);
logger.debug("Schedule a check every minute");
} else {
logger.debug("initNetwork alreadys started");
}
updateStatus();
return;
}
} catch (CGateException e) {
logger.warn("Cannot load C-Bus network {}", networkID, e);
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.COMMUNICATION_ERROR);
}
updateStatus();
}
private void checkNetworkOnline() {
Network network = getNetwork();
try {
if (network != null && network.isOnline()) {
logger.debug("Network is online");
ScheduledFuture<?> initNetwork = this.initNetwork;
if (initNetwork != null) {
initNetwork.cancel(false);
this.initNetwork = null;
}
} else {
ThingStatus lastStatus = getThing().getStatus();
logger.debug("Network still not online {}", lastStatus);
}
} catch (CGateException e) {
logger.warn("Cannot check if network is online {} ", network.getNetworkID());
}
updateStatus();
}
private void updateStatus() {
CBusNetworkConfiguration configuration = this.configuration;
if (configuration == null) {
logger.debug("updateStatus - NetworkHandler not initialised");
return;
}
ThingStatus lastStatus = getThing().getStatus();
Network network = getNetwork();
CBusCGateHandler cbusCGateHandler = getCBusCGateHandler();
try {
if (cbusCGateHandler == null || !cbusCGateHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "CGate connection offline");
} else if (network == null) {
logger.debug("No network - set configuration error");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No Network object available");
} else if (network.isOnline()) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Network is not reporting online");
}
} catch (CGateException e) {
logger.warn("Problem checking network state for network {}", network.getNetworkID(), e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
}
if (!getThing().getStatus().equals(lastStatus)) {
ScheduledFuture<?> networkSync = this.networkSync;
if (lastStatus == ThingStatus.OFFLINE) {
if (networkSync == null || networkSync.isCancelled()) {
this.networkSync = scheduler.scheduleWithFixedDelay(this::doNetworkSync, 10,
configuration.syncInterval, TimeUnit.SECONDS);
}
} else {
if (networkSync != null) {
networkSync.cancel(false);
}
}
for (Thing thing : getThing().getThings()) {
ThingHandler handler = thing.getHandler();
if (handler instanceof CBusGroupHandler) {
((CBusGroupHandler) handler).updateStatus();
}
}
}
}
private void doNetworkSync() {
Network network = getNetwork();
try {
if (getThing().getStatus().equals(ThingStatus.ONLINE) && network != null) {
logger.info("Starting network sync on network {}", network.getNetworkID());
network.startSync();
}
} catch (CGateException e) {
logger.warn("Cannot start network sync on network {} - {}", network.getNetworkID(), e.getMessage());
}
}
private @Nullable CBusCGateHandler getCBusCGateHandler() {
logger.debug("getCBusCGateHandler");
Bridge bridge = getBridge();
if (bridge == null) {
logger.debug("Required bridge not defined for device.");
return null;
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof CBusCGateHandler) {
return (CBusCGateHandler) handler;
} else {
logger.debug("No available bridge handler found for bridge: {}.", bridge.getUID());
return null;
}
}
public @Nullable Network getNetwork() {
return network;
}
public int getNetworkId() {
CBusNetworkConfiguration configuration = this.configuration;
if (configuration == null) {
logger.debug("getNetworkId - NetworkHandler not initialised");
return -1;
}
return configuration.id;
}
private @Nullable Project getProjectObject() {
return projectObject;
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
/**
* The {@link CBusTemperatureHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusTemperatureHandler extends CBusGroupHandler {
private final Logger logger = LoggerFactory.getLogger(CBusTemperatureHandler.class);
public CBusTemperatureHandler(Thing thing) {
super(thing, CBusBindingConstants.CBUS_APPLICATION_TEMPERATURE);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// Read only thing - no commands to handle
if (command instanceof RefreshType) {
try {
Group group = this.group;
if (group != null) {
int level = group.getLevel();
logger.debug("handle RefreshType Command for Chanell {} Group {} level {}", channelUID, groupId,
level);
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_TEMP)) {
updateState(channelUID, new QuantityType<>(level, CELSIUS));
}
}
} catch (CGateException e) {
logger.debug("Failed to getLevel for group {}", groupId, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
}
}
public void updateGroup(int updateApplicationId, int updateGroupId, String value) {
if (updateGroupId == groupId && updateApplicationId == applicationId) {
Thing thing = getThing();
Channel channel = thing.getChannel(CBusBindingConstants.CHANNEL_TEMP);
if (channel != null) {
ChannelUID channelUID = channel.getUID();
updateState(channelUID, new QuantityType<>(Double.parseDouble(value), CELSIUS));
logger.trace("Updating CBus Temperature Group {} with value {}", thing.getUID(), value);
} else {
logger.trace("Failed to Update CBus Temperature Group {} with value {}: No Channel", thing.getUID(),
value);
}
}
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
/**
* The {@link CBusTriggerHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusTriggerHandler extends CBusGroupHandler {
private final Logger logger = LoggerFactory.getLogger(CBusTriggerHandler.class);
public CBusTriggerHandler(Thing thing) {
super(thing, CBusBindingConstants.CBUS_APPLICATION_TRIGGER);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
Group group = this.group;
if (group == null) {
return;
}
if (command instanceof RefreshType) {
/*
* Cgate cant provide the current value for a trigger group
*/
logger.debug("Refresh for Trigger group not implemented");
} else {
if (channelUID.getId().equals(CBusBindingConstants.CHANNEL_VALUE)) {
logger.debug("Channel Value command for {}: {}", channelUID, command);
try {
if (command instanceof DecimalType) {
group.TriggerEvent(((DecimalType) command).intValue());
}
} catch (CGateException e) {
logger.debug("Failed to send trigger command {} to {}", command, group, e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Communication Error");
}
}
}
}
public void updateGroup(int updateApplicationId, int updateGroupId, String value) {
if (updateGroupId == groupId && updateApplicationId == applicationId) {
Thing thing = getThing();
Channel channel = thing.getChannel(CBusBindingConstants.CHANNEL_VALUE);
if (channel != null) {
ChannelUID channelUID = channel.getUID();
DecimalType val = new DecimalType(value);
updateState(channelUID, val);
logger.trace("Updating CBus Trigger Group {} with value {}", thing.getUID(), value);
}
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for {@link CBusCGateConfiguration}.
*
* @author John Harvey - Initial contribution
*/
@NonNullByDefault
public class CBusCGateConfiguration {
public String ipAddress = "";
@Override
public String toString() {
return String.format("[ipAddress=%s]", ipAddress);
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for {@link CBusGroupConfiguration}.
*
* @author John Harvey - Initial contribution
*/
@NonNullByDefault
public class CBusGroupConfiguration {
public int group;
@Override
public String toString() {
return String.format("[group=%d]", group);
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal;
import java.util.Hashtable;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.binding.cbus.handler.CBusCGateHandler;
import org.openhab.binding.cbus.handler.CBusDaliHandler;
import org.openhab.binding.cbus.handler.CBusLightHandler;
import org.openhab.binding.cbus.handler.CBusNetworkHandler;
import org.openhab.binding.cbus.handler.CBusTemperatureHandler;
import org.openhab.binding.cbus.handler.CBusTriggerHandler;
import org.openhab.binding.cbus.internal.discovery.CBusGroupDiscovery;
import org.openhab.binding.cbus.internal.discovery.CBusNetworkDiscovery;
import org.openhab.core.config.discovery.DiscoveryService;
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.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Component;
/**
* The {@link CBusHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.cbus")
public class CBusHandlerFactory extends BaseThingHandlerFactory {
private @Nullable ServiceRegistration<?> cbusCGateHandlerServiceReg = null;
private @Nullable ServiceRegistration<?> cbusNetworkHandlerServiceReg = null;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return CBusBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (CBusBindingConstants.BRIDGE_TYPE_CGATE.equals(thingTypeUID)) {
CBusCGateHandler handler = new CBusCGateHandler((Bridge) thing);
registerDeviceDiscoveryService(handler);
return handler;
}
if (CBusBindingConstants.BRIDGE_TYPE_NETWORK.equals(thingTypeUID)) {
CBusNetworkHandler handler = new CBusNetworkHandler((Bridge) thing);
registerDeviceDiscoveryService(handler);
return handler;
}
if (CBusBindingConstants.THING_TYPE_LIGHT.equals(thingTypeUID)) {
return new CBusLightHandler(thing);
}
if (CBusBindingConstants.THING_TYPE_TEMPERATURE.equals(thingTypeUID)) {
return new CBusTemperatureHandler(thing);
}
if (CBusBindingConstants.THING_TYPE_TRIGGER.equals(thingTypeUID)) {
return new CBusTriggerHandler(thing);
}
if (CBusBindingConstants.THING_TYPE_DALI.equals(thingTypeUID)) {
return new CBusDaliHandler(thing);
}
return null;
}
@Override
protected void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof CBusCGateHandler) {
ServiceRegistration<?> serviceReg = this.cbusCGateHandlerServiceReg;
if (serviceReg != null) {
serviceReg.unregister();
}
} else if (thingHandler instanceof CBusNetworkHandler) {
ServiceRegistration<?> serviceReg = this.cbusNetworkHandlerServiceReg;
if (serviceReg != null) {
serviceReg.unregister();
}
}
super.removeHandler(thingHandler);
}
private void registerDeviceDiscoveryService(CBusCGateHandler cbusCgateHandler) {
CBusNetworkDiscovery discoveryService = new CBusNetworkDiscovery(cbusCgateHandler);
cbusCGateHandlerServiceReg = super.bundleContext.registerService(DiscoveryService.class.getName(),
discoveryService, new Hashtable<String, Object>());
}
private void registerDeviceDiscoveryService(CBusNetworkHandler cbusNetworkHandler) {
CBusGroupDiscovery discoveryService = new CBusGroupDiscovery(cbusNetworkHandler);
cbusNetworkHandlerServiceReg = bundleContext.registerService(DiscoveryService.class.getName(), discoveryService,
new Hashtable<String, Object>());
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration class for {@link CBusNetworkConfiguration}.
*
* @author John Harvey - Initial contribution
*/
@NonNullByDefault
public class CBusNetworkConfiguration {
public int id;
public String project = "";
public int syncInterval;
@Override
public String toString() {
return String.format("[id=%d, project=%s, syncInterval=%d]", id, project, syncInterval);
}
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.common.ThreadPoolManager;
import com.daveoxley.cbus.CGateThreadPool;
import com.daveoxley.cbus.CGateThreadPoolExecutor;
/**
* The {@link CBusThreadPool} is responsible for executing jobs from a threadpool
*
* @author John Harvey - Initial contribution
*/
@NonNullByDefault
public class CBusThreadPool extends CGateThreadPool {
private final Map<String, @Nullable CGateThreadPoolExecutor> executorMap = new HashMap<>();
@Override
protected synchronized CGateThreadPoolExecutor CreateExecutor(@Nullable String name) {
if (name == null || name.isEmpty()) {
name = "_default";
}
@Nullable
CGateThreadPoolExecutor executor = executorMap.get(name);
if (executor != null) {
return executor;
}
CGateThreadPoolExecutor newExecutor = new CBusThreadPoolExecutor(name);
executorMap.put(name, newExecutor);
return newExecutor;
}
@NonNullByDefault
public class CBusThreadPoolExecutor extends CGateThreadPoolExecutor {
private final ThreadPoolExecutor threadPool;
public CBusThreadPoolExecutor(@Nullable String poolName) {
threadPool = (ThreadPoolExecutor) ThreadPoolManager.getPool("binding.cbus-" + poolName);
}
@Override
protected void execute(@Nullable Runnable runnable) {
if (runnable != null) {
threadPool.execute(runnable);
}
}
}
}

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal.discovery;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.binding.cbus.handler.CBusNetworkHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.Application;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Group;
import com.daveoxley.cbus.Network;
/**
* The {@link CBusGroupDiscovery} class is used to discover CBus
* groups that are in the CBus Network
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusGroupDiscovery extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(CBusGroupDiscovery.class);
private final CBusNetworkHandler cbusNetworkHandler;
public CBusGroupDiscovery(CBusNetworkHandler cbusNetworkHandler) {
super(CBusBindingConstants.SUPPORTED_THING_TYPES_UIDS, 30, false);
this.cbusNetworkHandler = cbusNetworkHandler;
}
@Override
protected synchronized void startScan() {
if (cbusNetworkHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
ThingUID bridgeUid = cbusNetworkHandler.getThing().getBridgeUID();
if (bridgeUid == null) {
scanFinished();
return;
}
try {
Map<Integer, ThingTypeUID> applications = new HashMap<Integer, ThingTypeUID>();
applications.put(CBusBindingConstants.CBUS_APPLICATION_LIGHTING, CBusBindingConstants.THING_TYPE_LIGHT);
applications.put(CBusBindingConstants.CBUS_APPLICATION_DALI, CBusBindingConstants.THING_TYPE_DALI);
applications.put(CBusBindingConstants.CBUS_APPLICATION_TEMPERATURE,
CBusBindingConstants.THING_TYPE_TEMPERATURE);
applications.put(CBusBindingConstants.CBUS_APPLICATION_TRIGGER,
CBusBindingConstants.THING_TYPE_TRIGGER);
Network network = cbusNetworkHandler.getNetwork();
if (network == null) {
scanFinished();
return;
}
for (Map.Entry<Integer, ThingTypeUID> applicationItem : applications.entrySet()) {
Application application = network.getApplication(applicationItem.getKey());
if (application == null) {
continue;
}
ArrayList<Group> groups = application.getGroups(false);
for (Group group : groups) {
logger.debug("Found group: {} {} {}", application.getName(), group.getGroupID(),
group.getName());
Map<String, Object> properties = new HashMap<>();
properties.put(CBusBindingConstants.PROPERTY_APPLICATION_ID,
Integer.toString(applicationItem.getKey()));
properties.put(CBusBindingConstants.CONFIG_GROUP_ID, Integer.toString(group.getGroupID()));
properties.put(CBusBindingConstants.PROPERTY_GROUP_NAME, group.getName());
properties.put(CBusBindingConstants.PROPERTY_NETWORK_ID,
Integer.toString(network.getNetworkID()));
ThingUID uid = new ThingUID(applicationItem.getValue(), Integer.toString(group.getGroupID()),
bridgeUid.getId(), cbusNetworkHandler.getThing().getUID().getId());
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
.withLabel("CBUS " + group.getName() + "(" + group.getGroupID() + ")")
.withBridge(cbusNetworkHandler.getThing().getUID()).build();
thingDiscovered(result);
}
}
} catch (CGateException e) {
logger.debug("Failed to discover groups", e);
}
}
scanFinished();
}
private synchronized void scanFinished() {
stopScan();// this notifies the scan listener that the scan is finished
abortScan();// this clears the scheduled call to stopScan
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.cbus.internal.discovery;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.cbus.CBusBindingConstants;
import org.openhab.binding.cbus.handler.CBusCGateHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.daveoxley.cbus.CGateException;
import com.daveoxley.cbus.Network;
/**
* The {@link CBusNetworkDiscovery} class is used to discover CBus
* networks that are in the CBus Project
*
* @author Scott Linton - Initial contribution
*/
@NonNullByDefault
public class CBusNetworkDiscovery extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(CBusNetworkDiscovery.class);
private final CBusCGateHandler cBusCGateHandler;
public CBusNetworkDiscovery(CBusCGateHandler cBusCGateHandler) {
super(CBusBindingConstants.NETWORK_DISCOVERY_THING_TYPES_UIDS, 60, false);
this.cBusCGateHandler = cBusCGateHandler;
}
@Override
protected void startScan() {
if (cBusCGateHandler.getThing().getStatus().equals(ThingStatus.ONLINE)) {
try {
ArrayList<Network> networks = Network.listAll(cBusCGateHandler.getCGateSession(), false);
for (Network network : networks) {
logger.debug("Found Network: {} {}", network.getNetworkID(), network.getName());
Map<String, Object> properties = new HashMap<>(3);
properties.put(CBusBindingConstants.CONFIG_NETWORK_ID, network.getNetworkID());
properties.put(CBusBindingConstants.PROPERTY_NETWORK_NAME, network.getName());
properties.put(CBusBindingConstants.CONFIG_NETWORK_PROJECT, network.getProjectName());
ThingUID uid = new ThingUID(CBusBindingConstants.BRIDGE_TYPE_NETWORK,
network.getProjectName().toLowerCase().replace(" ", "_") + network.getNetworkID(),
cBusCGateHandler.getThing().getUID().getId());
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withProperties(properties)
.withLabel(
network.getProjectName() + "/" + network.getNetworkID() + " - " + network.getName())
.withBridge(cBusCGateHandler.getThing().getUID()).build();
thingDiscovered(result);
}
} catch (CGateException e) {
logger.warn("Failed to discover networks", e);
}
}
}
@Override
protected synchronized void stopScan() {
super.stopScan();
removeOlderResults(getTimestampOfLastScan());
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="cbus" 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>C-Bus Binding</name>
<description>Clipsal C-Bus Binding</description>
<author>Scott Linton</author>
</binding:binding>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="cbus"
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="cgate">
<label>CGate Connection</label>
<description>CGate Connection to connect to physical C-Bus Networks</description>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<context>network-address</context>
<label>CGate Server IP</label>
<description>The IP address of the CGate Server.</description>
</parameter>
</config-description>
</bridge-type>
<bridge-type id="network">
<label>C-Bus Network</label>
<description>C-Bus Network bridge</description>
<config-description>
<parameter name="id" type="integer" required="true">
<label>C-Bus Network ID</label>
<description>Network number of C-Bus Network.</description>
</parameter>
<parameter name="project" type="text" required="true">
<label>C-Bus Project Name</label>
<description>Project name that contains the network.</description>
</parameter>
<parameter name="syncInterval" type="integer" required="false" min="120" unit="s">
<label>Network Sync Interval</label>
<description>The interval in seconds between fetching current group states from the network.</description>
<default>600</default>
</parameter>
</config-description>
</bridge-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="cbus"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="light">
<supported-bridge-type-refs>
<bridge-type-ref id="network"/>
</supported-bridge-type-refs>
<label>Lighting Group</label>
<description>Lighting Group</description>
<channels>
<channel id="state" typeId="light-channel"/>
<channel id="level" typeId="level-channel"/>
</channels>
<properties>
<property name="CBUS Network Id"/>
<property name="CBUS Application Id"/>
<property name="CBUS Group Name"/>
</properties>
<config-description>
<parameter name="group" type="integer" required="true">
<label>CBus Group ID</label>
<description>Group number of this Thing on the C-Bus Network.</description>
<default>-1</default>
</parameter>
</config-description>
</thing-type>
<thing-type id="temperature">
<supported-bridge-type-refs>
<bridge-type-ref id="network"/>
</supported-bridge-type-refs>
<label>Temperature Group</label>
<description>Temperature Group</description>
<channels>
<channel id="temp" typeId="temp-channel"/>
</channels>
<properties>
<property name="CBUS Network Id"/>
<property name="CBUS Application Id"/>
<property name="CBUS Group Name"/>
</properties>
<config-description>
<parameter name="group" type="integer" required="true">
<label>CBus Group ID</label>
<description>Group number of this Thing on the C-Bus Network.</description>
<default>-1</default>
</parameter>
</config-description>
</thing-type>
<thing-type id="trigger">
<supported-bridge-type-refs>
<bridge-type-ref id="network"/>
</supported-bridge-type-refs>
<label>Trigger Group</label>
<description>Trigger Group</description>
<channels>
<channel id="value" typeId="trigger-channel"/>
</channels>
<properties>
<property name="CBUS Network Id"/>
<property name="CBUS Application Id"/>
<property name="CBUS Group Name"/>
</properties>
<config-description>
<parameter name="group" type="integer" required="true">
<label>CBus Group ID</label>
<description>Group number of this Thing on the C-Bus Network.</description>
<default>-1</default>
</parameter>
</config-description>
</thing-type>
<thing-type id="dali">
<supported-bridge-type-refs>
<bridge-type-ref id="network"/>
</supported-bridge-type-refs>
<label>DALI Group</label>
<description>DALI Group</description>
<channels>
<channel id="level" typeId="dali-channel"/>
</channels>
<properties>
<property name="CBUS Network Id"/>
<property name="CBUS Application Id"/>
<property name="CBUS Group Name"/>
</properties>
<config-description>
<parameter name="group" type="integer" required="true">
<label>CBus Group ID</label>
<description>Group number of this Thing on the C-Bus Network.</description>
<default>-1</default>
</parameter>
</config-description>
</thing-type>
<!-- Lighting Group Channel Type -->
<channel-type id="light-channel">
<item-type>Switch</item-type>
<label>Light Channel</label>
<description>Group channel for CBus on/off lighting groups</description>
<category>Light</category>
</channel-type>
<channel-type id="level-channel">
<item-type>Dimmer</item-type>
<label>Level Channel</label>
<description>Group channel for CBus variable value lighting groups</description>
<category>DimmableLight</category>
</channel-type>
<!-- Temperature Group Channel Type -->
<channel-type id="temp-channel">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Group channel for CBus temperature groups</description>
<category>Temperature</category>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<!-- Trigger Group Channel Type -->
<channel-type id="trigger-channel">
<item-type>Number</item-type>
<label>Trigger Channel</label>
<description>Group channel for CBus trigger groups</description>
</channel-type>
<!-- DALI Group Channel Type -->
<channel-type id="dali-channel">
<item-type>Dimmer</item-type>
<label>DALI Channel</label>
<description>Group channel for CBus DALI groups</description>
</channel-type>
</thing:thing-descriptions>