2 * Copyright (c) 2010-2020 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.io.imperihome.internal;
15 import java.io.IOException;
16 import java.util.Dictionary;
17 import java.util.Hashtable;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
22 import javax.servlet.ServletException;
23 import javax.servlet.http.HttpServlet;
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletResponse;
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;
56 import com.google.gson.Gson;
57 import com.google.gson.GsonBuilder;
60 * Main OSGi service and HTTP servlet for ImperiHome integration.
62 * @author Pepijn de Geus - Initial contribution
64 @Component(immediate = true, service = HttpServlet.class, configurationPid = "org.openhab.imperihome", property = {
65 Constants.SERVICE_PID + "=org.openhab.imperihome",
66 ConfigurableService.SERVICE_PROPERTY_DESCRIPTION_URI + "=io:imperihome",
67 ConfigurableService.SERVICE_PROPERTY_CATEGORY + "=io",
68 ConfigurableService.SERVICE_PROPERTY_LABEL + "=ImperiHome Integration" })
69 public class ImperiHomeApiServlet extends HttpServlet {
71 private static final long serialVersionUID = -1966364789075448441L;
73 private static final String PATH = "/imperihome/iss";
75 private static final String APPLICATION_JSON = "application/json";
76 private static final String CHARSET = "utf-8";
78 private static final Pattern URL_PATTERN_SYSTEM = Pattern.compile(PATH + "/system$", Pattern.CASE_INSENSITIVE);
79 private static final Pattern URL_PATTERN_ROOMS = Pattern.compile(PATH + "/rooms$", Pattern.CASE_INSENSITIVE);
80 private static final Pattern URL_PATTERN_DEVICES = Pattern.compile(PATH + "/devices$", Pattern.CASE_INSENSITIVE);
81 private static final Pattern URL_PATTERN_DEVICE_ACTION = Pattern
82 .compile(PATH + "/devices/(.+?)/action/(.+?)(?:/(.*?))?$", Pattern.CASE_INSENSITIVE);
83 private static final Pattern URL_PATTERN_DEVICE_HISTORY = Pattern
84 .compile(PATH + "/devices/(.+?)/(.+?)/histo/(.+?)/(.+?)$", Pattern.CASE_INSENSITIVE);
86 private final Logger logger = LoggerFactory.getLogger(ImperiHomeApiServlet.class);
88 private final Gson gson;
89 private final ImperiHomeConfig imperiHomeConfig;
91 private HttpService httpService;
92 private ItemRegistry itemRegistry;
93 private PersistenceServiceRegistry persistenceServiceRegistry;
94 private EventPublisher eventPublisher;
96 private ItemProcessor itemProcessor;
97 private DevicesListHandler devicesListHandler;
98 private RoomListHandler roomListHandler;
99 private SystemHandler systemHandler;
100 private DeviceRegistry deviceRegistry;
101 private DeviceActionHandler deviceActionHandler;
102 private DeviceHistoryHandler deviceHistoryHandler;
103 private ActionRegistry actionRegistry;
106 * Default constructor.
108 public ImperiHomeApiServlet() {
109 imperiHomeConfig = new ImperiHomeConfig();
111 GsonBuilder gsonBuilder = new GsonBuilder();
112 gsonBuilder.registerTypeAdapter(DeviceType.class, new DeviceTypeSerializer());
113 gsonBuilder.registerTypeAdapter(ParamType.class, new ParamTypeSerializer());
114 gsonBuilder.registerTypeAdapter(DeviceParameters.class, new DeviceParametersSerializer());
115 gson = gsonBuilder.create();
119 * OSGi activation callback.
121 * @param config Service config.
124 protected void activate(Map<String, Object> config) {
127 systemHandler = new SystemHandler(imperiHomeConfig);
128 deviceRegistry = new DeviceRegistry();
129 actionRegistry = new ActionRegistry(eventPublisher, deviceRegistry);
130 itemProcessor = new ItemProcessor(itemRegistry, deviceRegistry, actionRegistry, imperiHomeConfig);
131 roomListHandler = new RoomListHandler(deviceRegistry);
132 devicesListHandler = new DevicesListHandler(deviceRegistry);
133 deviceActionHandler = new DeviceActionHandler(deviceRegistry);
134 deviceHistoryHandler = new DeviceHistoryHandler(deviceRegistry, persistenceServiceRegistry);
137 Dictionary<String, String> servletParams = new Hashtable<>();
138 httpService.registerServlet(PATH, this, servletParams, httpService.createDefaultHttpContext());
139 logger.info("Started ImperiHome integration service at " + PATH);
140 } catch (Exception e) {
141 logger.error("Could not start ImperiHome integration service: {}", e.getMessage(), e);
146 * OSGi config modification callback.
148 * @param config Service config.
151 protected void modified(Map<String, Object> config) {
152 imperiHomeConfig.update(config);
156 * OSGi deactivation callback.
159 protected void deactivate() {
161 httpService.unregister(PATH);
162 } catch (IllegalArgumentException ignored) {
165 itemProcessor.destroy();
167 systemHandler = null;
168 deviceRegistry = null;
169 itemProcessor = null;
170 roomListHandler = null;
171 devicesListHandler = null;
172 deviceActionHandler = null;
173 deviceHistoryHandler = null;
175 logger.info("ImperiHome integration service stopped");
178 @Reference(policy = ReferencePolicy.DYNAMIC)
179 protected void setItemRegistry(ItemRegistry itemRegistry) {
180 this.itemRegistry = itemRegistry;
183 protected void unsetItemRegistry(ItemRegistry itemRegistry) {
184 this.itemRegistry = null;
188 protected void setEventPublisher(EventPublisher eventPublisher) {
189 this.eventPublisher = eventPublisher;
192 protected void unsetEventPublisher(EventPublisher eventPublisher) {
193 this.eventPublisher = null;
197 protected void setHttpService(HttpService httpService) {
198 this.httpService = httpService;
201 protected void unsetHttpService(HttpService httpService) {
202 this.httpService = null;
206 protected void setPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
207 this.persistenceServiceRegistry = persistenceServiceRegistry;
210 protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
211 this.persistenceServiceRegistry = null;
215 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
216 String path = req.getRequestURI();
217 logger.debug("{}: {} {}", req.getRemoteAddr(), req.getMethod(), path);
220 Object response = null;
222 Matcher actionMatcher = URL_PATTERN_DEVICE_ACTION.matcher(path);
223 Matcher historyMatcher = URL_PATTERN_DEVICE_HISTORY.matcher(path);
225 if (URL_PATTERN_ROOMS.matcher(path).matches()) {
226 response = roomListHandler.handle(req);
227 } else if (URL_PATTERN_DEVICES.matcher(path).matches()) {
228 response = devicesListHandler.handle(req);
229 } else if (actionMatcher.matches()) {
230 deviceActionHandler.handle(req, actionMatcher);
231 } else if (historyMatcher.matches()) {
232 response = deviceHistoryHandler.handle(req, historyMatcher);
233 } else if (URL_PATTERN_SYSTEM.matcher(path).matches()) {
234 response = systemHandler.handle(req);
236 logger.warn("Unrecognized request: {}", path);
239 resp.getWriter().write(gson.toJson(response));
242 private void setHeaders(HttpServletResponse response) {
243 response.setCharacterEncoding(CHARSET);
244 response.setContentType(APPLICATION_JSON);
245 response.setHeader("Access-Control-Allow-Origin", "*");
246 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
247 response.setHeader("Access-Control-Max-Age", "3600");
248 response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");