mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
mDNS / UPnP discovery internationalization (#2547)
* mDNS / UPnP discovery internationalization Related to #2546 Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
+13
-3
@@ -29,9 +29,12 @@ import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.transport.mdns.MDNSClient;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
@@ -63,10 +66,13 @@ public class MDNSDiscoveryService extends AbstractDiscoveryService implements Se
|
||||
|
||||
@Activate
|
||||
public MDNSDiscoveryService(final @Nullable Map<String, Object> configProperties,
|
||||
final @Reference MDNSClient mdnsClient) {
|
||||
final @Reference MDNSClient mdnsClient, final @Reference TranslationProvider i18nProvider,
|
||||
final @Reference LocaleProvider localeProvider) {
|
||||
super(5);
|
||||
|
||||
this.mdnsClient = mdnsClient;
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
|
||||
super.activate(configProperties);
|
||||
|
||||
@@ -152,7 +158,9 @@ public class MDNSDiscoveryService extends AbstractDiscoveryService implements Se
|
||||
for (ServiceInfo service : services) {
|
||||
DiscoveryResult result = participant.createResult(service);
|
||||
if (result != null) {
|
||||
thingDiscovered(result);
|
||||
final DiscoveryResult resultNew = getLocalizedDiscoveryResult(result,
|
||||
FrameworkUtil.getBundle(participant.getClass()));
|
||||
thingDiscovered(resultNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +221,9 @@ public class MDNSDiscoveryService extends AbstractDiscoveryService implements Se
|
||||
try {
|
||||
DiscoveryResult result = participant.createResult(serviceEvent.getInfo());
|
||||
if (result != null) {
|
||||
thingDiscovered(result);
|
||||
final DiscoveryResult resultNew = getLocalizedDiscoveryResult(result,
|
||||
FrameworkUtil.getBundle(participant.getClass()));
|
||||
thingDiscovered(resultNew);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Participant '{}' threw an exception", participant.getClass().getName(), e);
|
||||
|
||||
+27
-2
@@ -33,11 +33,14 @@ import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.net.CidrAddress;
|
||||
import org.openhab.core.net.NetworkAddressChangeListener;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
@@ -104,6 +107,24 @@ public class UpnpDiscoveryService extends AbstractDiscoveryService
|
||||
networkAddressService.removeNetworkAddressChangeListener(this);
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setI18nProvider(TranslationProvider i18nProvider) {
|
||||
this.i18nProvider = i18nProvider;
|
||||
}
|
||||
|
||||
protected void unsetI18nProvider(TranslationProvider i18nProvider) {
|
||||
this.i18nProvider = null;
|
||||
}
|
||||
|
||||
@Reference
|
||||
protected void setLocaleProvider(LocaleProvider localeProvider) {
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
protected void unsetLocaleProvider(LocaleProvider localeProvider) {
|
||||
this.localeProvider = null;
|
||||
}
|
||||
|
||||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
|
||||
protected void addUpnpDiscoveryParticipant(UpnpDiscoveryParticipant participant) {
|
||||
this.participants.add(participant);
|
||||
@@ -113,7 +134,9 @@ public class UpnpDiscoveryService extends AbstractDiscoveryService
|
||||
for (RemoteDevice device : devices) {
|
||||
DiscoveryResult result = participant.createResult(device);
|
||||
if (result != null) {
|
||||
thingDiscovered(result);
|
||||
final DiscoveryResult resultNew = getLocalizedDiscoveryResult(result,
|
||||
FrameworkUtil.getBundle(participant.getClass()));
|
||||
thingDiscovered(resultNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +192,9 @@ public class UpnpDiscoveryService extends AbstractDiscoveryService
|
||||
if (participant.getRemovalGracePeriodSeconds(device) > 0) {
|
||||
cancelRemovalTask(device.getIdentity().getUdn());
|
||||
}
|
||||
thingDiscovered(result);
|
||||
final DiscoveryResult resultNew = getLocalizedDiscoveryResult(result,
|
||||
FrameworkUtil.getBundle(participant.getClass()));
|
||||
thingDiscovered(resultNew);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Participant '{}' threw an exception", participant.getClass().getName(), e);
|
||||
|
||||
+52
-18
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.core.config.discovery;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -243,24 +244,8 @@ public abstract class AbstractDiscoveryService implements DiscoveryService {
|
||||
* @param discoveryResult Holds the information needed to identify the discovered device.
|
||||
*/
|
||||
protected void thingDiscovered(final DiscoveryResult discoveryResult) {
|
||||
final DiscoveryResult discoveryResultNew;
|
||||
if (i18nProvider != null && localeProvider != null) {
|
||||
Bundle bundle = FrameworkUtil.getBundle(this.getClass());
|
||||
|
||||
String defaultLabel = discoveryResult.getLabel();
|
||||
|
||||
String key = I18nUtil.stripConstantOr(defaultLabel, () -> inferKey(discoveryResult, "label"));
|
||||
|
||||
String label = i18nProvider.getText(bundle, key, defaultLabel, localeProvider.getLocale());
|
||||
|
||||
discoveryResultNew = DiscoveryResultBuilder.create(discoveryResult.getThingUID())
|
||||
.withThingType(discoveryResult.getThingTypeUID()).withBridge(discoveryResult.getBridgeUID())
|
||||
.withProperties(discoveryResult.getProperties())
|
||||
.withRepresentationProperty(discoveryResult.getRepresentationProperty()).withLabel(label)
|
||||
.withTTL(discoveryResult.getTimeToLive()).build();
|
||||
} else {
|
||||
discoveryResultNew = discoveryResult;
|
||||
}
|
||||
final DiscoveryResult discoveryResultNew = getLocalizedDiscoveryResult(discoveryResult,
|
||||
FrameworkUtil.getBundle(this.getClass()));
|
||||
for (DiscoveryListener discoveryListener : discoveryListeners) {
|
||||
try {
|
||||
discoveryListener.thingDiscovered(this, discoveryResultNew);
|
||||
@@ -454,4 +439,53 @@ public abstract class AbstractDiscoveryService implements DiscoveryService {
|
||||
private String inferKey(DiscoveryResult discoveryResult, String lastSegment) {
|
||||
return "discovery." + discoveryResult.getThingUID().getAsString().replaceAll(":", ".") + "." + lastSegment;
|
||||
}
|
||||
|
||||
protected DiscoveryResult getLocalizedDiscoveryResult(final DiscoveryResult discoveryResult,
|
||||
@Nullable Bundle bundle) {
|
||||
if (i18nProvider != null && localeProvider != null) {
|
||||
String currentLabel = discoveryResult.getLabel();
|
||||
|
||||
String key = I18nUtil.stripConstantOr(currentLabel, () -> inferKey(discoveryResult, "label"));
|
||||
|
||||
ParsedKey parsedKey = new ParsedKey(key);
|
||||
|
||||
String label = i18nProvider.getText(bundle, parsedKey.key, currentLabel, localeProvider.getLocale(),
|
||||
parsedKey.args);
|
||||
|
||||
if (currentLabel.equals(label)) {
|
||||
return discoveryResult;
|
||||
} else {
|
||||
return DiscoveryResultBuilder.create(discoveryResult.getThingUID())
|
||||
.withThingType(discoveryResult.getThingTypeUID()).withBridge(discoveryResult.getBridgeUID())
|
||||
.withProperties(discoveryResult.getProperties())
|
||||
.withRepresentationProperty(discoveryResult.getRepresentationProperty()).withLabel(label)
|
||||
.withTTL(discoveryResult.getTimeToLive()).build();
|
||||
}
|
||||
} else {
|
||||
return discoveryResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to parse the key with parameters into the key and optional arguments.
|
||||
*/
|
||||
private final class ParsedKey {
|
||||
|
||||
private static final int LIMIT = 2;
|
||||
|
||||
private final String key;
|
||||
private final Object @Nullable [] args;
|
||||
|
||||
private ParsedKey(String label) {
|
||||
String[] parts = label.split("\\s+", LIMIT);
|
||||
this.key = parts[0];
|
||||
|
||||
if (parts.length == 1) {
|
||||
this.args = null;
|
||||
} else {
|
||||
this.args = Arrays.stream(parts[1].replaceAll("\\[|\\]|\"", "").split(","))
|
||||
.filter(s -> s != null && !s.isBlank()).map(s -> s.trim()).toArray(Object[]::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 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.config.discovery;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsMapContaining.hasEntry;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
/**
|
||||
* Tests the {@link DiscoveryResultBuilder}.
|
||||
*
|
||||
* @author Laurent Garnier - Initial contribution
|
||||
*/
|
||||
public class AbstractDiscoveryServiceTest implements DiscoveryListener {
|
||||
|
||||
private static final String BINDING_ID = "bindingId";
|
||||
private static final ThingUID BRIDGE_UID = new ThingUID(new ThingTypeUID(BINDING_ID, "bridgeTypeId"), "bridgeId");
|
||||
private static final ThingTypeUID THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "thingTypeId");
|
||||
private static final ThingUID THING_UID1 = new ThingUID(THING_TYPE_UID, BRIDGE_UID, "thingId1");
|
||||
private static final ThingUID THING_UID2 = new ThingUID(THING_TYPE_UID, "thingId2");
|
||||
private static final ThingUID THING_UID3 = new ThingUID(THING_TYPE_UID, BRIDGE_UID, "thingId3");
|
||||
private static final ThingUID THING_UID4 = new ThingUID(THING_TYPE_UID, "thingId4");
|
||||
private static final String KEY1 = "key1";
|
||||
private static final String KEY2 = "key2";
|
||||
private static final String VALUE1 = "value1";
|
||||
private static final String VALUE2 = "value2";
|
||||
private final Map<String, Object> properties = Map.of(KEY1, VALUE1, KEY2, VALUE2);
|
||||
private static final String DISCOVERY_THING2_INFERED_KEY = "discovery."
|
||||
+ THING_UID2.getAsString().replaceAll(":", ".") + ".label";
|
||||
private static final String DISCOVERY_THING4_INFERED_KEY = "discovery."
|
||||
+ THING_UID4.getAsString().replaceAll(":", ".") + ".label";
|
||||
private static final String DISCOVERY_LABEL = "Result Test";
|
||||
private static final String DISCOVERY_LABEL_KEY1 = "@text/test";
|
||||
private static final String DISCOVERY_LABEL_KEY2 = "@text/test2 [ \"50\", \"number\" ]";
|
||||
private static final String PROPERTY_LABEL1 = "Label from property (text key)";
|
||||
private static final String PROPERTY_LABEL2 = "Label from property (infered key)";
|
||||
private static final String PROPERTY_LABEL3 = "Label from property (parameters 50 and number)";
|
||||
|
||||
private TranslationProvider i18nProvider = new TranslationProvider() {
|
||||
@Override
|
||||
public @Nullable String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
|
||||
@Nullable Locale locale, @Nullable Object... arguments) {
|
||||
if (Locale.ENGLISH.equals(locale)) {
|
||||
if ("test".equals(key)) {
|
||||
return PROPERTY_LABEL1;
|
||||
} else if ("test2".equals(key) && arguments != null && arguments.length == 2
|
||||
&& "50".equals(arguments[0]) && "number".equals(arguments[1])) {
|
||||
return PROPERTY_LABEL3;
|
||||
} else if (DISCOVERY_THING2_INFERED_KEY.equals(key) || DISCOVERY_THING4_INFERED_KEY.equals(key)) {
|
||||
return PROPERTY_LABEL2;
|
||||
}
|
||||
}
|
||||
return defaultText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getText(@Nullable Bundle bundle, @Nullable String key, @Nullable String defaultText,
|
||||
@Nullable Locale locale) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private LocaleProvider localeProvider = new LocaleProvider() {
|
||||
@Override
|
||||
public @NonNull Locale getLocale() {
|
||||
return Locale.ENGLISH;
|
||||
}
|
||||
};
|
||||
|
||||
class TestDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
public TestDiscoveryService(TranslationProvider i18nProvider, LocaleProvider localeProvider)
|
||||
throws IllegalArgumentException {
|
||||
super(Set.of(THING_TYPE_UID), 1, false);
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
// Discovered thing 1 has a hard coded label and no key based on its thing UID defined in the properties
|
||||
// file => the hard coded label should be considered
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(THING_UID1).withThingType(THING_TYPE_UID)
|
||||
.withProperties(properties).withRepresentationProperty(KEY1).withBridge(BRIDGE_UID)
|
||||
.withLabel(DISCOVERY_LABEL).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
|
||||
// Discovered thing 2 has a hard coded label but with a key based on its thing UID defined in the properties
|
||||
// file => the value from the properties file should be considered
|
||||
discoveryResult = DiscoveryResultBuilder.create(THING_UID2).withThingType(THING_TYPE_UID)
|
||||
.withProperties(properties).withRepresentationProperty(KEY1).withLabel(DISCOVERY_LABEL).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
|
||||
// Discovered thing 3 has a label referencing an entry in the properties file and no key based on its thing
|
||||
// UID defined in the properties file => the value from the properties file should be considered
|
||||
discoveryResult = DiscoveryResultBuilder.create(THING_UID3).withThingType(THING_TYPE_UID)
|
||||
.withProperties(properties).withRepresentationProperty(KEY1).withBridge(BRIDGE_UID)
|
||||
.withLabel(DISCOVERY_LABEL_KEY1).build();
|
||||
thingDiscovered(discoveryResult);
|
||||
|
||||
// Discovered thing 4 has a label referencing an entry in the properties file and a key based on its thing
|
||||
// UID defined in the properties file => the value from the properties file (the one referenced by the
|
||||
// label) should be considered
|
||||
discoveryResult = DiscoveryResultBuilder.create(THING_UID4).withThingType(THING_TYPE_UID)
|
||||
.withProperties(properties).withRepresentationProperty(KEY1).withLabel(DISCOVERY_LABEL_KEY2)
|
||||
.build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
};
|
||||
|
||||
private TestDiscoveryService discoveryService;
|
||||
|
||||
@Override
|
||||
public void thingDiscovered(@NonNull DiscoveryService source, @NonNull DiscoveryResult result) {
|
||||
assertThat(result.getThingTypeUID(), is(THING_TYPE_UID));
|
||||
assertThat(result.getBindingId(), is(BINDING_ID));
|
||||
assertThat(result.getProperties().size(), is(2));
|
||||
assertThat(result.getProperties(), hasEntry(KEY1, VALUE1));
|
||||
assertThat(result.getProperties(), hasEntry(KEY2, VALUE2));
|
||||
assertThat(result.getRepresentationProperty(), is(KEY1));
|
||||
assertThat(result.getTimeToLive(), is(DiscoveryResult.TTL_UNLIMITED));
|
||||
|
||||
if (THING_UID1.equals(result.getThingUID())) {
|
||||
assertThat(result.getBridgeUID(), is(BRIDGE_UID));
|
||||
assertThat(result.getLabel(), is(DISCOVERY_LABEL));
|
||||
} else if (THING_UID2.equals(result.getThingUID())) {
|
||||
assertNull(result.getBridgeUID());
|
||||
assertThat(result.getLabel(), is(PROPERTY_LABEL2));
|
||||
} else if (THING_UID3.equals(result.getThingUID())) {
|
||||
assertThat(result.getBridgeUID(), is(BRIDGE_UID));
|
||||
assertThat(result.getLabel(), is(PROPERTY_LABEL1));
|
||||
} else if (THING_UID4.equals(result.getThingUID())) {
|
||||
assertNull(result.getBridgeUID());
|
||||
assertThat(result.getLabel(), is(PROPERTY_LABEL3));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thingRemoved(@NonNull DiscoveryService source, @NonNull ThingUID thingUID) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<@NonNull ThingUID> removeOlderResults(@NonNull DiscoveryService source, long timestamp,
|
||||
@Nullable Collection<@NonNull ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiscoveryResults() {
|
||||
discoveryService = new TestDiscoveryService(i18nProvider, localeProvider);
|
||||
discoveryService.addDiscoveryListener(this);
|
||||
discoveryService.startScan();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user