diff --git a/bundles/org.openhab.binding.sbus/README.md b/bundles/org.openhab.binding.sbus/README.md index 62b2b14efdf8a..fd494bff30f4c 100644 --- a/bundles/org.openhab.binding.sbus/README.md +++ b/bundles/org.openhab.binding.sbus/README.md @@ -1,22 +1,23 @@ # Sbus Binding -This binding integrates Sbus devices with openHAB, allowing control and monitoring of Sbus-compatible devices over UDP. -Sbus is a protocol used for home automation devices that communicate over UDP networks. -The binding supports various device types including RGB/RGBW controllers, temperature sensors, and switch controllers. +This binding integrates Sbus-compatible hardware with openHAB, allowing control and monitoring over UDP networks. +Sbus is a protocol used for home automation that communicates via UDP broadcast messages. +The binding supports various thing types including RGB/RGBW controllers, temperature sensors, switch controllers, and multiple sensor types. ## Supported Things - `udp` - Sbus Bridge for UDP communication - `rgbw` - RGB/RGBW Controllers for color and brightness control -- `temperature` - Temperature Sensors for monitoring environmental conditions +- `temperature-sensor` - Temperature Sensors for monitoring environmental conditions - `switch` - Switch Controllers for basic on/off and dimming control -- `contact-sensor` - Contact Sensors for monitoring open/closed states -- `motion-sensor` - Motion Sensors for detecting movement (9-in-1 devices) -- `lux-sensor` - Light Level Sensors for monitoring illuminance (LUX values) +- `contact-sensor` - Contact Sensors for monitoring open/closed states (supports both 012C and 02CA sensor types) +- `motion-sensor` - Motion Sensors for detecting movement +- `lux-sensor` - Light Level Sensors for measuring illuminance +- `temperature` - (Deprecated) Use `temperature-sensor` instead ## Discovery -Sbus devices communicate via UDP broadcast, but manual configuration is required to set up the devices in openHAB. +Sbus hardware communicates via UDP broadcast, but manual configuration is required to set up things in openHAB. Auto-discovery is not supported at this moment. ## Binding Configuration @@ -33,21 +34,29 @@ The Sbus Bridge has the following configuration parameters: | Name | Type | Description | Default | Required | Advanced | |:--------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:| -| host | text | IP address of the Sbus device (typically broadcast) | N/A | yes | no | +| host | text | IP address for Sbus communication (typically broadcast) | N/A | yes | no | | port | integer | UDP port number | 6000 | no | no | | timeout | integer | Response timeout in milliseconds | 3000 | no | yes | -### Device Configuration +### Thing Configuration -All device types (RGBW Controller, Contact Sensor, Switch, Temperature, Motion Sensor, Lux Sensor) share the same configuration parameters: +Most thing types share the same basic configuration parameters: | Name | Type | Description | Default | Required | Advanced | |:--------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:| -| subnetId| integer | Subnet ID the device is part of | N/A | yes | no | -| id | integer | Device ID | N/A | yes | no | +| subnetId| integer | Subnet ID | 1 | yes | no | +| id | integer | Unit ID | N/A | yes | no | | refresh | integer | Refresh interval in seconds (0 = listen-only mode) | 30 | no | yes | -**Note:** Setting `refresh=0` enables listen-only mode for all device types. In this mode, handlers only process asynchronous broadcast messages without actively polling the devices. This is particularly useful for Motion Sensor and Lux Sensor devices that broadcast status updates (0x02CA) from 9-in-1 devices. +**Contact Sensor Additional Configuration:** + +The `contact-sensor` thing type has an additional `type` parameter: + +| Name | Type | Description | Default | Required | Advanced | +|:--------|:--------|:-----------------------------------------------------|:-------:|:--------:|:---------:| +| type | text | Sensor type: `012c` (dry contact) or `02ca` (multi-sensor) | 012c | no | no | + +**Listen-Only Mode:** Setting `refresh=0` enables listen-only mode where the binding only processes broadcast messages without actively polling. This is useful for sensors that automatically broadcast their status updates. ## Channels @@ -62,7 +71,7 @@ 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 | +| channelNumber | integer | The physical channel number | N/A | yes | no | | enableWhite | boolean | Controls the white component support for RGB palette | true | no | yes | ### Temperature Sensor Channels @@ -87,15 +96,17 @@ The color channel of RGBW controllers supports these additional parameters: ### Motion Sensor Channels -| Channel | Type | Read/Write | Description | -|:--------|:-------|:----------:|:----------------------------------------------------------| -| motion | Switch | R | Motion detection state (ON=motion detected, OFF=no motion) | +| Channel | Type | Read/Write | Description | +|:--------|:--------|:----------:|:----------------------------------------------------------| +| motion | Switch | R | Motion detection state (ON=motion detected, OFF=no motion)| ### Lux Sensor Channels -| Channel | Type | Read/Write | Description | -|:--------|:-------|:----------:|:----------------------------------------------------------| -| lux | Number | R | Light level in LUX units | +| Channel | Type | Read/Write | Description | +|:--------|:--------|:----------:|:----------------------------------------------------------| +| lux | Number | R | Light level in LUX units | + +**Note:** All sensor channels require a `channelNumber` parameter to specify the physical channel number. ## Full Example @@ -115,11 +126,6 @@ Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000, timeout=5000 ] { Type switch-channel : power [ channelNumber=1 ] } - Thing temperature temp1 [ id=62, refresh=30 ] { - Channels: - Type temperature-channel : temperature [ channelNumber=1 ] - } - Thing switch switch1 [ id=75, refresh=30 ] { Channels: Type switch-channel : first_switch [ channelNumber=1 ] @@ -127,19 +133,25 @@ Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000, timeout=5000 ] { Type paired-channel : third_switch [ channelNumber=3, pairedChannelNumber=4 ] } - Thing contact-sensor contact1 [ id=80, refresh=30 ] { + Thing temperature-sensor temp1 [ id=62, refresh=30 ] { + Channels: + Type temperature-channel : temperature [ channelNumber=1 ] + } + + Thing contact-sensor contact1 [ type="012c", id=80, refresh=30 ] { Channels: Type contact-channel : contact [ channelNumber=1 ] } + - Thing motion-sensor motion1 [ id=85, refresh=0 ] { + Thing motion-sensor sensor_motion [ id=85, refresh=0 ] { Channels: - Type motion-channel : motion + Type motion-channel : motion [ channelNumber=1 ] } - Thing lux-sensor lux1 [ id=85, refresh=0 ] { + Thing lux-sensor sensor_lux [ id=85, refresh=0 ] { Channels: - Type lux-channel : lux + Type lux-channel : lux [ channelNumber=1 ] } } ``` @@ -148,7 +160,7 @@ Bridge sbus:udp:mybridge [ host="192.168.1.255", port=5000, timeout=5000 ] { ```java // Temperature Sensor -Number:Temperature Temp_Sensor "Temperature [%.1f °C]" { channel="sbus:temperature:mybridge:temp1:temperature" } +Number:Temperature Temp_Sensor "Temperature [%.1f °C]" { channel="sbus:temperature-sensor:mybridge:temp1:temperature" } // Basic Switch Switch Light_Switch "Switch" { channel="sbus:switch:mybridge:switch1:switch" } @@ -162,30 +174,61 @@ Color rgbwColor "Color" (gLight) ["Control", "Light"] Switch rgbwPower "Power" (gLight) ["Switch", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:power" } // Contact Sensor -Contact Door_Contact "Door [%s]" { channel="sbus:contact-sensor:mybridge:contact1:contact" } +Contact Contact_Sensor "Contact [%s]" { channel="sbus:contact-sensor:mybridge:contact1:contact" } -// Motion Sensor (listen-only mode) -Switch Motion_Sensor "Motion [%s]" { channel="sbus:motion-sensor:mybridge:motion1:motion" } +// Motion Sensor +Switch Motion_Sensor "Motion [%s]" { channel="sbus:motion-sensor:mybridge:sensor_motion:motion" } -// Lux Sensor (listen-only mode) -Number Lux_Sensor "Light Level [%.0f lux]" { channel="sbus:lux-sensor:mybridge:lux1:lux" } +// Lux Sensor +Number Lux_Sensor "Light Level [%.0f lux]" { channel="sbus:lux-sensor:mybridge:sensor_lux:lux" } ``` -### Sitemap Configuration - -```perl -sitemap sbus label="Sbus Demo" -{ - Frame label="Sbus Controls" { - Colorpicker item=Light_RGB - Text item=Temp_Sensor - Switch item=Light_Switch - Rollershutter item=Rollershutter_Switch - Text item=Door_Contact - } +## Usage Notes + +### 9-in-1 Sensor Configuration + +9-in-1 sensors are multi-function sensors that combine motion detection, light level measurement, and dry contact monitoring in a single physical unit. To configure a 9-in-1 sensor in openHAB, you need to create **three separate things** that all reference the same physical sensor: + +1. **contact-sensor** (type: `02ca`) - For dry contact channels +2. **motion-sensor** - For motion detection +3. **lux-sensor** - For light level sensing + +All three things must use the **same subnet ID and unit ID** to represent the same physical sensor. + +**Example for a 9-in-1 sensor with ID 85:** + +```java +Thing contact-sensor sensor_contact [ type="02ca", id=85, refresh=0 ] { + Channels: + Type contact-channel : contact1 [ channelNumber=1 ] + Type contact-channel : contact2 [ channelNumber=2 ] } -## Usage Notes +Thing motion-sensor sensor_motion [ id=85, refresh=0 ] { + Channels: + Type motion-channel : motion [ channelNumber=1 ] +} + +Thing lux-sensor sensor_lux [ id=85, refresh=0 ] { + Channels: + Type lux-channel : lux [ channelNumber=1 ] +} +``` + +**Benefits of this approach:** +- Clear separation of concerns - each thing handles one sensor type +- Flexible configuration - only create the things you need +- Follows openHAB best practices for thing organization +- Each thing can be configured independently + +### Contact Sensor Types + +The `contact-sensor` thing type supports two different sensor types via the `type` parameter: + +- **`012c`** (default): 012C dry contact sensors +- **`02ca`**: 02CA multi-sensor dry contacts + +Choose the appropriate type based on your hardware. ### RGB vs. RGBW Mode diff --git a/bundles/org.openhab.binding.sbus/pom.xml b/bundles/org.openhab.binding.sbus/pom.xml index a8e5374f27ad5..6ec0736690da9 100644 --- a/bundles/org.openhab.binding.sbus/pom.xml +++ b/bundles/org.openhab.binding.sbus/pom.xml @@ -18,7 +18,7 @@ ro.ciprianpascu j2sbus - 1.6.3 + 1.6.4 diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/BindingConstants.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/BindingConstants.java index 32870e6cfdf2d..d6c8618d1f5bf 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/BindingConstants.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/BindingConstants.java @@ -34,7 +34,8 @@ private BindingConstants() { // Thing Types 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"); // Deprecated + public static final ThingTypeUID THING_TYPE_TEMPERATURE_SENSOR = new ThingTypeUID(BINDING_ID, "temperature-sensor"); public static final ThingTypeUID THING_TYPE_RGBW = new ThingTypeUID(BINDING_ID, "rgbw"); public static final ThingTypeUID THING_TYPE_CONTACT_SENSOR = new ThingTypeUID(BINDING_ID, "contact-sensor"); public static final ThingTypeUID THING_TYPE_MOTION_SENSOR = new ThingTypeUID(BINDING_ID, "motion-sensor"); diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/ContactSensorType.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/ContactSensorType.java new file mode 100644 index 0000000000000..44507697a313a --- /dev/null +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/ContactSensorType.java @@ -0,0 +1,64 @@ +/* + * 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.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ContactSensorType} enum defines the types of contact sensors supported by the binding. + * + * @author Ciprian Pascu - Initial contribution + */ +@NonNullByDefault +public enum ContactSensorType { + /** + * 012C dry contact sensor using ReadDryChannelsRequest/Response protocol + */ + SENSOR_012C("012c"), + + /** + * 02CA multi-sensor with dry contacts using ReadNineInOneStatusRequest/Response protocol + * and MotionSensorStatusReport (0x02CA) broadcasts + */ + MULTI_SENSOR_02CA("02ca"); + + private final String configValue; + + ContactSensorType(String configValue) { + this.configValue = configValue; + } + + /** + * Get the configuration value for this sensor type. + * + * @return the configuration value + */ + public String getConfigValue() { + return configValue; + } + + /** + * Parse a configuration value into a ContactSensorType. + * + * @param value the configuration value + * @return the corresponding ContactSensorType, or SENSOR_24Z if not recognized + */ + public static ContactSensorType fromConfigValue(String value) { + for (ContactSensorType type : values()) { + if (type.configValue.equals(value)) { + return type; + } + } + return SENSOR_012C; // Default to 012C for backward compatibility or null values + } +} diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/SbusContactConfig.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/SbusContactConfig.java new file mode 100644 index 0000000000000..6591cc11c7c23 --- /dev/null +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/config/SbusContactConfig.java @@ -0,0 +1,38 @@ +/* + * 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.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link SbusContactConfig} class extends device configuration with contact sensor specific parameters. + * + * @author Ciprian Pascu - Initial contribution + */ +@NonNullByDefault +public class SbusContactConfig extends SbusDeviceConfig { + /** + * Sensor type for contact sensor. + * String value is used for configuration binding, converted to enum via ContactSensorType.fromConfigValue() + */ + public String type = ContactSensorType.SENSOR_012C.getConfigValue(); // Default to traditional behavior + + /** + * Get the sensor type as an enum. + * + * @return the ContactSensorType enum value + */ + public ContactSensorType getSensorType() { + return ContactSensorType.fromConfigValue(type); + } +} diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/AbstractSbusHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/AbstractSbusHandler.java index 5c04316ae1eae..360d6fe48ac6a 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/AbstractSbusHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/AbstractSbusHandler.java @@ -122,10 +122,23 @@ protected void createChannel(String channelId, String channelTypeId) { updateThing(thingBuilder.build()); } + /** + * Public method for helpers to update channel states. + * This exposes the protected updateState method to helper classes. + * + * @param channelUID the channel to update + * @param state the new state value + */ + public void updateChannelState(ChannelUID channelUID, org.openhab.core.types.State state) { + updateState(channelUID, state); + } + /** * Start polling the device for updates based on the configured refresh interval. + * + * @param initialDelay the initial delay before the first poll (in seconds) */ - protected void startPolling() { + protected void startPolling(long initialDelay) { SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); if (config.refresh > 0) { pollingJob = scheduler.scheduleWithFixedDelay(() -> { @@ -134,9 +147,10 @@ protected void startPolling() { } catch (IllegalStateException e) { logger.warn("Error polling Sbus device: {}", e.getMessage()); } - }, 0, config.refresh, TimeUnit.SECONDS); - } else if (config.refresh == 0) { + }, initialDelay, config.refresh, TimeUnit.SECONDS); + } else if (config.refresh == 0 && initialDelay == 0) { // Run polling once to set initial thing state when refresh is disabled + // Only execute if initialDelay is 0 (startup), not when called from resetPollingTimer pollingJob = scheduler.schedule(() -> { try { pollDevice(); @@ -147,6 +161,14 @@ protected void startPolling() { } } + /** + * Start polling the device for updates based on the configured refresh interval. + * Uses a default initial delay of 0 seconds (immediate execution). + */ + protected void startPolling() { + startPolling(0); + } + /** * Poll the device for updates. This method should be implemented by concrete handlers * to update their specific channel states. @@ -166,14 +188,23 @@ protected void startPolling() { * Reset the polling timer by cancelling the current polling job and starting a new one. * This should be called when an async message is successfully processed to reduce * unnecessary polling when the device is actively sending updates. + * Only resets the timer if periodic polling is enabled (refresh > 0). + * The next poll will occur after the full refresh interval, not immediately. */ protected void resetPollingTimer() { - ScheduledFuture job = pollingJob; - if (job != null) { - job.cancel(false); // Cancel the polling job without interrupting if it is currently running (cancel(false)) + SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); + // Only reset polling timer if periodic polling is enabled + if (config.refresh > 0) { + ScheduledFuture job = pollingJob; + if (job != null) { + job.cancel(false); // Cancel the polling job without interrupting if it is currently running + // (cancel(false)) + } + // Use refresh interval as initial delay to prevent immediate polling + startPolling(config.refresh); + logger.debug("Reset polling timer for handler {} - next poll in {} seconds", getThing().getUID(), + config.refresh); } - startPolling(); - logger.debug("Reset polling timer for handler {}", getThing().getUID()); } // SbusMessageListener implementation diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/Sbus9in1ContactHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/Sbus9in1ContactHandler.java new file mode 100644 index 0000000000000..39ae576da2423 --- /dev/null +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/Sbus9in1ContactHandler.java @@ -0,0 +1,206 @@ +/* + * 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.internal.handler; + +import org.openhab.binding.sbus.BindingConstants; +import org.openhab.binding.sbus.internal.SbusService; +import org.openhab.binding.sbus.internal.config.SbusChannelConfig; +import org.openhab.binding.sbus.internal.config.SbusDeviceConfig; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ro.ciprianpascu.sbus.msg.MotionSensorStatusReport; +import ro.ciprianpascu.sbus.msg.ReadNineInOneStatusRequest; +import ro.ciprianpascu.sbus.msg.ReadNineInOneStatusResponse; +import ro.ciprianpascu.sbus.msg.SbusResponse; + +/** + * The {@link Sbus9in1ContactHandler} handles 9-in-1 sensor devices with dry contact channels. + * It uses ReadNineInOneStatusRequest/Response protocol for polling and processes + * MotionSensorStatusReport for async updates. + * + * @author Ciprian Pascu - Initial contribution + */ +public class Sbus9in1ContactHandler extends AbstractSbusHandler { + + private final Logger logger = LoggerFactory.getLogger(Sbus9in1ContactHandler.class); + + public Sbus9in1ContactHandler(Thing thing) { + super(thing); + } + + @Override + protected void initializeChannels() { + // Validate channel configuration + for (Channel channel : getThing().getChannels()) { + if (channel.getChannelTypeUID() != null + && BindingConstants.CHANNEL_TYPE_CONTACT.equals(channel.getChannelTypeUID().getId())) { + logger.debug("Initialized 9-in-1 contact channel: {}", channel.getUID().getId()); + } + } + } + + @Override + protected void pollDevice() { + final SbusService adapter = super.sbusAdapter; + if (adapter == null) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/error.device.adapter-not-initialized"); + return; + } + + try { + SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); + ReadNineInOneStatusResponse response = readNineInOneStatus(adapter, config.subnetId, config.id); + + // Update all contact channels from the response + updateContactChannelsFromResponse(response); + + updateStatus(ThingStatus.ONLINE); + } catch (IllegalStateException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "@text/error.device.communication"); + logger.warn("Error polling 9-in-1 contact sensor {}: {}", getThing().getUID(), e.getMessage()); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + // Contact sensors are read-only + logger.debug("9-in-1 contact sensor is read-only, ignoring command {} for channel {}", command, channelUID); + } + + @Override + protected void processAsyncMessage(SbusResponse response) { + try { + if (response instanceof MotionSensorStatusReport report) { + // Process motion sensor status report for dry contact updates + updateContactChannelsFromReport(report); + updateStatus(ThingStatus.ONLINE); + logger.debug("Processed async motion sensor status report for 9-in-1 contact handler {}", + getThing().getUID()); + } + } catch (IllegalStateException | IllegalArgumentException e) { + logger.warn("Error processing async message in 9-in-1 contact handler {}: {}", getThing().getUID(), + e.getMessage()); + } + } + + @Override + protected boolean isMessageRelevant(SbusResponse response) { + SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); + + if (response instanceof MotionSensorStatusReport) { + // Motion sensor status reports are broadcast messages + // They are relevant if they come from our device + return response.getSourceSubnetID() == config.subnetId && response.getSourceUnitID() == config.id; + } else if (response instanceof ReadNineInOneStatusResponse) { + // Check if the response is for this device + return response.getSubnetID() == config.subnetId && response.getUnitID() == config.id; + } + return false; + } + + /** + * Read 9-in-1 sensor status from device. + * + * @param adapter the SBUS service adapter + * @param subnetId the subnet ID + * @param deviceId the device ID + * @return ReadNineInOneStatusResponse + * @throws IllegalStateException if communication fails + */ + private ReadNineInOneStatusResponse readNineInOneStatus(SbusService adapter, int subnetId, int deviceId) + throws IllegalStateException { + ReadNineInOneStatusRequest request = new ReadNineInOneStatusRequest(); + request.setSubnetID(subnetId); + request.setUnitID(deviceId); + + SbusResponse response = adapter.executeTransaction(request); + if (!(response instanceof ReadNineInOneStatusResponse statusResponse)) { + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); + } + + return statusResponse; + } + + /** + * Update contact channels from ReadNineInOneStatusResponse. + * + * @param response the 9-in-1 status response + */ + private void updateContactChannelsFromResponse(ReadNineInOneStatusResponse response) { + for (Channel channel : getThing().getChannels()) { + ChannelUID channelUID = channel.getUID(); + if (channel.getChannelTypeUID() != null + && BindingConstants.CHANNEL_TYPE_CONTACT.equals(channel.getChannelTypeUID().getId())) { + SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class); + + // Use channelNumber to determine which dry contact (1 or 2, default to 1) + int channelNumber = channelConfig.channelNumber > 0 ? channelConfig.channelNumber : 1; + boolean contactState = false; + + if (channelNumber == 1) { + contactState = response.getDryContact1Status() > 0; + } else if (channelNumber == 2) { + contactState = response.getDryContact2Status() > 0; + } + + OpenClosedType state = contactState ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + updateState(channelUID, state); + + logger.debug("Updated 9-in-1 contact channel {} (number {}) state: {}", channelUID.getId(), + channelNumber, state); + } + } + } + + /** + * Update contact channels from MotionSensorStatusReport. + * + * @param report the motion sensor status report + */ + private void updateContactChannelsFromReport(MotionSensorStatusReport report) { + for (Channel channel : getThing().getChannels()) { + ChannelUID channelUID = channel.getUID(); + if (channel.getChannelTypeUID() != null + && BindingConstants.CHANNEL_TYPE_CONTACT.equals(channel.getChannelTypeUID().getId())) { + SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class); + + // Use channelNumber to determine which dry contact (1 or 2, default to 1) + int channelNumber = channelConfig.channelNumber > 0 ? channelConfig.channelNumber : 1; + boolean contactState = false; + + if (channelNumber == 1) { + contactState = report.getDryContactStatus(0) > 0; // First dry contact (index 0) + } else if (channelNumber == 2) { + contactState = report.getDryContactStatus(1) > 0; // Second dry contact (index 1) + } + + OpenClosedType state = contactState ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + updateState(channelUID, state); + + logger.debug("Updated 9-in-1 contact channel {} (number {}) state from report: {}", channelUID.getId(), + channelNumber, state); + } + } + } +} diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusContactHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusContactHandler.java index 7be7d34532173..b735042f0dc3d 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusContactHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusContactHandler.java @@ -27,13 +27,13 @@ import ro.ciprianpascu.sbus.msg.ReadDryChannelsRequest; import ro.ciprianpascu.sbus.msg.ReadDryChannelsResponse; -import ro.ciprianpascu.sbus.msg.ReadStatusChannelsResponse; import ro.ciprianpascu.sbus.msg.SbusResponse; import ro.ciprianpascu.sbus.procimg.InputRegister; /** - * The {@link SbusContactHandler} is responsible for handling commands for Sbus contact devices. - * It supports reading the current contact state (open/closed). + * The {@link SbusContactHandler} is responsible for handling traditional Sbus contact sensor devices. + * It supports reading the current contact state (open/closed) using the ReadDryChannelsRequest protocol. + * For 9-in-1 sensor devices, use the Sbus9in1ContactHandler instead. * * @author Ciprian Pascu - Initial contribution */ @@ -105,14 +105,11 @@ private boolean[] readContactStatusChannels(SbusService adapter, int subnetId, i // Execute transaction and parse response SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadDryChannelsResponse statusResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } - InputRegister[] registers = statusResponse.getRegisters(); - boolean[] contactStates = new boolean[registers.length]; - for (int i = 0; i < registers.length; i++) { - contactStates[i] = (registers[i].getValue() & 0xff) > 0; // Convert to boolean - } + boolean[] contactStates = extractContactStatuses(statusResponse); return contactStates; } @@ -121,12 +118,11 @@ private boolean[] readContactStatusChannels(SbusService adapter, int subnetId, i @Override protected void processAsyncMessage(SbusResponse response) { try { - if (response instanceof ReadStatusChannelsResponse statusResponse) { + if (response instanceof ReadDryChannelsResponse statusResponse) { // Process status channel response using existing logic boolean[] statuses = extractContactStatuses(statusResponse); - - // Update channel states based on async message updateChannelStatesFromStatuses(statuses); + updateStatus(ThingStatus.ONLINE); logger.debug("Processed async contact status message for handler {}", getThing().getUID()); } } catch (IllegalStateException | IllegalArgumentException e) { @@ -137,13 +133,12 @@ protected void processAsyncMessage(SbusResponse response) { @Override protected boolean isMessageRelevant(SbusResponse response) { - if (!(response instanceof ReadStatusChannelsResponse)) { - return false; + if (response instanceof ReadDryChannelsResponse) { + // Traditional contact sensor messages + SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); + return response.getSubnetID() == config.subnetId && response.getUnitID() == config.id; } - - // Check if the message is for this device based on subnet and unit ID - SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class); - return response.getSubnetID() == config.subnetId && response.getUnitID() == config.id; + return false; } /** @@ -165,10 +160,10 @@ private void updateChannelStatesFromStatuses(boolean[] contactStates) { } /** - * Extract contact status values from ReadStatusChannelsResponse. + * Extract contact status values from ReadDryChannelsResponse. * Reuses existing logic from readContactStatusChannels method. */ - private boolean[] extractContactStatuses(ReadStatusChannelsResponse response) { + private boolean[] extractContactStatuses(ReadDryChannelsResponse response) { InputRegister[] registers = response.getRegisters(); boolean[] statuses = new boolean[registers.length]; diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusHandlerFactory.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusHandlerFactory.java index 7b3af7159c5cc..d18518422e51b 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusHandlerFactory.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusHandlerFactory.java @@ -18,6 +18,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.sbus.internal.config.ContactSensorType; +import org.openhab.binding.sbus.internal.config.SbusContactConfig; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingTypeUID; @@ -40,8 +42,8 @@ public class SbusHandlerFactory extends BaseThingHandlerFactory { private final Logger logger = LoggerFactory.getLogger(SbusHandlerFactory.class); private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UDP_BRIDGE, THING_TYPE_SWITCH, - THING_TYPE_TEMPERATURE, THING_TYPE_RGBW, THING_TYPE_CONTACT_SENSOR, THING_TYPE_MOTION_SENSOR, - THING_TYPE_LUX_SENSOR); + THING_TYPE_TEMPERATURE, THING_TYPE_TEMPERATURE_SENSOR, THING_TYPE_RGBW, THING_TYPE_CONTACT_SENSOR, + THING_TYPE_MOTION_SENSOR, THING_TYPE_LUX_SENSOR); @Override public boolean supportsThingType(ThingTypeUID thingTypeUID) { @@ -60,15 +62,24 @@ public boolean supportsThingType(ThingTypeUID thingTypeUID) { if (thingTypeUID.equals(THING_TYPE_SWITCH)) { logger.debug("Creating Sbus switch handler for thing {}", thing.getUID()); return new SbusSwitchHandler(thing); - } else if (thingTypeUID.equals(THING_TYPE_TEMPERATURE)) { - logger.debug("Creating Sbus temperature handler for thing {}", thing.getUID()); + } else if (thingTypeUID.equals(THING_TYPE_TEMPERATURE) || thingTypeUID.equals(THING_TYPE_TEMPERATURE_SENSOR)) { + logger.debug("Creating Sbus temperature sensor handler for thing {}", thing.getUID()); return new SbusTemperatureHandler(thing); } else if (thingTypeUID.equals(THING_TYPE_RGBW)) { logger.debug("Creating Sbus RGBW handler for thing {}", thing.getUID()); return new SbusRgbwHandler(thing); } else if (thingTypeUID.equals(THING_TYPE_CONTACT_SENSOR)) { - logger.debug("Creating Sbus contact sensor handler for thing {}", thing.getUID()); - return new SbusContactHandler(thing); + // Determine which contact handler to create based on sensor type configuration + SbusContactConfig config = thing.getConfiguration().as(SbusContactConfig.class); + ContactSensorType sensorType = config.getSensorType(); + + if (sensorType == ContactSensorType.MULTI_SENSOR_02CA) { + logger.debug("Creating Sbus 02CA multi-sensor contact handler for thing {}", thing.getUID()); + return new Sbus9in1ContactHandler(thing); + } else { + logger.debug("Creating Sbus 012C contact sensor handler for thing {}", thing.getUID()); + return new SbusContactHandler(thing); + } } else if (thingTypeUID.equals(THING_TYPE_MOTION_SENSOR)) { logger.debug("Creating Sbus motion sensor handler for thing {}", thing.getUID()); return new SbusMotionSensorHandler(thing); diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusLuxSensorHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusLuxSensorHandler.java index cc168df68011f..e436ebe3d2c3c 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusLuxSensorHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusLuxSensorHandler.java @@ -15,8 +15,11 @@ import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.sbus.BindingConstants; import org.openhab.binding.sbus.internal.SbusService; +import org.openhab.binding.sbus.internal.config.SbusChannelConfig; import org.openhab.binding.sbus.internal.config.SbusDeviceConfig; -import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -48,8 +51,14 @@ public SbusLuxSensorHandler(Thing thing) { @Override protected void initializeChannels() { - // Create lux channel - createChannel(BindingConstants.CHANNEL_LUX, BindingConstants.CHANNEL_TYPE_LUX); + // 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) { + logger.warn("Channel {} has invalid channel number configuration", channel.getUID()); + } + } } @Override @@ -81,12 +90,17 @@ protected void pollDevice() { * @param response the sensor response containing lux data */ private void updateChannelStatesFromResponse(ReadNineInOneStatusResponse response) { - // Update lux channel (byte 2: LUX value) + // Update lux channel (byte 2: LUX value - one byte unsigned, 0-255 range) + // Shift 8-bit value to 16-bit range, then convert to lux with unit ChannelUID luxChannelUID = new ChannelUID(getThing().getUID(), BindingConstants.CHANNEL_LUX); - DecimalType luxValue = new DecimalType(response.getLuxValue()); + int luxValueSigned = response.getLuxValue(); + int luxValue8bit = luxValueSigned & 0xFF; // Convert to unsigned 8-bit value (0-255) + int luxValue16bit = luxValue8bit << 8; // Shift to 16-bit range (0-65280) + QuantityType luxValue = new QuantityType<>(luxValue16bit, Units.LUX); updateState(luxChannelUID, luxValue); - logger.debug("Updated lux sensor state - LUX: {}", luxValue); + logger.debug("Updated lux sensor state - LUX: {} (8-bit: {}, 16-bit: {}, raw: {})", luxValue16bit, luxValue8bit, + luxValue16bit, luxValueSigned); } /** @@ -95,12 +109,16 @@ private void updateChannelStatesFromResponse(ReadNineInOneStatusResponse respons * @param report the motion sensor status report containing lux data */ private void updateChannelStatesFromReport(MotionSensorStatusReport report) { - // Update lux channel (bytes 6-7: LUX value as 2-byte value) + // Update lux channel (bytes 6-7: LUX value - two bytes unsigned, 0-65535 range) + // Convert to lux with unit ChannelUID luxChannelUID = new ChannelUID(getThing().getUID(), BindingConstants.CHANNEL_LUX); - DecimalType luxValue = new DecimalType(report.getLuxValue()); + int luxValueSigned = report.getLuxValue(); + int luxValue16bit = luxValueSigned & 0xFFFF; // Convert to unsigned 16-bit value (0-65535) + QuantityType luxValue = new QuantityType<>(luxValue16bit, Units.LUX); updateState(luxChannelUID, luxValue); - logger.debug("Updated lux sensor state from report - LUX: {}", luxValue); + logger.debug("Updated lux sensor state from report - LUX: {} (16-bit: {}, raw: {})", luxValue16bit, + luxValue16bit, luxValueSigned); } @Override @@ -126,7 +144,8 @@ private ReadNineInOneStatusResponse readNineInOneStatus(SbusService adapter, int SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadNineInOneStatusResponse statusResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } return statusResponse; @@ -140,11 +159,8 @@ protected void processAsyncMessage(SbusResponse response) { if (response instanceof MotionSensorStatusReport report) { // Process motion sensor status report (0x02CA broadcast) updateChannelStatesFromReport(report); + updateStatus(ThingStatus.ONLINE); logger.debug("Processed async motion sensor status report for lux handler {}", getThing().getUID()); - } else if (response instanceof ReadNineInOneStatusResponse statusResponse) { - // Process 9-in-1 status response (0xDB01) - updateChannelStatesFromResponse(statusResponse); - logger.debug("Processed async 9-in-1 status response for lux handler {}", getThing().getUID()); } } catch (IllegalStateException | IllegalArgumentException e) { logger.warn("Error processing async message in lux sensor handler {}: {}", getThing().getUID(), diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusMotionSensorHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusMotionSensorHandler.java index 98c3fb106ad1e..cacc1791c9da2 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusMotionSensorHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusMotionSensorHandler.java @@ -15,8 +15,10 @@ import org.eclipse.jdt.annotation.NonNull; import org.openhab.binding.sbus.BindingConstants; import org.openhab.binding.sbus.internal.SbusService; +import org.openhab.binding.sbus.internal.config.SbusChannelConfig; import org.openhab.binding.sbus.internal.config.SbusDeviceConfig; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; @@ -48,8 +50,14 @@ public SbusMotionSensorHandler(Thing thing) { @Override protected void initializeChannels() { - // Create motion channel - this handler focuses only on motion detection - createChannel(BindingConstants.CHANNEL_MOTION, BindingConstants.CHANNEL_TYPE_MOTION); + // 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) { + logger.warn("Channel {} has invalid channel number configuration", channel.getUID()); + } + } } @Override @@ -126,7 +134,8 @@ private ReadNineInOneStatusResponse readNineInOneStatus(SbusService adapter, int SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadNineInOneStatusResponse statusResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } return statusResponse; @@ -140,11 +149,8 @@ protected void processAsyncMessage(SbusResponse response) { if (response instanceof MotionSensorStatusReport report) { // Process motion sensor status report (0x02CA broadcast) updateChannelStatesFromReport(report); + updateStatus(ThingStatus.ONLINE); logger.debug("Processed async motion sensor status report for handler {}", getThing().getUID()); - } else if (response instanceof ReadNineInOneStatusResponse statusResponse) { - // Process 9-in-1 status response (0xDB01) - updateChannelStatesFromResponse(statusResponse); - logger.debug("Processed async 9-in-1 status response for handler {}", getThing().getUID()); } } catch (IllegalStateException | IllegalArgumentException e) { logger.warn("Error processing async message in motion sensor handler {}: {}", getThing().getUID(), diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusRgbwHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusRgbwHandler.java index 72499f2d35dab..1f606b91752ee 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusRgbwHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusRgbwHandler.java @@ -295,7 +295,8 @@ private int[] readStatusChannels(SbusService adapter, int subnetId, int deviceId // Execute transaction and parse response SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadStatusChannelsResponse statusResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } InputRegister[] registers = statusResponse.getRegisters(); int[] statusValues = new int[registers.length]; @@ -328,7 +329,8 @@ private int[] readRgbw(SbusService adapter, int subnetId, int deviceId, int chan // Execute transaction and parse response SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadRgbwResponse rgbwResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } InputRegister[] registers = rgbwResponse.getRegisters(); int[] rgbwValues = new int[Math.min(4, registers.length)]; diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusSwitchHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusSwitchHandler.java index a5c4cddccb4db..63363db327807 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusSwitchHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusSwitchHandler.java @@ -191,7 +191,8 @@ private int[] readStatusChannels(SbusService adapter, int subnetId, int deviceId // Execute transaction and parse response SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadStatusChannelsResponse statusResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } InputRegister[] registers = statusResponse.getRegisters(); int[] statuses = new int[registers.length]; diff --git a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusTemperatureHandler.java b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusTemperatureHandler.java index b9bc18df758c3..dc021fdbebb0a 100644 --- a/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusTemperatureHandler.java +++ b/bundles/org.openhab.binding.sbus/src/main/java/org/openhab/binding/sbus/internal/handler/SbusTemperatureHandler.java @@ -138,7 +138,8 @@ private float[] readTemperatures(SbusService adapter, int subnetId, int deviceId // Execute transaction and parse response SbusResponse response = adapter.executeTransaction(request); if (!(response instanceof ReadTemperatureResponse tempResponse)) { - throw new IllegalStateException("Unexpected response type: " + response.getClass().getSimpleName()); + throw new IllegalStateException( + "Unexpected response type: " + (response != null ? response.getClass().getSimpleName() : "null")); } InputRegister[] registers = tempResponse.getRegisters(); float[] temperatures = new float[registers.length]; diff --git a/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/i18n/sbus.properties b/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/i18n/sbus.properties index c8984bbed8d26..7bda40eb86c0d 100644 --- a/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/i18n/sbus.properties +++ b/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/i18n/sbus.properties @@ -9,10 +9,16 @@ thing-type.sbus.rgbw.label = Sbus RGBW Controller thing-type.sbus.rgbw.description = Sbus RGBW lighting controller thing-type.sbus.switch.label = Sbus Switch thing-type.sbus.switch.description = Sbus switch device -thing-type.sbus.temperature.label = Sbus Temperature Sensor -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.temperature-sensor.label = Temperature Sensor +thing-type.sbus.temperature-sensor.description = Sbus temperature sensor +thing-type.sbus.temperature.label = Sbus Temperature Sensor (Deprecated) +thing-type.sbus.temperature.description = Sbus temperature sensor device (deprecated, use temperature-sensor instead) +thing-type.sbus.contact-sensor.label = Sbus Contact Sensor +thing-type.sbus.contact-sensor.description = Sbus contact sensor device +thing-type.sbus.motion-sensor.label = Sbus Motion Sensor +thing-type.sbus.motion-sensor.description = Sbus motion sensor device +thing-type.sbus.lux-sensor.label = Sbus Lux Sensor +thing-type.sbus.lux-sensor.description = Sbus light level sensor device thing-type.sbus.udp.label = Sbus UDP Bridge thing-type.sbus.udp.description = Endpoint for Sbus UDP slaves @@ -26,14 +32,18 @@ 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.option.1 = 1 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.contact-sensor.type.label = Device Type +thing-type.config.sbus.contact-sensor.type.description = Type of contact sensor device +thing-type.config.sbus.contact-sensor.type.option.012c = 012C (Dry Contact Sensor) +thing-type.config.sbus.contact-sensor.type.option.02ca = 02CA (Multi-Sensor) +thing-type.config.sbus.contact-sensor.id.label = Device ID +thing-type.config.sbus.contact-sensor.id.description = The ID of the Sbus device +thing-type.config.sbus.contact-sensor.refresh.label = Refresh Interval +thing-type.config.sbus.contact-sensor.refresh.description = Refresh interval in seconds +thing-type.config.sbus.contact-sensor.subnetId.label = SubnetId +thing-type.config.sbus.contact-sensor.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast. +thing-type.config.sbus.contact-sensor.subnetId.option.1 = 1 +thing-type.config.sbus.contact-sensor.subnetId.option.255 = 255 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.refresh.label = Refresh Interval @@ -42,6 +52,14 @@ thing-type.config.sbus.switch.subnetId.label = SubnetId thing-type.config.sbus.switch.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast. thing-type.config.sbus.switch.subnetId.option.1 = 1 thing-type.config.sbus.switch.subnetId.option.255 = 255 +thing-type.config.sbus.temperature-sensor.id.label = Device ID +thing-type.config.sbus.temperature-sensor.id.description = The ID of the Sbus device +thing-type.config.sbus.temperature-sensor.refresh.label = Refresh Interval +thing-type.config.sbus.temperature-sensor.refresh.description = Refresh interval in seconds +thing-type.config.sbus.temperature-sensor.subnetId.label = SubnetId +thing-type.config.sbus.temperature-sensor.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast. +thing-type.config.sbus.temperature-sensor.subnetId.option.1 = 1 +thing-type.config.sbus.temperature-sensor.subnetId.option.255 = 255 thing-type.config.sbus.temperature.id.label = Device ID thing-type.config.sbus.temperature.id.description = The ID of the Sbus device thing-type.config.sbus.temperature.refresh.label = Refresh Interval @@ -69,6 +87,10 @@ channel-type.sbus.temperature-channel.label = Temperature 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-type.sbus.motion-channel.label = Motion Detection +channel-type.sbus.motion-channel.description = Motion detection state (ON=motion detected, OFF=no motion) +channel-type.sbus.lux-channel.label = Light Level +channel-type.sbus.lux-channel.description = Light level in LUX units # channel types config @@ -129,6 +151,7 @@ error.device.bridge-not-initialized = Bridge connection not initialized error.channel.invalid-number = Channel {0} has invalid channel number configuration error.rgbw.too-many-switches = Only one switch channel is allowed for RGBW thing {0} error.device.adapter-not-initialized = Sbus adapter not initialized +error.device.communication = Communication error with device error.device.read-state = Error reading device state error.device.send-command = Error sending command to device diff --git a/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/thing/thing-types.xml index 4e4080fc8618f..0febe48c06b23 100644 --- a/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.sbus/src/main/resources/OH-INF/thing/thing-types.xml @@ -36,13 +36,44 @@ - + + + + + + + Sbus temperature sensor + TemperatureSensor + + + + Slave subnet id. Can take any value between 1 and 255. 255 for broadcast. + 1 + + + + + + + + The ID of the Sbus device + + + + Refresh interval in seconds + 30 + s + + + + + - - Sbus temperature sensor device + + Sbus temperature sensor device (deprecated, use temperature-sensor instead) TemperatureSensor @@ -107,6 +138,15 @@ Sbus contact sensor device ContactSensor + + + Type of contact sensor device + + + + + 012c + Slave subnet id. Can take any value between 1 and 255. 255 for broadcast. @@ -135,7 +175,7 @@ - Sbus motion sensor device (9-in-1 devices) + Sbus motion sensor device MotionDetector @@ -166,8 +206,8 @@ - Sbus light level sensor device (9-in-1 devices) - IlluminanceSensor + Sbus light level sensor device + Sensor @@ -332,10 +372,17 @@ Motion + + + + The physical channel number on the Sbus device + 1 + + - Number + Number:Illuminance Light level in LUX units Sun @@ -343,7 +390,14 @@ Measurement Light - + + + + + The physical channel number on the Sbus device + 1 + +