]> git.basschouten.com Git - openhab-addons.git/blob
5b15aa76752c83ade71b1b2f505c303f44f20793
[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.io.imperihome.internal;
14
15 import java.io.IOException;
16 import java.util.Dictionary;
17 import java.util.Hashtable;
18 import java.util.Map;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import javax.servlet.ServletException;
23 import javax.servlet.http.HttpServlet;
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletResponse;
26
27 import org.openhab.core.config.core.ConfigurableService;
28 import org.openhab.core.events.EventPublisher;
29 import org.openhab.core.items.ItemRegistry;
30 import org.openhab.core.persistence.PersistenceServiceRegistry;
31 import org.openhab.io.imperihome.internal.action.ActionRegistry;
32 import org.openhab.io.imperihome.internal.handler.DeviceActionHandler;
33 import org.openhab.io.imperihome.internal.handler.DeviceHistoryHandler;
34 import org.openhab.io.imperihome.internal.handler.DevicesListHandler;
35 import org.openhab.io.imperihome.internal.handler.RoomListHandler;
36 import org.openhab.io.imperihome.internal.handler.SystemHandler;
37 import org.openhab.io.imperihome.internal.io.DeviceParametersSerializer;
38 import org.openhab.io.imperihome.internal.io.DeviceTypeSerializer;
39 import org.openhab.io.imperihome.internal.io.ParamTypeSerializer;
40 import org.openhab.io.imperihome.internal.model.device.DeviceType;
41 import org.openhab.io.imperihome.internal.model.param.DeviceParameters;
42 import org.openhab.io.imperihome.internal.model.param.ParamType;
43 import org.openhab.io.imperihome.internal.processor.DeviceRegistry;
44 import org.openhab.io.imperihome.internal.processor.ItemProcessor;
45 import org.osgi.framework.Constants;
46 import org.osgi.service.component.annotations.Activate;
47 import org.osgi.service.component.annotations.Component;
48 import org.osgi.service.component.annotations.Deactivate;
49 import org.osgi.service.component.annotations.Modified;
50 import org.osgi.service.component.annotations.Reference;
51 import org.osgi.service.component.annotations.ReferencePolicy;
52 import org.osgi.service.http.HttpService;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 import com.google.gson.Gson;
57 import com.google.gson.GsonBuilder;
58
59 /**
60  * Main OSGi service and HTTP servlet for ImperiHome integration.
61  *
62  * @author Pepijn de Geus - Initial contribution
63  */
64 @Component(service = HttpServlet.class, configurationPid = "org.openhab.imperihome", //
65         property = Constants.SERVICE_PID + "=org.openhab.imperihome")
66 @ConfigurableService(category = "io", label = "ImperiHome Integration", description_uri = "io:imperihome")
67 public class ImperiHomeApiServlet extends HttpServlet {
68
69     private static final long serialVersionUID = -1966364789075448441L;
70
71     private static final String PATH = "/imperihome/iss";
72
73     private static final String APPLICATION_JSON = "application/json";
74     private static final String CHARSET = "utf-8";
75
76     private static final Pattern URL_PATTERN_SYSTEM = Pattern.compile(PATH + "/system$", Pattern.CASE_INSENSITIVE);
77     private static final Pattern URL_PATTERN_ROOMS = Pattern.compile(PATH + "/rooms$", Pattern.CASE_INSENSITIVE);
78     private static final Pattern URL_PATTERN_DEVICES = Pattern.compile(PATH + "/devices$", Pattern.CASE_INSENSITIVE);
79     private static final Pattern URL_PATTERN_DEVICE_ACTION = Pattern
80             .compile(PATH + "/devices/(.+?)/action/(.+?)(?:/(.*?))?$", Pattern.CASE_INSENSITIVE);
81     private static final Pattern URL_PATTERN_DEVICE_HISTORY = Pattern
82             .compile(PATH + "/devices/(.+?)/(.+?)/histo/(.+?)/(.+?)$", Pattern.CASE_INSENSITIVE);
83
84     private final Logger logger = LoggerFactory.getLogger(ImperiHomeApiServlet.class);
85
86     private final Gson gson;
87     private final ImperiHomeConfig imperiHomeConfig;
88
89     private HttpService httpService;
90     private ItemRegistry itemRegistry;
91     private PersistenceServiceRegistry persistenceServiceRegistry;
92     private EventPublisher eventPublisher;
93
94     private ItemProcessor itemProcessor;
95     private DevicesListHandler devicesListHandler;
96     private RoomListHandler roomListHandler;
97     private SystemHandler systemHandler;
98     private DeviceRegistry deviceRegistry;
99     private DeviceActionHandler deviceActionHandler;
100     private DeviceHistoryHandler deviceHistoryHandler;
101     private ActionRegistry actionRegistry;
102
103     /**
104      * Default constructor.
105      */
106     public ImperiHomeApiServlet() {
107         imperiHomeConfig = new ImperiHomeConfig();
108
109         GsonBuilder gsonBuilder = new GsonBuilder();
110         gsonBuilder.registerTypeAdapter(DeviceType.class, new DeviceTypeSerializer());
111         gsonBuilder.registerTypeAdapter(ParamType.class, new ParamTypeSerializer());
112         gsonBuilder.registerTypeAdapter(DeviceParameters.class, new DeviceParametersSerializer());
113         gson = gsonBuilder.create();
114     }
115
116     /**
117      * OSGi activation callback.
118      *
119      * @param config Service config.
120      */
121     @Activate
122     protected void activate(Map<String, Object> config) {
123         modified(config);
124
125         systemHandler = new SystemHandler(imperiHomeConfig);
126         deviceRegistry = new DeviceRegistry();
127         actionRegistry = new ActionRegistry(eventPublisher, deviceRegistry);
128         itemProcessor = new ItemProcessor(itemRegistry, deviceRegistry, actionRegistry, imperiHomeConfig);
129         roomListHandler = new RoomListHandler(deviceRegistry);
130         devicesListHandler = new DevicesListHandler(deviceRegistry);
131         deviceActionHandler = new DeviceActionHandler(deviceRegistry);
132         deviceHistoryHandler = new DeviceHistoryHandler(deviceRegistry, persistenceServiceRegistry);
133
134         try {
135             Dictionary<String, String> servletParams = new Hashtable<>();
136             httpService.registerServlet(PATH, this, servletParams, httpService.createDefaultHttpContext());
137             logger.info("Started ImperiHome integration service at " + PATH);
138         } catch (Exception e) {
139             logger.error("Could not start ImperiHome integration service: {}", e.getMessage(), e);
140         }
141     }
142
143     /**
144      * OSGi config modification callback.
145      *
146      * @param config Service config.
147      */
148     @Modified
149     protected void modified(Map<String, Object> config) {
150         imperiHomeConfig.update(config);
151     }
152
153     /**
154      * OSGi deactivation callback.
155      */
156     @Deactivate
157     protected void deactivate() {
158         try {
159             httpService.unregister(PATH);
160         } catch (IllegalArgumentException ignored) {
161         }
162
163         itemProcessor.destroy();
164
165         systemHandler = null;
166         deviceRegistry = null;
167         itemProcessor = null;
168         roomListHandler = null;
169         devicesListHandler = null;
170         deviceActionHandler = null;
171         deviceHistoryHandler = null;
172
173         logger.info("ImperiHome integration service stopped");
174     }
175
176     @Reference(policy = ReferencePolicy.DYNAMIC)
177     protected void setItemRegistry(ItemRegistry itemRegistry) {
178         this.itemRegistry = itemRegistry;
179     }
180
181     protected void unsetItemRegistry(ItemRegistry itemRegistry) {
182         this.itemRegistry = null;
183     }
184
185     @Reference
186     protected void setEventPublisher(EventPublisher eventPublisher) {
187         this.eventPublisher = eventPublisher;
188     }
189
190     protected void unsetEventPublisher(EventPublisher eventPublisher) {
191         this.eventPublisher = null;
192     }
193
194     @Reference
195     protected void setHttpService(HttpService httpService) {
196         this.httpService = httpService;
197     }
198
199     protected void unsetHttpService(HttpService httpService) {
200         this.httpService = null;
201     }
202
203     @Reference
204     protected void setPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
205         this.persistenceServiceRegistry = persistenceServiceRegistry;
206     }
207
208     protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
209         this.persistenceServiceRegistry = null;
210     }
211
212     @Override
213     protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
214         String path = req.getRequestURI();
215         logger.debug("{}: {} {}", req.getRemoteAddr(), req.getMethod(), path);
216         setHeaders(resp);
217
218         Object response = null;
219
220         Matcher actionMatcher = URL_PATTERN_DEVICE_ACTION.matcher(path);
221         Matcher historyMatcher = URL_PATTERN_DEVICE_HISTORY.matcher(path);
222
223         if (URL_PATTERN_ROOMS.matcher(path).matches()) {
224             response = roomListHandler.handle(req);
225         } else if (URL_PATTERN_DEVICES.matcher(path).matches()) {
226             response = devicesListHandler.handle(req);
227         } else if (actionMatcher.matches()) {
228             deviceActionHandler.handle(req, actionMatcher);
229         } else if (historyMatcher.matches()) {
230             response = deviceHistoryHandler.handle(req, historyMatcher);
231         } else if (URL_PATTERN_SYSTEM.matcher(path).matches()) {
232             response = systemHandler.handle(req);
233         } else {
234             logger.warn("Unrecognized request: {}", path);
235         }
236
237         resp.getWriter().write(gson.toJson(response));
238     }
239
240     private void setHeaders(HttpServletResponse response) {
241         response.setCharacterEncoding(CHARSET);
242         response.setContentType(APPLICATION_JSON);
243         response.setHeader("Access-Control-Allow-Origin", "*");
244         response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
245         response.setHeader("Access-Control-Max-Age", "3600");
246         response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
247     }
248 }