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.hueemulation.internal.rest;
16 import java.util.stream.Collectors;
17 import java.util.stream.Stream;
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;
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;
51 import io.swagger.annotations.ApiOperation;
52 import io.swagger.annotations.ApiParam;
53 import io.swagger.annotations.ApiResponse;
54 import io.swagger.annotations.ApiResponses;
57 * Listens to the ItemRegistry and add all DecimalType, OnOffType, ContactType, DimmerType items
60 * @author David Graeff - Initial contribution
62 @Component(immediate = false, service = { Sensors.class }, property = "com.eclipsesource.jaxrs.publish=false")
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());
74 protected @NonNullByDefault({}) ConfigStore cs;
76 protected @NonNullByDefault({}) UserManagement userManagement;
78 protected @NonNullByDefault({}) ItemRegistry itemRegistry;
81 * Registers to the {@link ItemRegistry} and enumerates currently existing items.
82 * Call {@link #close(ItemRegistry)} when you are done with this object.
84 * Only call this after you have set the filter tags with {@link #setFilterTags(Set, Set, Set)}.
87 protected void activate() {
90 itemRegistry.removeRegistryChangeListener(this);
91 itemRegistry.addRegistryChangeListener(this);
93 for (Item item : itemRegistry.getItems()) {
96 logger.debug("Added as sensor: {}",
97 cs.ds.sensors.values().stream().map(l -> l.name).collect(Collectors.joining(", ")));
101 * Unregisters from the {@link ItemRegistry}.
104 protected void deactivate() {
105 itemRegistry.removeRegistryChangeListener(this);
109 public synchronized void added(Item newElement) {
110 if (!(newElement instanceof GenericItem)) {
113 GenericItem element = (GenericItem) newElement;
115 if (!ALLOWED_ITEM_TYPES.contains(element.getType())) {
119 String hueID = cs.mapItemUIDtoHueID(element);
121 HueSensorEntry sensor = new HueSensorEntry(element);
122 cs.ds.sensors.put(hueID, sensor);
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);
133 public synchronized void updated(Item oldElement, Item newElement) {
134 if (!(newElement instanceof GenericItem)) {
137 GenericItem element = (GenericItem) newElement;
139 String hueID = cs.mapItemUIDtoHueID(element);
141 HueSensorEntry sensor = new HueSensorEntry(element);
142 cs.ds.sensors.put(hueID, sensor);
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");
154 return Response.ok(cs.gson.toJson(cs.ds.sensors)).build();
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");
166 return Response.ok(cs.gson.toJson(new HueNewLights())).build();
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");
178 return NetworkUtils.singleSuccess(cs.gson, "Searching for new sensors", "/sensors");
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");
191 return Response.ok(cs.gson.toJson(cs.ds.sensors.get(id))).build();
194 @SuppressWarnings({ "null", "unused" })
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");
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");
211 return Response.ok(cs.gson.toJson(sensor.config)).build();
214 @SuppressWarnings({ "null", "unused" })
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");
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");
232 if (itemRegistry.remove(id) != null) {
233 return NetworkUtils.singleSuccess(cs.gson, "/sensors/" + id + " deleted.");
235 return NetworkUtils.singleError(cs.gson, uri, HueResponse.NOT_AVAILABLE, "Sensor does not exist");
239 @SuppressWarnings({ "null", "unused" })
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");
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");
255 final HueChangeRequest changeRequest = cs.gson.fromJson(body, HueChangeRequest.class);
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");
262 sensor.item.setLabel(name);
263 itemRegistry.update(sensor.item);
265 return NetworkUtils.singleSuccess(cs.gson, name, "/sensors/" + id + "/name");
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");
279 return NetworkUtils.singleError(cs.gson, uri, HueResponse.SENSOR_NOT_CLIP_SENSOR,
280 "Invalid request: Not a clip sensor");