Ephemeris service (#506)

Also-by: Gaël L'hopital <gael@lhopital.org>
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2019-06-11 10:20:04 +02:00
committed by Markus Rathgeb
parent 7f5ceefa26
commit d0dfbe3f44
22 changed files with 592 additions and 34 deletions
+8
View File
@@ -270,6 +270,14 @@
<scope>compile</scope>
</dependency>
<!-- jollyday -->
<dependency>
<groupId>de.jollyday</groupId>
<artifactId>jollyday</artifactId>
<version>0.5.7</version>
<scope>compile</scope>
</dependency>
<!-- <dependency> <groupId></groupId> <artifactId></artifactId> <version></version> <scope>compile</scope> </dependency> -->
</dependencies>
+6
View File
@@ -63,6 +63,12 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.ephemeris</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.voice</artifactId>
+7
View File
@@ -860,6 +860,13 @@
<scope>compile</scope>
</dependency>
<!-- jollyday -->
<dependency>
<groupId>de.jollyday</groupId>
<artifactId>jollyday</artifactId>
<version>0.5.7</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.core.ephemeris</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
+14
View File
@@ -0,0 +1,14 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-core
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.reactor.bundles</artifactId>
<version>2.5.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.core.ephemeris</artifactId>
<name>openHAB Core :: Bundles :: Ephemeris</name>
<dependencies>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.config.core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2019 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.eclipse.smarthome.core.ephemeris;
import java.io.FileNotFoundException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* This service provides functionality around days of the year and is the central
* service to be used directly by others.
*
* @author Gaël L'hopital - Initial contribution and API
*/
@NonNullByDefault
public interface EphemerisManager {
/**
* Tests given day (related to today) status against configured weekend days
*
* @param offset Today +/- offset days (+1 = tomorrow, -1 = yesterday)
* @return whether the day is on weekend
*/
boolean isWeekend(int offset);
/**
* Tests given day (related to today) status against configured dayset
*
* @param daysetName name of the requested dayset, witout prefix
* @param offset Today +/- offset days (+1 = tomorrow, -1 = yesterday)
* @return whether the day is on weekend
*/
boolean isInDayset(String daysetName, int offset);
/**
* Tests given day status against official bank holidays
*
* @param offset Today +/- offset days (+1 = tomorrow, -1 = yesterday)
* @return whether the day is bank holiday or not
*/
boolean isBankHoliday(int offset);
/**
* Get given day bank holiday name
*
* @param offset Today +/- offset days (+1 = tomorrow, -1 = yesterday)
* @return name of the bank holiday or null if no bank holiday
*/
@Nullable
String getBankHolidayName(int offset);
/**
* Get given day name from given userfile
*
* @param filename absolute or relative path to the file on local file system
* @param offset Today +/- offset days (+1 = tomorrow, -1 = yesterday)
* @return name of the day or null if no corresponding entry
* @throws FileNotFoundException if given file does not exist
*/
@Nullable
String getBankHolidayName(int offset, String filename) throws FileNotFoundException;
}
@@ -0,0 +1,226 @@
/**
* Copyright (c) 2010-2019 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.eclipse.smarthome.core.ephemeris.internal;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.TextStyle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.config.core.ConfigOptionProvider;
import org.eclipse.smarthome.config.core.ConfigurableService;
import org.eclipse.smarthome.config.core.ParameterOption;
import org.eclipse.smarthome.core.ephemeris.EphemerisManager;
import org.eclipse.smarthome.core.i18n.LocaleProvider;
import org.osgi.framework.Constants;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.jollyday.Holiday;
import de.jollyday.HolidayManager;
import de.jollyday.ManagerParameter;
import de.jollyday.ManagerParameters;
/**
* This service provides functionality around ephemeris services and is the central service to be used directly by
* others.
*
* @author Gaël L'hopital - Initial contribution and API
*/
@Component(name = "org.openhab.ephemeris", property = { Constants.SERVICE_PID + "=org.openhab.ephemeris",
ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=system",
ConfigurableService.SERVICE_PROPERTY_LABEL + "=Ephemeris",
ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=" + EphemerisManagerImpl.CONFIG_URI })
@NonNullByDefault
public class EphemerisManagerImpl implements EphemerisManager, ConfigOptionProvider {
private final Logger logger = LoggerFactory.getLogger(EphemerisManagerImpl.class);
// constants for the configuration properties
protected static final String CONFIG_URI = "system:ephemeris";
private static final String CONFIG_DAYSET_PREFIX = "dayset-";
private static final String CONFIG_DAYSET_WEEKEND = "weekend";
private static final String CONFIG_COUNTRY = "country";
private static final String CONFIG_REGION = "region";
private static final String CONFIG_CITY = "city";
private final Map<String, Set<DayOfWeek>> daysets = new HashMap<>();
private final Map<Object, HolidayManager> holidayManagers = new HashMap<>();
private final List<String> countryParameters = new ArrayList<>();
private final LocaleProvider localeProvider;
private @NonNullByDefault({}) String country;
@Activate
public EphemerisManagerImpl(final @Reference LocaleProvider localeProvider) {
this.localeProvider = localeProvider;
}
@Activate
protected void activate(Map<String, Object> config) {
modified(config);
}
@Modified
protected void modified(Map<String, Object> config) {
config.entrySet().stream().filter(e -> e.getKey().startsWith(CONFIG_DAYSET_PREFIX)).forEach(e -> {
String[] setDefinition = e.getValue().toString().toUpperCase().split(",");
String[] setNameParts = e.getKey().split("-");
if (setDefinition.length > 0 && setNameParts.length > 1) {
Set<DayOfWeek> dayset = new HashSet<>();
Stream.of(setDefinition).forEach(day -> {
dayset.add(DayOfWeek.valueOf(day));
});
daysets.put(setNameParts[1], dayset);
} else {
logger.warn("Erroneous dayset definition {} : {}", e.getKey(), e.getValue());
}
});
if (config.containsKey(CONFIG_COUNTRY)) {
country = config.get(CONFIG_COUNTRY).toString();
} else {
country = localeProvider.getLocale().getCountry();
logger.debug("Using system default country '{}' ", country);
}
if (config.containsKey(CONFIG_REGION)) {
countryParameters.add(config.get(CONFIG_REGION).toString());
}
if (config.containsKey(CONFIG_CITY)) {
countryParameters.add(config.get(CONFIG_CITY).toString());
}
}
@Override
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable Locale locale) {
List<ParameterOption> options = null;
if (CONFIG_URI.equals(uri.toString())) {
Locale nullSafeLocale = locale == null ? localeProvider.getLocale() : locale;
options = new ArrayList<>();
for (DayOfWeek day : DayOfWeek.values()) {
ParameterOption option = new ParameterOption(day.name(),
day.getDisplayName(TextStyle.FULL, nullSafeLocale));
options.add(option);
}
}
return options;
}
private HolidayManager getHolidayManager(Object managerKey) {
if (!holidayManagers.containsKey(managerKey)) {
ManagerParameter parameters = managerKey.getClass() == String.class
? ManagerParameters.create((String) managerKey)
: ManagerParameters.create((URL) managerKey);
holidayManagers.put(managerKey, HolidayManager.getInstance(parameters));
}
return holidayManagers.get(managerKey);
}
private Optional<Holiday> getHoliday(ZonedDateTime date) {
HolidayManager manager = getHolidayManager(country);
LocalDate localDate = date.toLocalDate();
Set<Holiday> holidays = manager.getHolidays(localDate, localDate, countryParameters.toArray(new String[0]));
return holidays.isEmpty() ? Optional.empty() : Optional.of(holidays.iterator().next());
}
private boolean isBankHoliday(ZonedDateTime date) {
Optional<Holiday> holiday = getHoliday(date);
return holiday.isPresent();
}
@Override
public boolean isBankHoliday(int offset) {
return isBankHoliday(ZonedDateTime.now().plusDays(offset));
}
@Override
public boolean isWeekend(int offset) {
return isWeekend(ZonedDateTime.now().plusDays(offset));
}
private boolean isWeekend(ZonedDateTime date) {
return isInDayset(CONFIG_DAYSET_WEEKEND, date);
}
@Override
public boolean isInDayset(String daysetName, int offset) {
return isInDayset(daysetName, ZonedDateTime.now().plusDays(offset));
}
private boolean isInDayset(String daysetName, ZonedDateTime date) {
if (daysets.containsKey(daysetName)) {
DayOfWeek dow = date.getDayOfWeek();
return daysets.get(daysetName).contains(dow);
} else {
logger.warn("This dayset is not configured : {}", daysetName);
return false;
}
}
private String getBankHolidayName(ZonedDateTime date) {
Optional<Holiday> holiday = getHoliday(date);
return holiday.map(p -> p.getDescription(localeProvider.getLocale())).orElse(null);
}
@Override
public @Nullable String getBankHolidayName(int offset) {
return getBankHolidayName(ZonedDateTime.now().plusDays(offset));
}
private Optional<String> getHolidayUserFile(ZonedDateTime date, String filename) throws FileNotFoundException {
if (Files.exists(Paths.get(filename))) {
try {
URL url = new URL("file:" + filename);
Set<Holiday> days = getHolidayManager(url).getHolidays(date.toLocalDate(), date.toLocalDate());
return days.isEmpty() ? Optional.empty() : Optional.of(days.iterator().next().getPropertiesKey());
} catch (MalformedURLException e) {
throw new FileNotFoundException(e.getMessage());
}
} else {
throw new FileNotFoundException();
}
}
@Override
public @Nullable String getBankHolidayName(int offset, String filename) throws FileNotFoundException {
Optional<String> holiday = getHolidayUserFile(ZonedDateTime.now().plusDays(offset), filename);
return holiday.isPresent() ? holiday.get() : null;
}
}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="http://eclipse.org/smarthome/schemas/config-description/v1.0.0"
xsi:schemaLocation="http://eclipse.org/smarthome/schemas/config-description/v1.0.0 http://eclipse.org/smarthome/schemas/config-description-1.0.0.xsd">
<config-description uri="system:ephemeris">
<parameter name="weekendDays" type="text" required="true" multiple="true">
<label>Weekend Days</label>
<description>List of usual non workable days.</description>
</parameter>
</config-description>
</config-description:config-descriptions>
@@ -24,6 +24,11 @@
<artifactId>org.openhab.core.thing</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.ephemeris</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.core.bundles</groupId>
<artifactId>org.openhab.core.transform</artifactId>
@@ -0,0 +1,86 @@
/**
* Copyright (c) 2010-2019 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.eclipse.smarthome.model.script.actions;
import java.io.FileNotFoundException;
import org.eclipse.smarthome.model.script.engine.action.ActionDoc;
import org.eclipse.smarthome.model.script.internal.engine.action.EphemerisActionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The static methods of this class are made available as functions in the scripts.
* This allows a script to use ephemeris features.
*
* @author Gaël L'hopital - Initial contribution and API
*/
public class Ephemeris {
private final static Logger logger = LoggerFactory.getLogger(Ephemeris.class);
@ActionDoc(text = "checks if today is a weekend day")
public static boolean isWeekend() {
return isWeekend(0);
}
@ActionDoc(text = "checks if today plus or minus a given offset is a weekend day")
public static boolean isWeekend(int offset) {
return EphemerisActionService.ephemerisManager.isWeekend(offset);
}
@ActionDoc(text = "checks if today is defined in a given dayset")
public static boolean isInDayset(String daysetName) {
return isInDayset(daysetName, 0);
}
@ActionDoc(text = "checks if today plus or minus a given offset is defined in a given dayset")
public static boolean isInDayset(String daysetName, int offset) {
return EphemerisActionService.ephemerisManager.isInDayset(daysetName, offset);
}
@ActionDoc(text = "checks if today is bank holiday")
public static boolean isBankHoliday() {
return isBankHoliday(0);
}
@ActionDoc(text = "checks if today plus or minus a given offset is bank holiday")
public static boolean isBankHoliday(int offset) {
return EphemerisActionService.ephemerisManager.isBankHoliday(offset);
}
@ActionDoc(text = "get todays bank holiday name")
public static String getBankHolidayName() {
return getBankHolidayName(0);
}
@ActionDoc(text = "get bank holiday name for today plus or minus a given offset")
public static String getBankHolidayName(int offset) {
return EphemerisActionService.ephemerisManager.getBankHolidayName(offset);
}
@ActionDoc(text = "get holiday for today from a given userfile")
public static String getBankHolidayName(String filename) {
return getBankHolidayName(0, filename);
}
@ActionDoc(text = "get holiday for today plus or minus an offset from a given userfile")
public static String getBankHolidayName(int offset, String filename) {
try {
return EphemerisActionService.ephemerisManager.getBankHolidayName(offset, filename);
} catch (FileNotFoundException e) {
logger.error("Error reading holiday user file {} : {}", filename, e.getMessage());
return null;
}
}
}
@@ -21,14 +21,16 @@ public interface ActionService {
/**
* returns the FQCN of the action class.
*
*
* @return the FQCN of the action class
*/
String getActionClassName();
default String getActionClassName() {
return getActionClass().getCanonicalName();
}
/**
* Returns the action class itself
*
*
* @return the action class
*/
Class<?> getActionClass();
@@ -23,11 +23,6 @@ public class AudioActionService implements ActionService {
public static AudioManager audioManager;
@Override
public String getActionClassName() {
return Audio.class.getCanonicalName();
}
@Override
public Class<?> getActionClass() {
return Audio.class;
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2019 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.eclipse.smarthome.model.script.internal.engine.action;
import org.eclipse.smarthome.core.ephemeris.EphemerisManager;
import org.eclipse.smarthome.model.script.actions.Ephemeris;
import org.eclipse.smarthome.model.script.engine.action.ActionService;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* This class registers an OSGi service for the ephemeris action.
*
* @author Gaël L'hopital - Initial contribution and API
*/
@Component
public class EphemerisActionService implements ActionService {
public static EphemerisManager ephemerisManager;
@Override
public Class<?> getActionClass() {
return Ephemeris.class;
}
@Reference
protected void setEphemerisManager(EphemerisManager ephemerisManager) {
EphemerisActionService.ephemerisManager = ephemerisManager;
}
protected void unsetEphemerisManager(EphemerisManager ephemerisManager) {
EphemerisActionService.ephemerisManager = null;
}
}
@@ -27,11 +27,6 @@ public class PersistenceActionService implements ActionService {
public PersistenceActionService() {
}
@Override
public String getActionClassName() {
return PersistenceExtensions.class.getCanonicalName();
}
@Override
public Class<?> getActionClass() {
return PersistenceExtensions.class;
@@ -42,11 +42,6 @@ public class ThingActionService implements ActionService {
private static ThingRegistry thingRegistry;
private static final Map<String, ThingActions> thingActionsMap = new HashMap<>();
@Override
public String getActionClassName() {
return Things.class.getCanonicalName();
}
@Override
public Class<?> getActionClass() {
return Things.class;
@@ -27,11 +27,6 @@ public class TransformationActionService implements ActionService {
public TransformationActionService() {
}
@Override
public String getActionClassName() {
return Transformation.class.getCanonicalName();
}
@Override
public Class<?> getActionClass() {
return Transformation.class;
@@ -28,11 +28,6 @@ public class VoiceActionService implements ActionService {
public static VoiceManager voiceManager;
@Override
public String getActionClassName() {
return Voice.class.getCanonicalName();
}
@Override
public Class<?> getActionClass() {
return Voice.class;
+1
View File
@@ -35,6 +35,7 @@
<module>org.openhab.core</module>
<module>org.openhab.core.audio</module>
<module>org.openhab.core.binding.xml</module>
<module>org.openhab.core.ephemeris</module>
<module>org.openhab.core.extension.sample</module>
<module>org.openhab.core.id</module>
<module>org.openhab.core.persistence</module>
@@ -114,6 +114,17 @@
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.config.discovery.usbserial.linuxsysfs/${project.version}</bundle>
</feature>
<feature name="openhab-core-ephemeris" version="${project.version}">
<requirement>openhab.tp;filter:="(feature=jollyday)"</requirement>
<feature dependency="true">openhab.tp-jollyday</feature>
<feature>openhab-core-base</feature>
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.ephemeris/${project.version}</bundle>
<config name="org.openhab.ephemeris">
dayset-weekend=Saturday,Sunday
dayset-school=Monday,Tuesday,Wednesday,Thursday,Friday
</config>
</feature>
<feature name="openhab-core-io-console-karaf" description="Karaf Console support for SmartHome stuff" version="${project.version}">
<feature>openhab-core-base</feature>
<feature>shell</feature>
@@ -310,6 +321,7 @@
<feature name="openhab-core-model-script" version="${project.version}">
<feature>openhab-core-base</feature>
<feature>openhab-core-ephemeris</feature>
<feature>openhab-core-model-persistence</feature>
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.model.script/${project.version}</bundle>
<bundle>mvn:org.openhab.core.bundles/org.openhab.core.model.script.runtime/${project.version}</bundle>
@@ -405,6 +417,7 @@
<requirement>openhab.tp;filter:="(feature=commons-net)"</requirement>
<feature dependency="true">openhab.tp-commons-net</feature>
<feature>openhab-core-base</feature>
<feature>openhab-core-ephemeris</feature>
<feature>openhab-core-io-console-karaf</feature>
<feature>openhab-core-io-rest-sitemap</feature>
<feature>openhab-core-io-rest-voice</feature>
@@ -109,6 +109,17 @@
<bundle dependency="true">mvn:org.jvnet.mimepull/mimepull/1.9.6</bundle>
</feature>
<feature name="openhab.tp-jollyday" description="Jollyday library" version="${project.version}">
<capability>openhab.tp;feature=jollyday;version=0.5.7</capability>
<bundle>mvn:org.threeten/threeten-extra/1.4</bundle>
<bundle>mvn:de.jollyday/jollyday/0.5.7</bundle>
</feature>
<feature name="openhab.tp-jmdns" description="An implementation of multi-cast DNS in Java." version="${project.version}">
<capability>openhab.tp;feature=jmdns;version=3.5.5</capability>
<bundle>mvn:org.jmdns/jmdns/3.5.5</bundle>
</feature>
<feature name="openhab.tp-jupnp" description="UPnP/DLNA library for Java" version="${project.version}">
<capability>openhab.tp;feature=jupnp;version=2.5.2</capability>
<feature dependency="true">http</feature>
@@ -117,12 +128,6 @@
<bundle>mvn:org.jupnp/org.jupnp/2.5.2</bundle>
</feature>
<feature name="openhab.tp-jmdns" description="An implementation of multi-cast DNS in Java." version="${project.version}">
<capability>openhab.tp;feature=jmdns;version=3.5.5</capability>
<bundle>mvn:org.jmdns/jmdns/3.5.5</bundle>
</feature>
<feature name="openhab.tp-lsp4j" description="Eclipse LSP4J" version="${project.version}">
<capability>openhab.tp;feature=lsp4j;version=0.6.0</capability>
<bundle>mvn:org.eclipse.lsp4j/org.eclipse.lsp4j/0.6.0</bundle>