[knx] postUpdate for contact-control sends to bus (#16263)

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>
Signed-off-by: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Holger Friedrich 2024-01-28 21:44:09 +01:00 committed by Ciprian Pascu
parent 23ac34fd80
commit fbb3d099fd
4 changed files with 217 additions and 0 deletions

View 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, //

View File

@ -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
}
}

View 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;

View File

@ -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;
}
}
}