]> git.basschouten.com Git - openhab-addons.git/blob
938b6f2ce3081a57eff50eae902e69c1822fa80a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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(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 {
70
71     private static final long serialVersionUID = -1966364789075448441L;
72
73     private static final String PATH = "/imperihome/iss";
74
75     private static final String APPLICATION_JSON = "application/json";
76     private static final String CHARSET = "utf-8";
77
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);
85
86     private final Logger logger = LoggerFactory.getLogger(ImperiHomeApiServlet.class);
87
88     private final Gson gson;
89     private final ImperiHomeConfig imperiHomeConfig;
90
91     private HttpService httpService;
92     private ItemRegistry itemRegistry;
93     private PersistenceServiceRegistry persistenceServiceRegistry;
94     private EventPublisher eventPublisher;
95
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;
104
105     /**
106      * Default constructor.
107      */
108     public ImperiHomeApiServlet() {
109         imperiHomeConfig = new ImperiHomeConfig();
110
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();
116     }
117
118     /**
119      * OSGi activation callback.
120      *
121      * @param config Service config.
122      */
123     @Activate
124     protected void activate(Map<String, Object> config) {
125         modified(config);
126
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);
135
136         try {
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);
142         }
143     }
144
145     /**
146      * OSGi config modification callback.
147      *
148      * @param config Service config.
149      */
150     @Modified
151     protected void modified(Map<String, Object> config) {
152         imperiHomeConfig.update(config);
153     }
154
155     /**
156      * OSGi deactivation callback.
157      */
158     @Deactivate
159     protected void deactivate() {
160         try {
161             httpService.unregister(PATH);
162         } catch (IllegalArgumentException ignored) {
163         }
164
165         itemProcessor.destroy();
166
167         systemHandler = null;
168         deviceRegistry = null;
169         itemProcessor = null;
170         roomListHandler = null;
171         devicesListHandler = null;
172         deviceActionHandler = null;
173         deviceHistoryHandler = null;
174
175         logger.info("ImperiHome integration service stopped");
176     }
177
178     @Reference(policy = ReferencePolicy.DYNAMIC)
179     protected void setItemRegistry(ItemRegistry itemRegistry) {
180         this.itemRegistry = itemRegistry;
181     }
182
183     protected void unsetItemRegistry(ItemRegistry itemRegistry) {
184         this.itemRegistry = null;
185     }
186
187     @Reference
188     protected void setEventPublisher(EventPublisher eventPublisher) {
189         this.eventPublisher = eventPublisher;
190     }
191
192     protected void unsetEventPublisher(EventPublisher eventPublisher) {
193         this.eventPublisher = null;
194     }
195
196     @Reference
197     protected void setHttpService(HttpService httpService) {
198         this.httpService = httpService;
199     }
200
201     protected void unsetHttpService(HttpService httpService) {
202         this.httpService = null;
203     }
204
205     @Reference
206     protected void setPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
207         this.persistenceServiceRegistry = persistenceServiceRegistry;
208     }
209
210     protected void unsetPersistenceServiceRegistry(PersistenceServiceRegistry persistenceServiceRegistry) {
211         this.persistenceServiceRegistry = null;
212     }
213
214     @Override
215     protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
216         String path = req.getRequestURI();
217         logger.debug("{}: {} {}", req.getRemoteAddr(), req.getMethod(), path);
218         setHeaders(resp);
219
220         Object response = null;
221
222         Matcher actionMatcher = URL_PATTERN_DEVICE_ACTION.matcher(path);
223         Matcher historyMatcher = URL_PATTERN_DEVICE_HISTORY.matcher(path);
224
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);
235         } else {
236             logger.warn("Unrecognized request: {}", path);
237         }
238
239         resp.getWriter().write(gson.toJson(response));
240     }
241
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");
249     }
250 }