Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tcpudp] Use core ChannelHandler #550

Merged
merged 1 commit into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading