Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddonInfo extensions #3865

Merged
merged 11 commits into from
Dec 5, 2023
82 changes: 55 additions & 27 deletions bundles/org.openhab.core.addon/schema/addon-1.0.0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,36 @@
<xs:import namespace="https://openhab.org/schemas/config-description/v1.0.0"
schemaLocation="https://openhab.org/schemas/config-description-1.0.0.xsd"/>

<xs:element name="addon">
<xs:complexType>
<xs:sequence>
<xs:element name="type" type="addon:addonType"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="connection" type="addon:connectionType" minOccurs="0"/>
<xs:element name="countries" type="addon:countryType" minOccurs="0">
<xs:annotation>
<xs:documentation>Comma-separated list of two-letter ISO country codes.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="service-id" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>The ID (service.pid or component.name) of the main add-on service, which can be configured through OSGi configuration admin service. Should only be used in combination with a config description definition. The default value is &lt;type&gt;.&lt;name&gt;</xs:documentation>
</xs:annotation>
</xs:element>
<xs:choice minOccurs="0">
<xs:element name="config-description" type="config-description:configDescription"/>
<xs:element name="config-description-ref" type="config-description:configDescriptionRef"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="id" type="config-description:idRestrictionPattern" use="required">
<xs:element name="addon" type="addon:addonInfo"/>

<xs:complexType name="addonInfo">
<xs:sequence>
<xs:element name="type" type="addon:addonType"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="connection" type="addon:connectionType" minOccurs="0"/>
<xs:element name="countries" type="addon:countryType" minOccurs="0">
<xs:annotation>
<xs:documentation>Comma-separated list of two-letter ISO country codes.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="service-id" type="xs:string" minOccurs="0">
<xs:annotation>
<xs:documentation>The id is used to construct the UID of this add-on to &lt;type&gt;-&lt;name&gt;</xs:documentation>
<xs:documentation>The ID (service.pid or component.name) of the main add-on service, which can be configured through OSGi configuration admin service. Should only be used in combination with a config description definition. The default value is &lt;type&gt;.&lt;name&gt;</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:element>
<xs:choice minOccurs="0">
<xs:element name="config-description" type="config-description:configDescription"/>
<xs:element name="config-description-ref" type="config-description:configDescriptionRef"/>
</xs:choice>
<xs:element name="discovery-methods" type="addon:discoveryMethodsType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="id" type="config-description:idRestrictionPattern" use="required">
<xs:annotation>
<xs:documentation>The id is used to construct the UID of this add-on to &lt;type&gt;-&lt;name&gt;</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>

<xs:simpleType name="addonType">
<xs:restriction base="xs:string">
Expand Down Expand Up @@ -80,4 +81,31 @@
</xs:restriction>
</xs:simpleType>

<xs:complexType name="discoveryMethodsType">
<xs:sequence>
<xs:element type="addon:discoveryMethodType" name="discovery-method" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="discoveryMethodType">
<xs:sequence>
<xs:element type="xs:string" name="service-type"/>
<xs:element type="xs:string" name="mdns-service-type" minOccurs="0"/>
<xs:element type="addon:matchPropertiesType" name="match-properties" minOccurs="0"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="matchPropertiesType">
<xs:sequence>
<xs:element type="addon:matchPropertyType" name="match-property" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="matchPropertyType">
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element type="xs:string" name="regex"/>
</xs:sequence>
</xs:complexType>

</xs:schema>
23 changes: 23 additions & 0 deletions bundles/org.openhab.core.addon/schema/addon-info-list-1.0.0.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
xmlns:addon-info-list="https://openhab.org/schemas/addon-info-list/v1.0.0"
targetNamespace="https://openhab.org/schemas/addon-info-list/v1.0.0">

<xs:import namespace="https://openhab.org/schemas/addon/v1.0.0" schemaLocation="addon-1.0.0.xsd"/>

<xs:element name="addon-info-list">
<xs:complexType>
<xs:sequence>
<xs:element name="addons" type="addon-info-list:addons"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="addons">
<xs:sequence>
<xs:element name="addon" type="addon:addonInfo" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

</xs:schema>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2010-2023 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.addon;

