Skip to content

Commit

Permalink
[tcpudp] Use core ChannelHandler (#550)
Browse files Browse the repository at this point in the history
Signed-off-by: Jan N. Klug <github@klug.nrw>
(cherry picked from commit d22cee8)
  • Loading branch information
J-N-K committed Dec 28, 2023
1 parent 6f78af1 commit 9c168b3
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Copyright (c) 2021-2023 Contributors to the SmartHome/J 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.smarthomej.binding.tcpudp.internal;

import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.PointType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.binding.generic.ChannelHandler;
import org.openhab.core.thing.binding.generic.ChannelTransformation;
import org.openhab.core.thing.binding.generic.converter.ColorChannelHandler;
import org.openhab.core.thing.binding.generic.converter.DimmerChannelHandler;
import org.openhab.core.thing.binding.generic.converter.FixedValueMappingChannelHandler;
import org.openhab.core.thing.binding.generic.converter.GenericChannelHandler;
import org.openhab.core.thing.binding.generic.converter.ImageChannelHandler;
import org.openhab.core.thing.binding.generic.converter.NumberChannelHandler;
import org.openhab.core.thing.binding.generic.converter.PlayerChannelHandler;
import org.openhab.core.thing.binding.generic.converter.RollershutterChannelHandler;
import org.openhab.core.thing.internal.binding.generic.converter.AbstractTransformingChannelHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smarthomej.binding.tcpudp.internal.config.TcpUdpChannelConfig;

/**
* The {@link ChannelHandlerFactory} is a helper class for creating {@link ChannelHandler}s
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class ChannelHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(ChannelHandlerFactory.class);

private @Nullable Consumer<String> sendValue;
private BiConsumer<ChannelUID, State> updateState;
private BiConsumer<ChannelUID, Command> postCommand;

public ChannelHandlerFactory(BiConsumer<ChannelUID, State> updateState, BiConsumer<ChannelUID, Command> postCommand,
@Nullable Consumer<String> sendValue) {
this.updateState = updateState;
this.postCommand = postCommand;
this.sendValue = sendValue;
}

public void setSendValue(Consumer<String> sendValue) {
this.sendValue = sendValue;
}

public void setUpdateState(BiConsumer<ChannelUID, State> updateState) {
this.updateState = updateState;
}

public void setPostCommand(BiConsumer<ChannelUID, Command> postCommand) {
this.postCommand = postCommand;
}

public Optional<ChannelHandler> create(ChannelUID channelUID, @Nullable String acceptedItemType,
TcpUdpChannelConfig channelConfig) {
if (acceptedItemType == null) {
logger.warn("Cannot determine item-type for channel '{}'", channelUID);
return Optional.empty();
}

ChannelHandler channelHandler = null;
switch (acceptedItemType) {
case "Color":
channelHandler = createChannelHandler(ColorChannelHandler::new, channelUID, channelConfig);
break;
case "DateTime":
channelHandler = createGenericChannelHandler(channelUID, channelConfig, DateTimeType::new);
break;
case "Dimmer":
channelHandler = createChannelHandler(DimmerChannelHandler::new, channelUID, channelConfig);
break;
case "Contact":
case "Switch":
channelHandler = createChannelHandler(FixedValueMappingChannelHandler::new, channelUID, channelConfig);
break;
case "Image":
channelHandler = new ImageChannelHandler(state -> updateState.accept(channelUID, state));
break;
case "Location":
channelHandler = createGenericChannelHandler(channelUID, channelConfig, PointType::new);
break;
case "Number":
channelHandler = createChannelHandler(NumberChannelHandler::new, channelUID, channelConfig);
break;
case "Player":
channelHandler = createChannelHandler(PlayerChannelHandler::new, channelUID, channelConfig);
break;
case "Rollershutter":
channelHandler = createChannelHandler(RollershutterChannelHandler::new, channelUID, channelConfig);
break;
case "String":
channelHandler = createGenericChannelHandler(channelUID, channelConfig, StringType::new);
break;
default:
logger.warn("Unsupported item-type '{}'", acceptedItemType);
}

return Optional.ofNullable(channelHandler);
}

private ChannelHandler createChannelHandler(AbstractTransformingChannelHandler.Factory factory,
ChannelUID channelUID, TcpUdpChannelConfig channelConfig) {
return factory.create(state -> updateState.accept(channelUID, state),
command -> postCommand.accept(channelUID, command), sendValue,
new ChannelTransformation(channelConfig.stateTransformation),
new ChannelTransformation(channelConfig.commandTransformation), channelConfig);
}

private ChannelHandler createGenericChannelHandler(ChannelUID channelUID, TcpUdpChannelConfig channelConfig,
Function<String, State> toState) {
AbstractTransformingChannelHandler.Factory factory = (state, command, value, stateTrans, commandTrans,
config) -> new GenericChannelHandler(toState, state, command, value, stateTrans, commandTrans, config);
return createChannelHandler(factory, channelUID, channelConfig);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.generic.ChannelHandler;
import org.openhab.core.thing.binding.generic.ChannelHandlerContent;
import org.openhab.core.thing.binding.generic.ChannelMode;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.StateDescription;
Expand All @@ -49,10 +52,6 @@
import org.smarthomej.binding.tcpudp.internal.config.ClientConfiguration;
import org.smarthomej.binding.tcpudp.internal.config.TcpUdpChannelConfig;
import org.smarthomej.commons.SimpleDynamicStateDescriptionProvider;
import org.smarthomej.commons.itemvalueconverter.ChannelMode;
import org.smarthomej.commons.itemvalueconverter.ContentWrapper;
import org.smarthomej.commons.itemvalueconverter.ItemValueConverter;
import org.smarthomej.commons.transform.ValueTransformationProvider;

/**
* The {@link ClientThingHandler} is the thing handler for client type things
Expand All @@ -65,27 +64,25 @@ public class ClientThingHandler extends BaseThingHandler {
private final ScheduledExecutorService scheduler = ThreadPoolManager.getScheduledPool("SHJ-tcpudp");

private final SimpleDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
private final Map<ChannelUID, ItemValueConverter> channels = new HashMap<>();
private final Map<ChannelUID, ChannelHandler> channels = new HashMap<>();
private final Map<ChannelUID, String> readCommands = new HashMap<>();

private Function<String, Optional<ContentWrapper>> doSyncRequest = this::doTcpSyncRequest;
private ItemValueConverterFactory itemValueConverterFactory;
private Function<String, Optional<ChannelHandlerContent>> doSyncRequest = this::doTcpSyncRequest;
private final ChannelHandlerFactory channelHandlerFactory;
private @Nullable ScheduledFuture<?> refreshJob = null;

protected ClientConfiguration config = new ClientConfiguration();

public ClientThingHandler(Thing thing, ValueTransformationProvider valueTransformationProvider,
SimpleDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
public ClientThingHandler(Thing thing, SimpleDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing);
this.dynamicStateDescriptionProvider = dynamicStateDescriptionProvider;

itemValueConverterFactory = new ItemValueConverterFactory(valueTransformationProvider, this::updateState,
this::postCommand, null);
channelHandlerFactory = new ChannelHandlerFactory(this::updateState, this::postCommand, null);
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
ItemValueConverter itemValueConverter = channels.get(channelUID);
ChannelHandler itemValueConverter = channels.get(channelUID);
if (itemValueConverter == null) {
logger.warn("Cannot find channel implementation for channel {}.", channelUID);
return;
Expand Down Expand Up @@ -123,11 +120,11 @@ public void initialize() {
// set methods depending on thing-type
if (config.protocol == ClientConfiguration.Protocol.UDP) {
doSyncRequest = this::doUdpSyncRequest;
itemValueConverterFactory.setSendValue(this::doUdpAsyncSend);
channelHandlerFactory.setSendValue(this::doUdpAsyncSend);
logger.debug("Configured '{}' for UDP connections.", thing.getUID());
} else if (config.protocol == ClientConfiguration.Protocol.TCP) {
doSyncRequest = this::doTcpSyncRequest;
itemValueConverterFactory.setSendValue(this::doTcpAsyncSend);
channelHandlerFactory.setSendValue(this::doTcpAsyncSend);
logger.debug("Configured '{}' for TCP connections.", thing.getUID());
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
Expand Down Expand Up @@ -171,22 +168,22 @@ public void dispose() {

private void refreshChannel(ChannelUID channelUID, String stateContent) {
logger.trace("Refreshing '{}' with command '{}'", channelUID, stateContent);
ItemValueConverter itemValueConverter = channels.get(channelUID);
ChannelHandler channelHandler = channels.get(channelUID);

if (itemValueConverter == null) {
if (channelHandler == null) {
logger.warn("Failed to refresh '{}': itemValueConverter not found.", channelUID);
return;
}

doSyncRequest.apply(stateContent).ifPresent(itemValueConverter::process);
doSyncRequest.apply(stateContent).ifPresent(channelHandler::process);
}

private void createChannel(Channel channel) {
ChannelUID channelUID = channel.getUID();
TcpUdpChannelConfig channelConfig = channel.getConfiguration().as(TcpUdpChannelConfig.class);
String acceptedItemType = channel.getAcceptedItemType();

itemValueConverterFactory.create(channelUID, acceptedItemType, channelConfig).ifPresent(itemValueConverter -> {
channelHandlerFactory.create(channelUID, acceptedItemType, channelConfig).ifPresent(itemValueConverter -> {
if (channelConfig.mode == ChannelMode.READONLY || channelConfig.mode == ChannelMode.READWRITE) {
if (channelConfig.stateContent.isEmpty()) {
logger.warn(
Expand Down Expand Up @@ -234,7 +231,7 @@ protected void doTcpAsyncSend(String command) {
});
}

protected Optional<ContentWrapper> doTcpSyncRequest(String request) {
protected Optional<ChannelHandlerContent> doTcpSyncRequest(String request) {
try (Socket socket = new Socket(config.host, config.port);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
Expand All @@ -257,7 +254,7 @@ protected Optional<ContentWrapper> doTcpSyncRequest(String request) {

outputByteArrayStream.flush();

ContentWrapper contentWrapper = new ContentWrapper(outputByteArrayStream.toByteArray(),
ChannelHandlerContent contentWrapper = new ChannelHandlerContent(outputByteArrayStream.toByteArray(),
Objects.requireNonNullElse(config.encoding, StandardCharsets.UTF_8.name()), null);

updateStatus(ThingStatus.ONLINE);
Expand Down Expand Up @@ -288,7 +285,7 @@ protected void doUdpAsyncSend(String command) {
});
}

protected Optional<ContentWrapper> doUdpSyncRequest(String request) {
protected Optional<ChannelHandlerContent> doUdpSyncRequest(String request) {
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(config.timeout);
InetAddress inetAddress = InetAddress.getByName(config.host);
Expand All @@ -301,8 +298,8 @@ protected Optional<ContentWrapper> doUdpSyncRequest(String request) {
packet = new DatagramPacket(receiveBuffer, receiveBuffer.length);
socket.receive(packet);

ContentWrapper contentWrapper = new ContentWrapper(Arrays.copyOf(packet.getData(), packet.getLength()),
getEncoding(), null);
ChannelHandlerContent contentWrapper = new ChannelHandlerContent(
Arrays.copyOf(packet.getData(), packet.getLength()), getEncoding(), null);

updateStatus(ThingStatus.ONLINE);
return Optional.of(contentWrapper);
Expand Down
Loading

0 comments on commit 9c168b3

Please sign in to comment.