]> git.basschouten.com Git - openhab-addons.git/blob
330c7b5cc042f9c1dad6151a11e932c021420456
[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.shelly.internal.api;
14
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
17
18 import java.io.IOException;
19 import java.nio.charset.StandardCharsets;
20 import java.util.Map;
21 import java.util.TreeMap;
22
23 import javax.servlet.ServletException;
24 import javax.servlet.annotation.WebServlet;
25 import javax.servlet.http.HttpServlet;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
32 import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
33 import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
34 import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
35 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
36 import org.openhab.binding.shelly.internal.ShellyHandlerFactory;
37 import org.openhab.binding.shelly.internal.api2.Shelly2RpcSocket;
38 import org.openhab.binding.shelly.internal.handler.ShellyThingTable;
39 import org.osgi.service.component.annotations.Activate;
40 import org.osgi.service.component.annotations.Component;
41 import org.osgi.service.component.annotations.ConfigurationPolicy;
42 import org.osgi.service.component.annotations.Deactivate;
43 import org.osgi.service.component.annotations.Reference;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * {@link ShellyEventServlet} implements the WebSocket callback for Gen2 devices
49  *
50  * @author Markus Michels - Initial contribution
51  */
52 @NonNullByDefault
53 @WebServlet(name = "ShellyEventServlet", urlPatterns = { SHELLY1_CALLBACK_URI, SHELLY2_CALLBACK_URI })
54 @Component(service = HttpServlet.class, configurationPolicy = ConfigurationPolicy.OPTIONAL)
55 public class ShellyEventServlet extends WebSocketServlet {
56     private static final long serialVersionUID = -1210354558091063207L;
57     private final Logger logger = LoggerFactory.getLogger(ShellyEventServlet.class);
58
59     private final ShellyHandlerFactory handlerFactory;
60     private final ShellyThingTable thingTable;
61
62     @Activate
63     public ShellyEventServlet(@Reference ShellyHandlerFactory handlerFactory, @Reference ShellyThingTable thingTable) {
64         this.handlerFactory = handlerFactory;
65         this.thingTable = thingTable;
66         logger.debug("Shelly EventServlet started at {} and {}", SHELLY1_CALLBACK_URI, SHELLY2_CALLBACK_URI);
67     }
68
69     @Deactivate
70     protected void deactivate() {
71         logger.debug("ShellyEventServlet: Stopping");
72     }
73
74     /**
75      * Servlet handler. Shelly1: http request, Shelly2: WebSocket call
76      */
77     @Override
78     protected void service(HttpServletRequest request, HttpServletResponse resp)
79             throws ServletException, IOException, IllegalArgumentException {
80         String path = getString(request.getRequestURI()).toLowerCase();
81
82         if (path.equals(SHELLY2_CALLBACK_URI)) { // Shelly2 WebSocket
83             super.service(request, resp);
84             return;
85         }
86
87         // Shelly1: http events, URL looks like
88         // <ip address>:<remote port>/shelly/event/shellyrelay-XXXXXX/relay/n?xxxxx or
89         // <ip address>:<remote port>/shelly/event/shellyrelay-XXXXXX/roller/n?xxxxx or
90         // <ip address>:<remote port>/shelly/event/shellyht-XXXXXX/sensordata?hum=53,temp=26.50
91         String deviceName = "";
92         String index = "";
93         String type = "";
94         try {
95             String ipAddress = request.getRemoteAddr();
96             Map<String, String[]> parameters = request.getParameterMap();
97             logger.debug("ShellyEventServlet: {} Request from {}:{}{}?{}", request.getProtocol(), ipAddress,
98                     request.getRemotePort(), path, parameters.toString());
99             if (!path.toLowerCase().startsWith(SHELLY1_CALLBACK_URI) || !path.contains("/event/shelly")) {
100                 logger.warn("ShellyEventServlet received unknown request: path = {}", path);
101                 return;
102             }
103
104             deviceName = substringBetween(path, "/event/", "/").toLowerCase();
105             if (path.contains("/" + EVENT_TYPE_RELAY + "/") || path.contains("/" + EVENT_TYPE_ROLLER + "/")
106                     || path.contains("/" + EVENT_TYPE_LIGHT + "/")) {
107                 index = substringAfterLast(path, "/").toLowerCase();
108                 type = substringBetween(path, deviceName + "/", "/" + index);
109             } else {
110                 index = "";
111                 type = substringAfterLast(path, "/").toLowerCase();
112             }
113             logger.trace("{}: Process event of type type={}, index={}", deviceName, type, index);
114
115             Map<String, String> parms = new TreeMap<>();
116             for (Map.Entry<String, String[]> p : parameters.entrySet()) {
117                 parms.put(p.getKey(), p.getValue()[0]);
118
119             }
120             handlerFactory.onEvent(ipAddress, deviceName, index, type, parms);
121         } catch (IllegalArgumentException e) {
122             logger.debug("{}: Exception processing callback: path={}; index={}, type={}, parameters={}", deviceName,
123                     path, index, type, request.getParameterMap().toString());
124         } finally {
125             resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
126             resp.getWriter().write("");
127         }
128     }
129
130     /*
131      * @Override
132      * public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
133      * {
134      * response.getWriter().println("HTTP GET method not implemented.");
135      * }
136      */
137     /**
138      * WebSocket: register Shelly2RpcSocket class
139      */
140     @Override
141     public void configure(@Nullable WebSocketServletFactory factory) {
142         if (factory != null) {
143             factory.getPolicy().setIdleTimeout(15000);
144             factory.setCreator(new Shelly2WebSocketCreator(thingTable));
145             factory.register(Shelly2RpcSocket.class);
146         }
147     }
148
149     public static class Shelly2WebSocketCreator implements WebSocketCreator {
150         private final Logger logger = LoggerFactory.getLogger(Shelly2WebSocketCreator.class);
151
152         private final ShellyThingTable thingTable;
153
154         public Shelly2WebSocketCreator(ShellyThingTable thingTable) {
155             this.thingTable = thingTable;
156         }
157
158         @Override
159         public Object createWebSocket(@Nullable ServletUpgradeRequest req, @Nullable ServletUpgradeResponse resp) {
160             logger.debug("WebSocket: Create socket from servlet");
161             return new Shelly2RpcSocket(thingTable, true);
162         }
163     }
164 }