mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[tr064] Improvements of Phonebook Profile (#9054)
* Improvements of Phonebook Profile Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
parent
4cdbe65662
commit
0c30d90757
@ -26,7 +26,7 @@ import org.osgi.service.component.annotations.Component;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class AvmFritzTlsTrustManagerProvider implements TlsTrustManagerProvider {
|
public class AVMFritzTlsTrustManagerProvider implements TlsTrustManagerProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHostName() {
|
public String getHostName() {
|
@ -12,18 +12,27 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.tr064.internal.phonebook;
|
package org.openhab.binding.tr064.internal.phonebook;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.library.CoreItemFactory;
|
||||||
|
import org.openhab.core.library.types.StringListType;
|
||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
import org.openhab.core.thing.profiles.*;
|
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileContext;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileType;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileTypeBuilder;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
||||||
|
import org.openhab.core.thing.profiles.StateProfile;
|
||||||
import org.openhab.core.transform.TransformationService;
|
import org.openhab.core.transform.TransformationService;
|
||||||
import org.openhab.core.types.Command;
|
import org.openhab.core.types.Command;
|
||||||
import org.openhab.core.types.State;
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
import org.openhab.core.util.UIDUtils;
|
import org.openhab.core.util.UIDUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -37,11 +46,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
public class PhonebookProfile implements StateProfile {
|
public class PhonebookProfile implements StateProfile {
|
||||||
public static final ProfileTypeUID PHONEBOOK_PROFILE_TYPE_UID = new ProfileTypeUID(
|
public static final ProfileTypeUID PHONEBOOK_PROFILE_TYPE_UID = new ProfileTypeUID(
|
||||||
TransformationService.TRANSFORM_PROFILE_SCOPE, "PHONEBOOK");
|
TransformationService.TRANSFORM_PROFILE_SCOPE, "PHONEBOOK");
|
||||||
public static final ProfileType PHONEBOOK_PROFILE_TYPE = ProfileTypeBuilder
|
public static final ProfileType PHONEBOOK_PROFILE_TYPE = ProfileTypeBuilder //
|
||||||
.newState(PhonebookProfile.PHONEBOOK_PROFILE_TYPE_UID, "Phonebook").build();
|
.newState(PHONEBOOK_PROFILE_TYPE_UID, "Phonebook") //
|
||||||
|
.withSupportedItemTypesOfChannel(CoreItemFactory.CALL, CoreItemFactory.STRING) //
|
||||||
|
.withSupportedItemTypes(CoreItemFactory.STRING) //
|
||||||
|
.build();
|
||||||
|
|
||||||
public static final String PHONEBOOK_PARAM = "phonebook";
|
public static final String PHONEBOOK_PARAM = "phonebook";
|
||||||
private static final String MATCH_COUNT_PARAM = "matchCount";
|
public static final String MATCH_COUNT_PARAM = "matchCount";
|
||||||
|
public static final String PHONE_NUMBER_INDEX_PARAM = "phoneNumberIndex";
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(PhonebookProfile.class);
|
private final Logger logger = LoggerFactory.getLogger(PhonebookProfile.class);
|
||||||
|
|
||||||
@ -51,6 +64,7 @@ public class PhonebookProfile implements StateProfile {
|
|||||||
private final @Nullable ThingUID thingUID;
|
private final @Nullable ThingUID thingUID;
|
||||||
private final Map<ThingUID, PhonebookProvider> phonebookProviders;
|
private final Map<ThingUID, PhonebookProvider> phonebookProviders;
|
||||||
private final int matchCount;
|
private final int matchCount;
|
||||||
|
private final int phoneNumberIndex;
|
||||||
|
|
||||||
public PhonebookProfile(ProfileCallback callback, ProfileContext context,
|
public PhonebookProfile(ProfileCallback callback, ProfileContext context,
|
||||||
Map<ThingUID, PhonebookProvider> phonebookProviders) {
|
Map<ThingUID, PhonebookProvider> phonebookProviders) {
|
||||||
@ -60,32 +74,44 @@ public class PhonebookProfile implements StateProfile {
|
|||||||
Configuration configuration = context.getConfiguration();
|
Configuration configuration = context.getConfiguration();
|
||||||
Object phonebookParam = configuration.get(PHONEBOOK_PARAM);
|
Object phonebookParam = configuration.get(PHONEBOOK_PARAM);
|
||||||
Object matchCountParam = configuration.get(MATCH_COUNT_PARAM);
|
Object matchCountParam = configuration.get(MATCH_COUNT_PARAM);
|
||||||
|
Object phoneNumberIndexParam = configuration.get(PHONE_NUMBER_INDEX_PARAM);
|
||||||
|
|
||||||
logger.debug("Profile configured with '{}'='{}', '{}'='{}'", PHONEBOOK_PARAM, phonebookParam, MATCH_COUNT_PARAM,
|
logger.debug("Profile configured with '{}'='{}', '{}'='{}', '{}'='{}'", PHONEBOOK_PARAM, phonebookParam,
|
||||||
matchCountParam);
|
MATCH_COUNT_PARAM, matchCountParam, PHONE_NUMBER_INDEX_PARAM, phoneNumberIndexParam);
|
||||||
|
|
||||||
ThingUID thingUID;
|
ThingUID thingUID;
|
||||||
String phonebookName = null;
|
String phonebookName = null;
|
||||||
int matchCount = 0;
|
int matchCount = 0;
|
||||||
|
int phoneNumberIndex = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!(phonebookParam instanceof String)
|
if (!(phonebookParam instanceof String)) {
|
||||||
|| ((matchCountParam != null) && !(matchCountParam instanceof String))) {
|
throw new IllegalArgumentException("Parameter 'phonebook' need to be a String");
|
||||||
throw new IllegalArgumentException("Parameters need to be Strings");
|
|
||||||
}
|
}
|
||||||
String[] phonebookParams = ((String) phonebookParam).split(":");
|
String[] phonebookParams = ((String) phonebookParam).split(":");
|
||||||
if (phonebookParams.length > 2) {
|
if (phonebookParams.length > 2) {
|
||||||
throw new IllegalArgumentException("Could not split 'phonebook' parameter");
|
throw new IllegalArgumentException("Cannot split 'phonebook' parameter");
|
||||||
}
|
}
|
||||||
thingUID = new ThingUID(UIDUtils.decode(phonebookParams[0]));
|
thingUID = new ThingUID(UIDUtils.decode(phonebookParams[0]));
|
||||||
if (phonebookParams.length == 2) {
|
if (phonebookParams.length == 2) {
|
||||||
phonebookName = UIDUtils.decode(phonebookParams[1]);
|
phonebookName = UIDUtils.decode(phonebookParams[1]);
|
||||||
}
|
}
|
||||||
if (matchCountParam != null) {
|
if (matchCountParam != null) {
|
||||||
matchCount = Integer.parseInt((String) matchCountParam);
|
if (matchCountParam instanceof BigDecimal) {
|
||||||
|
matchCount = ((BigDecimal) matchCountParam).intValue();
|
||||||
|
} else if (matchCountParam instanceof String) {
|
||||||
|
matchCount = Integer.parseInt((String) matchCountParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (phoneNumberIndexParam != null) {
|
||||||
|
if (phoneNumberIndexParam instanceof BigDecimal) {
|
||||||
|
phoneNumberIndex = ((BigDecimal) phoneNumberIndexParam).intValue();
|
||||||
|
} else if (phoneNumberIndexParam instanceof String) {
|
||||||
|
phoneNumberIndex = Integer.parseInt((String) phoneNumberIndexParam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.warn("Could not initialize PHONEBOOK transformation profile: {}. Profile will be inactive.",
|
logger.warn("Cannot initialize PHONEBOOK transformation profile: {}. Profile will be inactive.",
|
||||||
e.getMessage());
|
e.getMessage());
|
||||||
thingUID = null;
|
thingUID = null;
|
||||||
}
|
}
|
||||||
@ -93,6 +119,7 @@ public class PhonebookProfile implements StateProfile {
|
|||||||
this.thingUID = thingUID;
|
this.thingUID = thingUID;
|
||||||
this.phonebookName = phonebookName;
|
this.phonebookName = phonebookName;
|
||||||
this.matchCount = matchCount;
|
this.matchCount = matchCount;
|
||||||
|
this.phoneNumberIndex = phoneNumberIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,29 +132,53 @@ public class PhonebookProfile implements StateProfile {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateUpdateFromHandler(State state) {
|
public void onStateUpdateFromHandler(State state) {
|
||||||
|
if (state instanceof UnDefType) {
|
||||||
|
// we cannot adjust UNDEF or NULL values, thus we simply apply them without reporting an error or warning
|
||||||
|
callback.sendUpdate(state);
|
||||||
|
}
|
||||||
if (state instanceof StringType) {
|
if (state instanceof StringType) {
|
||||||
PhonebookProvider provider = phonebookProviders.get(thingUID);
|
Optional<String> match = resolveNumber(state.toString());
|
||||||
if (provider == null) {
|
|
||||||
logger.warn("Could not get phonebook provider with thing UID '{}'.", thingUID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final String phonebookName = this.phonebookName;
|
|
||||||
Optional<String> match;
|
|
||||||
if (phonebookName != null) {
|
|
||||||
match = provider.getPhonebookByName(phonebookName).or(() -> {
|
|
||||||
logger.warn("Could not get phonebook '{}' from provider '{}'", phonebookName, thingUID);
|
|
||||||
return Optional.empty();
|
|
||||||
}).flatMap(phonebook -> phonebook.lookupNumber(state.toString(), matchCount));
|
|
||||||
} else {
|
|
||||||
match = provider.getPhonebooks().stream().map(p -> p.lookupNumber(state.toString(), matchCount))
|
|
||||||
.filter(Optional::isPresent).map(Optional::get).findAny();
|
|
||||||
}
|
|
||||||
State newState = match.map(name -> (State) new StringType(name)).orElse(state);
|
State newState = match.map(name -> (State) new StringType(name)).orElse(state);
|
||||||
if (newState == state) {
|
if (newState == state) {
|
||||||
logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", state, phonebookName,
|
logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", state, phonebookName,
|
||||||
thingUID);
|
thingUID);
|
||||||
}
|
}
|
||||||
callback.sendUpdate(newState);
|
callback.sendUpdate(newState);
|
||||||
|
} else if (state instanceof StringListType) {
|
||||||
|
StringListType stringList = (StringListType) state;
|
||||||
|
try {
|
||||||
|
String phoneNumber = stringList.getValue(phoneNumberIndex);
|
||||||
|
Optional<String> match = resolveNumber(phoneNumber);
|
||||||
|
final State newState;
|
||||||
|
if (match.isPresent()) {
|
||||||
|
newState = new StringType(match.get());
|
||||||
|
} else {
|
||||||
|
logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", phoneNumber,
|
||||||
|
phonebookName, thingUID);
|
||||||
|
newState = new StringType(phoneNumber);
|
||||||
|
}
|
||||||
|
callback.sendUpdate(newState);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
logger.debug("StringListType does not contain a number at index {}", phoneNumberIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<String> resolveNumber(String phoneNumber) {
|
||||||
|
PhonebookProvider provider = phonebookProviders.get(thingUID);
|
||||||
|
if (provider == null) {
|
||||||
|
logger.warn("Could not get phonebook provider with thing UID '{}'.", thingUID);
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
final String phonebookName = this.phonebookName;
|
||||||
|
if (phonebookName != null) {
|
||||||
|
return provider.getPhonebookByName(phonebookName).or(() -> {
|
||||||
|
logger.warn("Could not get phonebook '{}' from provider '{}'", phonebookName, thingUID);
|
||||||
|
return Optional.empty();
|
||||||
|
}).flatMap(phonebook -> phonebook.lookupNumber(phoneNumber, matchCount));
|
||||||
|
} else {
|
||||||
|
return provider.getPhonebooks().stream().map(p -> p.lookupNumber(phoneNumber, matchCount))
|
||||||
|
.filter(Optional::isPresent).map(Optional::get).findAny();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,19 +12,23 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.tr064.internal.phonebook;
|
package org.openhab.binding.tr064.internal.phonebook;
|
||||||
|
|
||||||
|
import static java.util.Comparator.comparing;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.core.config.core.ConfigOptionProvider;
|
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||||
import org.openhab.core.config.core.ParameterOption;
|
import org.openhab.core.config.core.ParameterOption;
|
||||||
|
import org.openhab.core.i18n.LocalizedKey;
|
||||||
import org.openhab.core.thing.ThingUID;
|
import org.openhab.core.thing.ThingUID;
|
||||||
import org.openhab.core.thing.profiles.Profile;
|
import org.openhab.core.thing.profiles.Profile;
|
||||||
import org.openhab.core.thing.profiles.ProfileCallback;
|
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||||
@ -33,8 +37,13 @@ import org.openhab.core.thing.profiles.ProfileFactory;
|
|||||||
import org.openhab.core.thing.profiles.ProfileType;
|
import org.openhab.core.thing.profiles.ProfileType;
|
||||||
import org.openhab.core.thing.profiles.ProfileTypeProvider;
|
import org.openhab.core.thing.profiles.ProfileTypeProvider;
|
||||||
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
import org.openhab.core.thing.profiles.ProfileTypeUID;
|
||||||
|
import org.openhab.core.thing.profiles.i18n.ProfileTypeI18nLocalizationService;
|
||||||
|
import org.openhab.core.util.BundleResolver;
|
||||||
import org.openhab.core.util.UIDUtils;
|
import org.openhab.core.util.UIDUtils;
|
||||||
|
import org.osgi.framework.Bundle;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -50,6 +59,19 @@ public class PhonebookProfileFactory implements ProfileFactory, ProfileTypeProvi
|
|||||||
private final Logger logger = LoggerFactory.getLogger(PhonebookProfileFactory.class);
|
private final Logger logger = LoggerFactory.getLogger(PhonebookProfileFactory.class);
|
||||||
private final Map<ThingUID, PhonebookProvider> phonebookProviders = new ConcurrentHashMap<>();
|
private final Map<ThingUID, PhonebookProvider> phonebookProviders = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final Map<LocalizedKey, ProfileType> localizedProfileTypeCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService;
|
||||||
|
private final Bundle bundle;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public PhonebookProfileFactory(
|
||||||
|
final @Reference ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService,
|
||||||
|
final @Reference BundleResolver bundleResolver) {
|
||||||
|
this.profileTypeI18nLocalizationService = profileTypeI18nLocalizationService;
|
||||||
|
this.bundle = bundleResolver.resolveBundle(PhonebookProfileFactory.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
|
public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback,
|
||||||
ProfileContext profileContext) {
|
ProfileContext profileContext) {
|
||||||
@ -58,12 +80,31 @@ public class PhonebookProfileFactory implements ProfileFactory, ProfileTypeProvi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
public Collection<ProfileTypeUID> getSupportedProfileTypeUIDs() {
|
||||||
return Collections.singleton(PhonebookProfile.PHONEBOOK_PROFILE_TYPE_UID);
|
return Set.of(PhonebookProfile.PHONEBOOK_PROFILE_TYPE_UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
|
public Collection<ProfileType> getProfileTypes(@Nullable Locale locale) {
|
||||||
return Collections.singleton(PhonebookProfile.PHONEBOOK_PROFILE_TYPE);
|
return Set.of(createLocalizedProfileType(PhonebookProfile.PHONEBOOK_PROFILE_TYPE, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProfileType createLocalizedProfileType(ProfileType profileType, @Nullable Locale locale) {
|
||||||
|
final LocalizedKey localizedKey = new LocalizedKey(profileType.getUID(),
|
||||||
|
locale != null ? locale.toLanguageTag() : null);
|
||||||
|
|
||||||
|
final ProfileType cachedlocalizedProfileType = localizedProfileTypeCache.get(localizedKey);
|
||||||
|
if (cachedlocalizedProfileType != null) {
|
||||||
|
return cachedlocalizedProfileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ProfileType localizedProfileType = profileTypeI18nLocalizationService.createLocalizedProfileType(bundle,
|
||||||
|
profileType, locale);
|
||||||
|
if (localizedProfileType != null) {
|
||||||
|
localizedProfileTypeCache.put(localizedKey, localizedProfileType);
|
||||||
|
return localizedProfileType;
|
||||||
|
} else {
|
||||||
|
return profileType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,16 +131,17 @@ public class PhonebookProfileFactory implements ProfileFactory, ProfileTypeProvi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Stream<ParameterOption> createPhonebookList(Map.Entry<ThingUID, PhonebookProvider> entry) {
|
private List<ParameterOption> createPhonebookList(Map.Entry<ThingUID, PhonebookProvider> entry) {
|
||||||
String thingUid = UIDUtils.encode(entry.getKey().toString());
|
String thingUid = UIDUtils.encode(entry.getKey().toString());
|
||||||
String thingName = entry.getValue().getFriendlyName();
|
String thingName = entry.getValue().getFriendlyName();
|
||||||
|
|
||||||
Stream<ParameterOption> parameterOptions = entry.getValue().getPhonebooks().stream()
|
List<ParameterOption> parameterOptions = entry.getValue().getPhonebooks().stream()
|
||||||
.map(phonebook -> new ParameterOption(thingUid + ":" + UIDUtils.encode(phonebook.getName()),
|
.map(phonebook -> new ParameterOption(thingUid + ":" + UIDUtils.encode(phonebook.getName()),
|
||||||
thingName + " " + phonebook.getName()));
|
thingName + " - " + phonebook.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (parameterOptions.count() > 0) {
|
if (parameterOptions.size() > 0) {
|
||||||
return Stream.concat(Stream.of(new ParameterOption(thingUid, thingName)), parameterOptions);
|
parameterOptions.add(new ParameterOption(thingUid, thingName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameterOptions;
|
return parameterOptions;
|
||||||
@ -110,8 +152,12 @@ public class PhonebookProfileFactory implements ProfileFactory, ProfileTypeProvi
|
|||||||
@Nullable Locale locale) {
|
@Nullable Locale locale) {
|
||||||
if (uri.getSchemeSpecificPart().equals(PhonebookProfile.PHONEBOOK_PROFILE_TYPE_UID.toString())
|
if (uri.getSchemeSpecificPart().equals(PhonebookProfile.PHONEBOOK_PROFILE_TYPE_UID.toString())
|
||||||
&& s.equals(PhonebookProfile.PHONEBOOK_PARAM)) {
|
&& s.equals(PhonebookProfile.PHONEBOOK_PARAM)) {
|
||||||
return phonebookProviders.entrySet().stream().flatMap(this::createPhonebookList)
|
List<ParameterOption> parameterOptions = new ArrayList<>();
|
||||||
.collect(Collectors.toSet());
|
for (Map.Entry<ThingUID, PhonebookProvider> entry : phonebookProviders.entrySet()) {
|
||||||
|
parameterOptions.addAll(createPhonebookList(entry));
|
||||||
|
}
|
||||||
|
parameterOptions.sort(comparing(o -> o.getLabel()));
|
||||||
|
return parameterOptions;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,12 @@ public class Tr064PhonebookImpl implements Phonebook {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> lookupNumber(String number, int matchCount) {
|
public Optional<String> lookupNumber(String number, int matchCount) {
|
||||||
String matchString = matchCount < number.length() ? number.substring(number.length() - matchCount) : number;
|
String matchString = matchCount > 0 && matchCount < number.length()
|
||||||
logger.trace("matchString for {} is {}", number, matchString);
|
? number.substring(number.length() - matchCount)
|
||||||
return phonebook.keySet().stream().filter(n -> n.endsWith(matchString)).findAny().map(phonebook::get);
|
: number;
|
||||||
|
logger.trace("matchString for '{}' is '{}'", number, matchString);
|
||||||
|
return matchString.isBlank() ? Optional.empty()
|
||||||
|
: phonebook.keySet().stream().filter(n -> n.endsWith(matchString)).findFirst().map(phonebook::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3,14 +3,22 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||||
|
|
||||||
<config-description uri="profile:transform:PHONEBOOK">
|
<config-description uri="profile:transform:PHONEBOOK">
|
||||||
<parameter name="phonebook" type="text" required="true">
|
<parameter name="phonebook" type="text" required="true">
|
||||||
<label>Phonebook</label>
|
<label>Phone book</label>
|
||||||
<description>The name of the the phonebook</description>
|
<description>The name of the the phone book</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="matchCount" type="text" required="false">
|
<parameter name="matchCount" type="integer" min="0" step="1">
|
||||||
<label>Match Count</label>
|
<label>Match Count</label>
|
||||||
<description>The number of digits matching the incoming value, counted from far right (default is 0 = all matching)</description>
|
<description>The number of digits matching the incoming value, counted from far right (default is 0 = all matching)</description>
|
||||||
|
<default>0</default>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="phoneNumberIndex" type="integer" min="0" max="1" step="1">
|
||||||
|
<label>Phone Number Index</label>
|
||||||
|
<description>The index of the phone number to be resolved from a CallItem state (StringListType), 0 or 1 (default is
|
||||||
|
0)</description>
|
||||||
|
<default>0</default>
|
||||||
</parameter>
|
</parameter>
|
||||||
</config-description>
|
</config-description>
|
||||||
</config-description:config-descriptions>
|
</config-description:config-descriptions>
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
profile-type.transform.PHONEBOOK.label = Telefonbuch
|
||||||
|
profile.config.transform.PHONEBOOK.phonebook.label = Telefonbuch
|
||||||
|
profile.config.transform.PHONEBOOK.phonebook.description = Der Name des Telefonbuches.
|
||||||
|
profile.config.transform.PHONEBOOK.matchCount.label = Übereinstimmungen
|
||||||
|
profile.config.transform.PHONEBOOK.matchCount.description = Die Anzahl der Ziffern, die mit dem eingehenden Wert übereinstimmen, von rechts gezählt (Vorgabe ist 0 = alle müssen übereinstimmen).
|
||||||
|
profile.config.transform.PHONEBOOK.phoneNumberIndex.label = Telefonnummern-Index
|
||||||
|
profile.config.transform.PHONEBOOK.phoneNumberIndex.description = Der Index der Telefonnummer, die aus einem CallItem-State (StringListType) aufgelöst werden soll, 0 oder 1 (Vorgabe ist 0).
|
@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2020 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.binding.tr064.internal.phonebook;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
import static org.mockito.MockitoAnnotations.openMocks;
|
||||||
|
import static org.openhab.binding.tr064.internal.Tr064BindingConstants.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.library.types.StringListType;
|
||||||
|
import org.openhab.core.library.types.StringType;
|
||||||
|
import org.openhab.core.thing.ThingUID;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileCallback;
|
||||||
|
import org.openhab.core.thing.profiles.ProfileContext;
|
||||||
|
import org.openhab.core.thing.profiles.StateProfile;
|
||||||
|
import org.openhab.core.types.State;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
import org.openhab.core.util.UIDUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Christoph Weitkamp - Initial contribution
|
||||||
|
*/
|
||||||
|
class PhonebookProfileTest {
|
||||||
|
|
||||||
|
private static final String INTERNAL_PHONE_NUMBER = "999";
|
||||||
|
private static final String OTHER_PHONE_NUMBER = "555-456";
|
||||||
|
private static final String JOHN_DOES_PHONE_NUMBER = "12345";
|
||||||
|
private static final String JOHN_DOES_NAME = "John Doe";
|
||||||
|
private static final ThingUID THING_UID = new ThingUID(BINDING_ID, THING_TYPE_FRITZBOX.getId(), "test");
|
||||||
|
private static final String MY_PHONEBOOK = UIDUtils.encode(THING_UID.getAsString()) + ":MyPhonebook";
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
public static class ParameterSet {
|
||||||
|
public final State state;
|
||||||
|
public final State resultingState;
|
||||||
|
public final @Nullable Object matchCount;
|
||||||
|
public final @Nullable Object phoneNumberIndex;
|
||||||
|
|
||||||
|
public ParameterSet(State state, State resultingState, @Nullable Object matchCount,
|
||||||
|
@Nullable Object phoneNumberIndex) {
|
||||||
|
this.state = state;
|
||||||
|
this.resultingState = resultingState;
|
||||||
|
this.matchCount = matchCount;
|
||||||
|
this.phoneNumberIndex = phoneNumberIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Object[]> parameters() {
|
||||||
|
return Arrays.asList(new Object[][] { //
|
||||||
|
{ new ParameterSet(UnDefType.UNDEF, UnDefType.UNDEF, null, null) }, //
|
||||||
|
{ new ParameterSet(new StringType(JOHN_DOES_PHONE_NUMBER), new StringType(JOHN_DOES_NAME), null,
|
||||||
|
null) }, //
|
||||||
|
{ new ParameterSet(new StringType(JOHN_DOES_PHONE_NUMBER), new StringType(JOHN_DOES_NAME),
|
||||||
|
BigDecimal.ONE, null) }, //
|
||||||
|
{ new ParameterSet(new StringType(JOHN_DOES_PHONE_NUMBER), new StringType(JOHN_DOES_NAME), "3", null) }, //
|
||||||
|
{ new ParameterSet(new StringListType(JOHN_DOES_PHONE_NUMBER, INTERNAL_PHONE_NUMBER),
|
||||||
|
new StringType(JOHN_DOES_NAME), null, null) }, //
|
||||||
|
{ new ParameterSet(new StringListType(JOHN_DOES_PHONE_NUMBER, INTERNAL_PHONE_NUMBER),
|
||||||
|
new StringType(JOHN_DOES_NAME), null, BigDecimal.ZERO) }, //
|
||||||
|
{ new ParameterSet(new StringListType(INTERNAL_PHONE_NUMBER, JOHN_DOES_PHONE_NUMBER),
|
||||||
|
new StringType(JOHN_DOES_NAME), null, BigDecimal.ONE) }, //
|
||||||
|
{ new ParameterSet(new StringType(OTHER_PHONE_NUMBER), new StringType(OTHER_PHONE_NUMBER), null,
|
||||||
|
null) }, //
|
||||||
|
{ new ParameterSet(new StringListType(OTHER_PHONE_NUMBER, INTERNAL_PHONE_NUMBER),
|
||||||
|
new StringType(OTHER_PHONE_NUMBER), null, null) }, //
|
||||||
|
{ new ParameterSet(new StringListType(OTHER_PHONE_NUMBER, INTERNAL_PHONE_NUMBER),
|
||||||
|
new StringType(OTHER_PHONE_NUMBER), null, BigDecimal.ZERO) }, //
|
||||||
|
{ new ParameterSet(new StringListType(INTERNAL_PHONE_NUMBER, OTHER_PHONE_NUMBER),
|
||||||
|
new StringType(OTHER_PHONE_NUMBER), null, BigDecimal.ONE) }, //
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private AutoCloseable mocksCloseable;
|
||||||
|
|
||||||
|
private @Mock ProfileCallback mockCallback;
|
||||||
|
private @Mock ProfileContext mockContext;
|
||||||
|
private @Mock PhonebookProvider mockPhonebookProvider;
|
||||||
|
|
||||||
|
@NonNullByDefault
|
||||||
|
private final Phonebook phonebook = new Phonebook() {
|
||||||
|
@Override
|
||||||
|
public Optional<String> lookupNumber(String number, int matchCount) {
|
||||||
|
switch (number) {
|
||||||
|
case JOHN_DOES_PHONE_NUMBER:
|
||||||
|
return Optional.of(JOHN_DOES_NAME);
|
||||||
|
default:
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return MY_PHONEBOOK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
mocksCloseable = openMocks(this);
|
||||||
|
|
||||||
|
when(mockPhonebookProvider.getPhonebookByName(any(String.class))).thenReturn(Optional.of(phonebook));
|
||||||
|
when(mockPhonebookProvider.getPhonebooks()).thenReturn(Set.of(phonebook));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void afterEach() throws Exception {
|
||||||
|
mocksCloseable.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("parameters")
|
||||||
|
public void testPhonebookProfileResolvesPhoneNumber(ParameterSet parameterSet) {
|
||||||
|
StateProfile profile = initProfile(MY_PHONEBOOK, parameterSet.matchCount, parameterSet.phoneNumberIndex);
|
||||||
|
verifySendUpdate(profile, parameterSet.state, parameterSet.resultingState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StateProfile initProfile(Object phonebookName, @Nullable Object matchCount,
|
||||||
|
@Nullable Object phoneNumberIndex) {
|
||||||
|
Map<String, Object> properties = new HashMap<>();
|
||||||
|
properties.put(PhonebookProfile.PHONEBOOK_PARAM, phonebookName);
|
||||||
|
if (matchCount != null) {
|
||||||
|
properties.put(PhonebookProfile.MATCH_COUNT_PARAM, matchCount);
|
||||||
|
}
|
||||||
|
if (phoneNumberIndex != null) {
|
||||||
|
properties.put(PhonebookProfile.PHONE_NUMBER_INDEX_PARAM, phoneNumberIndex);
|
||||||
|
}
|
||||||
|
when(mockContext.getConfiguration()).thenReturn(new Configuration(properties));
|
||||||
|
return new PhonebookProfile(mockCallback, mockContext, Map.of(THING_UID, mockPhonebookProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifySendUpdate(StateProfile profile, State state, State expectedState) {
|
||||||
|
reset(mockCallback);
|
||||||
|
profile.onStateUpdateFromHandler(state);
|
||||||
|
verify(mockCallback, times(1)).sendUpdate(eq(expectedState));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user