]> git.basschouten.com Git - openhab-addons.git/blob
3de7469785349064788aa0416e5ce046257db34c
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.util;
14
15 import java.io.ByteArrayInputStream;
16 import java.io.InputStream;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Optional;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.TimeoutException;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
27
28 import javax.xml.bind.JAXBContext;
29 import javax.xml.bind.JAXBException;
30 import javax.xml.bind.Unmarshaller;
31 import javax.xml.transform.stream.StreamSource;
32
33 import org.eclipse.jdt.annotation.NonNullByDefault;
34 import org.eclipse.jdt.annotation.Nullable;
35 import org.eclipse.jetty.client.HttpClient;
36 import org.eclipse.jetty.client.api.ContentResponse;
37 import org.eclipse.jetty.http.HttpMethod;
38 import org.openhab.binding.tr064.internal.SCPDException;
39 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDDeviceType;
40 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDRootType;
41 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType;
42 import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDScpdType;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * The {@link SCPDUtil} is responsible for handling commands, which are
48  * sent to one of the channels.
49  *
50  * @author Jan N. Klug - Initial contribution
51  */
52 @NonNullByDefault
53 public class SCPDUtil {
54     private final Logger logger = LoggerFactory.getLogger(SCPDUtil.class);
55
56     private final HttpClient httpClient;
57
58     private SCPDRootType scpdRoot;
59     private final List<SCPDDeviceType> scpdDevicesList = new ArrayList<>();
60     private final Map<String, SCPDScpdType> serviceMap = new HashMap<>();
61
62     public SCPDUtil(HttpClient httpClient, String endpoint) throws SCPDException {
63         this.httpClient = httpClient;
64
65         SCPDRootType scpdRoot = getAndUnmarshalSCPD(endpoint + "/tr64desc.xml", SCPDRootType.class);
66         if (scpdRoot == null) {
67             throw new SCPDException("could not get SCPD root");
68         }
69         this.scpdRoot = scpdRoot;
70
71         scpdDevicesList.addAll(flatDeviceList(scpdRoot.getDevice()).collect(Collectors.toList()));
72         for (SCPDDeviceType device : scpdDevicesList) {
73             for (SCPDServiceType service : device.getServiceList()) {
74                 SCPDScpdType scpd = serviceMap.computeIfAbsent(service.getServiceId(),
75                         serviceId -> getAndUnmarshalSCPD(endpoint + service.getSCPDURL(), SCPDScpdType.class));
76                 if (scpd == null) {
77                     throw new SCPDException("could not get SCPD service");
78                 }
79             }
80         }
81     }
82
83     /**
84      * generic unmarshaller
85      *
86      * @param uri the uri of the XML file
87      * @param clazz the class describing the XML file
88      * @return unmarshalling result
89      */
90     private <T> @Nullable T getAndUnmarshalSCPD(String uri, Class<T> clazz) {
91         try {
92             ContentResponse contentResponse = httpClient.newRequest(uri).timeout(2, TimeUnit.SECONDS)
93                     .method(HttpMethod.GET).send();
94             InputStream xml = new ByteArrayInputStream(contentResponse.getContent());
95
96             JAXBContext context = JAXBContext.newInstance(clazz);
97             Unmarshaller um = context.createUnmarshaller();
98             return um.unmarshal(new StreamSource(xml), clazz).getValue();
99         } catch (ExecutionException | InterruptedException | TimeoutException e) {
100             logger.debug("HTTP Failed to GET uri '{}': {}", uri, e.getMessage());
101         } catch (JAXBException e) {
102             logger.debug("Unmarshalling failed: {}", e.getMessage());
103         }
104         return null;
105     }
106
107     /**
108      * recursively flatten the device tree to a stream
109      *
110      * @param device a device
111      * @return stream of sub-devices
112      */
113     private Stream<SCPDDeviceType> flatDeviceList(SCPDDeviceType device) {
114         return Stream.concat(Stream.of(device), device.getDeviceList().stream().flatMap(this::flatDeviceList));
115     }
116
117     /**
118      * get a list of all sub-devices (root device not included)
119      *
120      * @return the device list
121      */
122     public List<SCPDDeviceType> getAllSubDevices() {
123         return scpdDevicesList.stream().filter(device -> !device.getUDN().equals(scpdRoot.getDevice().getUDN()))
124                 .collect(Collectors.toList());
125     }
126
127     /**
128      * get a single device by it's UDN
129      *
130      * @param udn the device UDN
131      * @return the device
132      */
133     public Optional<SCPDDeviceType> getDevice(String udn) {
134         if (udn.isEmpty()) {
135             return Optional.of(scpdRoot.getDevice());
136         } else {
137             return getAllSubDevices().stream().filter(device -> udn.equals(device.getUDN())).findFirst();
138         }
139     }
140
141     /**
142      * get a single service by it's serviceId
143      *
144      * @param serviceId the service id
145      * @return the service
146      */
147     public Optional<SCPDScpdType> getService(String serviceId) {
148         return Optional.ofNullable(serviceMap.get(serviceId));
149     }
150 }