mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Introduce metadata for all add-ons (#3050)
* Introduce addon.xml Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.core.bundles</groupId>
|
||||
<artifactId>org.openhab.core.binding.xml</artifactId>
|
||||
<artifactId>org.openhab.core.addon.xml</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
+11
-11
@@ -19,7 +19,7 @@ import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.binding.xml.internal.BindingInfoXmlResult;
|
||||
import org.openhab.core.addon.xml.internal.AddonInfoXmlResult;
|
||||
import org.openhab.core.config.core.ConfigDescription;
|
||||
import org.openhab.core.thing.xml.internal.ChannelGroupTypeXmlResult;
|
||||
import org.openhab.core.thing.xml.internal.ChannelTypeXmlResult;
|
||||
@@ -35,8 +35,8 @@ import com.google.gson.JsonObject;
|
||||
@NonNullByDefault
|
||||
public class BundleInfo {
|
||||
|
||||
private String bindingId = "";
|
||||
private @Nullable BindingInfoXmlResult bindingInfoXml;
|
||||
private String addonId = "";
|
||||
private @Nullable AddonInfoXmlResult addonInfoXml;
|
||||
private List<ConfigDescription> configDescriptions = new ArrayList<>(5);
|
||||
private List<ChannelGroupTypeXmlResult> channelGroupTypesXml = new ArrayList<>(5);
|
||||
private List<ChannelTypeXmlResult> channelTypesXml = new ArrayList<>(5);
|
||||
@@ -44,20 +44,20 @@ public class BundleInfo {
|
||||
private List<JsonObject> moduleTypesJson = new ArrayList<>(5);
|
||||
private List<JsonObject> ruleTemplateJson = new ArrayList<>(5);
|
||||
|
||||
public String getBindingId() {
|
||||
return bindingId;
|
||||
public String getAddonId() {
|
||||
return addonId;
|
||||
}
|
||||
|
||||
public void setBindingId(String bindingId) {
|
||||
this.bindingId = bindingId;
|
||||
public void setAddonId(String addonId) {
|
||||
this.addonId = addonId;
|
||||
}
|
||||
|
||||
public @Nullable BindingInfoXmlResult getBindingInfoXml() {
|
||||
return bindingInfoXml;
|
||||
public @Nullable AddonInfoXmlResult getAddonInfoXml() {
|
||||
return addonInfoXml;
|
||||
}
|
||||
|
||||
public void setBindingInfoXml(BindingInfoXmlResult bindingInfo) {
|
||||
this.bindingInfoXml = bindingInfo;
|
||||
public void setAddonInfoXml(AddonInfoXmlResult addonInfo) {
|
||||
this.addonInfoXml = addonInfo;
|
||||
}
|
||||
|
||||
public List<ConfigDescription> getConfigDescriptions() {
|
||||
|
||||
+15
-15
@@ -25,8 +25,8 @@ import java.util.stream.StreamSupport;
|
||||
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.binding.xml.internal.BindingInfoReader;
|
||||
import org.openhab.core.binding.xml.internal.BindingInfoXmlResult;
|
||||
import org.openhab.core.addon.xml.internal.AddonInfoReader;
|
||||
import org.openhab.core.addon.xml.internal.AddonInfoXmlResult;
|
||||
import org.openhab.core.config.core.ConfigDescription;
|
||||
import org.openhab.core.config.xml.internal.ConfigDescriptionReader;
|
||||
import org.openhab.core.thing.xml.internal.ChannelGroupTypeXmlResult;
|
||||
@@ -58,7 +58,7 @@ public class BundleInfoReader {
|
||||
|
||||
public BundleInfo readBundleInfo(Path ohinfPath) throws IOException {
|
||||
BundleInfo bundleInfo = new BundleInfo();
|
||||
readBindingInfo(ohinfPath, bundleInfo);
|
||||
readAddonInfo(ohinfPath, bundleInfo);
|
||||
readConfigInfo(ohinfPath, bundleInfo);
|
||||
readThingInfo(ohinfPath, bundleInfo);
|
||||
readModuleTypeInfo(ohinfPath, bundleInfo);
|
||||
@@ -73,16 +73,16 @@ public class BundleInfoReader {
|
||||
: Stream.of();
|
||||
}
|
||||
|
||||
private void readBindingInfo(Path ohinfPath, BundleInfo bundleInfo) throws IOException {
|
||||
BindingInfoReader reader = new BindingInfoReader();
|
||||
try (Stream<Path> xmlPathStream = xmlPathStream(ohinfPath, "binding")) {
|
||||
private void readAddonInfo(Path ohinfPath, BundleInfo bundleInfo) throws IOException {
|
||||
AddonInfoReader reader = new AddonInfoReader();
|
||||
try (Stream<Path> xmlPathStream = xmlPathStream(ohinfPath, "addon")) {
|
||||
xmlPathStream.forEach(path -> {
|
||||
log.info("Reading: " + path);
|
||||
try {
|
||||
BindingInfoXmlResult bindingInfoXml = reader.readFromXML(path.toUri().toURL());
|
||||
AddonInfoXmlResult bindingInfoXml = reader.readFromXML(path.toUri().toURL());
|
||||
if (bindingInfoXml != null) {
|
||||
bundleInfo.setBindingId(bindingInfoXml.getBindingInfo().getUID());
|
||||
bundleInfo.setBindingInfoXml(bindingInfoXml);
|
||||
bundleInfo.setAddonId(bindingInfoXml.addonInfo().getId());
|
||||
bundleInfo.setAddonInfoXml(bindingInfoXml);
|
||||
}
|
||||
} catch (ConversionException | MalformedURLException e) {
|
||||
log.warn("Exception while reading binding info from: " + path, e);
|
||||
@@ -119,20 +119,20 @@ public class BundleInfoReader {
|
||||
if (type instanceof ThingTypeXmlResult) {
|
||||
ThingTypeXmlResult result = (ThingTypeXmlResult) type;
|
||||
bundleInfo.getThingTypesXml().add(result);
|
||||
if (bundleInfo.getBindingId().isBlank()) {
|
||||
bundleInfo.setBindingId(result.getUID().getBindingId());
|
||||
if (bundleInfo.getAddonId().isBlank()) {
|
||||
bundleInfo.setAddonId(result.getUID().getBindingId());
|
||||
}
|
||||
} else if (type instanceof ChannelGroupTypeXmlResult) {
|
||||
ChannelGroupTypeXmlResult result = (ChannelGroupTypeXmlResult) type;
|
||||
bundleInfo.getChannelGroupTypesXml().add(result);
|
||||
if (bundleInfo.getBindingId().isBlank()) {
|
||||
bundleInfo.setBindingId(result.getUID().getBindingId());
|
||||
if (bundleInfo.getAddonId().isBlank()) {
|
||||
bundleInfo.setAddonId(result.getUID().getBindingId());
|
||||
}
|
||||
} else if (type instanceof ChannelTypeXmlResult) {
|
||||
ChannelTypeXmlResult result = (ChannelTypeXmlResult) type;
|
||||
bundleInfo.getChannelTypesXml().add(result);
|
||||
if (bundleInfo.getBindingId().isBlank()) {
|
||||
bundleInfo.setBindingId(result.toChannelType().getUID().getBindingId());
|
||||
if (bundleInfo.getAddonId().isBlank()) {
|
||||
bundleInfo.setAddonId(result.toChannelType().getUID().getBindingId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public class GenerateDefaultTranslationsMojo extends AbstractI18nMojo {
|
||||
}
|
||||
|
||||
private String propertiesFileName(BundleInfo bundleInfo) {
|
||||
String name = bundleInfo.getBindingId();
|
||||
String name = bundleInfo.getAddonId();
|
||||
if (name.isEmpty()) {
|
||||
Optional<ConfigDescription> optional = bundleInfo.getConfigDescriptions().stream().findFirst();
|
||||
if (optional.isPresent()) {
|
||||
|
||||
+29
-29
@@ -25,8 +25,8 @@ import java.util.stream.Stream;
|
||||
import java.util.stream.Stream.Builder;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.binding.BindingInfo;
|
||||
import org.openhab.core.binding.xml.internal.BindingInfoXmlResult;
|
||||
import org.openhab.core.addon.AddonInfo;
|
||||
import org.openhab.core.addon.xml.internal.AddonInfoXmlResult;
|
||||
import org.openhab.core.config.core.ConfigDescription;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameter;
|
||||
import org.openhab.core.config.core.ConfigDescriptionParameterGroup;
|
||||
@@ -54,14 +54,14 @@ public class XmlToTranslationsConverter {
|
||||
private static final Pattern OPTION_ESCAPE_PATTERN = Pattern.compile("([ :=])");
|
||||
|
||||
public Translations convert(BundleInfo bundleInfo) {
|
||||
String bindingId = bundleInfo.getBindingId();
|
||||
return bindingId.isBlank() ? configTranslations(bundleInfo) : bindingTranslations(bundleInfo);
|
||||
String addonId = bundleInfo.getAddonId();
|
||||
return addonId.isBlank() ? configTranslations(bundleInfo) : addonTranslations(bundleInfo);
|
||||
}
|
||||
|
||||
private Translations bindingTranslations(BundleInfo bundleInfo) {
|
||||
private Translations addonTranslations(BundleInfo bundleInfo) {
|
||||
return Translations.translations( //
|
||||
bindingSection(bundleInfo), //
|
||||
bindingConfigSection(bundleInfo), //
|
||||
addonSection(bundleInfo), //
|
||||
addonConfigSection(bundleInfo), //
|
||||
thingTypesSection(bundleInfo), //
|
||||
thingTypesConfigSection(bundleInfo), //
|
||||
channelGroupTypesSection(bundleInfo), //
|
||||
@@ -85,39 +85,39 @@ public class XmlToTranslationsConverter {
|
||||
return Translations.translations(section(groupsBuilder.build()));
|
||||
}
|
||||
|
||||
private TranslationsSection bindingSection(BundleInfo bundleInfo) {
|
||||
String header = "binding";
|
||||
BindingInfoXmlResult bindingInfoXml = bundleInfo.getBindingInfoXml();
|
||||
if (bindingInfoXml == null) {
|
||||
private TranslationsSection addonSection(BundleInfo bundleInfo) {
|
||||
String header = "add-on";
|
||||
AddonInfoXmlResult addonInfoXml = bundleInfo.getAddonInfoXml();
|
||||
if (addonInfoXml == null) {
|
||||
return section(header);
|
||||
}
|
||||
|
||||
BindingInfo bindingInfo = bindingInfoXml.getBindingInfo();
|
||||
String bindingId = bundleInfo.getBindingId();
|
||||
AddonInfo addonInfo = addonInfoXml.addonInfo();
|
||||
String addonId = bundleInfo.getAddonId();
|
||||
|
||||
String keyPrefix = String.format("binding.%s", bindingId);
|
||||
String keyPrefix = String.format("addon.%s", addonId);
|
||||
|
||||
return section(header, group( //
|
||||
entry(String.format("%s.name", keyPrefix), bindingInfo.getName()),
|
||||
entry(String.format("%s.description", keyPrefix), bindingInfo.getDescription()) //
|
||||
entry(String.format("%s.name", keyPrefix), addonInfo.getName()),
|
||||
entry(String.format("%s.description", keyPrefix), addonInfo.getDescription()) //
|
||||
));
|
||||
}
|
||||
|
||||
private TranslationsSection bindingConfigSection(BundleInfo bundleInfo) {
|
||||
String header = "binding config";
|
||||
BindingInfoXmlResult bindingInfoXml = bundleInfo.getBindingInfoXml();
|
||||
if (bindingInfoXml == null) {
|
||||
private TranslationsSection addonConfigSection(BundleInfo bundleInfo) {
|
||||
String header = "add-on config";
|
||||
AddonInfoXmlResult addonInfoXml = bundleInfo.getAddonInfoXml();
|
||||
if (addonInfoXml == null) {
|
||||
return section(header);
|
||||
}
|
||||
ConfigDescription bindingConfig = bindingInfoXml.getConfigDescription();
|
||||
if (bindingConfig == null) {
|
||||
ConfigDescription addonConfig = addonInfoXml.configDescription();
|
||||
if (addonConfig == null) {
|
||||
return section(header);
|
||||
}
|
||||
|
||||
String keyPrefix = String.format("binding.config.%s", bundleInfo.getBindingId());
|
||||
String keyPrefix = String.format("addon.config.%s", bundleInfo.getAddonId());
|
||||
return section(header,
|
||||
Stream.concat(configDescriptionGroupParameters(keyPrefix, bindingConfig.getParameterGroups()),
|
||||
configDescriptionParameters(keyPrefix, bindingConfig.getParameters())));
|
||||
Stream.concat(configDescriptionGroupParameters(keyPrefix, addonConfig.getParameterGroups()),
|
||||
configDescriptionParameters(keyPrefix, addonConfig.getParameters())));
|
||||
}
|
||||
|
||||
private TranslationsSection thingTypesSection(BundleInfo bundleInfo) {
|
||||
@@ -127,8 +127,8 @@ public class XmlToTranslationsConverter {
|
||||
return section(header);
|
||||
}
|
||||
|
||||
String bindingId = bundleInfo.getBindingId();
|
||||
String keyPrefix = String.format("thing-type.%s", bindingId);
|
||||
String addonId = bundleInfo.getAddonId();
|
||||
String keyPrefix = String.format("thing-type.%s", addonId);
|
||||
|
||||
return section(header, thingTypesXml.stream().map(thingTypeXml -> {
|
||||
ThingType thingType = thingTypeXml.toThingType();
|
||||
@@ -216,7 +216,7 @@ public class XmlToTranslationsConverter {
|
||||
return section(header);
|
||||
}
|
||||
|
||||
String keyPrefix = String.format("channel-group-type.%s", bundleInfo.getBindingId());
|
||||
String keyPrefix = String.format("channel-group-type.%s", bundleInfo.getAddonId());
|
||||
|
||||
return section(header, channelGroupTypesXml.stream().map(ChannelGroupTypeXmlResult::toChannelGroupType)
|
||||
.map(channelGroupType -> {
|
||||
@@ -258,7 +258,7 @@ public class XmlToTranslationsConverter {
|
||||
return section(header);
|
||||
}
|
||||
|
||||
String keyPrefix = String.format("channel-type.%s", bundleInfo.getBindingId());
|
||||
String keyPrefix = String.format("channel-type.%s", bundleInfo.getAddonId());
|
||||
|
||||
return section(header, channelTypesXml.stream().map(channelTypeXml -> {
|
||||
ChannelType channelType = channelTypeXml.toChannelType();
|
||||
|
||||
+11
-11
@@ -21,7 +21,7 @@ import java.nio.file.Path;
|
||||
import org.apache.maven.plugin.logging.SystemStreamLog;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.binding.xml.internal.BindingInfoXmlResult;
|
||||
import org.openhab.core.addon.xml.internal.AddonInfoXmlResult;
|
||||
|
||||
/**
|
||||
* Tests {@link BundleInfoReader}.
|
||||
@@ -36,15 +36,15 @@ public class BundleInfoReaderTest {
|
||||
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
|
||||
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmeweather.bundle/OH-INF"));
|
||||
|
||||
BindingInfoXmlResult bindingInfoXml = bundleInfo.getBindingInfoXml();
|
||||
assertThat(bindingInfoXml, is(notNullValue()));
|
||||
if (bindingInfoXml != null) {
|
||||
assertThat(bindingInfoXml.getBindingInfo().getName(), is("ACME Weather Binding"));
|
||||
assertThat(bindingInfoXml.getBindingInfo().getDescription(),
|
||||
AddonInfoXmlResult addonInfoXml = bundleInfo.getAddonInfoXml();
|
||||
assertThat(addonInfoXml, is(notNullValue()));
|
||||
if (addonInfoXml != null) {
|
||||
assertThat(addonInfoXml.addonInfo().getName(), is("ACME Weather Binding"));
|
||||
assertThat(addonInfoXml.addonInfo().getDescription(),
|
||||
is("ACME Weather - Current weather and forecasts in your city."));
|
||||
}
|
||||
|
||||
assertThat(bundleInfo.getBindingId(), is("acmeweather"));
|
||||
assertThat(bundleInfo.getAddonId(), is("acmeweather"));
|
||||
assertThat(bundleInfo.getChannelGroupTypesXml().size(), is(1));
|
||||
assertThat(bundleInfo.getChannelTypesXml().size(), is(2));
|
||||
assertThat(bundleInfo.getConfigDescriptions().size(), is(1));
|
||||
@@ -58,8 +58,8 @@ public class BundleInfoReaderTest {
|
||||
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
|
||||
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/acmetts.bundle/OH-INF"));
|
||||
|
||||
assertThat(bundleInfo.getBindingInfoXml(), is(nullValue()));
|
||||
assertThat(bundleInfo.getBindingId(), is(""));
|
||||
assertThat(bundleInfo.getAddonInfoXml(), is(nullValue()));
|
||||
assertThat(bundleInfo.getAddonId(), is(""));
|
||||
assertThat(bundleInfo.getChannelGroupTypesXml().size(), is(0));
|
||||
assertThat(bundleInfo.getChannelTypesXml().size(), is(0));
|
||||
assertThat(bundleInfo.getConfigDescriptions().size(), is(1));
|
||||
@@ -73,8 +73,8 @@ public class BundleInfoReaderTest {
|
||||
BundleInfoReader reader = new BundleInfoReader(new SystemStreamLog());
|
||||
BundleInfo bundleInfo = reader.readBundleInfo(Path.of("src/test/resources/infoless.bundle/OH-INF"));
|
||||
|
||||
assertThat(bundleInfo.getBindingInfoXml(), is(nullValue()));
|
||||
assertThat(bundleInfo.getBindingId(), is(""));
|
||||
assertThat(bundleInfo.getAddonInfoXml(), is(nullValue()));
|
||||
assertThat(bundleInfo.getAddonId(), is(""));
|
||||
assertThat(bundleInfo.getChannelGroupTypesXml().size(), is(0));
|
||||
assertThat(bundleInfo.getChannelTypesXml().size(), is(0));
|
||||
assertThat(bundleInfo.getConfigDescriptions().size(), is(0));
|
||||
|
||||
+2
-2
@@ -123,7 +123,7 @@ public class GenerateDefaultTranslationsMojoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMissingBindingTranslationsWithoutI18nPath() throws IOException, MojoFailureException {
|
||||
public void addMissingAddonTranslationsWithoutI18nPath() throws IOException, MojoFailureException {
|
||||
copyPath(WEATHER_RESOURCES_PATH, tempPath);
|
||||
deleteTempI18nPath();
|
||||
|
||||
@@ -134,7 +134,7 @@ public class GenerateDefaultTranslationsMojoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMissingBindingTranslationsNoChanges() throws IOException, MojoFailureException {
|
||||
public void addMissingAddonTranslationsNoChanges() throws IOException, MojoFailureException {
|
||||
copyPath(WEATHER_RESOURCES_PATH, tempPath);
|
||||
|
||||
mojo.setGenerationMode(ADD_MISSING_TRANSLATIONS);
|
||||
|
||||
+3
-3
@@ -42,10 +42,10 @@ public class PropertiesToTranslationsConverterTest {
|
||||
assertThat(translations.keysStream().count(), is(44L));
|
||||
|
||||
String lines = translations.linesStream().collect(Collectors.joining(System.lineSeparator()));
|
||||
assertThat(lines, containsString("# binding"));
|
||||
assertThat(lines, containsString("binding.acmeweather.name = ACME Weather Binding"));
|
||||
assertThat(lines, containsString("# add-on"));
|
||||
assertThat(lines, containsString("addon.acmeweather.name = ACME Weather Binding"));
|
||||
assertThat(lines, containsString(
|
||||
"binding.acmeweather.description = ACME Weather - Current weather and forecasts in your city."));
|
||||
"addon.acmeweather.description = ACME Weather - Current weather and forecasts in your city."));
|
||||
assertThat(lines, containsString(
|
||||
"channel-group-type.acmeweather.forecast.channel.minTemperature.description = Minimum forecasted temperature in degrees Celsius (metric) or Fahrenheit (imperial)."));
|
||||
assertThat(lines, containsString(
|
||||
|
||||
+3
-3
@@ -45,10 +45,10 @@ public class XmlToTranslationsConverterTest {
|
||||
assertThat(translations.keysStream().count(), is(34L));
|
||||
|
||||
String lines = translations.linesStream().collect(Collectors.joining(System.lineSeparator()));
|
||||
assertThat(lines, containsString("# binding"));
|
||||
assertThat(lines, containsString("binding.acmeweather.name = ACME Weather Binding"));
|
||||
assertThat(lines, containsString("# add-on"));
|
||||
assertThat(lines, containsString("addon.acmeweather.name = ACME Weather Binding"));
|
||||
assertThat(lines, containsString(
|
||||
"binding.acmeweather.description = ACME Weather - Current weather and forecasts in your city."));
|
||||
"addon.acmeweather.description = ACME Weather - Current weather and forecasts in your city."));
|
||||
assertThat(lines, containsString(
|
||||
"channel-group-type.acmeweather.forecast.channel.minTemperature.description = Minimum forecasted temperature in degrees Celsius (metric) or Fahrenheit (imperial)."));
|
||||
assertThat(lines, containsString(
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="acmeweather" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>ACME Weather Binding</name>
|
||||
<description>ACME Weather - Current weather and forecasts in your city.</description>
|
||||
|
||||
</addon:addon>
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="acmeweather" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>ACME Weather Binding</name>
|
||||
<description>ACME Weather - Current weather and forecasts in your city.</description>
|
||||
|
||||
</binding:binding>
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# binding
|
||||
# add-on
|
||||
|
||||
binding.acmeweather.name = ACME Weather Binding
|
||||
binding.acmeweather.description = ACME Weather - Current weather and forecasts in your city.
|
||||
addon.acmeweather.name = ACME Weather Binding
|
||||
addon.acmeweather.description = ACME Weather - Current weather and forecasts in your city.
|
||||
|
||||
# thing types
|
||||
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# binding
|
||||
# add-on
|
||||
|
||||
binding.acmeweather.name = ACME Weather Binding
|
||||
binding.acmeweather.description = ACME Weather - Current weather and forecasts in your city.
|
||||
addon.acmeweather.name = ACME Weather Binding
|
||||
addon.acmeweather.description = ACME Weather - Current weather and forecasts in your city.
|
||||
|
||||
# thing types
|
||||
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# binding
|
||||
# add-on
|
||||
|
||||
binding.acmeweather.name = ACME Wetter Binding
|
||||
binding.acmeweather.description = ACME Wetter - Aktuelles Wetter und Prognosen in Ihrer Stadt.
|
||||
addon.acmeweather.name = ACME Wetter Binding
|
||||
addon.acmeweather.description = ACME Wetter - Aktuelles Wetter und Prognosen in Ihrer Stadt.
|
||||
|
||||
# thing types
|
||||
|
||||
|
||||
Reference in New Issue
Block a user