diff --git a/bundles/org.openhab.core.addon.eclipse/src/main/java/org/openhab/core/addon/eclipse/internal/EclipseAddonService.java b/bundles/org.openhab.core.addon.eclipse/src/main/java/org/openhab/core/addon/eclipse/internal/EclipseAddonService.java
index 77c5d270b..daf13dd8c 100644
--- a/bundles/org.openhab.core.addon.eclipse/src/main/java/org/openhab/core/addon/eclipse/internal/EclipseAddonService.java
+++ b/bundles/org.openhab.core.addon.eclipse/src/main/java/org/openhab/core/addon/eclipse/internal/EclipseAddonService.java
@@ -144,6 +144,7 @@ public class EclipseAddonService implements AddonService {
if (addonInfo != null) {
// only enrich if this add-on is installed, otherwise wrong data might be added
addon = addon.withLabel(addonInfo.getName()).withDescription(addonInfo.getDescription())
+ .withKeywords(Objects.requireNonNullElse(addonInfo.getKeywords(), ""))
.withConnection(addonInfo.getConnection()).withCountries(addonInfo.getCountries())
.withLink(getDefaultDocumentationLink(type, name))
.withConfigDescriptionURI(addonInfo.getConfigDescriptionURI());
diff --git a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java
index 5e35a3807..62042bbfa 100644
--- a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java
+++ b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/JsonAddonService.java
@@ -209,7 +209,7 @@ public class JsonAddonService extends AbstractRemoteAddonService {
.withDetailedDescription(addonEntry.description).withContentType(addonEntry.contentType)
.withAuthor(addonEntry.author).withVersion(addonEntry.version).withLabel(addonEntry.title)
.withCompatible(compatible).withMaturity(addonEntry.maturity).withProperties(properties)
- .withLink(addonEntry.link).withImageLink(addonEntry.imageUrl)
+ .withLink(addonEntry.link).withImageLink(addonEntry.imageUrl).withKeywords(addonEntry.keywords)
.withConfigDescriptionURI(addonEntry.configDescriptionURI).withLoggerPackages(addonEntry.loggerPackages)
.withConnection(addonEntry.connection).withCountries(addonEntry.countries).build();
}
diff --git a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/model/AddonEntryDTO.java b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/model/AddonEntryDTO.java
index b91a135df..35f128061 100644
--- a/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/model/AddonEntryDTO.java
+++ b/bundles/org.openhab.core.addon.marketplace/src/main/java/org/openhab/core/addon/marketplace/internal/json/model/AddonEntryDTO.java
@@ -26,6 +26,7 @@ public class AddonEntryDTO {
public String id = "";
public String type = "";
public String description = "";
+ public String keywords = "";
public String title = "";
public String link = "";
public String version = "";
diff --git a/bundles/org.openhab.core.addon/schema/addon-1.0.0.xsd b/bundles/org.openhab.core.addon/schema/addon-1.0.0.xsd
index 1420776fa..3411feefe 100644
--- a/bundles/org.openhab.core.addon/schema/addon-1.0.0.xsd
+++ b/bundles/org.openhab.core.addon/schema/addon-1.0.0.xsd
@@ -13,6 +13,11 @@
+
+
+ Comma-separated list of key words for search.
+
+
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfo.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfo.java
index 7958d1a26..7399b3add 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfo.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfo.java
@@ -41,6 +41,7 @@ public class AddonInfo implements Identifiable {
private final String uid;
private final String name;
private final String description;
+ private final @Nullable String keywords;
private final @Nullable String connection;
private final List countries;
private final @Nullable String configDescriptionURI;
@@ -49,8 +50,8 @@ public class AddonInfo implements Identifiable {
private @Nullable List discoveryMethods;
private AddonInfo(String id, String type, @Nullable String uid, String name, String description,
- @Nullable String connection, List countries, @Nullable String configDescriptionURI,
- @Nullable String serviceId, @Nullable String sourceBundle,
+ @Nullable String keywords, @Nullable String connection, List countries,
+ @Nullable String configDescriptionURI, @Nullable String serviceId, @Nullable String sourceBundle,
@Nullable List discoveryMethods) throws IllegalArgumentException {
// mandatory fields
if (id.isBlank()) {
@@ -73,6 +74,7 @@ public class AddonInfo implements Identifiable {
this.description = description;
// optional fields
+ this.keywords = keywords;
this.connection = connection;
this.countries = countries;
this.configDescriptionURI = configDescriptionURI;
@@ -127,6 +129,15 @@ public class AddonInfo implements Identifiable {
return description;
}
+ /**
+ * Returns a comma-separated list of keywords related to the add-on. e.g. "bluetooth".
+ *
+ * @return a comma-separated list of keywords, or null if no keywords string available
+ */
+ public @Nullable String getKeywords() {
+ return keywords;
+ }
+
/**
* Returns the link to a concrete {@link org.openhab.core.config.core.ConfigDescription}.
*
@@ -168,6 +179,7 @@ public class AddonInfo implements Identifiable {
private @Nullable String uid;
private String name = "";
private String description = "";
+ private @Nullable String keywords;
private @Nullable String connection;
private List countries = List.of();
private @Nullable String configDescriptionURI = "";
@@ -186,6 +198,7 @@ public class AddonInfo implements Identifiable {
this.uid = addonInfo.uid;
this.name = addonInfo.name;
this.description = addonInfo.description;
+ this.keywords = addonInfo.keywords;
this.connection = addonInfo.connection;
this.countries = addonInfo.countries;
this.configDescriptionURI = addonInfo.configDescriptionURI;
@@ -209,6 +222,11 @@ public class AddonInfo implements Identifiable {
return this;
}
+ public Builder withKeywords(@Nullable String keywords) {
+ this.keywords = keywords;
+ return this;
+ }
+
public Builder withConnection(@Nullable String connection) {
this.connection = connection;
return this;
@@ -251,8 +269,8 @@ public class AddonInfo implements Identifiable {
* @throws IllegalArgumentException if any of the information in this builder is invalid
*/
public AddonInfo build() throws IllegalArgumentException {
- return new AddonInfo(id, type, uid, name, description, connection, countries, configDescriptionURI,
- serviceId, sourceBundle, discoveryMethods);
+ return new AddonInfo(id, type, uid, name, description, keywords, connection, countries,
+ configDescriptionURI, serviceId, sourceBundle, discoveryMethods);
}
}
}
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfoRegistry.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfoRegistry.java
index 6bdc24d27..2072b5765 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfoRegistry.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/AddonInfoRegistry.java
@@ -12,6 +12,7 @@
*/
package org.openhab.core.addon;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
@@ -98,6 +99,16 @@ public class AddonInfoRegistry {
if (a.getDescription().isBlank()) {
builder.withDescription(b.getDescription());
}
+ Set keywords = new HashSet<>();
+ if (a.getKeywords() instanceof String ka) {
+ Arrays.stream(ka.split(",")).map(String::trim).filter(s -> !s.isEmpty()).forEach(keywords::add);
+ }
+ if (b.getKeywords() instanceof String kb) {
+ Arrays.stream(kb.split(",")).map(String::trim).filter(s -> !s.isEmpty()).forEach(keywords::add);
+ }
+ if (!keywords.isEmpty()) {
+ builder.withKeywords(keywords.stream().collect(Collectors.joining(",")));
+ }
if (a.getConnection() == null && b.getConnection() != null) {
builder.withConnection(b.getConnection());
}
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/JarFileAddonService.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/JarFileAddonService.java
index 011653a89..b2759297c 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/JarFileAddonService.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/JarFileAddonService.java
@@ -157,6 +157,7 @@ public class JarFileAddonService extends BundleTracker implements AddonS
.withConnection(addonInfo.getConnection()).withCountries(addonInfo.getCountries())
.withConfigDescriptionURI(addonInfo.getConfigDescriptionURI())
.withDescription(Objects.requireNonNullElse(addonInfo.getDescription(), bundle.getSymbolicName()))
+ .withKeywords(Objects.requireNonNullElse(addonInfo.getKeywords(), ""))
.withContentType(ADDONS_CONTENT_TYPE).withLoggerPackages(List.of(bundle.getSymbolicName())).build();
}
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoConverter.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoConverter.java
index 1f3a54218..3a0989835 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoConverter.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoConverter.java
@@ -87,6 +87,8 @@ public class AddonInfoConverter extends GenericUnmarshaller
"Add-on description is null or empty");
AddonInfo.Builder addonInfo = AddonInfo.builder(id, type).withName(name).withDescription(description);
+
+ addonInfo.withKeywords((String) nodeIterator.nextValue("keywords", false));
addonInfo.withConnection((String) nodeIterator.nextValue("connection", false));
addonInfo.withCountries((String) nodeIterator.nextValue("countries", false));
addonInfo.withServiceId((String) nodeIterator.nextValue("service-id", false));
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoListReader.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoListReader.java
index db5babc72..79d551957 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoListReader.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoListReader.java
@@ -82,6 +82,7 @@ public class AddonInfoListReader extends XmlDocumentReader {
xstream.alias("name", NodeValue.class);
xstream.alias("description", NodeValue.class);
xstream.alias("type", NodeValue.class);
+ xstream.alias("keywords", NodeValue.class);
xstream.alias("connection", NodeValue.class);
xstream.alias("countries", NodeValue.class);
xstream.alias("config-description", ConfigDescription.class);
diff --git a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoReader.java b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoReader.java
index 821581922..d65216da5 100644
--- a/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoReader.java
+++ b/bundles/org.openhab.core.addon/src/main/java/org/openhab/core/addon/internal/xml/AddonInfoReader.java
@@ -81,6 +81,7 @@ public class AddonInfoReader extends XmlDocumentReader {
xstream.alias("name", NodeValue.class);
xstream.alias("description", NodeValue.class);
xstream.alias("type", NodeValue.class);
+ xstream.alias("keywords", NodeValue.class);
xstream.alias("connection", NodeValue.class);
xstream.alias("countries", NodeValue.class);
xstream.alias("config-description", ConfigDescription.class);
diff --git a/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoListReaderTest.java b/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoListReaderTest.java
index 9b1b4b9ed..1391da6ed 100644
--- a/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoListReaderTest.java
+++ b/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoListReaderTest.java
@@ -37,6 +37,7 @@ class AddonInfoListReaderTest {
+ " automation"
+ " Groovy Scripting"
+ " This adds a Groovy script engine."
+ + " The,quick,brown,fox"
+ " none"
+ " "
+ " "
@@ -86,6 +87,7 @@ class AddonInfoListReaderTest {
assertEquals(1, addonsInfos.size());
AddonInfo addon = addonsInfos.getFirst();
assertNotNull(addon);
+ assertEquals("The,quick,brown,fox", addon.getKeywords());
List discoveryMethods = addon.getDiscoveryMethods();
assertNotNull(discoveryMethods);
assertEquals(2, discoveryMethods.size());
diff --git a/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoRegistryMergeTest.java b/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoRegistryMergeTest.java
index 3d0d94ede..92347978e 100644
--- a/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoRegistryMergeTest.java
+++ b/bundles/org.openhab.core.addon/src/test/java/org/openhab/core/addon/AddonInfoRegistryMergeTest.java
@@ -13,6 +13,7 @@
package org.openhab.core.addon;
import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.List;
@@ -47,7 +48,7 @@ class AddonInfoRegistryMergeTest {
}
private AddonInfoProvider createAddonInfoProvider0() {
- AddonInfo addonInfo = AddonInfo.builder("hue", "binding").withName("name-zero")
+ AddonInfo addonInfo = AddonInfo.builder("hue", "binding").withName("name-zero").withKeywords("the,quick")
.withDescription("description-zero").build();
AddonInfoProvider provider = mock(AddonInfoProvider.class);
when(provider.getAddonInfo(anyString(), any(Locale.class))).thenReturn(null);
@@ -60,7 +61,7 @@ class AddonInfoRegistryMergeTest {
private AddonInfoProvider createAddonInfoProvider1() {
AddonDiscoveryMethod discoveryMethod = new AddonDiscoveryMethod().setServiceType("mdns")
.setParameters(List.of(new AddonParameter("mdnsServiceType", "_hue._tcp.local.")));
- AddonInfo addonInfo = AddonInfo.builder("hue", "binding").withName("name-one")
+ AddonInfo addonInfo = AddonInfo.builder("hue", "binding").withName("name-one").withKeywords("brown,fox")
.withDescription("description-one").withCountries("GB,NL").withConnection("local")
.withDiscoveryMethods(List.of(discoveryMethod)).build();
AddonInfoProvider provider = mock(AddonInfoProvider.class);
@@ -150,6 +151,11 @@ class AddonInfoRegistryMergeTest {
assertNotEquals("http://www.openhab.org", addonInfo.getConfigDescriptionURI());
assertEquals("binding.hue", addonInfo.getServiceId());
assertEquals(1, addonInfo.getDiscoveryMethods().size());
+
+ assertTrue(addonInfo.getKeywords().contains("the"));
+ assertTrue(addonInfo.getKeywords().contains("quick"));
+ assertTrue(addonInfo.getKeywords().contains("brown"));
+ assertTrue(addonInfo.getKeywords().contains("fox"));
}
/**
diff --git a/bundles/org.openhab.core.karaf/src/main/java/org/openhab/core/karaf/internal/KarafAddonService.java b/bundles/org.openhab.core.karaf/src/main/java/org/openhab/core/karaf/internal/KarafAddonService.java
index 800252812..a00eff2f8 100644
--- a/bundles/org.openhab.core.karaf/src/main/java/org/openhab/core/karaf/internal/KarafAddonService.java
+++ b/bundles/org.openhab.core.karaf/src/main/java/org/openhab/core/karaf/internal/KarafAddonService.java
@@ -21,6 +21,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
@@ -135,6 +136,7 @@ public class KarafAddonService implements AddonService {
if (addonInfo != null) {
addon = addon.withLabel(addonInfo.getName()).withDescription(addonInfo.getDescription())
+ .withKeywords(Objects.requireNonNullElse(addonInfo.getKeywords(), ""))
.withConnection(addonInfo.getConnection()).withCountries(addonInfo.getCountries())
.withLink(getDefaultDocumentationLink(type, name))
.withConfigDescriptionURI(addonInfo.getConfigDescriptionURI());