From d0211a2786574408d48c07a3a8fe44121cdcc439 Mon Sep 17 00:00:00 2001 From: Timotheos Constambeys Date: Sat, 17 Aug 2024 12:59:36 +0300 Subject: [PATCH 1/5] [org.openhab.binding.http] autorefesh after sending command to update channel state Signed-off-by: Timotheos Constambeys --- .../http/internal/HttpThingHandler.java | 36 +++++++++++-------- .../internal/http/RefreshingUrlCache.java | 7 +++- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java index 19bfbb9bfbb87..346c243ede2ca 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java @@ -114,20 +114,19 @@ public void handleCommand(ChannelUID channelUID, Command command) { return; } + String key = channelUrls.get(channelUID); + RefreshingUrlCache refreshingUrlCache = (key != null) ? urlHandlers.get(key) : null; + if (command instanceof RefreshType) { - String key = channelUrls.get(channelUID); - if (key != null) { - RefreshingUrlCache refreshingUrlCache = urlHandlers.get(key); - if (refreshingUrlCache != null) { - try { - refreshingUrlCache.get().ifPresentOrElse(itemValueConverter::process, () -> { - if (config.strictErrorHandling) { - itemValueConverter.process(null); - } - }); - } catch (IllegalArgumentException | IllegalStateException e) { - logger.warn("Failed processing REFRESH command for channel {}: {}", channelUID, e.getMessage()); - } + if (refreshingUrlCache != null) { + try { + refreshingUrlCache.getCached().ifPresentOrElse(itemValueConverter::process, () -> { + if (config.strictErrorHandling) { + itemValueConverter.process(null); + } + }); + } catch (IllegalArgumentException | IllegalStateException e) { + logger.warn("Failed processing REFRESH command for channel {}: {}", channelUID, e.getMessage()); } } } else { @@ -138,6 +137,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { } catch (IllegalStateException e) { logger.debug("Writing to read-only channel {} not permitted", channelUID); } + + if (refreshingUrlCache != null) + refreshingUrlCache.run(scheduler); } } @@ -418,10 +420,14 @@ private String concatenateUrlParts(String baseUrl, @Nullable String extension) { private ChannelHandler createChannelHandler(AbstractTransformingChannelHandler.Factory factory, String commandUrl, ChannelUID channelUID, HttpChannelConfig channelConfig) { - return factory.create(state -> updateState(channelUID, state), command -> postCommand(channelUID, command), + return factory.create( + state -> updateState(channelUID, state), + command -> postCommand(channelUID, command), command -> sendHttpValue(commandUrl, command), new ChannelTransformation(channelConfig.stateTransformation), - new ChannelTransformation(channelConfig.commandTransformation), channelConfig); + new ChannelTransformation(channelConfig.commandTransformation), + channelConfig + ); } private ChannelHandler createGenericChannelHandler(String commandUrl, ChannelUID channelUID, diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java index da2fbc264e3c6..93cae9cba4a89 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java @@ -86,6 +86,11 @@ public void start(ScheduledExecutorService executor, int refreshTime) { logger.trace("Started refresh task for URL '{}' with interval {}s", url, refreshTime); } + public void run(ScheduledExecutorService executor) { + executor.schedule(() -> this.refresh(), 1, TimeUnit.SECONDS); + logger.trace("Started refresh task for URL '{}'", url); + } + public void stop() { // clearing all listeners to prevent further updates consumers.clear(); @@ -151,7 +156,7 @@ public void addConsumer(Consumer<@Nullable ChannelHandlerContent> consumer) { consumers.add(consumer); } - public Optional get() { + public Optional getCached() { return Optional.ofNullable(lastContent); } From 7d3e50e319cef4361b2ce0a30d76ef2455affa06 Mon Sep 17 00:00:00 2001 From: Timotheos Constambeys Date: Sat, 17 Aug 2024 13:17:01 +0300 Subject: [PATCH 2/5] undo change Signed-off-by: Timotheos Constambeys --- .../openhab/binding/http/internal/HttpThingHandler.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java index 346c243ede2ca..75462f50a959f 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java @@ -420,14 +420,10 @@ private String concatenateUrlParts(String baseUrl, @Nullable String extension) { private ChannelHandler createChannelHandler(AbstractTransformingChannelHandler.Factory factory, String commandUrl, ChannelUID channelUID, HttpChannelConfig channelConfig) { - return factory.create( - state -> updateState(channelUID, state), - command -> postCommand(channelUID, command), + return factory.create(state -> updateState(channelUID, state), command -> postCommand(channelUID, command), command -> sendHttpValue(commandUrl, command), new ChannelTransformation(channelConfig.stateTransformation), - new ChannelTransformation(channelConfig.commandTransformation), - channelConfig - ); + new ChannelTransformation(channelConfig.commandTransformation), channelConfig); } private ChannelHandler createGenericChannelHandler(String commandUrl, ChannelUID channelUID, From b43870474096790f406a1bfde1ae0b0cec34a7fa Mon Sep 17 00:00:00 2001 From: Timotheos Constambeys Date: Fri, 16 May 2025 21:20:42 +0300 Subject: [PATCH 3/5] Update bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java Co-authored-by: lsiepel Signed-off-by: Timotheos Constambeys --- .../org/openhab/binding/http/internal/HttpThingHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java index a746d6ffbeff6..5a4111fc2d5e9 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java @@ -138,8 +138,9 @@ public void handleCommand(ChannelUID channelUID, Command command) { logger.debug("Writing to read-only channel {} not permitted", channelUID); } - if (refreshingUrlCache != null) + if (refreshingUrlCache != null) { refreshingUrlCache.run(scheduler); + } } } From f141ff1aa0253713869a3a8157de99f54c8ec1df Mon Sep 17 00:00:00 2001 From: constambeys Date: Mon, 21 Jul 2025 10:39:34 +0300 Subject: [PATCH 4/5] add channel config --- .../http/internal/HttpThingHandler.java | 9 ++-- .../internal/config/HttpChannelConfig.java | 1 + .../internal/http/RefreshingUrlCache.java | 20 ++++++--- .../main/resources/OH-INF/config/config.xml | 45 +++++++++++++++++++ .../resources/OH-INF/i18n/http.properties | 18 ++++++++ 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java index 5a4111fc2d5e9..2c2e57e09e4c9 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/HttpThingHandler.java @@ -139,7 +139,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { } if (refreshingUrlCache != null) { - refreshingUrlCache.run(scheduler); + refreshingUrlCache.refreshAfterCommand(scheduler); } } } @@ -335,10 +335,9 @@ private void createChannel(Channel channel) { // we need a key consisting of stateContent and URL, only if both are equal, we can use the same cache String key = channelConfig.stateContent + "$" + stateUrl; channelUrls.put(channelUID, key); - Objects.requireNonNull( - urlHandlers.computeIfAbsent(key, - k -> new RefreshingUrlCache(rateLimitedHttpClient, stateUrl, config, - channelConfig.stateContent, config.contentType, this))) + Objects.requireNonNull(urlHandlers.computeIfAbsent(key, + k -> new RefreshingUrlCache(rateLimitedHttpClient, stateUrl, config, channelConfig.stateContent, + config.contentType, this, channelConfig.refreshAfterCommand))) .addConsumer(itemValueConverter::process); } diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpChannelConfig.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpChannelConfig.java index c8e1ee83f5704..8afa8bcbb42d4 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpChannelConfig.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/config/HttpChannelConfig.java @@ -31,4 +31,5 @@ public class HttpChannelConfig extends ChannelValueConverterConfig { public @Nullable List stateTransformation; public @Nullable List commandTransformation; public String stateContent = ""; + public Boolean refreshAfterCommand = false; } diff --git a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java index 139cf514cc2dc..8fe31e1a24598 100644 --- a/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java +++ b/bundles/org.openhab.binding.http/src/main/java/org/openhab/binding/http/internal/http/RefreshingUrlCache.java @@ -58,12 +58,19 @@ public class RefreshingUrlCache { private final String httpContent; private final @Nullable String httpContentType; private final HttpStatusListener httpStatusListener; + private final boolean refreshAfterCommand; private @Nullable ScheduledFuture future; private @Nullable ChannelHandlerContent lastContent; public RefreshingUrlCache(RateLimitedHttpClient httpClient, String url, HttpThingConfig thingConfig, String httpContent, @Nullable String httpContentType, HttpStatusListener httpStatusListener) { + this(httpClient, url, thingConfig, httpContent, httpContentType, httpStatusListener, false); + } + + public RefreshingUrlCache(RateLimitedHttpClient httpClient, String url, HttpThingConfig thingConfig, + String httpContent, @Nullable String httpContentType, HttpStatusListener httpStatusListener, + boolean refreshAfterCommand) { this.httpClient = httpClient; this.url = url; this.strictErrorHandling = thingConfig.strictErrorHandling; @@ -74,6 +81,7 @@ public RefreshingUrlCache(RateLimitedHttpClient httpClient, String url, HttpThin this.httpContent = httpContent; this.httpContentType = httpContentType; this.httpStatusListener = httpStatusListener; + this.refreshAfterCommand = refreshAfterCommand; fallbackEncoding = thingConfig.encoding; } @@ -86,11 +94,6 @@ public void start(ScheduledExecutorService executor, int refreshTime) { logger.trace("Started refresh task for URL '{}' with interval {}s", url, refreshTime); } - public void run(ScheduledExecutorService executor) { - executor.schedule(() -> this.refresh(), 1, TimeUnit.SECONDS); - logger.trace("Started refresh task for URL '{}'", url); - } - public void stop() { // clearing all listeners to prevent further updates consumers.clear(); @@ -105,6 +108,13 @@ private void refresh() { refresh(false); } + public void refreshAfterCommand(ScheduledExecutorService executor) { + if (refreshAfterCommand) { + executor.schedule(() -> this.refresh(), 1, TimeUnit.SECONDS); + logger.trace("Started refresh task for URL '{}'", url); + } + } + private void refresh(boolean isRetry) { if (consumers.isEmpty()) { // do not refresh if we don't have listeners diff --git a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml index 5a2388dca6fa5..55fc6cf19a935 100644 --- a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/config/config.xml @@ -30,6 +30,11 @@ Content for state request (only used if method is POST/PUT) true + + + Refresh channel state after command execution + true + @@ -69,6 +74,11 @@ Content for state request (only used if method is POST/PUT) true + + + Refresh channel state after command execution + true + The value that represents ON @@ -140,6 +150,11 @@ Content for state request (only used if method is POST/PUT) true + + + Refresh channel state after command execution + true + The value that represents OPEN @@ -177,6 +192,11 @@ This value is added to the base URL configured in the thing for retrieving values. true + + + Refresh channel state after command execution + true + This value is added to the base URL configured in the thing for sending values. @@ -233,6 +253,11 @@ Content for state request (only used if method is POST/PUT) true + + + Refresh channel state after command execution + true + @@ -251,6 +276,11 @@ This value is added to the base URL configured in the thing for retrieving values. true + + + Refresh channel state after command execution + true + This value is added to the base URL configured in the thing for sending values. @@ -295,6 +325,11 @@ This value is added to the base URL configured in the thing for retrieving values. true + + + Refresh channel state after command execution + true + This value is added to the base URL configured in the thing for sending values. @@ -358,6 +393,11 @@ This value is added to the base URL configured in the thing for retrieving values. true + + + Refresh channel state after command execution + true + This value is added to the base URL configured in the thing for sending values. @@ -423,6 +463,11 @@ Content for state request (only used if method is POST/PUT) true + + + Refresh channel state after command execution + true + The value that represents ON diff --git a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/i18n/http.properties b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/i18n/http.properties index 09f42d1a16f63..8d2aef9487f6b 100644 --- a/bundles/org.openhab.binding.http/src/main/resources/OH-INF/i18n/http.properties +++ b/bundles/org.openhab.binding.http/src/main/resources/OH-INF/i18n/http.properties @@ -97,6 +97,8 @@ channel-type.config.http.channel-config-color.onValue.label = On Value channel-type.config.http.channel-config-color.onValue.description = The value that represents ON channel-type.config.http.channel-config-color.stateContent.label = State Content channel-type.config.http.channel-config-color.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-color.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-color.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-color.stateExtension.label = State URL Extension channel-type.config.http.channel-config-color.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-color.stateTransformation.label = State Transformation @@ -117,6 +119,8 @@ channel-type.config.http.channel-config-contact.openValue.label = Open Value channel-type.config.http.channel-config-contact.openValue.description = The value that represents OPEN channel-type.config.http.channel-config-contact.stateContent.label = State Content channel-type.config.http.channel-config-contact.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-contact.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-contact.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-contact.stateExtension.label = State URL Extension channel-type.config.http.channel-config-contact.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-contact.stateTransformation.label = State Transformation @@ -139,6 +143,8 @@ channel-type.config.http.channel-config-dimmer.onValue.label = On Value channel-type.config.http.channel-config-dimmer.onValue.description = The value that represents ON channel-type.config.http.channel-config-dimmer.stateContent.label = State Content channel-type.config.http.channel-config-dimmer.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-dimmer.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-dimmer.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-dimmer.stateExtension.label = State URL Extension channel-type.config.http.channel-config-dimmer.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-dimmer.stateTransformation.label = State Transformation @@ -147,6 +153,8 @@ channel-type.config.http.channel-config-dimmer.step.label = Increase/Decrease St channel-type.config.http.channel-config-dimmer.step.description = The value by which the current brightness is increased/decreased if the corresponding command is received channel-type.config.http.channel-config-image.stateContent.label = State Content channel-type.config.http.channel-config-image.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-image.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-image.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-image.stateExtension.label = State URL Extension channel-type.config.http.channel-config-image.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-number.commandExtension.label = Command URL Extension @@ -159,6 +167,8 @@ channel-type.config.http.channel-config-number.mode.option.READONLY = Read Only channel-type.config.http.channel-config-number.mode.option.WRITEONLY = Write Only channel-type.config.http.channel-config-number.stateContent.label = State Content channel-type.config.http.channel-config-number.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-number.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-number.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-number.stateExtension.label = State URL Extension channel-type.config.http.channel-config-number.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-number.stateTransformation.label = State Transformation @@ -187,6 +197,8 @@ channel-type.config.http.channel-config-player.rewindValue.label = Rewind Value channel-type.config.http.channel-config-player.rewindValue.description = The value that represents REWIND channel-type.config.http.channel-config-player.stateContent.label = State Content channel-type.config.http.channel-config-player.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-player.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-player.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-player.stateExtension.label = State URL Extension channel-type.config.http.channel-config-player.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-player.stateTransformation.label = State Transformation @@ -205,6 +217,8 @@ channel-type.config.http.channel-config-rollershutter.moveValue.label = Move Val channel-type.config.http.channel-config-rollershutter.moveValue.description = The value that represents MOVE channel-type.config.http.channel-config-rollershutter.stateContent.label = State Content channel-type.config.http.channel-config-rollershutter.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-rollershutter.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-rollershutter.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-rollershutter.stateExtension.label = State URL Extension channel-type.config.http.channel-config-rollershutter.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-rollershutter.stateTransformation.label = State Transformation @@ -227,6 +241,8 @@ channel-type.config.http.channel-config-switch.onValue.label = On Value channel-type.config.http.channel-config-switch.onValue.description = The value that represents ON channel-type.config.http.channel-config-switch.stateContent.label = State Content channel-type.config.http.channel-config-switch.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config-switch.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config-switch.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config-switch.stateExtension.label = State URL Extension channel-type.config.http.channel-config-switch.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config-switch.stateTransformation.label = State Transformation @@ -241,6 +257,8 @@ channel-type.config.http.channel-config.mode.option.READONLY = Read Only channel-type.config.http.channel-config.mode.option.WRITEONLY = Write Only channel-type.config.http.channel-config.stateContent.label = State Content channel-type.config.http.channel-config.stateContent.description = Content for state request (only used if method is POST/PUT) +channel-type.config.http.channel-config.refreshAfterCommand.label = Refresh after Command +channel-type.config.http.channel-config.refreshAfterCommand.description = Refresh channel state after command execution channel-type.config.http.channel-config.stateExtension.label = State URL Extension channel-type.config.http.channel-config.stateExtension.description = This value is added to the base URL configured in the thing for retrieving values. channel-type.config.http.channel-config.stateTransformation.label = State Transformation From a31d46e1b8d2c9385568c347a087c327f4fccd5b Mon Sep 17 00:00:00 2001 From: constambeys Date: Sat, 1 Nov 2025 11:55:34 +0200 Subject: [PATCH 5/5] Update README.md --- bundles/org.openhab.binding.http/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/org.openhab.binding.http/README.md b/bundles/org.openhab.binding.http/README.md index 9eda9e021bd46..7993d22921b92 100644 --- a/bundles/org.openhab.binding.http/README.md +++ b/bundles/org.openhab.binding.http/README.md @@ -58,6 +58,7 @@ The `image` channel-type supports `stateExtension` only. | `stateTransformation` | yes | - | One or more transformation applied to received values before updating channel. | | `commandTransformation` | yes | - | One or more transformation applied to channel value before sending to a remote. | | `stateContent` | yes | - | Content for state requests (if method is `PUT` or `POST`) | +| `refreshAfterCommand` | yes | `FALSE` | Refresh channel state after command execution | | `mode` | no | `READWRITE` | Mode this channel is allowed to operate. `READONLY` means receive state, `WRITEONLY` means send commands. | Some channels have additional parameters.