mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[dali] Initial contribution (#10093)
Signed-off-by: Robert Schmid <r.schmid@outlook.com>
This commit is contained in:
parent
5e3717c22a
commit
ada54dbcfa
@ -50,6 +50,7 @@
|
||||
/bundles/org.openhab.binding.coolmasternet/ @projectgus
|
||||
/bundles/org.openhab.binding.coronastats/ @DerOetzi
|
||||
/bundles/org.openhab.binding.daikin/ @caffineehacker
|
||||
/bundles/org.openhab.binding.dali/ @rs22
|
||||
/bundles/org.openhab.binding.danfossairunit/ @pravussum
|
||||
/bundles/org.openhab.binding.darksky/ @cweitkamp
|
||||
/bundles/org.openhab.binding.deconz/ @openhab/add-ons-maintainers
|
||||
|
@ -236,6 +236,11 @@
|
||||
<artifactId>org.openhab.binding.daikin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.dali</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.danfossairunit</artifactId>
|
||||
|
13
bundles/org.openhab.binding.dali/NOTICE
Normal file
13
bundles/org.openhab.binding.dali/NOTICE
Normal file
@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
71
bundles/org.openhab.binding.dali/README.md
Normal file
71
bundles/org.openhab.binding.dali/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# DALI Binding
|
||||
|
||||
This binding supports controlling devices on a DALI bus (Digital Addressable Lighting Interface) via a [daliserver](https://github.com/onitake/daliserver) connection.
|
||||
|
||||
Daliserver supports the Tridonic/Lunatone DALI USB adapter.
|
||||
As it only provides a thin multiplexer for the USB interface, the DALI messages themselves are implemented as part of this binding.
|
||||
|
||||
## Supported Things
|
||||
|
||||
Currently, these things are supported:
|
||||
|
||||
- daliserver (bridge)
|
||||
- device (single device/ballast on the DALI bus)
|
||||
- group (group of DALI devices)
|
||||
- rgb (virtual device consisting of three directly addressed devices that represent r/g/b (LED) color channels)
|
||||
|
||||
This binding was tested on a DALI 1 bus with daliserver 0.2.
|
||||
|
||||
## Discovery
|
||||
|
||||
Automatic device discovery is not yet implemented.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### Bridge `daliserver`
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | description |
|
||||
|-------------|--------------|-------------------|----------------------------------------|
|
||||
| Hostname | host | Required | IP address or host name of daliserver |
|
||||
| Port Number | port | Required | Port of the daliserver TCP interface |
|
||||
|
||||
### device
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | description |
|
||||
|-------------|--------------|-------------------|----------------------------------------|
|
||||
| Device ID | targetId | Required | Address of device in the DALI bus |
|
||||
|
||||
### group
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | description |
|
||||
|-------------|--------------|-------------------|----------------------------------------|
|
||||
| Group ID | targetId | Required | Address of group in the DALI bus |
|
||||
|
||||
### rgb
|
||||
|
||||
| Parameter | Parameter ID | Required/Optional | description |
|
||||
|-------------|--------------|-------------------|----------------------------------------|
|
||||
| R Device ID | targetIdR | Required | Address of device in the DALI bus |
|
||||
| G Device ID | targetIdG | Required | Address of device in the DALI bus |
|
||||
| B Device ID | targetIdB | Required | Address of device in the DALI bus |
|
||||
|
||||
## Full Example
|
||||
|
||||
.things file
|
||||
|
||||
```
|
||||
Bridge dali:daliserver:237dbae7 "Daliserver" [ host="localhost", port=55825] {
|
||||
Thing rgb 87bf0403-a45d-4037-b874-28f4ece30004 "RGB Lights" [ targetIdR=0, targetIdG=1, targetIdB=2 ]
|
||||
Thing device 995e16ca-07c4-4111-9cda-504cb5120f82 "Warm White" [ targetId=3 ]
|
||||
Thing group 31da8dac-8e09-455a-bc7a-6ed70f740001 "Living Room Lights" [ targetId=0 ]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
.items file
|
||||
|
||||
```
|
||||
Dimmer WarmWhiteLivingRoom "Warm White Living Room" {channel="dali:device:237dbae7:995e16ca-07c4-4111-9cda-504cb5120f82:dimImmediately"}
|
||||
Color ColorLivingRoom "Light Color Living Room" {channel="dali:device:237dbae7:87bf0403-a45d-4037-b874-28f4ece30004:color"}
|
||||
Switch LightsLivingRoom "Lights Living Room On/Off" {channel="dali:device:237dbae7:31da8dac-8e09-455a-bc7a-6ed70f740001:dimImmediately"}
|
||||
```
|
17
bundles/org.openhab.binding.dali/pom.xml
Normal file
17
bundles/org.openhab.binding.dali/pom.xml
Normal 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.dali</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: DALI Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.dali-${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-dali" description="DALI Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.dali/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.dali.internal;
|
||||
|
||||
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 DaliBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "dali";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID BRIDGE_TYPE = new ThingTypeUID(BINDING_ID, "daliserver");
|
||||
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
|
||||
public static final ThingTypeUID THING_TYPE_GROUP = new ThingTypeUID(BINDING_ID, "group");
|
||||
public static final ThingTypeUID THING_TYPE_RGB = new ThingTypeUID(BINDING_ID, "rgb");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_DEVICE_THING_TYPES_UIDS = new HashSet<>(
|
||||
Arrays.asList(THING_TYPE_DEVICE, THING_TYPE_GROUP, THING_TYPE_RGB));
|
||||
|
||||
public static final String CHANNEL_DIM_AT_FADE_RATE = "dimAtFadeRate";
|
||||
public static final String CHANNEL_DIM_IMMEDIATELY = "dimImmediately";
|
||||
public static final String CHANNEL_COLOR = "color";
|
||||
|
||||
public static final String TARGET_ID = "targetId";
|
||||
public static final String TARGET_ID_R = "targetIdR";
|
||||
public static final String TARGET_ID_G = "targetIdG";
|
||||
public static final String TARGET_ID_B = "targetIdB";
|
||||
|
||||
public static final int DALI_SWITCH_100_PERCENT = 254;
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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.dali.internal;
|
||||
|
||||
import static org.openhab.binding.dali.internal.DaliBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dali.internal.handler.DaliDeviceHandler;
|
||||
import org.openhab.binding.dali.internal.handler.DaliRgbHandler;
|
||||
import org.openhab.binding.dali.internal.handler.DaliserverBridgeHandler;
|
||||
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 DaliHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.dali", service = ThingHandlerFactory.class)
|
||||
public class DaliHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream
|
||||
.concat(DaliserverBridgeHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
DaliBindingConstants.SUPPORTED_DEVICE_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (DaliserverBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
|
||||
return new DaliserverBridgeHandler((Bridge) thing);
|
||||
}
|
||||
if (THING_TYPE_DEVICE.equals(thingTypeUID) || THING_TYPE_GROUP.equals(thingTypeUID)) {
|
||||
return new DaliDeviceHandler(thing);
|
||||
}
|
||||
if (THING_TYPE_RGB.equals(thingTypeUID)) {
|
||||
return new DaliRgbHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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.dali.internal.handler;
|
||||
|
||||
import static org.openhab.binding.dali.internal.DaliBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliAddress;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliDAPCCommand;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliResponse;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliStandardCommand;
|
||||
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.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.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DaliDeviceHandler} handles commands for things of type Device and Group.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliDeviceHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(DaliDeviceHandler.class);
|
||||
private @Nullable Integer targetId;
|
||||
|
||||
public DaliDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Bridge bridge = getBridge();
|
||||
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge configured");
|
||||
} else {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
targetId = ((BigDecimal) this.thing.getConfiguration().get(TARGET_ID)).intValueExact();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
try {
|
||||
if (CHANNEL_DIM_AT_FADE_RATE.equals(channelUID.getId())
|
||||
|| CHANNEL_DIM_IMMEDIATELY.equals(channelUID.getId())) {
|
||||
DaliAddress address;
|
||||
if (THING_TYPE_DEVICE.equals(this.thing.getThingTypeUID())) {
|
||||
address = DaliAddress.createShortAddress(targetId);
|
||||
} else if (THING_TYPE_GROUP.equals(this.thing.getThingTypeUID())) {
|
||||
address = DaliAddress.createGroupAddress(targetId);
|
||||
} else {
|
||||
throw new DaliException("unknown device type");
|
||||
}
|
||||
|
||||
boolean queryDeviceState = false;
|
||||
|
||||
if (command instanceof PercentType) {
|
||||
byte dimmValue = (byte) ((((PercentType) command).floatValue() * DALI_SWITCH_100_PERCENT) / 100);
|
||||
// A dimm value of zero is handled correctly by DALI devices, i.e. they are turned off
|
||||
getBridgeHandler().sendCommand(new DaliDAPCCommand(address, dimmValue));
|
||||
} else if (command instanceof OnOffType) {
|
||||
if ((OnOffType) command == OnOffType.ON) {
|
||||
getBridgeHandler().sendCommand(new DaliDAPCCommand(address, (byte) DALI_SWITCH_100_PERCENT));
|
||||
} else {
|
||||
getBridgeHandler().sendCommand(DaliStandardCommand.createOffCommand(address));
|
||||
}
|
||||
} else if (command instanceof IncreaseDecreaseType) {
|
||||
if (CHANNEL_DIM_AT_FADE_RATE.equals(channelUID.getId())) {
|
||||
if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) {
|
||||
getBridgeHandler().sendCommand(DaliStandardCommand.createUpCommand(address));
|
||||
} else {
|
||||
getBridgeHandler().sendCommand(DaliStandardCommand.createDownCommand(address));
|
||||
}
|
||||
} else {
|
||||
if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) {
|
||||
getBridgeHandler().sendCommand(DaliStandardCommand.createStepUpCommand(address));
|
||||
} else {
|
||||
getBridgeHandler().sendCommand(DaliStandardCommand.createStepDownCommand(address));
|
||||
}
|
||||
}
|
||||
queryDeviceState = true;
|
||||
} else if (command instanceof RefreshType) {
|
||||
queryDeviceState = true;
|
||||
}
|
||||
|
||||
if (queryDeviceState) {
|
||||
getBridgeHandler()
|
||||
.sendCommandWithResponse(DaliStandardCommand.createQueryActualLevelCommand(address),
|
||||
DaliResponse.NumericMask.class)
|
||||
.thenAccept(response -> {
|
||||
if (response != null && !response.mask) {
|
||||
Integer value = response.value != null ? response.value : 0;
|
||||
int percentValue = (int) (value.floatValue() * 100 / DALI_SWITCH_100_PERCENT);
|
||||
updateState(channelUID, new PercentType(percentValue));
|
||||
}
|
||||
}).exceptionally(e -> {
|
||||
logger.warn("Error querying device status: {}", e.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (DaliException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected DaliserverBridgeHandler getBridgeHandler() throws DaliException {
|
||||
Bridge bridge = this.getBridge();
|
||||
if (bridge == null) {
|
||||
throw new DaliException("No bridge was found");
|
||||
}
|
||||
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
throw new DaliException("No handler was found");
|
||||
}
|
||||
|
||||
return (DaliserverBridgeHandler) handler;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.dali.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link DaliException} signals exceptions within the DALI binding
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DaliException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DaliException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DaliException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DaliException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
/**
|
||||
* 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.dali.internal.handler;
|
||||
|
||||
import static org.openhab.binding.dali.internal.DaliBindingConstants.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliAddress;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliDAPCCommand;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliResponse;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliResponse.NumericMask;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliStandardCommand;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
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.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.BridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DaliRgbHandler} handles commands for things of type RGB.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliRgbHandler extends BaseThingHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(DaliRgbHandler.class);
|
||||
private @Nullable List<Integer> outputs;
|
||||
|
||||
public DaliRgbHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
Bridge bridge = getBridge();
|
||||
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge configured");
|
||||
} else {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
outputs = List.of(((BigDecimal) this.thing.getConfiguration().get(TARGET_ID_R)).intValueExact(),
|
||||
((BigDecimal) this.thing.getConfiguration().get(TARGET_ID_G)).intValueExact(),
|
||||
((BigDecimal) this.thing.getConfiguration().get(TARGET_ID_B)).intValueExact());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
try {
|
||||
if (CHANNEL_COLOR.equals(channelUID.getId())) {
|
||||
boolean queryDeviceState = false;
|
||||
|
||||
if (command instanceof HSBType) {
|
||||
PercentType[] rgb = ((HSBType) command).toRGB();
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
byte dimmValue = (byte) ((rgb[i].floatValue() * DALI_SWITCH_100_PERCENT) / 100);
|
||||
getBridgeHandler().sendCommand(
|
||||
new DaliDAPCCommand(DaliAddress.createShortAddress(outputs.get(i)), dimmValue));
|
||||
}
|
||||
} else if (command instanceof OnOffType) {
|
||||
if ((OnOffType) command == OnOffType.ON) {
|
||||
for (Integer output : outputs) {
|
||||
getBridgeHandler().sendCommand(new DaliDAPCCommand(DaliAddress.createShortAddress(output),
|
||||
(byte) DALI_SWITCH_100_PERCENT));
|
||||
}
|
||||
} else {
|
||||
for (Integer output : outputs) {
|
||||
getBridgeHandler().sendCommand(
|
||||
DaliStandardCommand.createOffCommand(DaliAddress.createShortAddress(output)));
|
||||
}
|
||||
}
|
||||
} else if (command instanceof IncreaseDecreaseType) {
|
||||
if ((IncreaseDecreaseType) command == IncreaseDecreaseType.INCREASE) {
|
||||
for (Integer output : outputs) {
|
||||
getBridgeHandler().sendCommand(
|
||||
DaliStandardCommand.createUpCommand(DaliAddress.createShortAddress(output)));
|
||||
}
|
||||
} else {
|
||||
for (Integer output : outputs) {
|
||||
getBridgeHandler().sendCommand(
|
||||
DaliStandardCommand.createDownCommand(DaliAddress.createShortAddress(output)));
|
||||
}
|
||||
}
|
||||
|
||||
queryDeviceState = true;
|
||||
} else if (command instanceof RefreshType) {
|
||||
queryDeviceState = true;
|
||||
}
|
||||
|
||||
if (queryDeviceState) {
|
||||
CompletableFuture<@Nullable NumericMask> responseR = getBridgeHandler()
|
||||
.sendCommandWithResponse(
|
||||
DaliStandardCommand.createQueryActualLevelCommand(
|
||||
DaliAddress.createShortAddress(outputs.get(0))),
|
||||
DaliResponse.NumericMask.class);
|
||||
CompletableFuture<@Nullable NumericMask> responseG = getBridgeHandler()
|
||||
.sendCommandWithResponse(
|
||||
DaliStandardCommand.createQueryActualLevelCommand(
|
||||
DaliAddress.createShortAddress(outputs.get(1))),
|
||||
DaliResponse.NumericMask.class);
|
||||
CompletableFuture<@Nullable NumericMask> responseB = getBridgeHandler()
|
||||
.sendCommandWithResponse(
|
||||
DaliStandardCommand.createQueryActualLevelCommand(
|
||||
DaliAddress.createShortAddress(outputs.get(2))),
|
||||
DaliResponse.NumericMask.class);
|
||||
|
||||
CompletableFuture.allOf(responseR, responseG, responseB).thenAccept(x -> {
|
||||
@Nullable
|
||||
NumericMask r = responseR.join(), g = responseG.join(), b = responseB.join();
|
||||
if (r != null && !r.mask && g != null && !g.mask && b != null && !b.mask) {
|
||||
Integer rValue = r.value != null ? r.value : 0;
|
||||
Integer gValue = g.value != null ? g.value : 0;
|
||||
Integer bValue = b.value != null ? b.value : 0;
|
||||
updateState(channelUID,
|
||||
HSBType.fromRGB((int) (rValue.floatValue() * 255 / DALI_SWITCH_100_PERCENT),
|
||||
(int) (gValue.floatValue() * 255 / DALI_SWITCH_100_PERCENT),
|
||||
(int) (bValue.floatValue() * 255 / DALI_SWITCH_100_PERCENT)));
|
||||
}
|
||||
}).exceptionally(e -> {
|
||||
logger.warn("Error querying device status: {}", e.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (DaliException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected DaliserverBridgeHandler getBridgeHandler() throws DaliException {
|
||||
Bridge bridge = this.getBridge();
|
||||
if (bridge == null) {
|
||||
throw new DaliException("No bridge was found");
|
||||
}
|
||||
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (handler == null) {
|
||||
throw new DaliException("No handler was found");
|
||||
}
|
||||
|
||||
return (DaliserverBridgeHandler) handler;
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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.dali.internal.handler;
|
||||
|
||||
import static org.openhab.binding.dali.internal.DaliBindingConstants.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliBackwardFrame;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliCommandBase;
|
||||
import org.openhab.binding.dali.internal.protocol.DaliResponse;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
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.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link DaliserverBridgeHandler} handles the lifecycle of daliserver connections.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliserverBridgeHandler extends BaseBridgeHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(BRIDGE_TYPE);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DaliserverBridgeHandler.class);
|
||||
private static final int DALI_DEFAULT_TIMEOUT = 5000;
|
||||
|
||||
private DaliserverConfig config = new DaliserverConfig();
|
||||
private @Nullable ExecutorService commandExecutor;
|
||||
|
||||
public DaliserverBridgeHandler(Bridge thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(DaliserverConfig.class);
|
||||
commandExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory(thing.getUID().getAsString(), true));
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private Socket getConnection() throws IOException {
|
||||
try {
|
||||
logger.debug("Creating connection to daliserver on: {} port: {}", config.host, config.port);
|
||||
Socket socket = new Socket(config.host, config.port);
|
||||
socket.setSoTimeout(DALI_DEFAULT_TIMEOUT);
|
||||
return socket;
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (commandExecutor != null) {
|
||||
commandExecutor.shutdownNow();
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<@Nullable Void> sendCommand(DaliCommandBase command) {
|
||||
return sendCommandWithResponse(command, DaliResponse.class).thenApply(c -> (Void) null);
|
||||
}
|
||||
|
||||
public <T extends DaliResponse> CompletableFuture<@Nullable T> sendCommandWithResponse(DaliCommandBase command,
|
||||
Class<T> responseType) {
|
||||
CompletableFuture<@Nullable T> future = new CompletableFuture<>();
|
||||
ExecutorService commandExecutor = this.commandExecutor;
|
||||
if (commandExecutor != null) {
|
||||
commandExecutor.submit(() -> {
|
||||
byte[] prefix = new byte[] { 0x2, 0x0 };
|
||||
byte[] message = command.frame.pack();
|
||||
byte[] frame = new byte[prefix.length + message.length];
|
||||
System.arraycopy(prefix, 0, frame, 0, prefix.length);
|
||||
System.arraycopy(message, 0, frame, prefix.length, message.length);
|
||||
|
||||
try (Socket socket = getConnection();
|
||||
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
DataInputStream in = new DataInputStream(socket.getInputStream())) {
|
||||
// send the command
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Sending: {}", HexUtils.bytesToHex(frame));
|
||||
}
|
||||
out.write(frame);
|
||||
if (command.sendTwice) {
|
||||
out.flush();
|
||||
in.readNBytes(4); // discard
|
||||
out.write(frame);
|
||||
}
|
||||
out.flush();
|
||||
|
||||
// read the response
|
||||
try {
|
||||
@Nullable
|
||||
T response = parseResponse(in, responseType);
|
||||
future.complete(response);
|
||||
return;
|
||||
} catch (DaliException e) {
|
||||
future.completeExceptionally(e);
|
||||
return;
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
logger.warn("Timeout sending command to daliserver: {} Message: {}", frame, e.getMessage());
|
||||
future.completeExceptionally(new DaliException("Timeout sending command to daliserver", e));
|
||||
} catch (IOException e) {
|
||||
logger.warn("Problem sending command to daliserver: {} Message: {}", frame, e.getMessage());
|
||||
future.completeExceptionally(new DaliException("Problem sending command to daliserver", e));
|
||||
} catch (Exception e) {
|
||||
logger.warn("Unexpected exception while sending command to daliserver: {} Message: {}", frame,
|
||||
e.getMessage());
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
future.complete(null);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
private <T extends DaliResponse> @Nullable T parseResponse(DataInputStream reader, Class<T> responseType)
|
||||
throws IOException, DaliException {
|
||||
try {
|
||||
T result = responseType.getDeclaredConstructor().newInstance();
|
||||
byte[] response = reader.readNBytes(4);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Received: {}", HexUtils.bytesToHex(response));
|
||||
}
|
||||
byte status = response[1], rval = response[2];
|
||||
if (status == 0) {
|
||||
result.parse(null);
|
||||
} else if (status == 1) {
|
||||
result.parse(new DaliBackwardFrame(rval));
|
||||
} else if (status == 255) {
|
||||
// This is "failure" - daliserver reports this for a garbled response when several ballasts reply. It
|
||||
// should be interpreted as "Yes".
|
||||
result.parse(null);
|
||||
} else {
|
||||
throw new DaliException("Invalid response status: " + status);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException
|
||||
| InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.dali.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link DaliserverConfig} holds connection parameters for a daliserver instance.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliserverConfig {
|
||||
public String host = "";
|
||||
public int port = 0;
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliAddress} represents an address on the DALI bus.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class DaliAddress {
|
||||
private DaliAddress() {
|
||||
}
|
||||
|
||||
protected abstract <T extends DaliFrame> T addToFrame(T frame) throws DaliException;
|
||||
|
||||
public static DaliAddress createShortAddress(int address) throws DaliException {
|
||||
if (address < 0 || address > 63) {
|
||||
throw new DaliException("address must be in the range 0..63");
|
||||
}
|
||||
return new DaliAddress() {
|
||||
@Override
|
||||
protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
|
||||
if (frame.length() == 16) {
|
||||
frame.data &= ~(1 << 15); // unset bit 15
|
||||
frame.data |= ((address & 0b11111) << 9);
|
||||
} else if (frame.length() == 24) {
|
||||
frame.data &= ~(1 << 23); // unset bit 23
|
||||
frame.data |= ((address & 0b11111) << 17);
|
||||
} else {
|
||||
throw new DaliException("Unsupported frame size");
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static DaliAddress createBroadcastAddress() {
|
||||
return new DaliAddress() {
|
||||
@Override
|
||||
protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
|
||||
if (frame.length() == 16) {
|
||||
frame.data |= 0x7f << 9;
|
||||
} else if (frame.length() == 24) {
|
||||
frame.data |= 0x7f << 17;
|
||||
} else {
|
||||
throw new DaliException("Unsupported frame size");
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static DaliAddress createBroadcastUnaddressedAddress() {
|
||||
return new DaliAddress() {
|
||||
@Override
|
||||
protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
|
||||
if (frame.length() == 16) {
|
||||
frame.data |= 0x7e << 9;
|
||||
} else if (frame.length() == 24) {
|
||||
frame.data |= 0x7e << 17;
|
||||
} else {
|
||||
throw new DaliException("Unsupported frame size");
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static DaliAddress createGroupAddress(int address) throws DaliException {
|
||||
if (address < 0 || address > 31) {
|
||||
throw new DaliException("address must be in the range 0..31");
|
||||
}
|
||||
return new DaliAddress() {
|
||||
@Override
|
||||
protected <T extends DaliFrame> T addToFrame(T frame) throws DaliException {
|
||||
if (frame.length() == 16) {
|
||||
if (address > 15) {
|
||||
throw new DaliException("Groups 16..31 are not supported in 16-bit forward frames");
|
||||
}
|
||||
frame.data |= ((0x4 << 3) & (address & 0b111)) << 9;
|
||||
} else if (frame.length() == 24) {
|
||||
frame.data |= ((0x2 << 4) & (address & 0b1111)) << 17;
|
||||
} else {
|
||||
throw new DaliException("Unsupported frame size");
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliBackwardFrame} represents a response message on the DALI bus.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliBackwardFrame extends DaliFrame {
|
||||
|
||||
public DaliBackwardFrame(byte data) throws DaliException {
|
||||
super(8, new byte[] { data });
|
||||
}
|
||||
}
|
@ -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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link DaliCommandBase} is an abstract command for DALI devices.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliCommandBase {
|
||||
public DaliForwardFrame frame;
|
||||
public boolean sendTwice;
|
||||
|
||||
public DaliCommandBase(DaliForwardFrame frame, boolean sendTwice) {
|
||||
this.frame = frame;
|
||||
this.sendTwice = sendTwice;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliDAPCCommand} represents a DALI Direct Arc Power Command.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliDAPCCommand extends DaliGearCommandBase {
|
||||
|
||||
public DaliDAPCCommand(DaliAddress target, Byte power) throws DaliException {
|
||||
super(target.addToFrame(new DaliForwardFrame(16, new byte[] { power })), false);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliForwardFrame} represents an outgoing DALI command.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliForwardFrame extends DaliFrame {
|
||||
|
||||
public DaliForwardFrame(int bits, byte[] data) throws DaliException {
|
||||
super(bits, data);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliFrame} represents a message on the DALI bus.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliFrame {
|
||||
int bits;
|
||||
int data;
|
||||
|
||||
public DaliFrame(int bits, byte[] data) throws DaliException {
|
||||
if (bits < 1) {
|
||||
throw new DaliException("Frames must contain at least 1 data bit");
|
||||
}
|
||||
|
||||
this.bits = bits;
|
||||
|
||||
int d = 0;
|
||||
for (byte b : data) {
|
||||
d = (d << 8) | Byte.toUnsignedInt(b);
|
||||
}
|
||||
|
||||
this.data = d;
|
||||
|
||||
if (this.data < 0) {
|
||||
throw new DaliException("Initial data must not be negative");
|
||||
}
|
||||
|
||||
if (Math.abs(this.data) >= (1 << this.bits)) {
|
||||
throw new DaliException("Initial data will not fit in the specified number of bits");
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return this.bits;
|
||||
}
|
||||
|
||||
public byte[] pack() {
|
||||
int remaining = length();
|
||||
List<Byte> bytesList = new ArrayList<Byte>();
|
||||
int tmp = this.data;
|
||||
while (remaining > 0) {
|
||||
bytesList.add((byte) (tmp & 0xff));
|
||||
tmp = tmp >> 8;
|
||||
remaining = remaining - 8;
|
||||
}
|
||||
byte[] result = new byte[bytesList.size()];
|
||||
int i = 0;
|
||||
for (byte b : bytesList) {
|
||||
result[bytesList.size() - i++] = b;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link DaliGearCommandBase} represents an abstract DALI gear command.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliGearCommandBase extends DaliCommandBase {
|
||||
|
||||
public DaliGearCommandBase(DaliForwardFrame frame, boolean sendTwice) {
|
||||
super(frame, sendTwice);
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link DaliResponse} represents different types of responses to DALI
|
||||
* commands.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliResponse {
|
||||
public void parse(@Nullable DaliBackwardFrame frame) {
|
||||
}
|
||||
|
||||
public static class Numeric extends DaliResponse {
|
||||
public @Nullable Integer value;
|
||||
|
||||
@Override
|
||||
public void parse(@Nullable DaliBackwardFrame frame) {
|
||||
if (frame != null) {
|
||||
value = frame.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NumericMask extends DaliResponse.Numeric {
|
||||
public @Nullable Boolean mask;
|
||||
|
||||
@Override
|
||||
public void parse(@Nullable DaliBackwardFrame frame) {
|
||||
super.parse(frame);
|
||||
if (this.value == 255) {
|
||||
this.value = null;
|
||||
this.mask = true;
|
||||
} else {
|
||||
this.mask = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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.dali.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.dali.internal.handler.DaliException;
|
||||
|
||||
/**
|
||||
* The {@link DaliStandardCommand} represents different types of commands for
|
||||
* controlling DALI equipment.
|
||||
*
|
||||
* @author Robert Schmid - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class DaliStandardCommand extends DaliGearCommandBase {
|
||||
|
||||
private DaliStandardCommand(DaliAddress target, int cmdval, int param, boolean sendTwice) throws DaliException {
|
||||
super(target.addToFrame(new DaliForwardFrame(16, new byte[] { 0x1, (byte) (cmdval | (param & 0b1111)) })),
|
||||
sendTwice);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createOffCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x00, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createUpCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x01, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createDownCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x02, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createStepUpCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x03, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createStepDownCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x04, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createRecallMaxLevelCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x05, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createRecallMinLevelCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x06, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createStepDownAndOffCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x07, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createOnAndStepUpCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x08, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createEnableDAPCSequenceCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x09, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createGoToSceneCommand(DaliAddress target, int scene) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x10, scene, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createResetCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x20, 0, true);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createQueryStatusCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0x90, 0, false);
|
||||
}
|
||||
|
||||
public static DaliStandardCommand createQueryActualLevelCommand(DaliAddress target) throws DaliException {
|
||||
return new DaliStandardCommand(target, 0xa0, 0, false);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="dali" 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>DALI Binding</name>
|
||||
<description>This is the binding for controlling lights using the Digital Addressable Lighting Interface (DALI).</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:dali:daliserver">
|
||||
<parameter name="host" type="text" required="true">
|
||||
<label>Host Address</label>
|
||||
<context>network-address</context>
|
||||
<description>IP address or host name of daliserver.</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="true" min="1" max="65535">
|
||||
<label>TCP Port</label>
|
||||
<description>Port of the daliserver TCP interface.</description>
|
||||
<default>55825</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="dali"
|
||||
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="daliserver">
|
||||
<label>Daliserver</label>
|
||||
<description>A running daliserver.</description>
|
||||
|
||||
<config-description-ref uri="thing-type:dali:daliserver"/>
|
||||
</bridge-type>
|
||||
|
||||
<!-- Single Device Type -->
|
||||
<thing-type id="device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="daliserver"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>DALI Device</label>
|
||||
<description>Controls a single device/ballast</description>
|
||||
<channels>
|
||||
<channel id="dimAtFadeRate" typeId="system.brightness"/>
|
||||
<channel id="dimImmediately" typeId="system.brightness"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="targetId" type="integer" required="true" min="0" max="63">
|
||||
<label>Device ID</label>
|
||||
<description>Address of the device in the DALI bus</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- Group Device Type -->
|
||||
<thing-type id="group">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="daliserver"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>DALI Group</label>
|
||||
<description>Controls a group of devices/ballasts</description>
|
||||
<channels>
|
||||
<channel id="dimAtFadeRate" typeId="system.brightness"/>
|
||||
<channel id="dimImmediately" typeId="system.brightness"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="targetId" type="integer" required="true" min="0" max="31">
|
||||
<label>Group ID</label>
|
||||
<description>Address of the group in the DALI bus</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<!-- RGB Type -->
|
||||
<thing-type id="rgb">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="daliserver"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>DALI RGB Device</label>
|
||||
<description>Controls three DALI devices representing R,G,B lighting channels</description>
|
||||
<channels>
|
||||
<channel id="color" typeId="system.color"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="targetIdR" type="integer" required="true" min="0" max="63">
|
||||
<label>R Device ID</label>
|
||||
<description>Address of the device in the DALI bus</description>
|
||||
</parameter>
|
||||
<parameter name="targetIdG" type="integer" required="true" min="0" max="63">
|
||||
<label>G Device ID</label>
|
||||
<description>Address of the device in the DALI bus</description>
|
||||
</parameter>
|
||||
<parameter name="targetIdB" type="integer" required="true" min="0" max="63">
|
||||
<label>B Device ID</label>
|
||||
<description>Address of the device in the DALI bus</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -82,6 +82,7 @@
|
||||
<module>org.openhab.binding.coolmasternet</module>
|
||||
<module>org.openhab.binding.coronastats</module>
|
||||
<module>org.openhab.binding.daikin</module>
|
||||
<module>org.openhab.binding.dali</module>
|
||||
<module>org.openhab.binding.danfossairunit</module>
|
||||
<module>org.openhab.binding.darksky</module>
|
||||
<module>org.openhab.binding.deconz</module>
|
||||
|
Loading…
Reference in New Issue
Block a user