[tr064] fix certificate problems and add call list channel (#9149)
* improvements - use insecure client and remove TrustManager - add call list channel * address review comments Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
parent
66421c6939
commit
936a4dc8d8
@ -49,7 +49,7 @@ This is an optional parameter and multiple values are allowed.
|
||||
|
||||
Most devices support call lists.
|
||||
The binding can analyze these call lists and provide channels for the number of missed calls, inbound calls, outbound calls and rejected (blocked) calls.
|
||||
The days for which this analysis takes place can be controlled with the `missedCallDays`, `rejectedCallDays`, `inboundCallDays` and `outboundCallDays`
|
||||
The days for which this analysis takes place can be controlled with the `missedCallDays`, `rejectedCallDays`, `inboundCallDays`, `outboundCallDays` and `callListDays`.
|
||||
This is an optional parameter and multiple values are allowed.
|
||||
|
||||
Since FritzOS! 7.20 WAN access of local devices can be controlled by their IPs.
|
||||
@ -75,7 +75,8 @@ This is an optional parameter and multiple values are allowed.
|
||||
| channel | item-type | advanced | description |
|
||||
|----------------------------|---------------------------|:--------:|----------------------------------------------------------------|
|
||||
| `callDeflectionEnable` | `Switch` | | Enable/Disable the call deflection setup with the given index. |
|
||||
| `deviceLog` | `String` | x | A string containing the last log messages. |
|
||||
| `callList` | `String` | x | A string containing the call list as JSON (see below) |
|
||||
| `deviceLog` | `String` | x | A string containing the last log messages |
|
||||
| `dslCRCErrors` | `Number:Dimensionless` | x | DSL CRC Errors |
|
||||
| `dslDownstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Downstream Noise Margin |
|
||||
| `dslDownstreamNoiseMargin` | `Number:Dimensionless` | x | DSL Downstream Attenuation |
|
||||
@ -107,6 +108,12 @@ This is an optional parameter and multiple values are allowed.
|
||||
| `wifi5GHzEnable` | `Switch` | | Enable/Disable the 5.0 GHz WiFi device. |
|
||||
| `wifiGuestEnable` | `Switch` | | Enable/Disable the guest WiFi. |
|
||||
|
||||
### Channel `callList`
|
||||
|
||||
Call lists are provided for one or more days (as configured) as JSON.
|
||||
The JSON consists of an array of individual calls with the fields `date`, `type`, `localNumber`, `remoteNumber`, `duration`.
|
||||
The call-types are the same as provided by the FritzBox, i.e. `1` (inbound), `2` (missed), `3` (outbound), `10` (rejected).
|
||||
|
||||
## `PHONEBOOK` Profile
|
||||
|
||||
The binding provides a profile for using the FritzBox phonebooks for resolving numbers to names.
|
||||
@ -117,3 +124,4 @@ If only a specific phonebook from the device should be used, this can be specifi
|
||||
The default is to use all available phonebooks from the specified thing.
|
||||
In case the format of the number in the phonebook and the format of the number from the channel are different (e.g. regarding country prefixes), the `matchCount` parameter can be used.
|
||||
The configured `matchCount` is counted from the right end and denotes the number of matching characters needed to consider this number as matching.
|
||||
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
|
||||
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* Provides a TrustManager to allow secure connections to any FRITZ!Box
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial Contribution
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class AVMFritzTlsTrustManagerProvider implements TlsTrustManagerProvider {
|
||||
|
||||
@Override
|
||||
public String getHostName() {
|
||||
return "fritz.box";
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509ExtendedTrustManager getTrustManager() {
|
||||
return TrustAllTrustManager.getInstance();
|
||||
}
|
||||
}
|
||||
@ -21,8 +21,8 @@ import java.util.stream.Stream;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.openhab.binding.tr064.internal.phonebook.PhonebookProfileFactory;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
@ -31,7 +31,10 @@ import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Deactivate;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link Tr064HandlerFactory} is responsible for creating things and thing
|
||||
@ -46,6 +49,7 @@ public class Tr064HandlerFactory extends BaseThingHandlerFactory {
|
||||
.of(Tr064RootHandler.SUPPORTED_THING_TYPES, Tr064SubHandler.SUPPORTED_THING_TYPES).flatMap(Set::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Tr064HandlerFactory.class);
|
||||
private final HttpClient httpClient;
|
||||
private final PhonebookProfileFactory phonebookProfileFactory;
|
||||
|
||||
@ -56,12 +60,29 @@ public class Tr064HandlerFactory extends BaseThingHandlerFactory {
|
||||
private final Tr064ChannelTypeProvider channelTypeProvider;
|
||||
|
||||
@Activate
|
||||
public Tr064HandlerFactory(@Reference HttpClientFactory httpClientFactory,
|
||||
@Reference Tr064ChannelTypeProvider channelTypeProvider,
|
||||
public Tr064HandlerFactory(@Reference Tr064ChannelTypeProvider channelTypeProvider,
|
||||
@Reference PhonebookProfileFactory phonebookProfileFactory) {
|
||||
httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.channelTypeProvider = channelTypeProvider;
|
||||
this.phonebookProfileFactory = phonebookProfileFactory;
|
||||
// use an insecure client (i.e. without verifying the certificate)
|
||||
this.httpClient = new HttpClient(new SslContextFactory(true));
|
||||
try {
|
||||
this.httpClient.start();
|
||||
} catch (Exception e) {
|
||||
// catching exception is necessary due to the signature of HttpClient.start()
|
||||
logger.warn("Failed to start http client: {}", e.getMessage());
|
||||
throw new IllegalStateException("Could not create HttpClient instance.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Deactivate
|
||||
public void deactivate() {
|
||||
try {
|
||||
httpClient.stop();
|
||||
} catch (Exception e) {
|
||||
// catching exception is necessary due to the signature of HttpClient.stop()
|
||||
logger.warn("Failed to stop http client: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -40,6 +40,8 @@ import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDActionType;
|
||||
import org.openhab.binding.tr064.internal.phonebook.Phonebook;
|
||||
import org.openhab.binding.tr064.internal.phonebook.PhonebookProvider;
|
||||
import org.openhab.binding.tr064.internal.phonebook.Tr064PhonebookImpl;
|
||||
import org.openhab.binding.tr064.internal.soap.SOAPConnector;
|
||||
import org.openhab.binding.tr064.internal.soap.SOAPValueConverter;
|
||||
import org.openhab.binding.tr064.internal.util.SCPDUtil;
|
||||
import org.openhab.binding.tr064.internal.util.Util;
|
||||
import org.openhab.core.cache.ExpiringCacheMap;
|
||||
|
||||
@ -26,6 +26,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.tr064.internal.config.Tr064ChannelConfig;
|
||||
import org.openhab.binding.tr064.internal.config.Tr064SubConfiguration;
|
||||
import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDDeviceType;
|
||||
import org.openhab.binding.tr064.internal.soap.SOAPConnector;
|
||||
import org.openhab.binding.tr064.internal.util.SCPDUtil;
|
||||
import org.openhab.binding.tr064.internal.util.Util;
|
||||
import org.openhab.core.cache.ExpiringCacheMap;
|
||||
|
||||
@ -35,6 +35,7 @@ public class Tr064RootConfiguration extends Tr064BaseThingConfiguration {
|
||||
public List<String> rejectedCallDays = Collections.emptyList();
|
||||
public List<String> inboundCallDays = Collections.emptyList();
|
||||
public List<String> outboundCallDays = Collections.emptyList();
|
||||
public List<String> callListDays = Collections.emptyList();
|
||||
public int phonebookInterval = 0;
|
||||
|
||||
public boolean isValid() {
|
||||
|
||||
@ -31,8 +31,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.tr064.internal.dto.phonebook.NumberType;
|
||||
import org.openhab.binding.tr064.internal.dto.phonebook.PhonebooksType;
|
||||
import org.openhab.binding.tr064.internal.dto.additions.NumberType;
|
||||
import org.openhab.binding.tr064.internal.dto.additions.PhonebooksType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.soap;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.tr064.internal.dto.additions.Call;
|
||||
|
||||
/**
|
||||
* The {@link CallListEntry} is used for post processing the retrieved call
|
||||
* lists
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CallListEntry {
|
||||
private static final SimpleDateFormat DATE_FORMAT_PARSER = new SimpleDateFormat("dd.MM.yy hh:mm");
|
||||
public @Nullable String localNumber;
|
||||
public @Nullable String remoteNumber;
|
||||
public @Nullable Date date;
|
||||
public @Nullable Integer type;
|
||||
public @Nullable Integer duration;
|
||||
|
||||
public CallListEntry(Call call) {
|
||||
try {
|
||||
date = DATE_FORMAT_PARSER.parse(call.getDate());
|
||||
} catch (ParseException e) {
|
||||
// ignore parsing error
|
||||
date = null;
|
||||
}
|
||||
String[] durationParts = call.getDuration().split(":");
|
||||
duration = Integer.parseInt(durationParts[0]) * 60 + Integer.parseInt(durationParts[1]);
|
||||
type = Integer.parseInt(call.getType());
|
||||
if (CallListType.OUTBOUND_COUNT.typeString().equals(call.getType())) {
|
||||
localNumber = call.getCallerNumber();
|
||||
remoteNumber = call.getCalled();
|
||||
} else {
|
||||
localNumber = call.getCalledNumber();
|
||||
remoteNumber = call.getCaller();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.soap;
|
||||
|
||||
/**
|
||||
* The {@link CallListType} is used for post processing the retrieved call list
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
*/
|
||||
public enum CallListType {
|
||||
MISSED_COUNT("2"),
|
||||
INBOUND_COUNT("1"),
|
||||
REJECTED_COUNT("10"),
|
||||
OUTBOUND_COUNT("3"),
|
||||
JSON_LIST("");
|
||||
|
||||
private final String value;
|
||||
|
||||
CallListType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String typeString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.tr064.internal;
|
||||
package org.openhab.binding.tr064.internal.soap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
@ -25,6 +25,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
public class PostProcessingException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PostProcessingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PostProcessingException(String message, Throwable t) {
|
||||
super(message, t);
|
||||
}
|
||||
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.tr064.internal;
|
||||
package org.openhab.binding.tr064.internal.soap;
|
||||
|
||||
import static org.openhab.binding.tr064.internal.util.Util.getSOAPElement;
|
||||
|
||||
@ -34,6 +34,7 @@ import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.BytesContentProvider;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.tr064.internal.Tr064CommunicationException;
|
||||
import org.openhab.binding.tr064.internal.config.Tr064ChannelConfig;
|
||||
import org.openhab.binding.tr064.internal.dto.config.ActionType;
|
||||
import org.openhab.binding.tr064.internal.dto.config.ChannelTypeDescription;
|
||||
@ -10,17 +10,19 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.tr064.internal;
|
||||
package org.openhab.binding.tr064.internal.soap;
|
||||
|
||||
import static org.openhab.binding.tr064.internal.util.Util.getSOAPElement;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
@ -29,15 +31,22 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.openhab.binding.tr064.internal.config.Tr064ChannelConfig;
|
||||
import org.openhab.binding.tr064.internal.dto.additions.Call;
|
||||
import org.openhab.binding.tr064.internal.dto.additions.Root;
|
||||
import org.openhab.binding.tr064.internal.util.Util;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* The {@link SOAPValueConverter} converts SOAP values and openHAB states
|
||||
*
|
||||
@ -189,7 +198,7 @@ public class SOAPValueConverter {
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private State processMissedCalls(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException {
|
||||
return processCallList(state, channelConfig.getParameter(), "2");
|
||||
return processCallList(state, channelConfig.getParameter(), CallListType.MISSED_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,7 +211,7 @@ public class SOAPValueConverter {
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private State processInboundCalls(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException {
|
||||
return processCallList(state, channelConfig.getParameter(), "1");
|
||||
return processCallList(state, channelConfig.getParameter(), CallListType.INBOUND_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -215,7 +224,7 @@ public class SOAPValueConverter {
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private State processRejectedCalls(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException {
|
||||
return processCallList(state, channelConfig.getParameter(), "3");
|
||||
return processCallList(state, channelConfig.getParameter(), CallListType.REJECTED_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,7 +237,20 @@ public class SOAPValueConverter {
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private State processOutboundCalls(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException {
|
||||
return processCallList(state, channelConfig.getParameter(), "4");
|
||||
return processCallList(state, channelConfig.getParameter(), CallListType.OUTBOUND_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* post processor for JSON call list
|
||||
*
|
||||
* @param state the call list URL
|
||||
* @param channelConfig channel config of the call list channel (contains day number)
|
||||
* @return caller list in JSON format
|
||||
* @throws PostProcessingException if call list could not be retrieved
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private State processCallListJSON(State state, Tr064ChannelConfig channelConfig) throws PostProcessingException {
|
||||
return processCallList(state, channelConfig.getParameter(), CallListType.JSON_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -236,20 +258,30 @@ public class SOAPValueConverter {
|
||||
*
|
||||
* @param state the call list URL
|
||||
* @param days number of days to get
|
||||
* @param type type of call (1=missed 2=inbound 3=rejected 4=outbund)
|
||||
* @param type type of call (2=missed 1=inbound 4=rejected 3=outbund)
|
||||
* @return the quantity of calls of the given type within the given number of days
|
||||
* @throws PostProcessingException if the call list could not be retrieved
|
||||
*/
|
||||
private State processCallList(State state, @Nullable String days, String type) throws PostProcessingException {
|
||||
try {
|
||||
ContentResponse response = httpClient.newRequest(state.toString() + "&days=" + days)
|
||||
.timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).send();
|
||||
String responseContent = response.getContentAsString();
|
||||
int callCount = responseContent.split("<Type>" + type + "</Type>").length - 1;
|
||||
|
||||
return new DecimalType(callCount);
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new PostProcessingException("Failed to get call list from URL " + state.toString(), e);
|
||||
private State processCallList(State state, @Nullable String days, CallListType type)
|
||||
throws PostProcessingException {
|
||||
Root callListRoot = Util.getAndUnmarshalXML(httpClient, state.toString() + "&days=" + days, Root.class);
|
||||
if (callListRoot == null) {
|
||||
throw new PostProcessingException("Failed to get call list from URL " + state.toString());
|
||||
}
|
||||
List<Call> calls = callListRoot.getCall();
|
||||
switch (type) {
|
||||
case INBOUND_COUNT:
|
||||
case MISSED_COUNT:
|
||||
case OUTBOUND_COUNT:
|
||||
case REJECTED_COUNT:
|
||||
long callCount = calls.stream().filter(call -> type.typeString().equals(call.getType())).count();
|
||||
return new DecimalType(callCount);
|
||||
case JSON_LIST:
|
||||
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssX").serializeNulls().create();
|
||||
List<CallListEntry> callListEntries = calls.stream().map(CallListEntry::new)
|
||||
.collect(Collectors.toList());
|
||||
return new StringType(gson.toJson(callListEntries));
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
@ -12,36 +12,21 @@
|
||||
*/
|
||||
package org.openhab.binding.tr064.internal.util;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.tr064.internal.SCPDException;
|
||||
import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDDeviceType;
|
||||
import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDRootType;
|
||||
import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType;
|
||||
import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDScpdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SCPDUtil} is responsible for handling commands, which are
|
||||
@ -51,18 +36,12 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SCPDUtil {
|
||||
private final Logger logger = LoggerFactory.getLogger(SCPDUtil.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
private SCPDRootType scpdRoot;
|
||||
private final List<SCPDDeviceType> scpdDevicesList = new ArrayList<>();
|
||||
private final Map<String, SCPDScpdType> serviceMap = new HashMap<>();
|
||||
|
||||
public SCPDUtil(HttpClient httpClient, String endpoint) throws SCPDException {
|
||||
this.httpClient = httpClient;
|
||||
|
||||
SCPDRootType scpdRoot = getAndUnmarshalSCPD(endpoint + "/tr64desc.xml", SCPDRootType.class);
|
||||
SCPDRootType scpdRoot = Util.getAndUnmarshalXML(httpClient, endpoint + "/tr64desc.xml", SCPDRootType.class);
|
||||
if (scpdRoot == null) {
|
||||
throw new SCPDException("could not get SCPD root");
|
||||
}
|
||||
@ -71,8 +50,8 @@ public class SCPDUtil {
|
||||
scpdDevicesList.addAll(flatDeviceList(scpdRoot.getDevice()).collect(Collectors.toList()));
|
||||
for (SCPDDeviceType device : scpdDevicesList) {
|
||||
for (SCPDServiceType service : device.getServiceList()) {
|
||||
SCPDScpdType scpd = serviceMap.computeIfAbsent(service.getServiceId(),
|
||||
serviceId -> getAndUnmarshalSCPD(endpoint + service.getSCPDURL(), SCPDScpdType.class));
|
||||
SCPDScpdType scpd = serviceMap.computeIfAbsent(service.getServiceId(), serviceId -> Util
|
||||
.getAndUnmarshalXML(httpClient, endpoint + service.getSCPDURL(), SCPDScpdType.class));
|
||||
if (scpd == null) {
|
||||
throw new SCPDException("could not get SCPD service");
|
||||
}
|
||||
@ -80,30 +59,6 @@ public class SCPDUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generic unmarshaller
|
||||
*
|
||||
* @param uri the uri of the XML file
|
||||
* @param clazz the class describing the XML file
|
||||
* @return unmarshalling result
|
||||
*/
|
||||
private <T> @Nullable T getAndUnmarshalSCPD(String uri, Class<T> clazz) {
|
||||
try {
|
||||
ContentResponse contentResponse = httpClient.newRequest(uri).timeout(2, TimeUnit.SECONDS)
|
||||
.method(HttpMethod.GET).send();
|
||||
InputStream xml = new ByteArrayInputStream(contentResponse.getContent());
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(clazz);
|
||||
Unmarshaller um = context.createUnmarshaller();
|
||||
return um.unmarshal(new StreamSource(xml), clazz).getValue();
|
||||
} catch (ExecutionException | InterruptedException | TimeoutException e) {
|
||||
logger.debug("HTTP Failed to GET uri '{}': {}", uri, e.getMessage());
|
||||
} catch (JAXBException e) {
|
||||
logger.debug("Unmarshalling failed: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* recursively flatten the device tree to a stream
|
||||
*
|
||||
|
||||
@ -14,9 +14,13 @@ package org.openhab.binding.tr064.internal.util;
|
||||
|
||||
import static org.openhab.binding.tr064.internal.Tr064BindingConstants.*;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -29,6 +33,10 @@ import javax.xml.soap.SOAPMessage;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.openhab.binding.tr064.internal.ChannelConfigException;
|
||||
import org.openhab.binding.tr064.internal.Tr064RootHandler;
|
||||
import org.openhab.binding.tr064.internal.config.Tr064BaseThingConfiguration;
|
||||
@ -75,7 +83,7 @@ public class Util {
|
||||
return root.getValue().getChannel();
|
||||
} catch (JAXBException e) {
|
||||
LOGGER.warn("Failed to read channel definitions", e);
|
||||
return Collections.emptyList();
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +282,7 @@ public class Util {
|
||||
}
|
||||
return parameters;
|
||||
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
|
||||
throw new ChannelConfigException("Could not get required parameter '" + channelId
|
||||
throw new ChannelConfigException("Could not get required parameter for channel '" + channelId
|
||||
+ "' from thing config (missing, empty or invalid)");
|
||||
}
|
||||
}
|
||||
@ -290,4 +298,32 @@ public class Util {
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* generic unmarshaller
|
||||
*
|
||||
* @param uri the uri of the XML file
|
||||
* @param clazz the class describing the XML file
|
||||
* @return unmarshalling result
|
||||
*/
|
||||
public static <T> @Nullable T getAndUnmarshalXML(HttpClient httpClient, String uri, Class<T> clazz) {
|
||||
try {
|
||||
ContentResponse contentResponse = httpClient.newRequest(uri).timeout(2, TimeUnit.SECONDS)
|
||||
.method(HttpMethod.GET).send();
|
||||
byte[] response = contentResponse.getContent();
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("XML = {}", new String(response));
|
||||
}
|
||||
InputStream xml = new ByteArrayInputStream(response);
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(clazz);
|
||||
Unmarshaller um = context.createUnmarshaller();
|
||||
return um.unmarshal(new StreamSource(xml), clazz).getValue();
|
||||
} catch (ExecutionException | InterruptedException | TimeoutException e) {
|
||||
LOGGER.debug("HTTP Failed to GET uri '{}': {}", uri, e.getMessage());
|
||||
} catch (JAXBException e) {
|
||||
LOGGER.debug("Unmarshalling failed: {}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +82,11 @@
|
||||
<description>List of days for which outbound calls should be calculated.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="callListDays" type="text" multiple="true">
|
||||
<label>Call List Days</label>
|
||||
<description>List of days for which JSON call list should be generated.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="wanBlockIPs" type="text" multiple="true">
|
||||
<label>WAN Block IPs</label>
|
||||
<description>List of IPs that can be blocked for WAN access.</description>
|
||||
|
||||
@ -106,6 +106,14 @@
|
||||
<parameter name="CallDays" thingParameter="outboundCallDays" pattern="[0-9]+" internalOnly="true"/>
|
||||
</getAction>
|
||||
</channel>
|
||||
<channel name="callList" label="Call List" description="Call list in JSON format for the given number of days.">
|
||||
<item type="String"/>
|
||||
<service deviceType="urn:dslforum-org:device:InternetGatewayDevice:1"
|
||||
serviceId="urn:X_AVM-DE_OnTel-com:serviceId:X_AVM-DE_OnTel1"/>
|
||||
<getAction name="GetCallList" argument="NewCallListURL" postProcessor="processCallListJSON">
|
||||
<parameter name="CallDays" thingParameter="callListDays" pattern="[0-9]+" internalOnly="true"/>
|
||||
</getAction>
|
||||
</channel>
|
||||
|
||||
<!-- LAN Device -->
|
||||
<channel name="wifi24GHzEnable" label="WiFi 2.4 GHz" description="Enable/Disable the 2.4 GHz WiFi device.">
|
||||
|
||||
@ -51,4 +51,47 @@
|
||||
<xs:element type="phonebookType" name="phonebook"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="root">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="timestamp"/>
|
||||
<xs:element maxOccurs="unbounded" ref="Call"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="timestamp" type="xs:integer"/>
|
||||
<xs:element name="Call">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="Id"/>
|
||||
<xs:element ref="Type"/>
|
||||
<xs:element ref="Called"/>
|
||||
<xs:element ref="Caller"/>
|
||||
<xs:element ref="CalledNumber"/>
|
||||
<xs:element ref="CallerNumber"/>
|
||||
<xs:element ref="Name"/>
|
||||
<xs:element ref="Numbertype"/>
|
||||
<xs:element ref="Device"/>
|
||||
<xs:element ref="Port"/>
|
||||
<xs:element ref="Date"/>
|
||||
<xs:element ref="Duration"/>
|
||||
<xs:element ref="Count"/>
|
||||
<xs:element ref="Path"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Id" type="xs:integer"/>
|
||||
<xs:element name="Type" type="xs:string"/>
|
||||
<xs:element name="Called" type="xs:string"/>
|
||||
<xs:element name="Caller" type="xs:string"/>
|
||||
<xs:element name="CalledNumber" type="xs:string"/>
|
||||
<xs:element name="CallerNumber" type="xs:string"/>
|
||||
<xs:element name="Name" type="xs:string" />
|
||||
<xs:element name="Numbertype" type="xs:string"/>
|
||||
<xs:element name="Device" type="xs:string"/>
|
||||
<xs:element name="Port" type="xs:string"/>
|
||||
<xs:element name="Date" type="xs:string"/>
|
||||
<xs:element name="Duration" type="xs:string"/>
|
||||
<xs:element name="Count" type="xs:string"/>
|
||||
<xs:element name="Path" type = "xs:string"/>
|
||||
</xs:schema>
|
||||
@ -4,9 +4,10 @@
|
||||
<xjc:serializable uid="1"/>
|
||||
</jaxb:globalBindings>
|
||||
|
||||
<jaxb:bindings schemaLocation="phonebook.xsd">
|
||||
<!-- additions (without namespace): phonebook, calllist -->
|
||||
<jaxb:bindings schemaLocation="additions.xsd">
|
||||
<jaxb:schemaBindings>
|
||||
<jaxb:package name="org.openhab.binding.tr064.internal.dto.phonebook"/>
|
||||
<jaxb:package name="org.openhab.binding.tr064.internal.dto.additions"/>
|
||||
</jaxb:schemaBindings>
|
||||
</jaxb:bindings>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user