]> git.basschouten.com Git - openhab-addons.git/blob
c438fc650ace272003df0c33fe5f87b49dd9434c
[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.hueemulation.internal.rest;
14
15 import java.util.Set;
16 import java.util.stream.Collectors;
17 import java.util.stream.Stream;
18
19 import javax.ws.rs.Consumes;
20 import javax.ws.rs.DELETE;
21 import javax.ws.rs.GET;
22 import javax.ws.rs.POST;
23 import javax.ws.rs.PUT;
24 import javax.ws.rs.Path;
25 import javax.ws.rs.PathParam;
26 import javax.ws.rs.Produces;
27 import javax.ws.rs.core.Context;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.UriInfo;
31
32 import org.eclipse.jdt.annotation.NonNullByDefault;
33 import org.openhab.core.common.registry.RegistryChangeListener;
34 import org.openhab.core.items.GenericItem;
35 import org.openhab.core.items.Item;
36 import org.openhab.core.items.ItemRegistry;
37 import org.openhab.core.library.CoreItemFactory;
38 import org.openhab.io.hueemulation.internal.ConfigStore;
39 import org.openhab.io.hueemulation.internal.NetworkUtils;
40 import org.openhab.io.hueemulation.internal.dto.HueNewLights;
41 import org.openhab.io.hueemulation.internal.dto.HueSensorEntry;
42 import org.openhab.io.hueemulation.internal.dto.changerequest.HueChangeRequest;
43 import org.openhab.io.hueemulation.internal.dto.response.HueResponse;
44 import org.osgi.service.component.annotations.Activate;
45 import org.osgi.service.component.annotations.Component;
46 import org.osgi.service.component.annotations.Deactivate;
47 import org.osgi.service.component.annotations.Reference;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import io.swagger.annotations.ApiOperation;
52 import io.swagger.annotations.ApiParam;
53 import io.swagger.annotations.ApiResponse;
54 import io.swagger.annotations.ApiResponses;
55
56 /**
57  * Listens to the ItemRegistry and add all DecimalType, OnOffType, ContactType, DimmerType items
58  * as sensors.
59  *
60  * @author David Graeff - Initial contribution
61  */
62 @Component(immediate = false, service = { Sensors.class }, property = "com.eclipsesource.jaxrs.publish=false")
63 @NonNullByDefault
64 @Path("")
65 @Produces(MediaType.APPLICATION_JSON)
66 @Consumes(MediaType.APPLICATION_JSON)
67 public class Sensors implements RegistryChangeListener<Item> {
68     private final Logger logger = LoggerFactory.getLogger(Sensors.class);
69     private static final Set<String> ALLOWED_ITEM_TYPES = Stream.of(CoreItemFactory.COLOR, CoreItemFactory.DIMMER,
70             CoreItemFactory.ROLLERSHUTTER, CoreItemFactory.SWITCH, CoreItemFactory.CONTACT, CoreItemFactory.NUMBER)
71             .collect(Collectors.toSet());
72
73     @Reference
74     protected @NonNullByDefault({}) ConfigStore cs;
75     @Reference
76     protected @NonNullByDefault({}) UserManagement userManagement;
77     @Reference
78     protected @NonNullByDefault({}) ItemRegistry itemRegistry;
79
80     /**
81      * Registers to the {@link ItemRegistry} and enumerates currently existing items.
82      * Call {@link #close(ItemRegistry)} when you are done with this object.
83      *
84      * Only call this after you have set the filter tags with {@link #setFilterTags(Set, Set, Set)}.
85      */
86     @Activate
87     protected void activate() {
88         cs.ds.resetSensors();
89
90         itemRegistry.removeRegistryChangeListener(this);
91         itemRegistry.addRegistryChangeListener(this);
92
93         for (Item item : itemRegistry.getItems()) {
94             added(item);
95         }
96         logger.debug("Added as sensor: {}",
97                 cs.ds.sensors.values().stream().map(l -> l.name).collect(Collectors.joining(", ")));
98     }
99
100     /**
101      * Unregisters from the {@link ItemRegistry}.
102      */
103     @Deactivate
104     protected void deactivate() {
105         itemRegistry.removeRegistryChangeListener(this);
106     }
107
108     @Override
109     public synchronized void added(Item newElement) {
110         if (!(newElement instanceof GenericItem)) {
111             return;
112         }
113         GenericItem element = (GenericItem) newElement;
114
115         if (!ALLOWED_ITEM_TYPES.contains(element.getType())) {
116             return;
117         }
118
119         String hueID = cs.mapItemUIDtoHueID(element);
120
121         HueSensorEntry sensor = new HueSensorEntry(element);
122         cs.ds.sensors.put(hueID, sensor);
123     }
124
125     @Override
126     public synchronized void removed(Item element) {
127         String hueID = cs.mapItemUIDtoHueID(element);
128         logger.debug("Remove item {}", hueID);
129         cs.ds.sensors.remove(hueID);
130     }
131
132     @Override
133     public synchronized void updated(Item oldElement, Item newElement) {
134         if (!(newElement instanceof GenericItem)) {
135             return;
136         }
137         GenericItem element = (GenericItem) newElement;
138
139         String hueID = cs.mapItemUIDtoHueID(element);
140
141         HueSensorEntry sensor = new HueSensorEntry(element);
142         cs.ds.sensors.put(hueID, sensor);
143     }
144
145     @GET
146     @Path("{username}/sensors")
147     @ApiOperation(value = "Return all sensors")
148     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
149     public Response getAllSensorsApi(@Context UriInfo uri,
150             @PathParam("username") @ApiParam(value = "username") String username) {
151         if (!userManagement.authorizeUser(username)) {
152             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
153         }
154         return Response.ok(cs.gson.toJson(cs.ds.sensors)).build();
155     }
156
157     @GET
158     @Path("{username}/sensors/new")
159     @ApiOperation(value = "Return new sensors since last scan. Returns an empty list for openHAB as we do not cache that information.")
160     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
161     public Response getNewSensors(@Context UriInfo uri,
162             @PathParam("username") @ApiParam(value = "username") String username) {
163         if (!userManagement.authorizeUser(username)) {
164             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
165         }
166         return Response.ok(cs.gson.toJson(new HueNewLights())).build();
167     }
168
169     @POST
170     @Path("{username}/sensors")
171     @ApiOperation(value = "Starts a new scan for compatible items. This is usually not necessary, because we are observing the item registry.")
172     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
173     public Response postNewLights(@Context UriInfo uri,
174             @PathParam("username") @ApiParam(value = "username") String username) {
175         if (!userManagement.authorizeUser(username)) {
176             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
177         }
178         return NetworkUtils.singleSuccess(cs.gson, "Searching for new sensors", "/sensors");
179     }
180
181     @GET
182     @Path("{username}/sensors/{id}")
183     @ApiOperation(value = "Return a sensor")
184     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
185     public Response getSensorApi(@Context UriInfo uri, //
186             @PathParam("username") @ApiParam(value = "username") String username,
187             @PathParam("id") @ApiParam(value = "sensor id") String id) {
188         if (!userManagement.authorizeUser(username)) {
189             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
190         }
191         return Response.ok(cs.gson.toJson(cs.ds.sensors.get(id))).build();
192     }
193
194     @SuppressWarnings({ "null", "unused" })
195     @GET
196     @Path("{username}/sensors/{id}/config")
197     @ApiOperation(value = "Return a sensor config. Always empty")
198     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
199     public Response getSensorConfigApi(@Context UriInfo uri, //
200             @PathParam("username") @ApiParam(value = "username") String username,
201             @PathParam("id") @ApiParam(value = "sensor id") String id) {
202         if (!userManagement.authorizeUser(username)) {
203             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
204         }
205
206         HueSensorEntry sensor = cs.ds.sensors.get(id);
207         if (sensor == null) {
208             return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist");
209         }
210
211         return Response.ok(cs.gson.toJson(sensor.config)).build();
212     }
213
214     @SuppressWarnings({ "null", "unused" })
215     @DELETE
216     @Path("{username}/sensors/{id}")
217     @ApiOperation(value = "Deletes the sensor that is represented by this id")
218     @ApiResponses(value = { @ApiResponse(code = 200, message = "The item got removed"),
219             @ApiResponse(code = 403, message = "Access denied") })
220     public Response removeSensorAPI(@Context UriInfo uri,
221             @PathParam("username") @ApiParam(value = "username") String username,
222             @PathParam("id") @ApiParam(value = "id") String id) {
223         if (!userManagement.authorizeUser(username)) {
224             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
225         }
226
227         HueSensorEntry sensor = cs.ds.sensors.get(id);
228         if (sensor == null) {
229             return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist");
230         }
231
232         if (itemRegistry.remove(id) != null) {
233             return NetworkUtils.singleSuccess(cs.gson, "/sensors/" + id + " deleted.");
234         } else {
235             return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist");
236         }
237     }
238
239     @SuppressWarnings({ "null", "unused" })
240     @PUT
241     @Path("{username}/sensors/{id}")
242     @ApiOperation(value = "Rename a sensor")
243     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
244     public Response renameLightApi(@Context UriInfo uri, //
245             @PathParam("username") @ApiParam(value = "username") String username,
246             @PathParam("id") @ApiParam(value = "light id") String id, String body) {
247         if (!userManagement.authorizeUser(username)) {
248             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
249         }
250         HueSensorEntry sensor = cs.ds.sensors.get(id);
251         if (sensor == null) {
252             return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor not existing");
253         }
254
255         final HueChangeRequest changeRequest = cs.gson.fromJson(body, HueChangeRequest.class);
256
257         String name = changeRequest.name;
258         if (name == null || name.isEmpty()) {
259             return NetworkUtils.singleError(cs.gson, uri, HueResponse.INVALID_JSON, "Invalid request: No name set");
260         }
261
262         sensor.item.setLabel(name);
263         itemRegistry.update(sensor.item);
264
265         return NetworkUtils.singleSuccess(cs.gson, name, "/sensors/" + id + "/name");
266     }
267
268     @PUT
269     @Path("{username}/sensors/{id}/state")
270     @ApiOperation(value = "Set sensor state")
271     @ApiResponses(value = { @ApiResponse(code = 200, message = "OK") })
272     public Response setSensorStateApi(@Context UriInfo uri, //
273             @PathParam("username") @ApiParam(value = "username") String username,
274             @PathParam("id") @ApiParam(value = "sensor id") String id, String body) {
275         if (!userManagement.authorizeUser(username)) {
276             return NetworkUtils.singleError(cs.gson, uri, HueResponse.UNAUTHORIZED, "Not Authorized");
277         }
278
279         return NetworkUtils.singleError(cs.gson, uri, HueResponse.SENSOR_NOT_CLIP_SENSOR,
280                 "Invalid request: Not a clip sensor");
281     }
282 }