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