[sbus] Add support for Contact Sensor and (white) in RGBW channels (#18747)
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>pull/18753/head
parent
19a755cfe8
commit
28aec48cb5
|
@ -10,6 +10,7 @@ The binding supports various device types including RGB/RGBW controllers, temper
|
||||||
- `rgbw` - RGB/RGBW Controllers for color and brightness control
|
- `rgbw` - RGB/RGBW Controllers for color and brightness control
|
||||||
- `temperature` - Temperature Sensors for monitoring environmental conditions
|
- `temperature` - Temperature Sensors for monitoring environmental conditions
|
||||||
- `switch` - Switch Controllers for basic on/off and dimming control
|
- `switch` - Switch Controllers for basic on/off and dimming control
|
||||||
|
- `contact` - Contact Sensors for monitoring open/closed states
|
||||||
|
|
||||||
## Discovery
|
## Discovery
|
||||||
|
|
||||||
|
@ -29,57 +30,54 @@ The binding itself does not require any special configuration.
|
||||||
The Sbus Bridge has the following configuration parameters:
|
The Sbus Bridge has the following configuration parameters:
|
||||||
|
|
||||||
| Name | Type | Description | Default | Required | Advanced |
|
| Name | Type | Description | Default | Required | Advanced |
|
||||||
|---------|---------|------------------------------------------------------|---------|----------|-----------|
|
|:--------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:|
|
||||||
| host | text | IP address of the Sbus device (typically broadcast) | N/A | yes | no |
|
| host | text | IP address of the Sbus device (typically broadcast) | N/A | yes | no |
|
||||||
| port | integer | UDP port number | 6000 | no | no |
|
| port | integer | UDP port number | 6000 | no | no |
|
||||||
|
|
||||||
### RGBW Controller Configuration
|
### RGBW Controller, Contact, Switch, Temperature Configuration
|
||||||
|
|
||||||
| Name | Type | Description | Default | Required | Advanced |
|
| Name | Type | Description | Default | Required | Advanced |
|
||||||
|---------|---------|------------------------------------------------------|---------|----------|-----------|
|
|:--------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:|
|
||||||
| subnetId| integer | Subnet ID the RGBW controller is part of | N/A | yes | no |
|
| subnetId| integer | Subnet ID the RGBW controller is part of | N/A | yes | no |
|
||||||
| id | integer | Device ID of the RGBW controller | N/A | yes | no |
|
| id | integer | Device ID of the RGBW controller | N/A | yes | no |
|
||||||
| refresh | integer | Refresh interval in seconds | 30 | no | yes |
|
| refresh | integer | Refresh interval in seconds | 30 | no | yes |
|
||||||
|
|
||||||
### Temperature Sensor Configuration
|
|
||||||
|
|
||||||
| Name | Type | Description | Default | Required | Advanced |
|
|
||||||
|---------|---------|------------------------------------------------------|---------|----------|-----------|
|
|
||||||
| subnetId| integer | Subnet ID the temperature sensor is part of | N/A | yes | no |
|
|
||||||
| id | integer | Device ID of the temperature sensor | N/A | yes | no |
|
|
||||||
| refresh | integer | Refresh interval in seconds | 30 | no | yes |
|
|
||||||
|
|
||||||
### Switch Controller Configuration
|
|
||||||
|
|
||||||
| Name | Type | Description | Default | Required | Advanced |
|
|
||||||
|---------|---------|------------------------------------------------------|---------|----------|-----------|
|
|
||||||
| subnetId| integer | Subnet ID the switch controller is part of | N/A | yes | no |
|
|
||||||
| id | integer | Device ID of the switch controller | N/A | yes | no |
|
|
||||||
| refresh | integer | Refresh interval in seconds | 30 | no | yes |
|
|
||||||
|
|
||||||
## Channels
|
## Channels
|
||||||
|
|
||||||
### RGBW Controller Channels
|
### RGBW Controller Channels
|
||||||
|
|
||||||
| Channel | Type | Read/Write | Description |
|
| Channel | Type | Read/Write | Description |
|
||||||
|---------|--------|------------|------------------------------------------------------------|
|
|:--------|:-------|:----------:|:-----------------------------------------------------------|
|
||||||
| color | Color | RW | HSB color picker that controls RGBW components (0-100%) |
|
| color | Color | RW | HSB color picker that controls RGBW components (0-100%). Can be configured to disable the white channel. |
|
||||||
| switch | Switch | RW | On/Off control for the RGBW output with optional timer |
|
| switch | Switch | RW | On/Off control for the RGBW output with optional timer |
|
||||||
|
|
||||||
|
The color channel of RGBW controllers supports these additional parameters:
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Default | Required | Advanced |
|
||||||
|
|:------------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:|
|
||||||
|
| channelNumber | integer | The physical channel number on the Sbus device | N/A | yes | no |
|
||||||
|
| enableWhite | boolean | Controls the white component support for RGB palette | true | no | yes |
|
||||||
|
|
||||||
### Temperature Sensor Channels
|
### Temperature Sensor Channels
|
||||||
|
|
||||||
| Channel | Type | Read/Write | Description |
|
| Channel | Type | Read/Write | Description |
|
||||||
|-------------|---------------------|------------|--------------------------------|
|
|:------------|:--------------------|:----------:|:-------------------------------|
|
||||||
| temperature | Number:Temperature | R | Current temperature reading. Can be configured to use Celsius (default) or Fahrenheit units |
|
| temperature | Number:Temperature | R | Current temperature reading. Can be configured to use Celsius (default) or Fahrenheit units |
|
||||||
|
|
||||||
### Switch Controller Channels
|
### Switch Controller Channels
|
||||||
|
|
||||||
| Channel | Type | Read/Write | Description |
|
| Channel | Type | Read/Write | Description |
|
||||||
|---------|----------------|------------|-----------------------------------------------------------|
|
|:--------|:---------------|:----------:|:----------------------------------------------------------|
|
||||||
| switch | Switch | RW | Basic ON/OFF state control |
|
| switch | Switch | RW | Basic ON/OFF state control |
|
||||||
| dimmer | Dimmer | RW | ON/OFF state with timer transition |
|
| dimmer | Dimmer | RW | ON/OFF state with timer transition |
|
||||||
| paired | Rollershutter | RW | UP/DOWN/STOP control for two paired channels (e.g., rollershutters)|
|
| paired | Rollershutter | RW | UP/DOWN/STOP control for two paired channels (e.g., rollershutters)|
|
||||||
|
|
||||||
|
### Contact Sensor Channels
|
||||||
|
|
||||||
|
| Channel | Type | Read/Write | Description |
|
||||||
|
|:--------|:--------|:----------:|:----------------------------------------------------------|
|
||||||
|
| contact | Contact | R | Contact state (OPEN/CLOSED) |
|
||||||
|
|
||||||
## Full Example
|
## Full Example
|
||||||
|
|
||||||
### Thing Configuration
|
### Thing Configuration
|
||||||
|
@ -88,8 +86,14 @@ The Sbus Bridge has the following configuration parameters:
|
||||||
Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000 ] {
|
Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000 ] {
|
||||||
Thing rgbw colorctrl [ id=72, refresh=30 ] {
|
Thing rgbw colorctrl [ id=72, refresh=30 ] {
|
||||||
Channels:
|
Channels:
|
||||||
Type color-channel : color [ channelNumber=1 ] // HSB color picker, RGBW values stored at channel 1
|
Type color-channel : color [ channelNumber=1, enableWhite=true ] // Full RGBW control with white component
|
||||||
Type switch-channel : power [ channelNumber=1 ] // On/Off control for the RGBW output For complex scenes, one Sbus color controller can keep up to 40 color states. The switch channelNumber has to fall into this range.
|
Type switch-channel : power [ channelNumber=1 ] // On/Off control for the RGBW output
|
||||||
|
}
|
||||||
|
|
||||||
|
Thing rgbw rgbonly [ id=73, refresh=30 ] {
|
||||||
|
Channels:
|
||||||
|
Type color-channel : color [ channelNumber=1, enableWhite=false ] // RGB only, no white component
|
||||||
|
Type switch-channel : power [ channelNumber=1 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
Thing temperature temp1 [ id=62, refresh=30 ] {
|
Thing temperature temp1 [ id=62, refresh=30 ] {
|
||||||
|
@ -103,6 +107,11 @@ Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000 ] {
|
||||||
Type dimmer-channel : second_switch [ channelNumber=2 ]
|
Type dimmer-channel : second_switch [ channelNumber=2 ]
|
||||||
Type paired-channel : third_switch [ channelNumber=3, pairedChannelNumber=4 ]
|
Type paired-channel : third_switch [ channelNumber=3, pairedChannelNumber=4 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thing contact contact1 [ id=80, refresh=30 ] {
|
||||||
|
Channels:
|
||||||
|
Type contact-channel : contact [ channelNumber=1 ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -122,6 +131,9 @@ Rollershutter Rollershutter_Switch "Rollershutter [%s]" { channel="sbus:switch:m
|
||||||
Group gLight "RGBW Light" <light> ["Lighting"]
|
Group gLight "RGBW Light" <light> ["Lighting"]
|
||||||
Color rgbwColor "Color" <colorwheel> (gLight) ["Control", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:color" }
|
Color rgbwColor "Color" <colorwheel> (gLight) ["Control", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:color" }
|
||||||
Switch rgbwPower "Power" <switch> (gLight) ["Switch", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:power" }
|
Switch rgbwPower "Power" <switch> (gLight) ["Switch", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:power" }
|
||||||
|
|
||||||
|
// Contact Sensor
|
||||||
|
Contact Door_Contact "Door [%s]" <door> { channel="sbus:contact:mybridge:contact1:contact" }
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sitemap Configuration
|
### Sitemap Configuration
|
||||||
|
@ -134,5 +146,30 @@ sitemap sbus label="Sbus Demo"
|
||||||
Text item=Temp_Sensor
|
Text item=Temp_Sensor
|
||||||
Switch item=Light_Switch
|
Switch item=Light_Switch
|
||||||
Rollershutter item=Rollershutter_Switch
|
Rollershutter item=Rollershutter_Switch
|
||||||
|
Text item=Door_Contact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## Usage Notes
|
||||||
|
|
||||||
|
### RGB vs. RGBW Mode
|
||||||
|
|
||||||
|
The `enableWhite` parameter for color channels controls whether the white component is used:
|
||||||
|
|
||||||
|
- Set to `true` (default): Full RGBW control with white component
|
||||||
|
- Set to `false`: RGB-only control with no white component
|
||||||
|
|
||||||
|
This is useful for:
|
||||||
|
- Pure RGB fixtures without a white channel
|
||||||
|
- Creating saturated colors without white dilution
|
||||||
|
- Specialized color effects where white would wash out the intended color
|
||||||
|
|
||||||
|
### Color Control and On/Off Functionality
|
||||||
|
|
||||||
|
The Color item type in openHAB inherently supports both color selection and on/off functionality:
|
||||||
|
- The color picker controls hue and saturation
|
||||||
|
- The brightness component (0-100%) functions as the on/off control
|
||||||
|
- When brightness is 0%, the light is OFF
|
||||||
|
- When brightness is >0%, the light is ON
|
||||||
|
|
||||||
|
This is why a Color item shows both a color picker and an on/off button in the UI without requiring a separate Switch item.
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ro.ciprianpascu</groupId>
|
<groupId>ro.ciprianpascu</groupId>
|
||||||
<artifactId>j2sbus</artifactId>
|
<artifactId>j2sbus</artifactId>
|
||||||
<version>1.5.8</version>
|
<version>1.5.9</version>
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ public class BindingConstants {
|
||||||
public static final ThingTypeUID THING_TYPE_SWITCH = new ThingTypeUID(BINDING_ID, "switch");
|
public static final ThingTypeUID THING_TYPE_SWITCH = new ThingTypeUID(BINDING_ID, "switch");
|
||||||
public static final ThingTypeUID THING_TYPE_TEMPERATURE = new ThingTypeUID(BINDING_ID, "temperature");
|
public static final ThingTypeUID THING_TYPE_TEMPERATURE = new ThingTypeUID(BINDING_ID, "temperature");
|
||||||
public static final ThingTypeUID THING_TYPE_RGBW = new ThingTypeUID(BINDING_ID, "rgbw");
|
public static final ThingTypeUID THING_TYPE_RGBW = new ThingTypeUID(BINDING_ID, "rgbw");
|
||||||
|
public static final ThingTypeUID THING_TYPE_CONTACT = new ThingTypeUID(BINDING_ID, "contact");
|
||||||
|
|
||||||
// Channel IDs for Switch Device
|
// Channel IDs for Switch Device
|
||||||
public static final String CHANNEL_SWITCH_STATE = "state";
|
public static final String CHANNEL_SWITCH_STATE = "state";
|
||||||
|
@ -55,4 +56,5 @@ public class BindingConstants {
|
||||||
public static final String CHANNEL_TYPE_SWITCH = "switch-channel";
|
public static final String CHANNEL_TYPE_SWITCH = "switch-channel";
|
||||||
public static final String CHANNEL_TYPE_DIMMER = "dimmer-channel";
|
public static final String CHANNEL_TYPE_DIMMER = "dimmer-channel";
|
||||||
public static final String CHANNEL_TYPE_PAIRED = "paired-channel";
|
public static final String CHANNEL_TYPE_PAIRED = "paired-channel";
|
||||||
|
public static final String CHANNEL_TYPE_CONTACT = "contact-channel";
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2025 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.sbus.handler;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.sbus.handler.config.SbusChannelConfig;
|
||||||
|
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
|
||||||
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
|
import org.openhab.core.library.types.OpenClosedType;
|
||||||
|
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.osgi.framework.Bundle;
|
||||||
|
import org.osgi.framework.FrameworkUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link SbusContactHandler} is responsible for handling commands for Sbus contact devices.
|
||||||
|
* It supports reading the current contact state (open/closed).
|
||||||
|
*
|
||||||
|
* @author Ciprian Pascu - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class SbusContactHandler extends AbstractSbusHandler {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(SbusContactHandler.class);
|
||||||
|
|
||||||
|
public SbusContactHandler(Thing thing, TranslationProvider translationProvider, LocaleProvider localeProvider) {
|
||||||
|
super(thing, translationProvider, localeProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeChannels() {
|
||||||
|
// Get all channel configurations from the thing
|
||||||
|
for (Channel channel : getThing().getChannels()) {
|
||||||
|
// Channels are already defined in thing-types.xml, just validate their configuration
|
||||||
|
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
|
||||||
|
if (channelConfig.channelNumber <= 0) {
|
||||||
|
Bundle bundle = FrameworkUtil.getBundle(getClass());
|
||||||
|
logger.warn("{}", translationProvider.getText(bundle, "error.channel.invalid-number",
|
||||||
|
channel.getUID().toString(), localeProvider.getLocale()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void pollDevice() {
|
||||||
|
final SbusService adapter = super.sbusAdapter;
|
||||||
|
if (adapter == null) {
|
||||||
|
Bundle bundle = FrameworkUtil.getBundle(getClass());
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
|
||||||
|
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class);
|
||||||
|
boolean[] contactStates = adapter.readContactStatusChannels(config.subnetId, config.id);
|
||||||
|
|
||||||
|
// Iterate over all channels and update their states
|
||||||
|
for (Channel channel : getThing().getChannels()) {
|
||||||
|
if (!isLinked(channel.getUID())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
|
||||||
|
if (channelConfig.channelNumber > 0 && channelConfig.channelNumber <= contactStates.length) {
|
||||||
|
boolean isOpen = contactStates[channelConfig.channelNumber - 1];
|
||||||
|
updateState(channel.getUID(), isOpen ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Bundle bundle = FrameworkUtil.getBundle(getClass());
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||||
|
translationProvider.getText(bundle, "error.device.read-state", null, localeProvider.getLocale()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
// Contact sensors are read-only
|
||||||
|
Bundle bundle = FrameworkUtil.getBundle(getClass());
|
||||||
|
logger.debug("{}",
|
||||||
|
translationProvider.getText(bundle, "info.contact.readonly", null, localeProvider.getLocale()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ public class SbusHandlerFactory extends BaseThingHandlerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UDP_BRIDGE, THING_TYPE_SWITCH,
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UDP_BRIDGE, THING_TYPE_SWITCH,
|
||||||
THING_TYPE_TEMPERATURE, THING_TYPE_RGBW);
|
THING_TYPE_TEMPERATURE, THING_TYPE_RGBW, THING_TYPE_CONTACT);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
@ -93,6 +93,9 @@ public class SbusHandlerFactory extends BaseThingHandlerFactory {
|
||||||
} else if (thingTypeUID.equals(THING_TYPE_RGBW)) {
|
} else if (thingTypeUID.equals(THING_TYPE_RGBW)) {
|
||||||
logger.debug("Creating Sbus RGBW handler for thing {}", thing.getUID());
|
logger.debug("Creating Sbus RGBW handler for thing {}", thing.getUID());
|
||||||
return new SbusRgbwHandler(thing, tp, lp);
|
return new SbusRgbwHandler(thing, tp, lp);
|
||||||
|
} else if (thingTypeUID.equals(THING_TYPE_CONTACT)) {
|
||||||
|
logger.debug("Creating Sbus contact handler for thing {}", thing.getUID());
|
||||||
|
return new SbusContactHandler(thing, tp, lp);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Unknown thing type: {}", thingTypeUID);
|
logger.debug("Unknown thing type: {}", thingTypeUID);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.openhab.core.i18n.LocaleProvider;
|
||||||
import org.openhab.core.i18n.TranslationProvider;
|
import org.openhab.core.i18n.TranslationProvider;
|
||||||
import org.openhab.core.library.types.HSBType;
|
import org.openhab.core.library.types.HSBType;
|
||||||
import org.openhab.core.library.types.OnOffType;
|
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.Channel;
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
|
@ -47,24 +48,6 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
|
||||||
super(thing, translationProvider, localeProvider);
|
super(thing, translationProvider, localeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if any RGBW value is greater than 0.
|
|
||||||
*
|
|
||||||
* @param rgbw an int array [R, G, B, W] each in [0..255]
|
|
||||||
* @return true if any value is greater than 0, false otherwise
|
|
||||||
*/
|
|
||||||
private boolean isAnyRgbwValueActive(int[] rgbw) {
|
|
||||||
if (rgbw.length < 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int value : rgbw) {
|
|
||||||
if (value > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initializeChannels() {
|
protected void initializeChannels() {
|
||||||
int switchChannelCount = 0;
|
int switchChannelCount = 0;
|
||||||
|
@ -129,13 +112,26 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
|
||||||
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
|
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
|
||||||
|
|
||||||
if (BindingConstants.CHANNEL_TYPE_COLOR.equals(channelTypeId)) {
|
if (BindingConstants.CHANNEL_TYPE_COLOR.equals(channelTypeId)) {
|
||||||
|
// Read status channels for switch states
|
||||||
|
int[] statuses = adapter.readStatusChannels(config.subnetId, config.id);
|
||||||
|
boolean isActive = isAnyRgbwValueActive(statuses);
|
||||||
// Read RGBW values for this channel
|
// Read RGBW values for this channel
|
||||||
int[] rgbwValues = adapter.readRgbw(config.subnetId, config.id, channelConfig.channelNumber);
|
int[] rgbwValues = isActive ? statuses
|
||||||
if (rgbwValues.length >= 4) {
|
: adapter.readRgbw(config.subnetId, config.id, channelConfig.channelNumber);
|
||||||
// Convert RGBW to HSB using our custom conversion
|
fixRgbwValues(rgbwValues);
|
||||||
HSBType hsbType = ColorUtil.rgbToHsb(rgbwValues);
|
// Check if white channel should be disabled
|
||||||
updateState(channel.getUID(), hsbType);
|
boolean enableWhite = true; // Default to true if not specified
|
||||||
|
if (channel.getConfiguration().containsKey("enableWhite")) {
|
||||||
|
enableWhite = (boolean) channel.getConfiguration().get("enableWhite");
|
||||||
}
|
}
|
||||||
|
if (!enableWhite) {
|
||||||
|
rgbwValues = new int[] { rgbwValues[0], rgbwValues[1], rgbwValues[2] };
|
||||||
|
}
|
||||||
|
// Convert RGBW to HSB using our custom conversion
|
||||||
|
HSBType color = ColorUtil.rgbToHsb(rgbwValues);
|
||||||
|
color = new HSBType(color.getHue(), color.getSaturation(),
|
||||||
|
isActive ? PercentType.HUNDRED : PercentType.ZERO);
|
||||||
|
updateState(channel.getUID(), color);
|
||||||
} else if (BindingConstants.CHANNEL_TYPE_SWITCH.equals(channelTypeId)) {
|
} else if (BindingConstants.CHANNEL_TYPE_SWITCH.equals(channelTypeId)) {
|
||||||
// Read status channels for switch states
|
// Read status channels for switch states
|
||||||
int[] statuses = adapter.readStatusChannels(config.subnetId, config.id);
|
int[] statuses = adapter.readStatusChannels(config.subnetId, config.id);
|
||||||
|
@ -180,9 +176,28 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
|
||||||
|
|
||||||
if (BindingConstants.CHANNEL_TYPE_COLOR.equals(channelTypeId)
|
if (BindingConstants.CHANNEL_TYPE_COLOR.equals(channelTypeId)
|
||||||
&& command instanceof HSBType hsbCommand) {
|
&& command instanceof HSBType hsbCommand) {
|
||||||
|
if (hsbCommand.getBrightness().intValue() == 0) {
|
||||||
|
adapter.writeSingleChannel(config.subnetId, config.id, channelConfig.channelNumber, 0, -1);
|
||||||
|
hsbCommand = new HSBType(hsbCommand.getHue(), hsbCommand.getSaturation(), PercentType.ZERO);
|
||||||
|
} else {
|
||||||
|
// Check if white channel should be disabled
|
||||||
|
boolean enableWhite = true; // Default to true if not specified
|
||||||
|
if (channel.getConfiguration().containsKey("enableWhite")) {
|
||||||
|
enableWhite = (boolean) channel.getConfiguration().get("enableWhite");
|
||||||
|
}
|
||||||
// Handle color command
|
// Handle color command
|
||||||
int[] rgbw = ColorUtil.hsbToRgbw(hsbCommand);
|
// If white is disabled, set the white component (index 3) to 0
|
||||||
|
int[] rgbw = null;
|
||||||
|
if (enableWhite) {
|
||||||
|
rgbw = ColorUtil.hsbToRgbw(hsbCommand);
|
||||||
|
} else {
|
||||||
|
var converted = ColorUtil.hsbToRgb(hsbCommand);
|
||||||
|
rgbw = new int[] { converted[0], converted[1], converted[2], 0 };
|
||||||
|
}
|
||||||
adapter.writeRgbw(config.subnetId, config.id, channelConfig.channelNumber, rgbw);
|
adapter.writeRgbw(config.subnetId, config.id, channelConfig.channelNumber, rgbw);
|
||||||
|
adapter.writeSingleChannel(config.subnetId, config.id, channelConfig.channelNumber, 100, -1);
|
||||||
|
hsbCommand = new HSBType(hsbCommand.getHue(), hsbCommand.getSaturation(), PercentType.HUNDRED);
|
||||||
|
}
|
||||||
updateState(channelUID, hsbCommand);
|
updateState(channelUID, hsbCommand);
|
||||||
} else if (BindingConstants.CHANNEL_TYPE_SWITCH.equals(channelTypeId)
|
} else if (BindingConstants.CHANNEL_TYPE_SWITCH.equals(channelTypeId)
|
||||||
&& command instanceof OnOffType onOffCommand) {
|
&& command instanceof OnOffType onOffCommand) {
|
||||||
|
@ -199,4 +214,32 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
|
||||||
translationProvider.getText(bundle, "error.device.send-command", null, localeProvider.getLocale()));
|
translationProvider.getText(bundle, "error.device.send-command", null, localeProvider.getLocale()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any RGBW value is greater than 0.
|
||||||
|
*
|
||||||
|
* @param rgbw an int array [R, G, B, W] each in [0..255]
|
||||||
|
* @return true if any value is greater than 0, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean isAnyRgbwValueActive(int[] rgbw) {
|
||||||
|
for (int value : rgbw) {
|
||||||
|
if (value > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if any RGBW value is smaller than 0.
|
||||||
|
*
|
||||||
|
* @param rgbw an int array [R, G, B, W] each in [0..255]
|
||||||
|
*/
|
||||||
|
private void fixRgbwValues(int[] rgbw) {
|
||||||
|
for (int i = 0; i < rgbw.length; i++) {
|
||||||
|
if (rgbw[i] < 0) {
|
||||||
|
rgbw[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,16 @@ public interface SbusService {
|
||||||
*/
|
*/
|
||||||
int[] readStatusChannels(int subnetId, int id) throws Exception;
|
int[] readStatusChannels(int subnetId, int id) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads contact status values from device channels.
|
||||||
|
*
|
||||||
|
* @param subnetId the subnet ID of the device
|
||||||
|
* @param id the device ID
|
||||||
|
* @return array of contact status values (true for open, false for closed)
|
||||||
|
* @throws Exception if reading fails
|
||||||
|
*/
|
||||||
|
boolean[] readContactStatusChannels(int subnetId, int id) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes RGBW values to a device channel.
|
* Writes RGBW values to a device channel.
|
||||||
*
|
*
|
||||||
|
|
|
@ -78,7 +78,16 @@ public class SbusServiceImpl implements SbusService {
|
||||||
if (adapter == null) {
|
if (adapter == null) {
|
||||||
throw new IllegalStateException("SbusAdapter not initialized");
|
throw new IllegalStateException("SbusAdapter not initialized");
|
||||||
}
|
}
|
||||||
return adapter.readStatusChannels(subnetId, id);
|
return adapter.readExecutionStatusChannels(subnetId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean[] readContactStatusChannels(int subnetId, int id) throws Exception {
|
||||||
|
final SbusAdapter adapter = this.adapter;
|
||||||
|
if (adapter == null) {
|
||||||
|
throw new IllegalStateException("SbusAdapter not initialized");
|
||||||
|
}
|
||||||
|
return adapter.readContactStatusChannels(subnetId, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,7 +105,7 @@ public class SbusServiceImpl implements SbusService {
|
||||||
if (adapter == null) {
|
if (adapter == null) {
|
||||||
throw new IllegalStateException("SbusAdapter not initialized");
|
throw new IllegalStateException("SbusAdapter not initialized");
|
||||||
}
|
}
|
||||||
adapter.writeSingleChannel(subnetId, id, channelNumber, value, timer);
|
adapter.writeSingleExecutionChannel(subnetId, id, channelNumber, value, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,6 +11,8 @@ thing-type.sbus.switch.label = Sbus Switch
|
||||||
thing-type.sbus.switch.description = Sbus switch device
|
thing-type.sbus.switch.description = Sbus switch device
|
||||||
thing-type.sbus.temperature.label = Sbus Temperature Sensor
|
thing-type.sbus.temperature.label = Sbus Temperature Sensor
|
||||||
thing-type.sbus.temperature.description = Sbus temperature sensor device
|
thing-type.sbus.temperature.description = Sbus temperature sensor device
|
||||||
|
thing-type.sbus.contact.label = Sbus Contact Sensor
|
||||||
|
thing-type.sbus.contact.description = Sbus contact sensor device
|
||||||
thing-type.sbus.udp.label = Sbus UDP Bridge
|
thing-type.sbus.udp.label = Sbus UDP Bridge
|
||||||
thing-type.sbus.udp.description = Endpoint for Sbus UDP slaves
|
thing-type.sbus.udp.description = Endpoint for Sbus UDP slaves
|
||||||
|
|
||||||
|
@ -24,6 +26,14 @@ thing-type.config.sbus.rgbw.subnetId.label = SubnetId
|
||||||
thing-type.config.sbus.rgbw.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast.
|
thing-type.config.sbus.rgbw.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast.
|
||||||
thing-type.config.sbus.rgbw.subnetId.option.1 = 1
|
thing-type.config.sbus.rgbw.subnetId.option.1 = 1
|
||||||
thing-type.config.sbus.rgbw.subnetId.option.255 = 255
|
thing-type.config.sbus.rgbw.subnetId.option.255 = 255
|
||||||
|
thing-type.config.sbus.contact.id.label = Device ID
|
||||||
|
thing-type.config.sbus.contact.id.description = The ID of the Sbus device
|
||||||
|
thing-type.config.sbus.contact.refresh.label = Refresh Interval
|
||||||
|
thing-type.config.sbus.contact.refresh.description = Refresh interval in seconds
|
||||||
|
thing-type.config.sbus.contact.subnetId.label = SubnetId
|
||||||
|
thing-type.config.sbus.contact.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast.
|
||||||
|
thing-type.config.sbus.contact.subnetId.option.1 = 1
|
||||||
|
thing-type.config.sbus.contact.subnetId.option.255 = 255
|
||||||
thing-type.config.sbus.switch.id.label = Device ID
|
thing-type.config.sbus.switch.id.label = Device ID
|
||||||
thing-type.config.sbus.switch.id.description = The ID of the Sbus device
|
thing-type.config.sbus.switch.id.description = The ID of the Sbus device
|
||||||
thing-type.config.sbus.switch.refresh.label = Refresh Interval
|
thing-type.config.sbus.switch.refresh.label = Refresh Interval
|
||||||
|
@ -57,6 +67,8 @@ channel-type.sbus.switch-channel.label = Switch State
|
||||||
channel-type.sbus.switch-channel.description = Switch state (ON/OFF)
|
channel-type.sbus.switch-channel.description = Switch state (ON/OFF)
|
||||||
channel-type.sbus.temperature-channel.label = Temperature
|
channel-type.sbus.temperature-channel.label = Temperature
|
||||||
channel-type.sbus.temperature-channel.description = Temperature reading from the device
|
channel-type.sbus.temperature-channel.description = Temperature reading from the device
|
||||||
|
channel-type.sbus.contact-channel.label = Contact State
|
||||||
|
channel-type.sbus.contact-channel.description = Contact state (OPEN/CLOSED)
|
||||||
|
|
||||||
# channel types config
|
# channel types config
|
||||||
|
|
||||||
|
@ -78,6 +90,8 @@ channel-type.config.sbus.temperature-channel.unit.label = Temperature Unit
|
||||||
channel-type.config.sbus.temperature-channel.unit.description = The unit to use for temperature readings (°C or °F)
|
channel-type.config.sbus.temperature-channel.unit.description = The unit to use for temperature readings (°C or °F)
|
||||||
channel-type.config.sbus.temperature-channel.unit.option.CELSIUS = Celsius
|
channel-type.config.sbus.temperature-channel.unit.option.CELSIUS = Celsius
|
||||||
channel-type.config.sbus.temperature-channel.unit.option.FAHRENHEIT = Fahrenheit
|
channel-type.config.sbus.temperature-channel.unit.option.FAHRENHEIT = Fahrenheit
|
||||||
|
channel-type.config.sbus.contact-channel.channelNumber.label = Channel Number
|
||||||
|
channel-type.config.sbus.contact-channel.channelNumber.description = The physical channel number on the Sbus device
|
||||||
|
|
||||||
# thing types config
|
# thing types config
|
||||||
|
|
||||||
|
@ -122,3 +136,4 @@ error.device.send-command = Error sending command to device
|
||||||
|
|
||||||
# info messages
|
# info messages
|
||||||
info.temperature.readonly = Temperature device is read-only, ignoring command
|
info.temperature.readonly = Temperature device is read-only, ignoring command
|
||||||
|
info.contact.readonly = Contact device is read-only, ignoring command
|
||||||
|
|
|
@ -98,6 +98,37 @@
|
||||||
</config-description>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
|
|
||||||
|
<!-- Contact Device -->
|
||||||
|
<thing-type id="contact">
|
||||||
|
<supported-bridge-type-refs>
|
||||||
|
<bridge-type-ref id="udp"/>
|
||||||
|
</supported-bridge-type-refs>
|
||||||
|
<label>Contact Sensor</label>
|
||||||
|
<description>Sbus contact sensor device</description>
|
||||||
|
<semantic-equipment-tag>ContactSensor</semantic-equipment-tag>
|
||||||
|
<config-description>
|
||||||
|
<parameter name="subnetId" type="integer">
|
||||||
|
<label>SubnetId</label>
|
||||||
|
<description>Slave subnet id. Can take any value between 1 and 255. 255 for broadcast.</description>
|
||||||
|
<default>1</default>
|
||||||
|
<options>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="255">255</option>
|
||||||
|
</options>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="id" type="integer" required="true">
|
||||||
|
<label>Device ID</label>
|
||||||
|
<description>The ID of the Sbus device</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="refresh" type="integer">
|
||||||
|
<label>Refresh Interval</label>
|
||||||
|
<description>Refresh interval in seconds</description>
|
||||||
|
<default>30</default>
|
||||||
|
<unitLabel>s</unitLabel>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
<!-- Channel Types -->
|
<!-- Channel Types -->
|
||||||
<channel-type id="switch-channel">
|
<channel-type id="switch-channel">
|
||||||
<item-type>Switch</item-type>
|
<item-type>Switch</item-type>
|
||||||
|
@ -182,7 +213,28 @@
|
||||||
<label>Channel Number</label>
|
<label>Channel Number</label>
|
||||||
<description>The physical channel number on the Sbus device</description>
|
<description>The physical channel number on the Sbus device</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="enableWhite" type="boolean">
|
||||||
|
<label>Enable White Channel</label>
|
||||||
|
<description>Whether to use the white channel component in addition to RGB</description>
|
||||||
|
<default>true</default>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
</parameter>
|
||||||
</config-description>
|
</config-description>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="contact-channel">
|
||||||
|
<item-type>Contact</item-type>
|
||||||
|
<label>Contact State</label>
|
||||||
|
<description>Contact state (OPEN/CLOSED)</description>
|
||||||
|
<category>Contact</category>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
<config-description>
|
||||||
|
<parameter name="channelNumber" type="integer" required="true">
|
||||||
|
<label>Channel Number</label>
|
||||||
|
<description>The physical channel number on the Sbus device</description>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
|
||||||
</thing:thing-descriptions>
|
</thing:thing-descriptions>
|
||||||
|
|
Loading…
Reference in New Issue