2 * Copyright (c) 2010-2023 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(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 {
69 private static final long serialVersionUID = -1966364789075448441L;
71 private static final String PATH = "/imperihome/iss";
73 private static final String APPLICATION_JSON = "application/json";
74 private static final String CHARSET = "utf-8";
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);
84 private final Logger logger = LoggerFactory.getLogger(ImperiHomeApiServlet.class);
86 private final Gson gson;
87 private final ImperiHomeConfig imperiHomeConfig;
89 private HttpService httpService;
90 private ItemRegistry itemRegistry;
91 private PersistenceServiceRegistry persistenceServiceRegistry;
92 private EventPublisher eventPublisher;
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;
104 * Default constructor.
106 public ImperiHomeApiServlet() {
107 imperiHomeConfig = new ImperiHomeConfig();
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();
117 * OSGi activation callback.
119 * @param config Service config.
122 protected void activate(Map<String, Object> config) {
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);
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);
144 * OSGi config modification callback.
146 * @param config Service config.
149 protected void modified(Map<String, Object> config) {
150 imperiHomeConfig.update(config);
154 * OSGi deactivation callback.
157 protected void deactivate() {
159 httpService.unregister(PATH);
160 } catch (IllegalArgumentException ignored) {
163 itemProcessor.destroy();
165 systemHandler = null;
166 deviceRegistry = null;
167 itemProcessor = null;
168 roomListHandler = null;
169 devicesListHandler = null;
170 deviceActionHandler = null;
171 deviceHistoryHandler = null;
173 logger.info("ImperiHome integration service stopped");
176 @Reference(policy = ReferencePolicy.DYNAMIC)
177 protected void setItemRegistry(ItemRegistry itemRegistry) {
178 this.itemRegistry = itemRegistry;
181 protected void unsetItemRegistry(ItemRegistry itemRegistry) {
182 this.itemRegistry = null;
186 protected void setEventPublisher(EventPublisher eventPublisher) {
187 this.eventPublisher = eventPublisher;
190 protected void unsetEventPublisher(EventPublisher eventPublisher) {
191 this.eventPublisher = null;
195 protected void setHttpService(HttpService httpService) {
196 this.httpService = httpService;
199 protected void unsetHttpService(HttpService httpService) {
200 this.httpService = null;
204 protected void setPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
205 this.persistenceServiceRegistry = persistenceServiceRegistry;
208 protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
209 this.persistenceServiceRegistry = null;
213 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
214 String path = req.getRequestURI();
215 logger.debug("{}: {} {}", req.getRemoteAddr(), req.getMethod(), path);
218 Object response = null;
220 Matcher actionMatcher = URL_PATTERN_DEVICE_ACTION.matcher(path);
221 Matcher historyMatcher = URL_PATTERN_DEVICE_HISTORY.matcher(path);
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);
234 logger.warn("Unrecognized request: {}", path);
237 resp.getWriter().write(gson.toJson(response));
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");