[sbus] i18n and the remaining open issues

Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Ciprian Pascu 2025-01-24 11:02:17 +02:00
parent b215860af8
commit 5702626fd4
12 changed files with 253 additions and 97 deletions

View File

@ -72,11 +72,11 @@ The Sbus Bridge has the following configuration parameters:
### Switch Controller Channels
| Channel | Type | Read/Write | Description |
|---------|--------|------------|-----------------------------------------------------------|
| switch | Switch | RW | Basic ON/OFF state control |
| dimmer | Dimmer | RW | ON/OFF state with timer transition |
| paired | Paired | RW | ON/OFF state for two paired channels (e.g., curtains) |
| Channel | Type | Read/Write | Description |
|---------|---------|------------|-----------------------------------------------------------|
| switch | Switch | RW | Basic ON/OFF state control |
| dimmer | Dimmer | RW | ON/OFF state with timer transition |
| paired | Contact | RW | OPEN/CLOSED state for two paired channels (e.g., curtains)|
## Full Example
@ -113,6 +113,9 @@ Number:Temperature Temp_Sensor "Temperature [%.1f °C]" { channel="sbus:temperat
// Basic Switch
Switch Light_Switch "Switch" { channel="sbus:switch:mybridge:switch1:switch" }
// Paired Channel (e.g., for curtains)
Contact Curtain_Switch "Curtain [%s]" { channel="sbus:switch:mybridge:switch1:third_switch" }
// RGBW Controller with Power Control
Group gLight "RGBW Light" <light> ["Lighting"]
Color rgbwColor "Color" <colorwheel> (gLight) ["Control", "Light"] { channel="sbus:rgbw:mybridge:colorctrl:color" }
@ -128,5 +131,6 @@ sitemap sbus label="Sbus Demo"
Colorpicker item=Light_RGB
Text item=Temp_Sensor
Switch item=Light_Switch
Text item=Curtain_Switch
}
}

View File

@ -23,23 +23,4 @@
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Import-Package>
ro.ciprianpascu.sbus.facade,
*
</Import-Package>
<Embed-Dependency>j2sbus;scope=compile</Embed-Dependency>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -21,6 +21,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
@ -31,6 +33,8 @@ import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -46,9 +50,13 @@ public abstract class AbstractSbusHandler extends BaseThingHandler {
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected @Nullable SbusService sbusAdapter;
protected @Nullable ScheduledFuture<?> pollingJob;
protected final TranslationProvider translationProvider;
protected final LocaleProvider localeProvider;
public AbstractSbusHandler(Thing thing) {
public AbstractSbusHandler(Thing thing, TranslationProvider translationProvider, LocaleProvider localeProvider) {
super(thing);
this.translationProvider = translationProvider;
this.localeProvider = localeProvider;
}
@Override
@ -59,7 +67,9 @@ public abstract class AbstractSbusHandler extends BaseThingHandler {
Bridge bridge = getBridge();
if (bridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge configured");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
translationProvider.getText(bundle, "error.device.no-bridge", null, localeProvider.getLocale()));
return;
}
@ -71,8 +81,9 @@ public abstract class AbstractSbusHandler extends BaseThingHandler {
sbusAdapter = bridgeHandler.getSbusConnection();
if (sbusAdapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED,
"Bridge connection not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, translationProvider
.getText(bundle, "error.device.bridge-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -122,7 +133,9 @@ public abstract class AbstractSbusHandler extends BaseThingHandler {
try {
pollDevice();
} catch (Exception e) {
logger.warn("Error polling Sbus device", e);
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.device.polling", null,
localeProvider.getLocale()), e);
}
}, 0, config.refresh, TimeUnit.SECONDS);
}

View File

