]> git.basschouten.com Git - openhab-addons.git/blob
783cce5e33c7cc38c24d741610eafb0e4a61ca64
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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     @SuppressWarnings("PMD.CompareObjectsWithEquals")
135     public void onStateUpdateFromHandler(State state) {
136         if (state instanceof UnDefType) {
137             // we cannot adjust UNDEF or NULL values, thus we simply apply them without reporting an error or warning
138             callback.sendUpdate(state);
139         }
140         if (state instanceof StringType) {
141             Optional<String> match = resolveNumber(state.toString());
142             State newState = match.map(name -> (State) new StringType(name)).orElse(state);
143             // Compare by reference to check if the name is mapped to the same state
144             if (newState == state) {
145                 logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", state, phonebookName,
146                         thingUID);
147             }
148             callback.sendUpdate(newState);
149         } else if (state instanceof StringListType) {
150             StringListType stringList = (StringListType) state;
151             try {
152                 String phoneNumber = stringList.getValue(phoneNumberIndex);
153                 Optional<String> match = resolveNumber(phoneNumber);
154                 final State newState;
155                 if (match.isPresent()) {
156                     newState = new StringType(match.get());
157                 } else {
158                     logger.debug("Number '{}' not found in phonebook '{}' from provider '{}'", phoneNumber,
159                             phonebookName, thingUID);
160                     newState = new StringType(phoneNumber);
161                 }
162                 callback.sendUpdate(newState);
163             } catch (IllegalArgumentException e) {
164                 logger.debug("StringListType does not contain a number at index {}", phoneNumberIndex);
165             }
166         }
167     }
168
169     private Optional<String> resolveNumber(String phoneNumber) {
170         PhonebookProvider provider = phonebookProviders.get(thingUID);
171         if (provider == null) {
172             logger.warn("Could not get phonebook provider with thing UID '{}'.", thingUID);
173             return Optional.empty();
174         }
175         final String phonebookName = this.phonebookName;
176         if (phonebookName != null) {
177             return provider.getPhonebookByName(phonebookName).or(() -> {
178                 logger.warn("Could not get phonebook '{}' from provider '{}'", phonebookName, thingUID);
179                 return Optional.empty();
180             }).flatMap(phonebook -> phonebook.lookupNumber(phoneNumber, matchCount));
181         } else {
182             return provider.getPhonebooks().stream().map(p -> p.lookupNumber(phoneNumber, matchCount))
183                     .filter(Optional::isPresent).map(Optional::get).findAny();
184         }
185     }
186
187     @Override
188     public ProfileTypeUID getProfileTypeUID() {
189         return PHONEBOOK_PROFILE_TYPE_UID;
190     }
191
192     @Override
193     public void onStateUpdateFromItem(State state) {
194     }
195 }