]> git.basschouten.com Git - openhab-addons.git/blob
dd41472e7015d3661fd391a8e5a68738af3db170
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.neeo.internal.servletservices;
14
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Objects;
19
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22
23 import org.apache.commons.io.IOUtils;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.io.neeo.NeeoService;
27 import org.openhab.io.neeo.internal.NeeoBrainServlet;
28 import org.openhab.io.neeo.internal.NeeoConstants;
29 import org.openhab.io.neeo.internal.NeeoUtil;
30 import org.openhab.io.neeo.internal.ServiceContext;
31 import org.openhab.io.neeo.internal.models.NeeoDevice;
32 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
33 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
34 import org.openhab.io.neeo.internal.models.NeeoDeviceType;
35 import org.openhab.io.neeo.internal.models.NeeoThingUID;
36 import org.openhab.io.neeo.internal.servletservices.models.ReturnStatus;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import com.google.gson.Gson;
41 import com.google.gson.JsonParseException;
42
43 /**
44  * A subclass of {@link DefaultServletService} that handles thing status/definitions for the web pages
45  *
46  * @author Tim Roberts - Initial Contribution
47  */
48 @NonNullByDefault
49 public class ThingDashboardService extends DefaultServletService {
50
51     /** The logger */
52     private final Logger logger = LoggerFactory.getLogger(ThingDashboardService.class);
53
54     /** The gson used for json manipulation */
55     private final Gson gson;
56
57     /** The service context */
58     private final ServiceContext context;
59
60     /** The service */
61     private final NeeoService service;
62
63     /**
64      * Constructs the servlet from the {@link NeeoService} and {@link ServiceContext}
65      *
66      * @param service the non-null {@link NeeoService}
67      * @param context the non-null {@link ServiceContext}
68      */
69     public ThingDashboardService(NeeoService service, ServiceContext context) {
70         Objects.requireNonNull(service, "service cannot be null");
71         Objects.requireNonNull(context, "context cannot be null");
72
73         this.context = context;
74         this.service = service;
75         gson = NeeoUtil.createNeeoDeviceGsonBuilder(service, context).create();
76     }
77
78     /**
79      * Returns true if the path starts with "thingstatus", "getchannel", "getvirtualdevice", "restoredevice",
80      * "refreshdevice", "deletedevice", "exportrules", "updatedevice"
81      *
82      * @see DefaultServletService#canHandleRoute(String[])
83      */
84     @Override
85     public boolean canHandleRoute(String[] paths) {
86         return paths.length >= 1 && (paths[0].equalsIgnoreCase("thingstatus") //
87                 || paths[0].equalsIgnoreCase("getchannel") //
88                 || paths[0].equalsIgnoreCase("getvirtualdevice") //
89                 || paths[0].equalsIgnoreCase("restoredevice") //
90                 || paths[0].equalsIgnoreCase("refreshdevice") //
91                 || paths[0].equalsIgnoreCase("deletedevice") //
92                 || paths[0].equalsIgnoreCase("exportrules") //
93                 || paths[0].equalsIgnoreCase("updatedevice"));
94     }
95
96     /**
97      * Handles the get for the 'thingstatus' and 'getchannel' URL (all other URLs do posts)
98      *
99      * @see DefaultServletService#handleGet(HttpServletRequest, String[], HttpServletResponse)
100      */
101     @Override
102     public void handleGet(HttpServletRequest req, String[] paths, HttpServletResponse resp) throws IOException {
103         Objects.requireNonNull(req, "req cannot be null");
104         Objects.requireNonNull(paths, "paths cannot be null");
105         Objects.requireNonNull(resp, "resp cannot be null");
106
107         try {
108             if (paths[0].equalsIgnoreCase("thingstatus")) {
109                 final List<NeeoDevice> devices = context.getDefinitions().getAllDevices();
110                 NeeoUtil.write(resp, gson.toJson(devices));
111             } else if (paths[0].equalsIgnoreCase("getchannel")) {
112                 final String itemName = NeeoUtil.decodeURIComponent(req.getParameter("itemname"));
113                 final List<NeeoDeviceChannel> channels = context.getDefinitions().getNeeoDeviceChannel(itemName);
114                 if (channels == null) {
115                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Channel no longer exists")));
116                 } else {
117                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus(channels)));
118                 }
119             } else if (paths[0].equalsIgnoreCase("getvirtualdevice")) {
120                 final NeeoThingUID uid = context.generate(NeeoConstants.VIRTUAL_THING_TYPE);
121                 final NeeoDevice device = new NeeoDevice(uid, 0, NeeoDeviceType.EXCLUDE, "NEEO Integration",
122                         "New Virtual Thing", new ArrayList<>(), null, null, null, null);
123                 NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device)));
124             } else {
125                 logger.debug("Unknown get path: {}", String.join(",", paths));
126             }
127         } catch (JsonParseException | IllegalArgumentException | NullPointerException e) {
128             logger.debug("Exception handling get: {}", e.getMessage(), e);
129             NeeoUtil.write(resp, gson.toJson(new ReturnStatus(e.getMessage())));
130         }
131     }
132
133     /**
134      * Handles the post for the 'updatedevice', 'restoredevice' or 'refreshdevice'.
135      *
136      * @see DefaultServletService#handlePost(HttpServletRequest, String[], HttpServletResponse)
137      */
138     @Override
139     public void handlePost(HttpServletRequest req, String[] paths, HttpServletResponse resp) throws IOException {
140         Objects.requireNonNull(req, "req cannot be null");
141         Objects.requireNonNull(paths, "paths cannot be null");
142         Objects.requireNonNull(resp, "resp cannot be null");
143         if (paths.length == 0) {
144             throw new IllegalArgumentException("paths cannot be empty");
145         }
146
147         try {
148             if (paths[0].equalsIgnoreCase("updatedevice")) {
149                 final NeeoDevice device = gson.fromJson(req.getReader(), NeeoDevice.class);
150                 context.getDefinitions().put(device);
151
152                 for (NeeoBrainServlet servlet : service.getServlets()) {
153                     servlet.getBrainApi().restart(); // restart so brain will query changes
154                 }
155
156                 NeeoUtil.write(resp, gson.toJson(ReturnStatus.SUCCESS));
157             } else if (paths[0].equalsIgnoreCase("restoredevice")) {
158                 final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader()));
159                 context.getDefinitions().remove(uid);
160                 final NeeoDevice device = context.getDefinitions().getDevice(uid);
161                 if (device == null) {
162                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device no longer exists in openHAB!")));
163                 } else {
164                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device)));
165                 }
166             } else if (paths[0].equalsIgnoreCase("refreshdevice")) {
167                 final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader()));
168                 final NeeoDevice device = context.getDefinitions().getDevice(uid);
169                 if (device == null) {
170                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device no longer exists in openHAB!")));
171                 } else {
172                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus(device)));
173                 }
174             } else if (paths[0].equalsIgnoreCase("deletedevice")) {
175                 final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader()));
176                 final boolean deleted = context.getDefinitions().remove(uid);
177                 NeeoUtil.write(resp, gson.toJson(new ReturnStatus(
178                         deleted ? null : "Device " + uid + " was not found (possibly already deleted?)")));
179             } else if (paths[0].equalsIgnoreCase("exportrules")) {
180                 final NeeoThingUID uid = new NeeoThingUID(IOUtils.toString(req.getReader()));
181                 final NeeoDevice device = context.getDefinitions().getDevice(uid);
182                 if (device == null) {
183                     NeeoUtil.write(resp, gson.toJson(new ReturnStatus("Device " + uid + " was not found")));
184                 } else {
185                     writeExampleRules(resp, device);
186                 }
187             } else {
188                 logger.debug("Unknown post path: {}", String.join(",", paths));
189             }
190         } catch (JsonParseException | IllegalArgumentException | NullPointerException e) {
191             logger.debug("Exception handling post: {}", e.getMessage(), e);
192             NeeoUtil.write(resp, gson.toJson(new ReturnStatus(e.getMessage())));
193         }
194     }
195
196     /**
197      * Helper method to produce an examples rules file and write it to the {@link HttpServletResponse}
198      *
199      * @param resp the non-null {@link HttpServletResponse}
200      * @param device the non-null {@link NeeoDevice}
201      * @throws IOException if an IOException occurs while writing the file
202      */
203     private void writeExampleRules(HttpServletResponse resp, NeeoDevice device) throws IOException {
204         Objects.requireNonNull(resp, "resp cannot be null");
205         Objects.requireNonNull(device, "device cannot be null");
206
207         final StringBuilder sb = new StringBuilder(5000);
208         appendLine(sb, "//////////////////////////////////////////////////////////////////////");
209         sb.append("// Example Rules for ");
210         appendLine(sb, device.getName());
211         appendLine(sb, "//////////////////////////////////////////////////////////////////////");
212         sb.append(System.lineSeparator());
213
214         device.getChannels().stream().filter(c -> c.getKind() == NeeoDeviceChannelKind.TRIGGER).forEach(channel -> {
215             sb.append("rule \"");
216             sb.append(channel.getItemName());
217             appendLine(sb, "\"");
218
219             appendLine(sb, "when");
220             sb.append("   Channel '");
221             sb.append(new ChannelUID(device.getUid(), channel.getItemName()));
222             appendLine(sb, "' triggered");
223             appendLine(sb, "then");
224             appendLine(sb, "   var data = receivedEvent.getEvent()");
225             appendLine(sb, "   # do something here with your data");
226             appendLine(sb, "end");
227         });
228
229         resp.setContentType("text/plain");
230         resp.setHeader("Content-disposition", "attachment; filename=\"" + device.getName() + ".rules\"");
231         IOUtils.write(sb, resp.getOutputStream());
232     }
233
234     /**
235      * Helper method to append a line of text ot the string builder with a line separator
236      *
237      * @param sb a non-null string builder
238      * @param text the non-null, possibly empty text
239      */
240     private void appendLine(StringBuilder sb, String text) {
241         Objects.requireNonNull(sb, "sb cannot be null");
242         Objects.requireNonNull(text, "text cannot be null");
243
244         sb.append(text);
245         sb.append(System.lineSeparator());
246     }
247 }