import java.util.List;
import java.util.Objects;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* DTO for serialization of a suggested addon discovery method.
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class AddonDiscoveryMethod {
private @NonNullByDefault({}) String serviceType;
private @Nullable String mdnsServiceType;
private @Nullable List<AddonMatchProperty> matchProperties;

public String getServiceType() {
return serviceType.toLowerCase();
}

public String getMdnsServiceType() {
String mdnsServiceType = this.mdnsServiceType;
return mdnsServiceType != null ? mdnsServiceType : "";
}

public List<AddonMatchProperty> getMatchProperties() {
List<AddonMatchProperty> matchProperties = this.matchProperties;
return matchProperties != null ? matchProperties : List.of();
}

public AddonDiscoveryMethod setServiceType(String serviceType) {
this.serviceType = serviceType.toLowerCase();
return this;
}

public AddonDiscoveryMethod setMdnsServiceType(@Nullable String mdnsServiceType) {
this.mdnsServiceType = mdnsServiceType;
return this;
}

public AddonDiscoveryMethod setMatchProperties(@Nullable List<AddonMatchProperty> matchProperties) {
this.matchProperties = matchProperties;
return this;
}

@Override
public int hashCode() {
return Objects.hash(serviceType, mdnsServiceType, matchProperties);
}

@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AddonDiscoveryMethod other = (AddonDiscoveryMethod) obj;
return Objects.equals(serviceType, other.serviceType) && Objects.equals(mdnsServiceType, other.mdnsServiceType)
&& Objects.equals(matchProperties, other.matchProperties);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,20 @@ public class AddonInfo implements Identifiable<String> {

private final String id;
private final String type;
private final String uid;
private final String name;
private final String description;
private final @Nullable String connection;
private final List<String> countries;
private final @Nullable String configDescriptionURI;
private final String serviceId;
private @Nullable String sourceBundle;
private @Nullable List<AddonDiscoveryMethod> discoveryMethods;

private AddonInfo(String id, String type, String name, String description, @Nullable String connection,
List<String> countries, @Nullable String configDescriptionURI, @Nullable String serviceId,
@Nullable String sourceBundle) throws IllegalArgumentException {
private AddonInfo(String id, String type, @Nullable String uid, String name, String description,
@Nullable String connection, List<String> countries, @Nullable String configDescriptionURI,
@Nullable String serviceId, @Nullable String sourceBundle,
@Nullable List<AddonDiscoveryMethod> discoveryMethods) throws IllegalArgumentException {
// mandatory fields
if (id.isBlank()) {
throw new IllegalArgumentException("The ID must neither be null nor empty!");
Expand All @@ -64,6 +67,7 @@ private AddonInfo(String id, String type, String name, String description, @Null
}
this.id = id;
this.type = type;
this.uid = uid != null ? uid : type + Addon.ADDON_SEPARATOR + id;
this.name = name;
this.description = description;

Expand All @@ -73,6 +77,7 @@ private AddonInfo(String id, String type, String name, String description, @Null
this.configDescriptionURI = configDescriptionURI;
this.serviceId = Objects.requireNonNullElse(serviceId, type + "." + id);
this.sourceBundle = sourceBundle;
this.discoveryMethods = discoveryMethods;
}

/**
Expand All @@ -82,7 +87,7 @@ private AddonInfo(String id, String type, String name, String description, @Null
*/
@Override
public String getUID() {
return type + Addon.ADDON_SEPARATOR + id;
return uid;
}

/**
Expand Down Expand Up @@ -142,6 +147,11 @@ public List<String> getCountries() {
return countries;
}

public List<AddonDiscoveryMethod> getDiscoveryMethods() {
List<AddonDiscoveryMethod> discoveryMethods = this.discoveryMethods;
return discoveryMethods != null ? discoveryMethods : List.of();
}

public static Builder builder(String id, String type) {
return new Builder(id, type);
}
Expand All @@ -154,13 +164,15 @@ public static class Builder {

private final String id;
private final String type;
private @Nullable String uid;
private String name = "";
private String description = "";
private @Nullable String connection;
private List<String> countries = List.of();
private @Nullable String configDescriptionURI = "";
private @Nullable String serviceId;
private @Nullable String sourceBundle;
private @Nullable List<AddonDiscoveryMethod> discoveryMethods;

private Builder(String id, String type) {
this.id = id;
Expand All @@ -170,13 +182,20 @@ private Builder(String id, String type) {
private Builder(AddonInfo addonInfo) {
this.id = addonInfo.id;
this.type = addonInfo.type;
this.uid = addonInfo.uid;
this.name = addonInfo.name;
this.description = addonInfo.description;
this.connection = addonInfo.connection;
this.countries = addonInfo.countries;
this.configDescriptionURI = addonInfo.configDescriptionURI;
this.serviceId = addonInfo.serviceId;
this.sourceBundle = addonInfo.sourceBundle;
this.discoveryMethods = addonInfo.discoveryMethods;
}

public Builder withUID(@Nullable String uid) {
this.uid = uid;
return this;
}

public Builder withName(String name) {
Expand Down Expand Up @@ -219,15 +238,20 @@ public Builder withSourceBundle(@Nullable String sourceBundle) {
return this;
}

public Builder withDiscoveryMethods(@Nullable List<AddonDiscoveryMethod> discoveryMethods) {
this.discoveryMethods = discoveryMethods;
return this;
}

/**
* Build an {@link AddonInfo} from this builder
*
* @return the add-on info object
* @throws IllegalArgumentException if any of the information in this builder is invalid
*/
public AddonInfo build() throws IllegalArgumentException {
return new AddonInfo(id, type, name, description, connection, countries, configDescriptionURI, serviceId,
sourceBundle);
return new AddonInfo(id, type, uid, name, description, connection, countries, configDescriptionURI,
serviceId, sourceBundle, discoveryMethods);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2023 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.addon;

import java.util.List;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* DTO containing a list of {@code AddonInfo}
*
* @author Andrew Fiddian-Green - Initial contribution
*/
@NonNullByDefault
public class AddonInfoList {
protected @Nullable List<AddonInfo> addons;

public List<AddonInfo> getAddons() {
List<AddonInfo> addons = this.addons;
return addons != null ? addons : List.of();
}

public AddonInfoList setAddons(@Nullable List<AddonInfo> addons) {
this.addons = addons;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@
public interface AddonInfoProvider {

/**
* Returns the binding information for the specified binding ID and locale (language),
* Returns the binding information for the specified binding UID and locale (language),
* or {@code null} if no binding information could be found.
*
* @param id the ID to be looked for (could be null or empty)
* @param uid the UID to be looked for (could be null or empty)
* @param locale the locale to be used for the binding information (could be null)
* @return a localized binding information object (could be null)
*/
@Nullable
AddonInfo getAddonInfo(@Nullable String id, @Nullable Locale locale);
AddonInfo getAddonInfo(@Nullable String uid, @Nullable Locale locale);

/**
* Returns all binding information in the specified locale (language) this provider contains.
Expand Down
Loading