2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.netatmo.internal.servlet;
15 import java.io.IOException;
16 import java.io.InputStream;
18 import java.nio.charset.StandardCharsets;
19 import java.util.ArrayList;
20 import java.util.List;
22 import java.util.Scanner;
23 import java.util.concurrent.ConcurrentHashMap;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.ws.rs.HttpMethod;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.UriBuilder;
30 import javax.ws.rs.core.UriBuilderException;
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.openhab.binding.netatmo.internal.api.NetatmoException;
34 import org.openhab.binding.netatmo.internal.api.SecurityApi;
35 import org.openhab.binding.netatmo.internal.api.dto.WebhookEvent;
36 import org.openhab.binding.netatmo.internal.deserialization.NADeserializer;
37 import org.openhab.binding.netatmo.internal.handler.ApiBridgeHandler;
38 import org.openhab.binding.netatmo.internal.handler.capability.EventCapability;
39 import org.osgi.service.http.HttpService;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * HTTP servlet for Netatmo Webhook.
46 * @author Gaƫl L'hopital - Initial contribution
49 public class WebhookServlet extends NetatmoServlet {
50 private static final long serialVersionUID = -354583910860541214L;
52 private final Map<String, EventCapability> dataListeners = new ConcurrentHashMap<>();
53 private final Logger logger = LoggerFactory.getLogger(WebhookServlet.class);
54 private final SecurityApi securityApi;
55 private final NADeserializer deserializer;
56 private final String webHookUrl;
58 private boolean hookSet = false;
60 public WebhookServlet(ApiBridgeHandler handler, HttpService httpService, NADeserializer deserializer,
61 SecurityApi securityApi, String webHookUrl) {
62 super(handler, httpService, "webhook");
63 this.deserializer = deserializer;
64 this.securityApi = securityApi;
65 this.webHookUrl = webHookUrl;
69 public void startListening() {
70 super.startListening();
71 URI uri = UriBuilder.fromUri(webHookUrl).path(path).build();
73 logger.info("Setting up WebHook at Netatmo to {}", uri.toString());
74 hookSet = securityApi.addwebhook(uri);
75 } catch (UriBuilderException e) {
76 logger.info("webhookUrl is not a valid URI '{}' : {}", uri, e.getMessage());
77 } catch (NetatmoException e) {
78 logger.info("Error setting webhook : {}", e.getMessage());
83 public void dispose() {
85 logger.info("Releasing WebHook at Netatmo ");
87 securityApi.dropWebhook();
89 } catch (NetatmoException e) {
90 logger.warn("Error releasing webhook : {}", e.getMessage());
97 protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
99 processEvent(inputStreamToString(req.getInputStream()));
102 private void processEvent(String data) throws IOException {
103 if (!data.isEmpty()) {
104 logger.debug("Event transmitted from restService : {}", data);
106 WebhookEvent event = deserializer.deserialize(WebhookEvent.class, data);
107 List<String> toBeNotified = new ArrayList<>();
108 toBeNotified.add(event.getCameraId());
109 toBeNotified.addAll(event.getPersons().keySet());
110 notifyListeners(toBeNotified, event);
111 } catch (NetatmoException e) {
112 logger.debug("Error deserializing webhook data received : {}. {}", data, e.getMessage());
117 private void replyQuick(HttpServletResponse resp) throws IOException {
118 resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
119 resp.setContentType(MediaType.APPLICATION_JSON);
120 resp.setHeader("Access-Control-Allow-Origin", "*");
121 resp.setHeader("Access-Control-Allow-Methods", HttpMethod.POST);
122 resp.setIntHeader("Access-Control-Max-Age", 3600);
123 resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
124 resp.getWriter().write("");
127 private String inputStreamToString(InputStream is) throws IOException {
129 try (Scanner scanner = new Scanner(is)) {
130 scanner.useDelimiter("\\A");
131 value = scanner.hasNext() ? scanner.next() : "";
136 private void notifyListeners(List<String> tobeNotified, WebhookEvent event) {
137 tobeNotified.forEach(id -> {
138 EventCapability module = dataListeners.get(id);
139 if (module != null) {
140 module.setNewData(event);
145 public void registerDataListener(String id, EventCapability eventCapability) {
146 dataListeners.put(id, eventCapability);
149 public void unregisterDataListener(String id) {
150 dataListeners.remove(id);