[sbus] keep the j2sbus package internal by decoupling all calls with an osgi service

Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Ciprian Pascu 2025-01-07 16:38:01 +02:00
parent 04f0cea871
commit 95781b189f
12 changed files with 243 additions and 59 deletions

View File

@ -19,8 +19,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.internal.SbusBridgeHandler;
import org.openhab.binding.sbus.internal.config.SbusDeviceConfig;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
@ -35,8 +34,6 @@ import org.openhab.core.thing.type.ChannelTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link AbstractSbusHandler} is the base class for all Sbus device handlers.
* It provides common functionality for device initialization, channel management, and polling.
@ -47,7 +44,7 @@ import ro.ciprianpascu.sbus.facade.SbusAdapter;
public abstract class AbstractSbusHandler extends BaseThingHandler {
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected @Nullable SbusAdapter sbusAdapter;
protected @Nullable SbusService sbusAdapter;
protected @Nullable ScheduledFuture<?> pollingJob;
public AbstractSbusHandler(Thing thing) {
@ -143,7 +140,7 @@ public abstract class AbstractSbusHandler extends BaseThingHandler {
if (job != null) {
job.cancel(true);
}
final SbusAdapter adapter = sbusAdapter;
final SbusService adapter = sbusAdapter;
if (adapter != null) {
adapter.close();
}

View File

@ -10,22 +10,22 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal;
package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.internal.config.SbusBridgeConfig;
import org.openhab.binding.sbus.handler.config.SbusBridgeConfig;
import org.openhab.binding.sbus.internal.SbusServiceImpl;
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.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link SbusBridgeHandler} is responsible for handling communication with the Sbus bridge.
*
@ -36,7 +36,8 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(SbusBridgeHandler.class);
private @Nullable SbusAdapter sbusConnection;
@Reference
private @Nullable SbusService sbusService;
/**
* Constructs a new SbusBridgeHandler.
@ -61,10 +62,20 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
return;
}
try {
// Initialize Sbus connection with the configuration parameters
sbusConnection = new SbusAdapter(config.host, config.port);
// 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");
return;
}
updateStatus(ThingStatus.ONLINE);
try {
((SbusServiceImpl) service).initialize(config.host, config.port);
updateStatus(ThingStatus.ONLINE);
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} catch (Exception e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
@ -72,12 +83,12 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
}
/**
* Gets the Sbus adapter connection.
* Gets the Sbus service.
*
* @return the Sbus adapter
* @return the Sbus service
*/
public @Nullable SbusAdapter getSbusConnection() {
return sbusConnection;
public @Nullable SbusService getSbusConnection() {
return sbusService;
}
/**
@ -86,11 +97,10 @@ public class SbusBridgeHandler extends BaseBridgeHandler {
@Override
public void dispose() {
logger.debug("Disposing Sbus bridge handler");
final SbusAdapter connection = sbusConnection;
if (connection != null) {
connection.close();
final SbusService service = sbusService;
if (service != null) {
service.close();
}
sbusConnection = null;
super.dispose();
}

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal;
package org.openhab.binding.sbus.handler;
import static org.openhab.binding.sbus.BindingConstants.*;
@ -18,9 +18,6 @@ import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.handler.SbusRgbwHandler;
import org.openhab.binding.sbus.handler.SbusSwitchHandler;
import org.openhab.binding.sbus.handler.SbusTemperatureHandler;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;

View File

@ -13,8 +13,8 @@
package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.internal.config.SbusChannelConfig;
import org.openhab.binding.sbus.internal.config.SbusDeviceConfig;
import org.openhab.binding.sbus.handler.config.SbusChannelConfig;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
@ -28,8 +28,6 @@ import org.openhab.core.util.ColorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link SbusRgbwHandler} is responsible for handling commands for Sbus RGBW devices.
* It supports reading and controlling red, green, blue, and white color channels.
@ -162,7 +160,7 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
@Override
protected void pollDevice() {
final SbusAdapter adapter = super.sbusAdapter;
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
return;
@ -184,7 +182,7 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
if ("color-channel".equals(channelTypeId)) {
// Read RGBW values for this channel
int[] rgbwValues = adapter.readRgbw(config.subnetId, config.id, channelConfig.channelNumber);
if (rgbwValues != null && rgbwValues.length >= 4) {
if (rgbwValues.length >= 4) {
// Convert RGBW to HSB using our custom conversion
HSBType hsbType = rgbwToHsb(rgbwValues);
updateState(channel.getUID(), hsbType);
@ -207,7 +205,7 @@ public class SbusRgbwHandler extends AbstractSbusHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
final SbusAdapter adapter = super.sbusAdapter;
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
logger.warn("Sbus adapter not initialized");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");

View File

@ -0,0 +1,86 @@
/**
* 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 SbusService} defines the interface for handling Sbus communication.
*
* @author Ciprian Pascu - Initial contribution
*/
@NonNullByDefault
public interface SbusService {
/**
* Reads temperature values from a device.
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @return array of temperature values in Celsius
* @throws Exception if reading fails
*/
float[] readTemperatures(int subnetId, int id) throws Exception;
/**
* Reads RGBW values from a device channel.
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @param channelNumber the channel number to read
* @return array of RGBW values [R, G, B, W]
* @throws Exception if reading fails
*/
int[] readRgbw(int subnetId, int id, int channelNumber) throws Exception;
/**
* Reads status values from device channels.
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @return array of channel status values
* @throws Exception if reading fails
*/
int[] readStatusChannels(int subnetId, int id) throws Exception;
/**
* Writes RGBW values to a device channel.
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @param channelNumber the channel number to write to
* @param r red value (0-255)
* @param g green value (0-255)
* @param b blue value (0-255)
* @param w white value (0-255)
* @throws Exception if writing fails
*/
void writeRgbw(int subnetId, int id, int channelNumber, int r, int g, int b, int w) throws Exception;
/**
* Writes a value to a single channel.
*
* @param subnetId the subnet ID of the device
* @param id the device ID
* @param channelNumber the channel number to write to
* @param value the value to write
* @param timer timer value (-1 for no timer)
* @throws Exception if writing fails
*/
void writeSingleChannel(int subnetId, int id, int channelNumber, int value, int timer) throws Exception;
/**
* Closes the service and releases resources.
*/
void close();
}

View File

@ -13,8 +13,8 @@
package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.internal.config.SbusChannelConfig;
import org.openhab.binding.sbus.internal.config.SbusDeviceConfig;
import org.openhab.binding.sbus.handler.config.SbusChannelConfig;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
@ -27,8 +27,6 @@ import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link SbusSwitchHandler} is responsible for handling commands for Sbus switch devices.
* It supports reading the current state and switching the device on/off.
@ -58,7 +56,7 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
@Override
protected void pollDevice() {
final SbusAdapter adapter = super.sbusAdapter;
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
return;
@ -67,10 +65,6 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
try {
SbusDeviceConfig config = getConfigAs(SbusDeviceConfig.class);
int[] statuses = adapter.readStatusChannels(config.subnetId, config.id);
if (statuses == null) {
logger.warn("Received null status channels from Sbus device");
return;
}
// Iterate over all channels and update their states
for (Channel channel : getThing().getChannels()) {
@ -101,7 +95,7 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
final SbusAdapter adapter = super.sbusAdapter;
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
logger.warn("Sbus adapter not initialized");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
@ -133,7 +127,7 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
}
private void handleOnOffCommand(OnOffType command, SbusDeviceConfig config, SbusChannelConfig channelConfig,
ChannelUID channelUID, SbusAdapter adapter) throws Exception {
ChannelUID channelUID, SbusService adapter) throws Exception {
boolean isOn = command == OnOffType.ON;
adapter.writeSingleChannel(config.subnetId, config.id, channelConfig.channelNumber, isOn ? 100 : 0,
channelConfig.timer);
@ -141,14 +135,14 @@ public class SbusSwitchHandler extends AbstractSbusHandler {
}
private void handlePercentCommand(PercentType command, SbusDeviceConfig config, SbusChannelConfig channelConfig,
ChannelUID channelUID, SbusAdapter adapter) throws Exception {
ChannelUID channelUID, SbusService adapter) throws Exception {
adapter.writeSingleChannel(config.subnetId, config.id, channelConfig.channelNumber, command.intValue(),
channelConfig.timer);
updateState(channelUID, command);
}
private void handleOpenClosedCommand(OpenClosedType command, SbusDeviceConfig config,
SbusChannelConfig channelConfig, ChannelUID channelUID, SbusAdapter adapter) throws Exception {
SbusChannelConfig channelConfig, ChannelUID channelUID, SbusService adapter) throws Exception {
boolean isOpen = command == OpenClosedType.OPEN;
// Set main channel
if (getChannelToClose(channelConfig, isOpen) > 0) {

View File

@ -13,8 +13,8 @@
package org.openhab.binding.sbus.handler;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.sbus.internal.config.SbusDeviceConfig;
import org.openhab.binding.sbus.internal.config.TemperatureChannelConfig;
import org.openhab.binding.sbus.handler.config.SbusDeviceConfig;
import org.openhab.binding.sbus.handler.config.TemperatureChannelConfig;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits;
@ -27,8 +27,6 @@ import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link SbusTemperatureHandler} is responsible for handling commands for Sbus temperature sensors.
* It supports reading temperature values in Celsius.
@ -60,7 +58,7 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
@Override
protected void pollDevice() {
final SbusAdapter adapter = super.sbusAdapter;
final SbusService adapter = super.sbusAdapter;
if (adapter == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Sbus adapter not initialized");
return;
@ -71,10 +69,6 @@ public class SbusTemperatureHandler extends AbstractSbusHandler {
// Read temperatures in Celsius from device
float[] temperatures = adapter.readTemperatures(config.subnetId, config.id);
if (temperatures == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Received null temperatures");
return;
}
// Iterate over all channels and update their states with corresponding temperatures
for (Channel channel : getThing().getChannels()) {

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal.config;
package org.openhab.binding.sbus.handler.config;
import org.eclipse.jdt.annotation.NonNullByDefault;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal.config;
package org.openhab.binding.sbus.handler.config;
import org.eclipse.jdt.annotation.NonNullByDefault;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal.config;
package org.openhab.binding.sbus.handler.config;
import org.eclipse.jdt.annotation.NonNullByDefault;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.sbus.internal.config;
package org.openhab.binding.sbus.handler.config;
import org.eclipse.jdt.annotation.NonNullByDefault;

View File

@ -0,0 +1,108 @@
/**
* 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;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.sbus.handler.SbusService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import ro.ciprianpascu.sbus.facade.SbusAdapter;
/**
* The {@link SbusServiceImpl} implements the SbusService interface by delegating to SbusAdapter.
*
* @author Ciprian Pascu - Initial contribution
*/
@Component(service = SbusService.class)
@NonNullByDefault
public class SbusServiceImpl implements SbusService {
private @Nullable SbusAdapter adapter;
@Activate
public SbusServiceImpl() {
// Service is activated but adapter is initialized later with connection parameters
}
/**
* Initializes the underlying SbusAdapter with connection parameters.
*
* @param host the host address of the Sbus device
* @param port the port number to use
* @throws Exception if initialization fails
*/
public void initialize(String host, int port) throws Exception {
this.adapter = new SbusAdapter(host, port);
}
@Deactivate
public void deactivate() {
close();
}
@Override
public float[] readTemperatures(int subnetId, int id) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
return adapter.readTemperatures(subnetId, id);
}
@Override
public int[] readRgbw(int subnetId, int id, int channelNumber) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
return adapter.readRgbw(subnetId, id, channelNumber);
}
@Override
public int[] readStatusChannels(int subnetId, int id) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
return adapter.readStatusChannels(subnetId, id);
}
@Override
public void writeRgbw(int subnetId, int id, int channelNumber, int r, int g, int b, int w) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
adapter.writeRgbw(subnetId, id, channelNumber, r, g, b, w);
}
@Override
public void writeSingleChannel(int subnetId, int id, int channelNumber, int value, int timer) throws Exception {
final SbusAdapter adapter = this.adapter;
if (adapter == null) {
throw new IllegalStateException("SbusAdapter not initialized");
}
adapter.writeSingleChannel(subnetId, id, channelNumber, value, timer);
}
@Override
public void close() {
final SbusAdapter adapter = this.adapter;
if (adapter != null) {
adapter.close();
this.adapter = null;
}
}
}