]> git.basschouten.com Git - openhab-addons.git/blob
944c87adaaac201f52405086946b5aca34fc0c7e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.tr064.internal.phonebook;
14
15 import java.math.BigDecimal;
16 import java.util.Map;
17 import java.util.Optional;
18
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.core.config.core.Configuration;
22 import org.openhab.core.library.CoreItemFactory;
23 import org.openhab.core.library.types.StringListType;
24 import org.openhab.core.library.types.StringType;
25 import org.openhab.core.thing.ThingUID;
26 import org.openhab.core.thing.profiles.ProfileCallback;
27 import org.openhab.core.thing.profiles.ProfileContext;
28 import org.openhab.core.thing.profiles.ProfileType;
29 import org.openhab.core.thing.profiles.ProfileTypeBuilder;
30 import org.openhab.core.thing.profiles.ProfileTypeUID;
31 import org.openhab.core.thing.profiles.StateProfile;
32 import org.openhab.core.transform.TransformationService;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.State;
35 import org.openhab.core.types.UnDefType;
36 import org.openhab.core.util.UIDUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link PhonebookProfile} class provides a profile for resolving phone number strings to names
42  *
43  * @author Jan N. Klug - Initial contribution
44  */
45 @NonNullByDefault
46 public class PhonebookProfile implements StateProfile {
47     public static final ProfileTypeUID PHONEBOOK_PROFILE_TYPE_UID = new ProfileTypeUID(
48             TransformationService.TRANSFORM_PROFILE_SCOPE, "PHONEBOOK");
49     public static final ProfileType PHONEBOOK_PROFILE_TYPE = ProfileTypeBuilder //
50             .newState(PHONEBOOK_PROFILE_TYPE_UID, "Phonebook") //
51             .withSupportedItemTypesOfChannel(CoreItemFactory.CALL, CoreItemFactory.STRING) //
52             .withSupportedItemTypes(CoreItemFactory.STRING) //
53             .build();
54
55     public static final String PHONEBOOK_PARAM = "phonebook";
56     public static final String MATCH_COUNT_PARAM = "matchCount";
57     public static final String PHONE_NUMBER_INDEX_PARAM = "phoneNumberIndex";
58
59     private final Logger logger = LoggerFactory.getLogger(PhonebookProfile.class);
60
61     private final ProfileCallback callback;
62
63     private final @Nullable String phonebookName;
64     private final @Nullable ThingUID thingUID;
65     private final Map<ThingUID, PhonebookProvider> phonebookProviders;
66     private final int matchCount;
67     private final int phoneNumberIndex;
68
69     public PhonebookProfile(ProfileCallback callback, ProfileContext context,
70             Map<ThingUID, PhonebookProvider> phonebookProviders) {
71         this.callback = callback;
72         this.phonebookProviders = phonebookProviders;
73
74         Configuration configuration = context.getConfiguration();
75         Object phonebookParam = configuration.get(PHONEBOOK_PARAM);
76         Object matchCountParam = configuration.get(MATCH_COUNT_PARAM);
77         Object phoneNumberIndexParam = configuration.get(PHONE_NUMBER_INDEX_PARAM);
78
79         logger.debug("Profile configured with '{}'='{}', '{}'='{}', '{}'='{}'", PHONEBOOK_PARAM, phonebookParam,
80                 MATCH_COUNT_PARAM, matchCountParam, PHONE_NUMBER_INDEX_PARAM, phoneNumberIndexParam);
81
82         ThingUID thingUID;
83         String phonebookName = null;
84         int matchCount = 0;
85         int phoneNumberIndex = 0;
86
87         try {
88             if (!(phonebookParam instanceof String)) {
89                 throw new IllegalArgumentException("Parameter 'phonebook' need to be a String");
90             }
91             String[] phonebookParams = ((String) phonebookParam).split(":");
92             if (phonebookParams.length > 2) {
93                 throw new IllegalArgumentException("Cannot split 'phonebook' parameter");
94             }
95             thingUID = new ThingUID(UIDUtils.decode(phonebookParams[0]));
96             if (phonebookParams.length == 2) {
97                 phonebookName = UIDUtils.decode(phonebookParams[1]);
98             }
99             if (matchCountParam != null) {
100                 if (matchCountParam instanceof BigDecimal) {
101                     matchCount = ((BigDecimal) matchCountParam).intValue();
102                 } else if (matchCountParam instanceof String) {
103                     matchCount = Integer.parseInt((String) matchCountParam);
104                 }
105             }
106             if (phoneNumberIndexParam != null) {
107                 if (phoneNumberIndexParam instanceof BigDecimal) {
108                     phoneNumberIndex = ((BigDecimal) phoneNumberIndexParam).intValue();
109                 } else if (phoneNumberIndexParam instanceof String) {
110                     phoneNumberIndex = Integer.parseInt((String) phoneNumberIndexParam);
111                 }
112             }
113         } catch (IllegalArgumentException e) {
114             logger.warn("Cannot initialize PHONEBOOK transformation profile: {}. Profile will be inactive.",
115                     e.getMessage());
116             thingUID = null;
117         }
118
119         this.thingUID = thingUID;
120         this.phonebookName = phonebookName;
121         this.matchCount = matchCount;
122         this.phoneNumberIndex = phoneNumberIndex;
123     }
124
125     @Override
126     public void onCommandFromItem(Command command) {
127     }
128
129     @Override
130     public void onCommandFromHandler(Command command) {
131     }
132
133     @Override
134     public void onStateUpdateFromHandler(State state) {
135         if (state instanceof UnDefType) {
136             // we cannot adjust UNDEF or NULL values, thus we simply apply them without reporting an error or warning
137             callback.sendUpdate(state);
138         }
139         if (state instanceof StringType) {
140             Optional<String> match = resolveNumber(state.toString());
141             State newState = match.map(name -> (State) new StringType(name)).orElse(state);
142             if (newState == state) {
143                 logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", state, phonebookName,
144                         thingUID);
145             }
146             callback.sendUpdate(newState);
147         } else if (state instanceof StringListType) {
148             StringListType stringList = (StringListType) state;
149             try {
150                 String phoneNumber = stringList.getValue(phoneNumberIndex);
151                 Optional<String> match = resolveNumber(phoneNumber);
152                 final State newState;
153                 if (match.isPresent()) {
154                     newState = new StringType(match.get());
155                 } else {
156                     logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", phoneNumber,
157                             phonebookName, thingUID);
158                     newState = new StringType(phoneNumber);
159                 }
160                 callback.sendUpdate(newState);
161             } catch (IllegalArgumentException e) {
162                 logger.debug("StringListType does not contain a number at index {}", phoneNumberIndex);
163             }
164         }
165     }
166
167     private Optional<String> resolveNumber(String phoneNumber) {
168         PhonebookProvider provider = phonebookProviders.get(thingUID);
169         if (provider == null) {
170             logger.warn("Could not get phonebook provider with thing UID '{}'.", thingUID);
171             return Optional.empty();
172         }
173         final String phonebookName = this.phonebookName;
174         if (phonebookName != null) {
175             return provider.getPhonebookByName(phonebookName).or(() -> {
176                 logger.warn("Could not get phonebook '{}' from provider '{}'", phonebookName, thingUID);
177                 return Optional.empty();
178             }).flatMap(phonebook -> phonebook.lookupNumber(phoneNumber, matchCount));
179         } else {
180             return provider.getPhonebooks().stream().map(p -> p.lookupNumber(phoneNumber, matchCount))
181                     .filter(Optional::isPresent).map(Optional::get).findAny();
182         }
183     }
184
185     @Override
186     public ProfileTypeUID getProfileTypeUID() {
187         return PHONEBOOK_PROFILE_TYPE_UID;
188     }
189
190     @Override
191     public void onStateUpdateFromItem(State state) {
192     }
193 }