@ -15,12 +15,16 @@ package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.handler.config.SbusBridgeConfig;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,6 +38,8 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(SbusBridgeHandler.class);
private final @Nullable SbusService sbusService;
private final TranslationProvider translationProvider;
private final LocaleProvider localeProvider;
/**
* Constructs a new SbusBridgeHandler.
@ -41,9 +47,12 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
* @param bridge the bridge
* @param sbusService the Sbus service
*/
public SbusBridgeHandler(Bridge bridge, @Nullable SbusService sbusService) {
public SbusBridgeHandler(Bridge bridge, @Nullable SbusService sbusService, TranslationProvider translationProvider,
LocaleProvider localeProvider) {
super(bridge);
this.sbusService = sbusService;
this.translationProvider = translationProvider;
this.localeProvider = localeProvider;
}
/**
@ -56,15 +65,18 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
// Get configuration using the config class
SbusBridgeConfig config = getConfigAs(SbusBridgeConfig.class);
if (config.host.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Host address not configured");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.bridge.host-not-configured", null, localeProvider.getLocale()));
return;
}
try {
// Initialize Sbus service with the configuration parameters
final SbusService service = sbusService;
if (service == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
"Sbus service not available");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, translationProvider
.getText(bundle, "error.bridge.service-not-available", null, localeProvider.getLocale()));
return;
}
service.initialize(config.host, config.port);

View File

@ -18,6 +18,8 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
@ -40,12 +42,24 @@ public class SbusHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(SbusHandlerFactory.class);
private @Nullable SbusService sbusService;
private @Nullable TranslationProvider translationProvider;
private @Nullable LocaleProvider localeProvider;
@Reference
public void setSbusService(final SbusService service) {
this.sbusService = service;
}
@Reference
public void setTranslationProvider(TranslationProvider translationProvider) {
this.translationProvider = translationProvider;
}
@Reference
public void setLocaleProvider(LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_UDP_BRIDGE, THING_TYPE_SWITCH,
THING_TYPE_TEMPERATURE, THING_TYPE_RGBW);
@ -58,20 +72,27 @@ public class SbusHandlerFactory extends BaseThingHandlerFactory {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
final TranslationProvider tp = translationProvider;
final LocaleProvider lp = localeProvider;
if (tp == null || lp == null) {
logger.error("Required services not available");
return null;
}
if (thingTypeUID.equals(THING_TYPE_UDP_BRIDGE)) {
logger.debug("Creating Sbus UDP bridge handler for thing {}", thing.getUID());
return new SbusBridgeHandler((Bridge) thing, sbusService);
return new SbusBridgeHandler((Bridge) thing, sbusService, tp, lp);
}
if (thingTypeUID.equals(THING_TYPE_SWITCH)) {
logger.debug("Creating Sbus switch handler for thing {}", thing.getUID());
return new SbusSwitchHandler(thing);
return new SbusSwitchHandler(thing, tp, lp);
} else if (thingTypeUID.equals(THING_TYPE_TEMPERATURE)) {
logger.debug("Creating Sbus temperature handler for thing {}", thing.getUID());
return new SbusTemperatureHandler(thing);
return new SbusTemperatureHandler(thing, tp, lp);
} else if (thingTypeUID.equals(THING_TYPE_RGBW)) {
logger.debug("Creating Sbus RGBW handler for thing {}", thing.getUID());
return new SbusRgbwHandler(thing);
return new SbusRgbwHandler(thing, tp, lp);
}
logger.debug("Unknown thing type: {}", thingTypeUID);

View File

@ -16,6 +16,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.BindingConstants;
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.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
@ -25,6 +27,8 @@ import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.types.Command;
import org.openhab.core.util.ColorUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,8 +43,8 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
private final Logger logger = LoggerFactory.getLogger(SbusRgbwHandler.class);
public SbusRgbwHandler(Thing thing) {
super(thing);
public SbusRgbwHandler(Thing thing, TranslationProvider translationProvider, LocaleProvider localeProvider) {
super(thing, translationProvider, localeProvider);
}
/**
@ -70,13 +74,17 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
var channelTypeUID = channel.getChannelTypeUID();
if (channelTypeUID == null) {
logger.warn("Channel {} has no channel type", channel.getUID());
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.no-type", channel.getUID().toString(),
localeProvider.getLocale()));
continue;
}
String channelTypeId = channelTypeUID.getId();
if (BindingConstants.CHANNEL_TYPE_COLOR.equals(channelTypeId)) {
if (channelConfig.channelNumber <= 0) {
logger.warn("Channel {} has invalid channel number configuration", channel.getUID());
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.invalid-number",
channel.getUID().toString(), localeProvider.getLocale()));
}
}
if (BindingConstants.CHANNEL_TYPE_SWITCH.equals(channelTypeId)) {
@ -87,8 +95,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
}
}
if (switchChannelCount > 1) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Only one switch channel is allowed for RGBW thing " + getThing().getUID());
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.rgbw.too-many-switches", getThing().getUID().toString(), localeProvider.getLocale()));
return;
}
}
@ -97,7 +106,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
protected void pollDevice() {
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -137,7 +148,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
updateStatus(ThingStatus.ONLINE);
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error reading device state");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
translationProvider.getText(bundle, "error.device.read-state", null, localeProvider.getLocale()));
}
}
@ -145,7 +158,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
public void handleCommand(ChannelUID channelUID, Command command) {
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -154,7 +169,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
if (channel != null) {
var channelTypeUID = channel.getChannelTypeUID();
if (channelTypeUID == null) {
logger.warn("Channel {} has no channel type", channel.getUID());
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.no-type",
channel.getUID().toString(), localeProvider.getLocale()));
return;
}
String channelTypeId = channelTypeUID.getId();
@ -177,7 +194,9 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
}
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error sending command to device");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
translationProvider.getText(bundle, "error.device.send-command", null, localeProvider.getLocale()));
}
}
}

View File

@ -27,11 +27,11 @@ public interface SbusService {
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @param temperatureUnit The unit of measurement (e.g., 0 for Fahrenheit, 1 for Celsius)
* @param temperatureUnit The unit of measurement (FAHRENHEIT or CELSIUS)
* @return array of temperature values in Celsius
* @throws Exception if reading fails
*/
float[] readTemperatures(int subnetId, int id, int temperatureUnit) throws Exception;
float[] readTemperatures(int subnetId, int id, TemperatureUnit temperatureUnit) throws Exception;
/**
* Reads RGBW values from a device channel.

View File

@ -16,6 +16,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.BindingConstants;
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.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
@ -25,6 +27,8 @@ 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;
@ -39,8 +43,8 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
private final Logger logger = LoggerFactory.getLogger(SbusSwitchHandler.class);
public SbusSwitchHandler(Thing thing) {
super(thing);
public SbusSwitchHandler(Thing thing, TranslationProvider translationProvider, LocaleProvider localeProvider) {
super(thing, translationProvider, localeProvider);
}
@Override
@ -50,7 +54,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
// 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());
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.invalid-number",
channel.getUID().toString(), localeProvider.getLocale()));
}
}
}
@ -59,7 +65,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
protected void pollDevice() {
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -76,7 +84,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
if (channelConfig.channelNumber > 0 && channelConfig.channelNumber <= statuses.length) {
var channelTypeUID = channel.getChannelTypeUID();
if (channelTypeUID == null) {
logger.warn("Channel {} has no channel type", channel.getUID());
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.no-type",
channel.getUID().toString(), localeProvider.getLocale()));
continue;
}
String channelTypeId = channelTypeUID.getId();
@ -93,7 +103,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
}
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error reading device state");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
translationProvider.getText(bundle, "error.device.read-state", null, localeProvider.getLocale()));
}
}
@ -101,7 +113,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
public void handleCommand(ChannelUID channelUID, Command command) {
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -110,7 +124,9 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
if (channel != null) {
SbusChannelConfig channelConfig = channel.getConfiguration().as(SbusChannelConfig.class);
if (channelConfig.channelNumber <= 0) {
logger.warn("Invalid channel number for {}", channelUID);
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(translationProvider.getText(bundle, "error.channel.invalid-number",
channelUID.toString(), localeProvider.getLocale()));
return;
}
@ -125,7 +141,10 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
}
}
} catch (Exception e) {
logger.warn("Error handling command", e);
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.warn(
translationProvider.getText(bundle, "error.device.send-command", null, localeProvider.getLocale()),
e);
}
}

View File

@ -15,6 +15,8 @@ package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.binding.sbus.handler.config.TemperatureChannelConfig;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
@ -24,6 +26,8 @@ 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;
@ -38,8 +42,8 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
private final Logger logger = LoggerFactory.getLogger(SbusTemperatureHandler.class);
public SbusTemperatureHandler(Thing thing) {
super(thing);
public SbusTemperatureHandler(Thing thing, TranslationProvider translationProvider, LocaleProvider localeProvider) {
super(thing, translationProvider, localeProvider);
}
@Override
@ -49,8 +53,10 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
// Channels are already defined in thing-types.xml, just validate their configuration
TemperatureChannelConfig channelConfig = channel.getConfiguration().as(TemperatureChannelConfig.class);
if (!channelConfig.isValid()) {
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Invalid channel configuration: " + channel.getUID());
translationProvider.getText(bundle, "error.channel.invalid-number", channel.getUID().toString(),
localeProvider.getLocale()));
return;
}
}
@ -60,7 +66,9 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
protected void pollDevice() {
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
Bundle bundle = FrameworkUtil.getBundle(getClass());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, translationProvider.getText(bundle,
"error.device.adapter-not-initialized", null, localeProvider.getLocale()));
return;
}
@ -68,7 +76,7 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class);
// Read temperatures in Celsius from device
float[] temperatures = adapter.readTemperatures(config.subnetId, config.id, 1);
float[] temperatures = adapter.readTemperatures(config.subnetId, config.id, TemperatureUnit.CELSIUS);
// Iterate over all channels and update their states with corresponding temperatures
for (Channel channel : getThing().getChannels()) {
@ -91,13 +99,17 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
updateStatus(ThingStatus.ONLINE);
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Error reading device state");
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) {
// Temperature sensors are read-only
logger.debug("Temperature device is read-only, ignoring command");
Bundle bundle = FrameworkUtil.getBundle(getClass());
logger.debug(
translationProvider.getText(bundle, "info.temperature.readonly", null, localeProvider.getLocale()));
}
}

View File

@ -0,0 +1,45 @@
/*
* 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;
/**
* The {@link TemperatureUnit} defines the available temperature units.
*
* @author Ciprian Pascu - Initial contribution
*/
@NonNullByDefault
public enum TemperatureUnit {
FAHRENHEIT(0),
CELSIUS(1);
private final int value;
TemperatureUnit(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static TemperatureUnit fromValue(int value) {
for (TemperatureUnit unit : values()) {
if (unit.getValue() == value) {
return unit;
}
}
throw new IllegalArgumentException("Invalid temperature unit value: " + value);
}
}

View File

@ -15,6 +15,7 @@ package org.openhab.binding.sbus.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.handler.SbusService;
import org.openhab.binding.sbus.handler.TemperatureUnit;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@ -54,12 +55,12 @@ public class SbusServiceImpl implements SbusService {
}
@Override
public float[] readTemperatures(int subnetId, int id, int temperatureUnit) throws Exception {
public float[] readTemperatures(int subnetId, int id, TemperatureUnit temperatureUnit) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
return adapter.readTemperatures(subnetId, id, temperatureUnit);
return adapter.readTemperatures(subnetId, id, temperatureUnit.getValue());
}
@Override

View File

@ -11,7 +11,7 @@ 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.udp.label = Sbus UDP Slave
thing-type.sbus.udp.label = Sbus UDP Bridge
thing-type.sbus.udp.description = Endpoint for Sbus UDP slaves
# thing types config
@ -21,48 +21,29 @@ thing-type.config.sbus.rgbw.id.description = The ID of the Sbus device
thing-type.config.sbus.rgbw.refresh.label = Refresh Interval
thing-type.config.sbus.rgbw.refresh.description = Refresh interval in seconds
thing-type.config.sbus.rgbw.subnetId.label = SubnetId
thing-type.config.sbus.rgbw.subnetId.description = Slave subnet id. Can take any value between 0 and 255.
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.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
thing-type.config.sbus.switch.refresh.description = Refresh interval in seconds
thing-type.config.sbus.switch.subnetId.label = SubnetId
thing-type.config.sbus.switch.subnetId.description = Slave subnet id. Can take any value between 0 and 255.
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.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
thing-type.config.sbus.temperature.refresh.description = Refresh interval in seconds
thing-type.config.sbus.temperature.subnetId.label = SubnetId
thing-type.config.sbus.temperature.subnetId.description = Slave subnet id. Can take any value between 0 and 255.
thing-type.config.sbus.udp.afterConnectionDelayMillis.label = Connection warm-up time
thing-type.config.sbus.udp.afterConnectionDelayMillis.description = Connection warm-up time. Additional time which is spent on preparing connection which should be spent waiting while end device is getting ready to answer first sbus call. In milliseconds.
thing-type.config.sbus.udp.connectMaxTries.label = Maximum Connection Tries
thing-type.config.sbus.udp.connectMaxTries.description = How many times we try to establish the connection. Should be at least 1.
thing-type.config.sbus.udp.connectTimeoutMillis.label = Timeout for Establishing the Connection
thing-type.config.sbus.udp.connectTimeoutMillis.description = The maximum time that is waited when establishing the connection. Value of zero means that system/OS default is respected. In milliseconds.
thing-type.config.sbus.udp.enableDiscovery.label = Discovery Enabled
thing-type.config.sbus.udp.enableDiscovery.description = When enabled we try to find a device specific handler. Turn this on if you're using one of the supported devices.
thing-type.config.sbus.temperature.subnetId.description = Slave subnet id. Can take any value between 1 and 255. 255 for broadcast.
thing-type.config.sbus.temperature.subnetId.option.1 = 1
thing-type.config.sbus.temperature.subnetId.option.255 = 255
thing-type.config.sbus.udp.host.label = IP Address or Hostname
thing-type.config.sbus.udp.host.description = Network address of the device
thing-type.config.sbus.udp.port.label = Port
thing-type.config.sbus.udp.port.description = Port of the slave
thing-type.config.sbus.udp.reconnectAfterMillis.label = Reconnect Again After
thing-type.config.sbus.udp.reconnectAfterMillis.description = The connection is kept open at least the time specified here. Value of zero means that connection is disconnected after every Sbus transaction. In milliseconds.
thing-type.config.sbus.udp.rtuEncoded.label = RTU Encoding
thing-type.config.sbus.udp.rtuEncoded.description = Use RTU Encoding over IP
thing-type.config.sbus.udp.timeBetweenReconnectMillis.label = Time Between Reconnections
thing-type.config.sbus.udp.timeBetweenReconnectMillis.description = How long to wait to before trying to establish a new connection after the previous one has been disconnected. In milliseconds.
thing-type.config.sbus.udp.timeBetweenTransactionsMillis.label = Time Between Transactions
thing-type.config.sbus.udp.timeBetweenTransactionsMillis.description = How long to delay we must have at minimum between two consecutive Sbus transactions. In milliseconds.
# channel group types
channel-group-type.sbus.colors.label = Color Channels
channel-group-type.sbus.colors.description = Group of RGBW color channels
channel-group-type.sbus.sensors.label = Temperature Sensors
channel-group-type.sbus.sensors.description = Group of temperature sensors
channel-group-type.sbus.switches.label = Switch Channels
channel-group-type.sbus.switches.description = Group of switch channels
# channel types
@ -93,3 +74,51 @@ channel-type.config.sbus.switch-channel.channelNumber.label = Channel Number
channel-type.config.sbus.switch-channel.channelNumber.description = The physical channel number on the Sbus device
channel-type.config.sbus.temperature-channel.channelNumber.label = Channel Number
channel-type.config.sbus.temperature-channel.channelNumber.description = The physical channel number on the Sbus device
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.option.CELSIUS = Celsius
channel-type.config.sbus.temperature-channel.unit.option.FAHRENHEIT = Fahrenheit
# thing types config
thing-type.config.sbus.udp.afterConnectionDelayMillis.label = Connection warm-up time
thing-type.config.sbus.udp.afterConnectionDelayMillis.description = Connection warm-up time. Additional time which is spent on preparing connection which should be spent waiting while end device is getting ready to answer first sbus call. In milliseconds.
thing-type.config.sbus.udp.connectMaxTries.label = Maximum Connection Tries
thing-type.config.sbus.udp.connectMaxTries.description = How many times we try to establish the connection. Should be at least 1.
thing-type.config.sbus.udp.connectTimeoutMillis.label = Timeout for Establishing the Connection
thing-type.config.sbus.udp.connectTimeoutMillis.description = The maximum time that is waited when establishing the connection. Value of zero means that system/OS default is respected. In milliseconds.
thing-type.config.sbus.udp.enableDiscovery.label = Discovery Enabled
thing-type.config.sbus.udp.enableDiscovery.description = When enabled we try to find a device specific handler. Turn this on if you're using one of the supported devices.
thing-type.config.sbus.udp.reconnectAfterMillis.label = Reconnect Again After
thing-type.config.sbus.udp.reconnectAfterMillis.description = The connection is kept open at least the time specified here. Value of zero means that connection is disconnected after every Sbus transaction. In milliseconds.
thing-type.config.sbus.udp.rtuEncoded.label = RTU Encoding
thing-type.config.sbus.udp.rtuEncoded.description = Use RTU Encoding over IP
thing-type.config.sbus.udp.timeBetweenReconnectMillis.label = Time Between Reconnections
thing-type.config.sbus.udp.timeBetweenReconnectMillis.description = How long to wait to before trying to establish a new connection after the previous one has been disconnected. In milliseconds.
thing-type.config.sbus.udp.timeBetweenTransactionsMillis.label = Time Between Transactions
thing-type.config.sbus.udp.timeBetweenTransactionsMillis.description = How long to delay we must have at minimum between two consecutive Sbus transactions. In milliseconds.
# channel group types
channel-group-type.sbus.colors.label = Color Channels
channel-group-type.sbus.colors.description = Group of RGBW color channels
channel-group-type.sbus.sensors.label = Temperature Sensors
channel-group-type.sbus.sensors.description = Group of temperature sensors
channel-group-type.sbus.switches.label = Switch Channels
channel-group-type.sbus.switches.description = Group of switch channels
# error messages
error.bridge.host-not-configured = Host address not configured
error.bridge.service-not-available = Sbus service not available
error.device.no-bridge = No bridge configured
error.device.bridge-not-initialized = Bridge connection not initialized
error.device.polling = Error polling Sbus device
error.channel.no-type = Channel {0} has no channel type
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.read-state = Error reading device state
error.device.send-command = Error sending command to device
# info messages
info.temperature.readonly = Temperature device is read-only, ignoring command