mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Apply default semantic tags from linked channels to items (#4913)
Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
+17
@@ -31,6 +31,9 @@ public class ItemChannelLink extends AbstractLink {
|
||||
private final @NonNullByDefault({}) ChannelUID channelUID;
|
||||
private final Configuration configuration;
|
||||
|
||||
// this field tracks whether the item's current tags were applied from the linked channel's default tags
|
||||
private transient boolean tagsLinked = false;
|
||||
|
||||
/**
|
||||
* Default constructor in package scope only. Will allow to instantiate this
|
||||
* class by reflection. Not intended to be used for normal instantiation.
|
||||
@@ -71,4 +74,18 @@ public class ItemChannelLink extends AbstractLink {
|
||||
public int hashCode() {
|
||||
return super.hashCode() * configuration.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the item's current tags were applied from the linked channel's default tags
|
||||
*/
|
||||
public boolean tagsLinked() {
|
||||
return tagsLinked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag that indicates if the item's current tags were applied from the linked channel's default tags
|
||||
*/
|
||||
public void setTagsLinked(boolean value) {
|
||||
tagsLinked = value;
|
||||
}
|
||||
}
|
||||
|
||||
+273
-10
@@ -12,20 +12,35 @@
|
||||
*/
|
||||
package org.openhab.core.thing.link;
|
||||
|
||||
import static org.openhab.core.service.StartLevelService.STARTLEVEL_COMPLETE;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
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.core.common.registry.ManagedProvider;
|
||||
import org.openhab.core.common.registry.RegistryChangeListener;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.events.Event;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.events.EventSubscriber;
|
||||
import org.openhab.core.events.system.StartlevelEvent;
|
||||
import org.openhab.core.items.ActiveItem;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemBuilderFactory;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.semantics.Point;
|
||||
import org.openhab.core.semantics.Property;
|
||||
import org.openhab.core.semantics.SemanticTags;
|
||||
import org.openhab.core.semantics.Tag;
|
||||
import org.openhab.core.service.ReadyService;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
@@ -34,11 +49,16 @@ import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.UID;
|
||||
import org.openhab.core.thing.link.events.LinkEventFactory;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link ItemChannelLinkRegistry} tracks all {@link ItemChannelLinkProvider}s
|
||||
@@ -47,20 +67,51 @@ import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
* @author Dennis Nobel - Initial contribution
|
||||
* @author Markus Rathgeb - Linked items returns only existing items
|
||||
* @author Markus Rathgeb - Rewrite collection handling to improve performance
|
||||
* @author Andrew Fiddian-Green - Apply channel default tags to items
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true, service = ItemChannelLinkRegistry.class)
|
||||
public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLink, ItemChannelLinkProvider> {
|
||||
@Component(immediate = true, service = ItemChannelLinkRegistry.class, configurationPid = "org.openhab.ItemChannelLinkRegistry")
|
||||
public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLink, ItemChannelLinkProvider>
|
||||
implements RegistryChangeListener<Item>, EventSubscriber {
|
||||
|
||||
public static final String USE_TAGS = "useTags";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ItemChannelLinkRegistry.class);
|
||||
|
||||
private final ThingRegistry thingRegistry;
|
||||
private final ItemRegistry itemRegistry;
|
||||
private final ItemBuilderFactory itemBuilderFactory;
|
||||
private final ServiceRegistration<?> eventRegistration;
|
||||
|
||||
private boolean useTagsGlobally = false;
|
||||
private int startlevel = 0;
|
||||
|
||||
@Activate
|
||||
public ItemChannelLinkRegistry(final @Reference ThingRegistry thingRegistry,
|
||||
final @Reference ItemRegistry itemRegistry) {
|
||||
public ItemChannelLinkRegistry(final @Nullable Map<String, @Nullable Object> configuration,
|
||||
final @Reference ThingRegistry thingRegistry, final @Reference ItemRegistry itemRegistry,
|
||||
final @Reference ItemBuilderFactory itemBuilderFactory, BundleContext bundleContext) {
|
||||
super(ItemChannelLinkProvider.class);
|
||||
|
||||
this.thingRegistry = thingRegistry;
|
||||
this.itemRegistry = itemRegistry;
|
||||
this.itemRegistry.addRegistryChangeListener(this);
|
||||
this.itemBuilderFactory = itemBuilderFactory;
|
||||
this.eventRegistration = bundleContext.registerService(EventSubscriber.class.getName(), this, null);
|
||||
|
||||
modified(configuration);
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(@Nullable Map<String, @Nullable Object> configuration) {
|
||||
Object entry = configuration != null ? configuration.get(USE_TAGS) : null;
|
||||
useTagsGlobally = entry != null ? Boolean.parseBoolean(entry.toString()) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
eventRegistration.unregister();
|
||||
itemRegistry.removeRegistryChangeListener(this);
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +137,8 @@ public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLin
|
||||
* @return an unmodifiable set of bound items for the given channel UID
|
||||
*/
|
||||
public Set<Item> getLinkedItems(final UID uid) {
|
||||
return ((Stream<Item>) super.getLinkedItemNames(uid).stream().map(itemName -> itemRegistry.get(itemName))
|
||||
.filter(Objects::nonNull)).collect(Collectors.toSet());
|
||||
return super.getLinkedItemNames(uid).stream().map(itemName -> itemRegistry.get(itemName))
|
||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +148,8 @@ public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLin
|
||||
* @return an unmodifiable set of bound things for the given item name
|
||||
*/
|
||||
public Set<Thing> getBoundThings(final String itemName) {
|
||||
return ((Stream<Thing>) getBoundChannels(itemName).stream()
|
||||
.map(channelUID -> thingRegistry.get(channelUID.getThingUID())).filter(Objects::nonNull))
|
||||
.collect(Collectors.toSet());
|
||||
return getBoundChannels(itemName).stream().map(channelUID -> thingRegistry.get(channelUID.getThingUID()))
|
||||
.filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Reference
|
||||
@@ -208,18 +258,21 @@ public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLin
|
||||
|
||||
@Override
|
||||
protected void notifyListenersAboutAddedElement(final ItemChannelLink element) {
|
||||
assignChannelDefaultTags(element);
|
||||
super.notifyListenersAboutAddedElement(element);
|
||||
postEvent(LinkEventFactory.createItemChannelLinkAddedEvent(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyListenersAboutRemovedElement(final ItemChannelLink element) {
|
||||
removeChannelDefaultTags(element);
|
||||
super.notifyListenersAboutRemovedElement(element);
|
||||
postEvent(LinkEventFactory.createItemChannelLinkRemovedEvent(element));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyListenersAboutUpdatedElement(final ItemChannelLink oldElement, final ItemChannelLink element) {
|
||||
assignChannelDefaultTags(element);
|
||||
super.notifyListenersAboutUpdatedElement(oldElement, element);
|
||||
// it is not needed to send an event, because links can not be updated
|
||||
}
|
||||
@@ -229,4 +282,214 @@ public class ItemChannelLinkRegistry extends AbstractLinkRegistry<ItemChannelLin
|
||||
ITEM_MISSING,
|
||||
ITEM_AND_THING_CHANNEL_MISSING
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item does not already have a Point and/or a Property tag and if the linked channel has
|
||||
* 'useTags=true' then assign the default tags from that channel to the respective item. By contrast
|
||||
* if the item does already have a Point and/or a Property tag then we write a warning message in
|
||||
* the log. The warning is also logged if the item has more than one linked channel with 'useTags=true'.
|
||||
* And in any case if the item has native custom tags then those tags remain.
|
||||
*/
|
||||
private void assignChannelDefaultTags(ItemChannelLink link, ActiveItem activeItem) {
|
||||
boolean alreadyHasPointOrPropertySemanticTag = false;
|
||||
|
||||
for (String tag : activeItem.getTags()) {
|
||||
Class<? extends Tag> type = SemanticTags.getById(tag);
|
||||
if ((type != null) && (Point.class.isAssignableFrom(type) || Property.class.isAssignableFrom(type))) {
|
||||
alreadyHasPointOrPropertySemanticTag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> channelDefaultTags = getChannelDefaultTags(link);
|
||||
if (!channelDefaultTags.isEmpty()) {
|
||||
if (alreadyHasPointOrPropertySemanticTag) {
|
||||
logger.debug("Item '{}' already tagged; ignoring tags supplied by channel '{}'.", activeItem.getName(),
|
||||
link.getLinkedUID());
|
||||
} else {
|
||||
Set<String> newTags = new HashSet<>(activeItem.getTags());
|
||||
newTags.addAll(channelDefaultTags);
|
||||
logger.info("Item '{}' adding tags '{}' supplied by channel '{}'.", activeItem.getName(),
|
||||
channelDefaultTags, link.getLinkedUID());
|
||||
|
||||
link.setTagsLinked(true);
|
||||
|
||||
updateExistingRegistryItemTagsAndNotifyRegistryListeners(activeItem.getName(), newTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assignChannelDefaultTags(ItemChannelLink link) {
|
||||
try {
|
||||
if (itemRegistry.getItem(link.getItemName()) instanceof ActiveItem activeItem) {
|
||||
assignChannelDefaultTags(link, activeItem);
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
// we don't expect this exception but log it anyway
|
||||
logger.debug("Item '{}' not found when assigning channel default tags.", link.getItemName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if the linked channel is the actual source of the item's tags then remove those tags from
|
||||
* the item. If the item had any native custom tags they shall NOT be removed. Finally iterate
|
||||
* over any other linked channels so they may eventually provide new tags.
|
||||
*/
|
||||
private void removeChannelDefaultTags(ItemChannelLink oldLink, ActiveItem activeItem) {
|
||||
if (oldLink.tagsLinked()) {
|
||||
Set<String> newTags = new HashSet<>(activeItem.getTags());
|
||||
|
||||
// remove old link's tags
|
||||
Set<String> oldLinkTags = getChannelDefaultTags(oldLink);
|
||||
newTags.removeAll(oldLinkTags);
|
||||
// on openHAB shutdown tagsLinked may be true but oldLinkTags is already empty so do not log
|
||||
if (startlevel >= STARTLEVEL_COMPLETE) {
|
||||
logger.info("Item '{}' removing tags '{}' supplied by channel '{}'.", activeItem.getName(), oldLinkTags,
|
||||
oldLink.getLinkedUID());
|
||||
}
|
||||
|
||||
// iterate over other links in case one may assign new tags
|
||||
boolean alreadyHasPointOrPropertyTag = false;
|
||||
for (ItemChannelLink otherLink : getLinks(activeItem.getName())) {
|
||||
if (!otherLink.getUID().equals(oldLink.getUID())) {
|
||||
Set<String> otherLinkTags = getChannelDefaultTags(otherLink);
|
||||
if (!otherLinkTags.isEmpty()) {
|
||||
if (alreadyHasPointOrPropertyTag) {
|
||||
logger.debug("Item '{}' already tagged; ignoring tags supplied by channel '{}'.",
|
||||
activeItem.getName(), otherLink.getLinkedUID());
|
||||
break;
|
||||
} else {
|
||||
alreadyHasPointOrPropertyTag = true;
|
||||
newTags.addAll(otherLinkTags);
|
||||
logger.info("Item '{}' adding tags '{}' supplied by channel '{}'.", activeItem.getName(),
|
||||
otherLinkTags, otherLink.getLinkedUID());
|
||||
otherLink.setTagsLinked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
oldLink.setTagsLinked(false);
|
||||
|
||||
updateExistingRegistryItemTagsAndNotifyRegistryListeners(activeItem.getName(), newTags);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeChannelDefaultTags(ItemChannelLink link) {
|
||||
try {
|
||||
if (itemRegistry.getItem(link.getItemName()) instanceof ActiveItem activeItem) {
|
||||
removeChannelDefaultTags(link, activeItem);
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
// we don't expect this exception but log it anyway
|
||||
logger.debug("Item '{}' not found when assigning channel default tags.", link.getItemName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we shall get the default tags from the linked channel. This depends on the global setting 'useTags'
|
||||
* and the per-link setting 'useTags'.
|
||||
*
|
||||
* @param link the ItemChannelLink to be used
|
||||
* @return the default tags from the linked channel or an empty set
|
||||
*/
|
||||
private Set<String> getChannelDefaultTags(ItemChannelLink link) {
|
||||
Configuration configuration = link.getConfiguration();
|
||||
Object entry = configuration.get(USE_TAGS);
|
||||
Boolean useTagsPerLink = entry != null ? Boolean.parseBoolean(entry.toString()) : null;
|
||||
boolean getChannelTags = useTagsGlobally ? !Boolean.FALSE.equals(useTagsPerLink)
|
||||
: Boolean.TRUE.equals(useTagsPerLink);
|
||||
|
||||
if (getChannelTags) {
|
||||
ChannelUID channelUID = link.getLinkedUID();
|
||||
Thing thing = thingRegistry.get(channelUID.getThingUID());
|
||||
if (thing != null) {
|
||||
Channel channel = thing.getChannel(channelUID.getId());
|
||||
if (channel != null) {
|
||||
return channel.getDefaultTags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tags on the item instance in item registry and notify the item registry listeners about
|
||||
* the change. For items provisioned by an .items file, the item registry is read only so instead of
|
||||
* trying to replace the item with a new instance, we just modify the tags on the existing item instance.
|
||||
* However since the notification method requires both old and new items so we create a 'fake old item'
|
||||
* instance having the prior tags.
|
||||
*
|
||||
* @param itemName the name of the item in the registry that shall be be updated.
|
||||
* @param newTags the new tags that shall be applied to that item instance.
|
||||
*/
|
||||
private void updateExistingRegistryItemTagsAndNotifyRegistryListeners(String itemName, Set<String> newTags) {
|
||||
try {
|
||||
if (itemRegistry.getItem(itemName) instanceof ActiveItem item) {
|
||||
Item fakeOldItem = itemBuilderFactory.newItemBuilder(item).build();
|
||||
item.removeAllTags();
|
||||
item.addTags(newTags);
|
||||
itemRegistry.notifyListenersAboutItemExternalUpdate(fakeOldItem, item);
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
// we don't expect this exception but log it anyway
|
||||
logger.debug("Item '{}' not found when assigning channel default tags.", itemName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a new item is added to the item registry and we already have prior "pre- orphaned"
|
||||
* links to it, then update the tags from the default tags of the prior linked channels.
|
||||
*/
|
||||
@Override
|
||||
public void added(Item item) {
|
||||
if (item instanceof ActiveItem activeItem) {
|
||||
getLinks(activeItem.getName()).forEach(link -> assignChannelDefaultTags(link, activeItem));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the item has gone then clear the 'tagsLinked' flag.
|
||||
*/
|
||||
@Override
|
||||
public void removed(Item item) {
|
||||
if (item instanceof ActiveItem activeItem) {
|
||||
getLinks(activeItem.getName()).forEach(link -> link.setTagsLinked(false));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Either this class applied channel default tags to the item, or something else updated the
|
||||
* item (including possibly updating its tags), so in any case do NOT try to re-apply the
|
||||
* channel default tags. Since that might cause an infinite loop.
|
||||
*/
|
||||
@Override
|
||||
public void updated(Item oldItem, Item item) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to system start level events.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getSubscribedEventTypes() {
|
||||
return Set.of(StartlevelEvent.TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-initialize (one off) all the channel default tags when startup is complete.
|
||||
*/
|
||||
@Override
|
||||
public void receive(Event event) {
|
||||
if (event instanceof StartlevelEvent startlevelEvent) {
|
||||
int newStartlevel = startlevelEvent.getStartlevel();
|
||||
if (newStartlevel != startlevel && newStartlevel >= STARTLEVEL_COMPLETE) {
|
||||
for (ItemChannelLink link : getAll()) {
|
||||
assignChannelDefaultTags(link);
|
||||
}
|
||||
}
|
||||
startlevel = newStartlevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -464,4 +464,9 @@ public class ItemRegistryImpl extends AbstractRegistry<Item, String, ItemProvide
|
||||
metadataAwareItem.updatedMetadata(oldElement, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyListenersAboutItemExternalUpdate(Item oldItem, Item newItem) {
|
||||
notifyListenersAboutUpdatedElement(oldItem, newItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,4 +105,11 @@ public interface ItemRegistry extends Registry<Item, String> {
|
||||
*/
|
||||
@Nullable
|
||||
Item remove(String itemName, boolean recursive);
|
||||
|
||||
/**
|
||||
* Called when an item instance has been externally updated in order to inform all registry
|
||||
* listeners the change between its old and new state.
|
||||
*/
|
||||
default void notifyListenersAboutItemExternalUpdate(Item oldItem, Item newItem) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,4 +130,7 @@ Fragment-Host: org.openhab.core.model.item
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-xml;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.19.2,2.19.3)',\
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)',\
|
||||
org.openhab.core.model.persistence.runtime;version='[5.1.0,5.1.1)',\
|
||||
de.focus_shift.jollyday-core;version='[1.5.4,1.5.5)',\
|
||||
de.focus_shift.jollyday-jackson;version='[1.5.4,1.5.5)'
|
||||
org.osgi.service.cm;version='[1.6.1,1.6.2)'
|
||||
|
||||
@@ -132,4 +132,5 @@ Fragment-Host: org.openhab.core.model.rule.runtime
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-xml;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.19.2,2.19.3)',\
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)',\
|
||||
org.openhab.core.model.persistence.runtime;version='[5.1.0,5.1.1)',\
|
||||
org.osgi.service.cm;version='[1.6.1,1.6.2)'
|
||||
|
||||
@@ -135,4 +135,7 @@ Fragment-Host: org.openhab.core.model.script
|
||||
com.fasterxml.jackson.core.jackson-databind;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-xml;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.19.2,2.19.3)',\
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)'
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)',\
|
||||
org.openhab.core.model.persistence.runtime;version='[5.1.0,5.1.1)',\
|
||||
de.focus_shift.jollyday-core;version='[1.5.4,1.5.5)',\
|
||||
de.focus_shift.jollyday-jackson;version='[1.5.4,1.5.5)'
|
||||
|
||||
@@ -137,4 +137,7 @@ Fragment-Host: org.openhab.core.model.thing
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-xml;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.19.2,2.19.3)',\
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)',\
|
||||
org.openhab.core.model.persistence.runtime;version='[5.1.0,5.1.1)',\
|
||||
de.focus_shift.jollyday-core;version='[1.5.4,1.5.5)',\
|
||||
de.focus_shift.jollyday-jackson;version='[1.5.4,1.5.5)'
|
||||
org.osgi.service.cm;version='[1.6.1,1.6.2)'
|
||||
|
||||
@@ -73,6 +73,65 @@ Fragment-Host: org.openhab.core.thing
|
||||
org.openhab.core.thing.tests;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.transform;version='[5.1.0,5.1.1)',\
|
||||
biz.aQute.tester.junit-platform;version='[7.1.0,7.1.1)',\
|
||||
org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\
|
||||
org.openhab.core.semantics;version='[5.1.0,5.1.1)',\
|
||||
org.objectweb.asm;version='[9.8.0,9.8.1)'
|
||||
com.google.inject;version='[7.0.0,7.0.1)',\
|
||||
de.focus_shift.jollyday-core;version='[1.5.4,1.5.5)',\
|
||||
de.focus_shift.jollyday-jackson;version='[1.5.4,1.5.5)',\
|
||||
io.github.classgraph.classgraph;version='[4.8.180,4.8.181)',\
|
||||
org.antlr.runtime;version='[3.2.0,3.2.1)',\
|
||||
org.eclipse.jetty.alpn.client;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.client;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.http2.client;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.http2.common;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.http2.hpack;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.jaas;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.websocket.api;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.websocket.client;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.websocket.common;version='[9.4.57,9.4.58)',\
|
||||
org.eclipse.jetty.xml;version='[9.4.57,9.4.58)',\
|
||||
org.glassfish.hk2.external.aopalliance-repackaged;version='[3.1.1,3.1.2)',\
|
||||
org.openhab.core.audio;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.automation;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.automation.module.script;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.automation.module.script.rulesupport;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.ephemeris;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.io.net;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.core;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.item;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.persistence;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.persistence.runtime;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.rule;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.script;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.script.runtime;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.sitemap;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.model.thing;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.persistence;version='[5.1.0,5.1.1)',\
|
||||
org.openhab.core.voice;version='[5.1.0,5.1.1)',\
|
||||
org.threeten.extra;version='[1.8.0,1.8.1)',\
|
||||
stax2-api;version='[4.2.2,4.2.3)',\
|
||||
com.fasterxml.jackson.core.jackson-annotations;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.core.jackson-core;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.core.jackson-databind;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-xml;version='[2.19.2,2.19.3)',\
|
||||
com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.19.2,2.19.3)',\
|
||||
com.google.guava;version='[33.4.8,33.4.9)',\
|
||||
com.google.guava.failureaccess;version='[1.0.3,1.0.4)',\
|
||||
org.eclipse.emf.common;version='[2.30.0,2.30.1)',\
|
||||
org.eclipse.emf.ecore;version='[2.36.0,2.36.1)',\
|
||||
org.eclipse.emf.ecore.xmi;version='[2.37.0,2.37.1)',\
|
||||
org.eclipse.equinox.common;version='[3.19.0,3.19.1)',\
|
||||
org.eclipse.xtend.lib;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtend.lib.macro;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtext;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtext.common.types;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtext.util;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtext.xbase;version='[2.40.0,2.40.1)',\
|
||||
org.eclipse.xtext.xbase.lib;version='[2.40.0,2.40.1)',\
|
||||
org.objectweb.asm;version='[9.8.0,9.8.1)',\
|
||||
org.ops4j.pax.logging.pax-logging-api;version='[2.3.0,2.3.1)',\
|
||||
org.ops4j.pax.web.pax-web-api;version='[8.0.33,8.0.34)',\
|
||||
org.ops4j.pax.web.pax-web-jetty;version='[8.0.33,8.0.34)',\
|
||||
org.ops4j.pax.web.pax-web-runtime;version='[8.0.33,8.0.34)',\
|
||||
org.ops4j.pax.web.pax-web-spi;version='[8.0.33,8.0.34)',\
|
||||
org.ops4j.pax.web.pax-web-tomcat-common;version='[8.0.33,8.0.34)',\
|
||||
org.yaml.snakeyaml;version='[2.4.0,2.4.1)'
|
||||
|
||||
+12
-5
@@ -34,6 +34,7 @@ import org.openhab.core.common.registry.ProviderChangeListener;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.i18n.UnitProvider;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemBuilderFactory;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.items.ItemStateConverter;
|
||||
import org.openhab.core.items.Metadata;
|
||||
@@ -78,6 +79,7 @@ import org.openhab.core.thing.type.ChannelType;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.TimeSeries;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -89,8 +91,9 @@ import org.openhab.core.types.TimeSeries;
|
||||
public class CommunicationManagerOSGiTest extends JavaOSGiTest {
|
||||
|
||||
private static class ItemChannelLinkRegistryAdvanced extends ItemChannelLinkRegistry {
|
||||
public ItemChannelLinkRegistryAdvanced(ThingRegistry thingRegistry, ItemRegistry itemRegistry) {
|
||||
super(thingRegistry, itemRegistry);
|
||||
public ItemChannelLinkRegistryAdvanced(ThingRegistry thingRegistry, ItemRegistry itemRegistry,
|
||||
ItemBuilderFactory itemBuilderFactory, BundleContext bundleContext) {
|
||||
super(null, thingRegistry, itemRegistry, itemBuilderFactory, bundleContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,15 +155,19 @@ public class CommunicationManagerOSGiTest extends JavaOSGiTest {
|
||||
private @Mock @NonNullByDefault({}) ThingHandler thingHandlerMock;
|
||||
private @Mock @NonNullByDefault({}) ThingRegistry thingRegistryMock;
|
||||
private @Mock @NonNullByDefault({}) TriggerProfile triggerProfileMock;
|
||||
private @Mock @NonNullByDefault({}) ItemBuilderFactory itemBuilderFactoryMock;
|
||||
|
||||
private @NonNullByDefault({}) CommunicationManager manager;
|
||||
private @NonNullByDefault({}) SafeCaller safeCaller;
|
||||
|
||||
private ItemChannelLinkRegistryAdvanced iclRegistry = new ItemChannelLinkRegistryAdvanced(thingRegistryMock,
|
||||
itemRegistryMock);
|
||||
private @NonNullByDefault({}) ItemChannelLinkRegistryAdvanced iclRegistry;
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
registerVolatileStorageService();
|
||||
|
||||
iclRegistry = new ItemChannelLinkRegistryAdvanced(thingRegistryMock, itemRegistryMock, itemBuilderFactoryMock,
|
||||
bundleContext);
|
||||
|
||||
when(UNIT_PROVIDER_MOCK.getUnit(Temperature.class)).thenReturn(SIUnits.CELSIUS);
|
||||
item5 = new NumberItem("Number:Temperature", ITEM_NAME_5, UNIT_PROVIDER_MOCK);
|
||||
|
||||
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2025 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.core.thing.link;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.model.ItemsStandaloneSetup;
|
||||
import org.openhab.core.model.core.ModelRepository;
|
||||
import org.openhab.core.model.thing.ThingStandaloneSetup;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link ItemChannelLinkRegistry} tagging.
|
||||
*
|
||||
* @author Andrew Fiddian-Green - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ItemChannelLinkTaggingOSGiTest extends JavaOSGiTest {
|
||||
|
||||
private static final String ITEMS_MODEL_ID = "test.items";
|
||||
private static final String ITEMS_MODEL = """
|
||||
String Item_01 {channel="hue:device:dummy:color" }
|
||||
String Item_02 {channel="hue:device:dummy:color" [useTags=false] }
|
||||
String Item_03 {channel="hue:device:dummy:color" [useTags="false"] }
|
||||
String Item_04 "Control, Color" {channel="hue:device:dummy:color" [useTags=true] }
|
||||
String Item_05 "Control, Color" {channel="hue:device:dummy:color" [useTags="true"] }
|
||||
String Item_06 "Control Color, Custom" ["Custom"] {channel="hue:device:dummy:color" [useTags=true] }
|
||||
String Item_07 "'Control', Power, Custom" ["Power", "Custom"] {channel="hue:device:dummy:color" }
|
||||
String Item_08 "'Control', Power, Custom" ["Power", "Custom"] {channel="hue:device:dummy:color" [useTags=true] }
|
||||
String Item_09 "Switch, Custom" ["Switch", "Custom"] {channel="hue:device:dummy:color" }
|
||||
String Item_10 "Switch, Custom" ["Switch", "Custom"] {channel="hue:device:dummy:color" [useTags=true] }
|
||||
String Item_11 "Switch, Power, Custom" ["Switch", "Power", "Custom"] {channel="hue:device:dummy:color" }
|
||||
String Item_12 "Switch, Power, Custom" ["Switch", "Power", "Custom"] {channel="hue:device:dummy:color" [useTags=true] }
|
||||
String Item_13 "Alarm, LowBattery" {channel="hue:device:dummy:battery-low" [useTags=true] }
|
||||
String Item_14 "Control, Color" {channel="hue:device:dummy:color" [useTags=true], channel="hue:device:dummy:battery-low" [useTags=true] }
|
||||
String Item_15 "Alarm, LowBattery" {channel="hue:device:dummy:color" [useTags=false], channel="hue:device:dummy:battery-low" [useTags=true] }
|
||||
""";
|
||||
|
||||
private @NonNullByDefault({}) ItemRegistry itemRegistry;
|
||||
private @NonNullByDefault({}) ThingRegistry thingRegistry;
|
||||
private @NonNullByDefault({}) ModelRepository modelRepository;
|
||||
private @NonNullByDefault({}) ItemChannelLinkRegistry itemChannelLinkRegistry;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
registerVolatileStorageService();
|
||||
|
||||
ItemsStandaloneSetup.doSetup();
|
||||
ThingStandaloneSetup.doSetup();
|
||||
|
||||
itemRegistry = getService(ItemRegistry.class);
|
||||
assertNotNull(itemRegistry);
|
||||
|
||||
thingRegistry = getService(ThingRegistry.class);
|
||||
assertNotNull(thingRegistry);
|
||||
|
||||
itemChannelLinkRegistry = getService(ItemChannelLinkRegistry.class);
|
||||
assertNotNull(itemChannelLinkRegistry);
|
||||
|
||||
modelRepository = getService(ModelRepository.class);
|
||||
assertNotNull(modelRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertTagsAreCorrect() {
|
||||
ThingTypeUID thingTypeUID = new ThingTypeUID("hue", "device");
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, "dummy");
|
||||
|
||||
Channel colorChannel = ChannelBuilder.create(new ChannelUID(thingUID, "color"), "Color")
|
||||
.withDefaultTags(Set.of("Control", "Color")).build();
|
||||
Channel batteryChannel = ChannelBuilder.create(new ChannelUID(thingUID, "battery-low"), "Switch")
|
||||
.withDefaultTags(Set.of("Alarm", "LowBattery")).build();
|
||||
|
||||
Thing thing = ThingBuilder.create(thingTypeUID, thingUID) //
|
||||
.withChannels(List.of(colorChannel, batteryChannel)).build();
|
||||
|
||||
thingRegistry.add(thing);
|
||||
assertEquals(1, thingRegistry.getAll().size());
|
||||
|
||||
modelRepository.addOrRefreshModel(ITEMS_MODEL_ID, new ByteArrayInputStream(ITEMS_MODEL.getBytes()));
|
||||
assertEquals(15, itemRegistry.getAll().size());
|
||||
assertEquals(17, itemChannelLinkRegistry.getAll().size());
|
||||
|
||||
Item item;
|
||||
|
||||
item = itemRegistry.get("Item_01");
|
||||
assertNotNull(item);
|
||||
assertTrue(item.getTags().isEmpty());
|
||||
|
||||
item = itemRegistry.get("Item_02");
|
||||
assertNotNull(item);
|
||||
assertTrue(item.getTags().isEmpty());
|
||||
|
||||
item = itemRegistry.get("Item_03");
|
||||
assertNotNull(item);
|
||||
assertTrue(item.getTags().isEmpty());
|
||||
|
||||
item = itemRegistry.get("Item_04");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Control", "Color"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_05");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Control", "Color"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_06");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Control", "Color", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_07");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Power", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_08");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Power", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_09");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Switch", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_10");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Switch", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_11");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Switch", "Power", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_12");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Switch", "Power", "Custom"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_13");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Alarm", "LowBattery"), item.getTags());
|
||||
|
||||
item = itemRegistry.get("Item_14");
|
||||
assertNotNull(item);
|
||||
assertTrue(item.getTags().equals(Set.of("Control", "Color")) //
|
||||
|| item.getTags().equals(Set.of("Alarm", "LowBattery")));
|
||||
|
||||
item = itemRegistry.get("Item_15");
|
||||
assertNotNull(item);
|
||||
assertEquals(Set.of("Alarm", "LowBattery"), item.getTags());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user