]> git.basschouten.com Git - openhab-addons.git/commitdiff
[knx] postUpdate for contact-control sends to bus (#16263)
authorHolger Friedrich <mail@holger-friedrich.de>
Sun, 28 Jan 2024 20:44:09 +0000 (21:44 +0100)
committerGitHub <noreply@github.com>
Sun, 28 Jan 2024 20:44:09 +0000 (21:44 +0100)
contact-control items need to send to the bus like a switch item,
to trigger a state update in the external device.

* Add a new profile for contact-control items
* Add a profile factory and a profile advisor class
* Handle postUpdate like a command and send message on KNX bus

Fixes #16115.

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/KNXBindingConstants.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXContactControlProfile.java [new file with mode: 0644]
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXProfileAdvisor.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXProfileFactory.java [new file with mode: 0644]

index 38961a9a0efdfab11dee3830cb6405f2e53fa018..1c88448ad3765a724d211b200daa2cc8e07ca8d4 100644 (file)
@@ -21,6 +21,7 @@ import java.util.stream.Collectors;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.type.ChannelTypeUID;
 
 /**
  * The {@link KNXBindingConstants} class defines common constants, which are
@@ -89,6 +90,9 @@ public class KNXBindingConstants {
     public static final String CHANNEL_SWITCH = "switch";
     public static final String CHANNEL_SWITCH_CONTROL = "switch-control";
 
+    public static final ChannelTypeUID CHANNEL_CONTACT_CONTROL_UID = new ChannelTypeUID(BINDING_ID,
+            CHANNEL_CONTACT_CONTROL);
+
     public static final Set<String> CONTROL_CHANNEL_TYPES = Set.of( //
             CHANNEL_COLOR_CONTROL, //
             CHANNEL_CONTACT_CONTROL, //
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXContactControlProfile.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXContactControlProfile.java
new file mode 100644 (file)
index 0000000..7685d45
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2010-2024 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.knx.internal.profiles;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+import org.openhab.core.thing.profiles.StateProfile;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the implementation of a specialized profile for KNX contact-control-items.
+ *
+ * In contrast to the profile {@code FOLLOW} from {@link org.openhab.core.thing.profiles.SystemProfiles}
+ * used for other *-control items, it sends to the bus also for contact items.
+ *
+ * @author Holger Friedrich - Initial contribution
+ */
+@NonNullByDefault
+public class KNXContactControlProfile implements StateProfile {
+
+    private final Logger logger = LoggerFactory.getLogger(KNXContactControlProfile.class);
+    private final ProfileCallback callback;
+    private final ThingRegistry thingRegistry;
+
+    public KNXContactControlProfile(ProfileCallback callback, ThingRegistry thingRegistry) {
+        this.callback = callback;
+        this.thingRegistry = thingRegistry;
+    }
+
+    @Override
+    public ProfileTypeUID getProfileTypeUID() {
+        return KNXProfileFactory.UID_CONTACT_CONTROL;
+    }
+
+    @Override
+    public void onStateUpdateFromItem(State state) {
+        ChannelUID linkedChannelUID = callback.getItemChannelLink().getLinkedUID();
+        logger.trace("onStateUpdateFromItem({}) to {}", state.toString(), linkedChannelUID);
+
+        if (!(state instanceof Command)) {
+            logger.debug("The given state {} could not be transformed to a command", state);
+            return;
+        }
+        Command command = (Command) state;
+
+        // this does not have effect for contact items
+        // callback.handleCommand(command);
+        // workaround is to call handleCommand of the Thing directly
+        @Nullable
+        Thing linkedThing = thingRegistry.get(linkedChannelUID.getThingUID());
+        if (linkedThing != null) {
+            @Nullable
+            ThingHandler linkedThingHandler = linkedThing.getHandler();
+            if (linkedThingHandler != null) {
+                linkedThingHandler.handleCommand(linkedChannelUID, command);
+            } else {
+                logger.warn("Failed to send to {}, no ThingHandler", linkedChannelUID);
+            }
+        } else {
+            logger.warn("Failed to send to {}, no linked Thing", linkedChannelUID);
+        }
+    }
+
+    @Override
+    public void onCommandFromHandler(Command command) {
+        logger.trace("onCommandFromHandler {}", command.toString());
+        callback.sendCommand(command);
+    }
+
+    @Override
+    public void onCommandFromItem(Command command) {
+        logger.trace("onCommandFromItem {}", command.toString());
+        // no-op
+    }
+
+    @Override
+    public void onStateUpdateFromHandler(State state) {
+        logger.trace("onStateUpdateFromHandler {}", state.toString());
+        // no-op
+    }
+}
index fe23188d6b9b84094ab2a2572d063c95791b2850..73d382d1e2af4fa88907c0948744c81bf5d53618 100644 (file)
@@ -50,6 +50,11 @@ public class KNXProfileAdvisor implements ProfileAdvisor {
 
     private ProfileTypeUID getSuggestedProfileTypeUID(ChannelTypeUID channelTypeUID) {
         if (isControl(channelTypeUID)) {
+            if (CHANNEL_CONTACT_CONTROL.equals(channelTypeUID.getId())) {
+                // Special handling for contact-control, as contact items do not send to bus:
+                // contact-control need to send out on postUpdate, as contact-control switches external device
+                return KNXProfileFactory.UID_CONTACT_CONTROL;
+            }
             return SystemProfiles.FOLLOW;
         } else {
             return SystemProfiles.DEFAULT;
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXProfileFactory.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/profiles/KNXProfileFactory.java
new file mode 100644 (file)
index 0000000..42ff673
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2010-2024 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.knx.internal.profiles;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.knx.internal.KNXBindingConstants;
+import org.openhab.core.library.CoreItemFactory;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ThingRegistry;
+import org.openhab.core.thing.profiles.Profile;
+import org.openhab.core.thing.profiles.ProfileAdvisor;
+import org.openhab.core.thing.profiles.ProfileCallback;
+import org.openhab.core.thing.profiles.ProfileContext;
+import org.openhab.core.thing.profiles.ProfileFactory;
+import org.openhab.core.thing.profiles.ProfileType;
+import org.openhab.core.thing.profiles.ProfileTypeBuilder;
+import org.openhab.core.thing.profiles.ProfileTypeProvider;
+import org.openhab.core.thing.profiles.ProfileTypeUID;
+import org.openhab.core.thing.profiles.StateProfileType;
+import org.openhab.core.thing.type.ChannelType;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+/**
+ * This class defines and provides specialized KNX profiles.
+ *
+ * @author Holger Friedrich - Initial contribution
+ *
+ */
+@NonNullByDefault
+@Component
+public class KNXProfileFactory implements ProfileFactory, ProfileAdvisor, ProfileTypeProvider {
+
+    static final ProfileTypeUID UID_CONTACT_CONTROL = new ProfileTypeUID(KNXBindingConstants.BINDING_ID,
+            "contact-control");
+
+    private static final StateProfileType CONTACT_CONTROL_TYPE = ProfileTypeBuilder
+            .newState(UID_CONTACT_CONTROL, "contact-control").withSupportedItemTypes(CoreItemFactory.CONTACT)
+            .withSupportedChannelTypeUIDs(KNXBindingConstants.CHANNEL_CONTACT_CONTROL_UID).build();
+
+    private final ThingRegistry thingRegistry;
+
+    @Activate
+    public KNXProfileFactory(@Reference ThingRegistry thingRegistry) {
+        this.thingRegistry = thingRegistry;
+    }
+
+    @Override
+    public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
+        return Stream.of(UID_CONTACT_CONTROL).collect(Collectors.toSet());
+    }
+
+    @Override
+    public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
+        return Stream.of(CONTACT_CONTROL_TYPE).collect(Collectors.toSet());
+    }
+
+    @Override
+    public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(Channel channel, @Nullable String itemType) {
+        return getSuggestedProfileTypeUID(channel.getChannelTypeUID(), itemType);
+    }
+
+    @Override
+    public @Nullable ProfileTypeUID getSuggestedProfileTypeUID(ChannelType channelType, @Nullable String itemType) {
+        return getSuggestedProfileTypeUID(channelType.getUID(), itemType);
+    }
+
+    private @Nullable ProfileTypeUID getSuggestedProfileTypeUID(@Nullable ChannelTypeUID channelTypeUID,
+            @Nullable String itemType) {
+        if (KNXBindingConstants.CHANNEL_CONTACT_CONTROL_UID.equals(channelTypeUID) && itemType != null) {
+            switch (itemType) {
+                case CoreItemFactory.CONTACT:
+                    return UID_CONTACT_CONTROL;
+                default:
+                    return null;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
+            ProfileContext profileContext) {
+        if (UID_CONTACT_CONTROL.equals(profileTypeUID)) {
+            return new KNXContactControlProfile(callback, thingRegistry);
+        } else {
+            return null;
+        }
+    }
+}