]> git.basschouten.com Git - openhab-addons.git/blob
4805506c4d183c241d2c5000cc657b05f3589d1e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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;
14
15 import java.io.IOException;
16 import java.nio.file.FileSystems;
17 import java.nio.file.Files;
18 import java.nio.file.Path;
19 import java.time.LocalDateTime;
20 import java.time.format.DateTimeFormatter;
21 import java.util.Collection;
22 import java.util.Map;
23 import java.util.Objects;
24 import java.util.Optional;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.TimeoutException;
27
28 import javax.xml.soap.SOAPMessage;
29
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.eclipse.jetty.client.api.ContentResponse;
33 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType;
34 import org.openhab.binding.tr064.internal.phonebook.Phonebook;
35 import org.openhab.binding.tr064.internal.soap.SOAPRequest;
36 import org.openhab.binding.tr064.internal.util.SCPDUtil;
37 import org.openhab.binding.tr064.internal.util.Util;
38 import org.openhab.core.automation.annotation.ActionInput;
39 import org.openhab.core.automation.annotation.ActionOutput;
40 import org.openhab.core.automation.annotation.RuleAction;
41 import org.openhab.core.thing.binding.ThingActions;
42 import org.openhab.core.thing.binding.ThingActionsScope;
43 import org.openhab.core.thing.binding.ThingHandler;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The {@link FritzboxActions} is responsible for handling phone book actions
49  *
50  * @author Jan N. Klug - Initial contribution
51  */
52 @ThingActionsScope(name = "tr064")
53 @NonNullByDefault
54 public class FritzboxActions implements ThingActions {
55     private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy_HHmm");
56
57     private final Logger logger = LoggerFactory.getLogger(FritzboxActions.class);
58
59     private @Nullable Tr064RootHandler handler;
60
61     @RuleAction(label = "@text/phonebookLookupActionLabel", description = "@text/phonebookLookupActionDescription")
62     public @ActionOutput(name = "name", label = "@text/phonebookLookupActionOutputLabel", description = "@text/phonebookLookupActionOutputDescription", type = "java.lang.String") String phonebookLookup(
63             @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,
64             @ActionInput(name = "matches", label = "@text/phonebookLookupActionInputMatchesLabel", description = "@text/phonebookLookupActionInputMatchesDescription", type = "java.lang.Integer") @Nullable Integer matchCount) {
65         return phonebookLookup(phonenumber, null, matchCount);
66     }
67
68     @RuleAction(label = "@text/phonebookLookupActionLabel", description = "@text/phonebookLookupActionDescription")
69     public @ActionOutput(name = "name", label = "@text/phonebookLookupActionOutputLabel", description = "@text/phonebookLookupActionOutputDescription", type = "java.lang.String") String phonebookLookup(
70             @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber) {
71         return phonebookLookup(phonenumber, null, null);
72     }
73
74     @RuleAction(label = "@text/phonebookLookupActionLabel", description = "@text/phonebookLookupActionDescription")
75     public @ActionOutput(name = "name", label = "@text/phonebookLookupActionOutputLabel", description = "@text/phonebookLookupActionOutputDescription", type = "java.lang.String") String phonebookLookup(
76             @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,
77             @ActionInput(name = "phonebook", label = "@text/phonebookLookupActionInputPhoneBookLabel", description = "@text/phonebookLookupActionInputPhoneBookDescription", type = "java.lang.String") @Nullable String phonebook) {
78         return phonebookLookup(phonenumber, phonebook, null);
79     }
80
81     @RuleAction(label = "@text/phonebookLookupActionLabel", description = "@text/phonebookLookupActionDescription")
82     public @ActionOutput(name = "name", label = "@text/phonebookLookupActionOutputLabel", description = "@text/phonebookLookupActionOutputDescription", type = "java.lang.String") String phonebookLookup(
83             @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,
84             @ActionInput(name = "phonebook", label = "@text/phonebookLookupActionInputPhoneBookLabel", description = "@text/phonebookLookupActionInputPhoneBookDescription", type = "java.lang.String") @Nullable String phonebook,
85             @ActionInput(name = "matches", label = "@text/phonebookLookupActionInputMatchesLabel", description = "@text/phonebookLookupActionInputMatchesDescription", type = "java.lang.Integer") @Nullable Integer matchCount) {
86         if (phonenumber == null) {
87             logger.warn("Cannot lookup a missing number.");
88             return "";
89         }
90
91         final Tr064RootHandler handler = this.handler;
92         if (handler == null) {
93             logger.info("Handler is null, cannot lookup number.");
94             return phonenumber;
95         } else {
96             int matchCountInt = matchCount == null ? 0 : matchCount;
97             if (phonebook != null && !phonebook.isEmpty()) {
98                 return Objects.requireNonNull(handler.getPhonebookByName(phonebook)
99                         .flatMap(p -> p.lookupNumber(phonenumber, matchCountInt)).orElse(phonenumber));
100             } else {
101                 Collection<Phonebook> phonebooks = handler.getPhonebooks();
102                 return Objects.requireNonNull(phonebooks.stream().map(p -> p.lookupNumber(phonenumber, matchCountInt))
103                         .filter(Optional::isPresent).map(Optional::get).findAny().orElse(phonenumber));
104             }
105         }
106     }
107
108     @RuleAction(label = "create configuration backup", description = "Creates a configuration backup")
109     public void createConfigurationBackup() {
110         Tr064RootHandler handler = this.handler;
111
112         if (handler == null) {
113             logger.warn("TR064 action service ThingHandler is null!");
114             return;
115         }
116
117         SCPDUtil scpdUtil = handler.getSCPDUtil();
118         if (scpdUtil == null) {
119             logger.warn("Could not get SCPDUtil, handler seems to be uninitialized.");
120             return;
121         }
122
123         Optional<SCPDServiceType> scpdService = scpdUtil.getDevice("")
124                 .flatMap(deviceType -> deviceType.getServiceList().stream().filter(
125                         service -> "urn:DeviceConfig-com:serviceId:DeviceConfig1".equals(service.getServiceId()))
126                         .findFirst());
127         if (scpdService.isEmpty()) {
128             logger.warn("Could not get service.");
129             return;
130         }
131
132         BackupConfiguration configuration = handler.getBackupConfiguration();
133         try {
134             SOAPRequest soapRequest = new SOAPRequest(scpdService.get(), "X_AVM-DE_GetConfigFile",
135                     Map.of("NewX_AVM-DE_Password", configuration.password));
136             SOAPMessage soapMessage = handler.getSOAPConnector().doSOAPRequestUncached(soapRequest);
137             String configBackupURL = Util.getSOAPElement(soapMessage, "NewX_AVM-DE_ConfigFileUrl")
138                     .orElseThrow(() -> new Tr064CommunicationException("Empty URL"));
139
140             ContentResponse content = handler.getUrl(configBackupURL);
141
142             String fileName = String.format("%s %s.export", handler.getFriendlyName(),
143                     DATE_TIME_FORMATTER.format(LocalDateTime.now()));
144             Path filePath = FileSystems.getDefault().getPath(configuration.directory, fileName);
145             Path folder = filePath.getParent();
146             if (folder != null) {
147                 Files.createDirectories(folder);
148             }
149             Files.write(filePath, content.getContent());
150         } catch (Tr064CommunicationException e) {
151             logger.warn("Failed to get configuration backup URL: {}", e.getMessage());
152         } catch (InterruptedException | ExecutionException | TimeoutException e) {
153             logger.warn("Failed to get remote backup file: {}", e.getMessage());
154         } catch (IOException e) {
155             logger.warn("Failed to create backup file: {}", e.getMessage());
156         }
157     }
158
159     public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber,
160             @Nullable Integer matchCount) {
161         return phonebookLookup(actions, phonenumber, null, matchCount);
162     }
163
164     public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber) {
165         return phonebookLookup(actions, phonenumber, null, null);
166     }
167
168     public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber,
169             @Nullable String phonebook) {
170         return phonebookLookup(actions, phonenumber, phonebook, null);
171     }
172
173     public static String phonebookLookup(ThingActions actions, @Nullable String phonenumber, @Nullable String phonebook,
174             @Nullable Integer matchCount) {
175         return ((FritzboxActions) actions).phonebookLookup(phonenumber, phonebook, matchCount);
176     }
177
178     public static void createConfigurationBackup(ThingActions actions) {
179         ((FritzboxActions) actions).createConfigurationBackup();
180     }
181
182     @Override
183     public void setThingHandler(@Nullable ThingHandler handler) {
184         this.handler = (Tr064RootHandler) handler;
185     }
186
187     @Override
188     public @Nullable ThingHandler getThingHandler() {
189         return handler;
190     }
191
192     public record BackupConfiguration(String directory, String password) {
193     }
194 }