mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[hue] Changed discovery to mDNS; added HTTPS handling; refactor HTTPClient to use jetty shared client (#11842)
* Changed discovery to MDNS; added HTTPS handling; refactor HTTPClient to use jetty shared client Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
parent
d2efe69a73
commit
ee34d92c17
@ -1,15 +1,15 @@
|
||||
# Philips Hue Binding
|
||||
|
||||
This binding integrates the [Philips Hue Lighting system](https://www.meethue.com).
|
||||
The integration happens through the Hue bridge, which acts as an IP gateway to the ZigBee devices.
|
||||
The integration happens through the Hue Bridge, which acts as an IP gateway to the ZigBee devices.
|
||||
|
||||
![Philips Hue](doc/hue.jpg)
|
||||
|
||||
## Supported Things
|
||||
|
||||
The Hue bridge is required as a "bridge" for accessing any other Hue device.
|
||||
The Hue Bridge is required as a "bridge" for accessing any other Hue device.
|
||||
It supports the ZigBee LightLink protocol as well as the upwards compatible ZigBee 3.0 protocol.
|
||||
There are two types of Hue bridges, generally referred to as v1 (the rounded version) and v2 (the squared version).
|
||||
There are two types of Hue Bridges, generally referred to as v1 (the rounded version) and v2 (the squared version).
|
||||
Only noticeable difference between the two generation of bridges is the added support for Apple HomeKit in v2.
|
||||
Both bridges are fully supported by this binding.
|
||||
|
||||
@ -17,7 +17,7 @@ Almost all available Hue devices are supported by this binding.
|
||||
This includes not only the "Friends of Hue", but also products like the LivingWhites adapter.
|
||||
Additionally, it is possible to use OSRAM Lightify devices as well as other ZigBee LightLink compatible products, including the IKEA TRÅDFRI lights (when updated).
|
||||
Beside bulbs and luminaires the Hue binding also supports some ZigBee sensors. Currently only Hue specific sensors are tested successfully (Hue Motion Sensor and Hue Dimmer Switch).
|
||||
Please note that the devices need to be registered with the Hue bridge before it is possible for this binding to use them.
|
||||
Please note that the devices need to be registered with the Hue Bridge before it is possible for this binding to use them.
|
||||
|
||||
The Hue binding supports all seven types of lighting devices defined for ZigBee LightLink ([see page 24, table 2](https://www.nxp.com/docs/en/user-guide/JN-UG-3091.pdf).
|
||||
These are:
|
||||
@ -65,28 +65,35 @@ They are presented by the following ZigBee Device ID and _Thing type_:
|
||||
|
||||
The Hue Dimmer Switch has 4 buttons and registers as a Non-Colour Controller switch, while the Hue Tap (also 4 buttons) registers as a Non-Colour Scene Controller in accordance with the ZLL standard.
|
||||
|
||||
Also, Hue bridge support CLIP Generic Status Sensor and CLIP Generic Flag Sensor.
|
||||
Also, Hue Bridge support CLIP Generic Status Sensor and CLIP Generic Flag Sensor.
|
||||
These sensors save state for rules and calculate what actions to do.
|
||||
CLIP Sensor set or get by JSON through IP.
|
||||
|
||||
Finally, the Hue binding also supports the groups of lights and rooms set up on the Hue bridge.
|
||||
Finally, the Hue binding also supports the groups of lights and rooms set up on the Hue Bridge.
|
||||
|
||||
## Discovery
|
||||
|
||||
The Hue bridge is discovered through UPnP in the local network.
|
||||
The Hue Bridge is discovered through mDNS in the local network.
|
||||
Auto-discovery is enabled by default.
|
||||
To disable it, you can add the following line to `<openHAB-conf>/services/runtime.cfg`:
|
||||
|
||||
```
|
||||
discovery.hue:background=false
|
||||
```
|
||||
|
||||
Once it is added as a Thing, its authentication button (in the middle) needs to be pressed in order to authorize the binding to access it.
|
||||
Once the binding is authorized, it automatically reads all devices and groups that are set up on the Hue bridge and puts them into the Inbox.
|
||||
Once the binding is authorized, it automatically reads all devices and groups that are set up on the Hue Bridge and puts them into the Inbox.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The Hue bridge requires the IP address as a configuration value in order for the binding to know where to access it.
|
||||
The Hue Bridge requires the IP address as a configuration value in order for the binding to know where to access it.
|
||||
In the thing file, this looks e.g. like
|
||||
|
||||
```
|
||||
Bridge hue:bridge:1 [ ipAddress="192.168.0.64" ]
|
||||
```
|
||||
|
||||
A user to authenticate against the Hue bridge is automatically generated.
|
||||
A user to authenticate against the Hue Bridge is automatically generated.
|
||||
Please note that the generated user name cannot be written automatically to the `.thing` file, and has to be set manually.
|
||||
The generated user name can be found, after pressing the authentication button on the bridge, with the following console command: `hue <bridgeUID> username`.
|
||||
The user name can be set using the `userName` configuration value, e.g.:
|
||||
@ -96,16 +103,18 @@ Bridge hue:bridge:1 [ ipAddress="192.168.0.64", userName="qwertzuiopasdfghjklyxc
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ipAddress | Network address of the Hue bridge. **Mandatory** |
|
||||
| port | Port of the Hue bridge. Optional, default value is 80 or 443, derived from protocol, otherwise user-defined. |
|
||||
| userName | Name of a registered Hue bridge user, that allows to access the API. **Mandatory** |
|
||||
| pollingInterval | Seconds between fetching light values from the Hue bridge. Optional, the default value is 10 (min="1", step="1"). |
|
||||
| sensorPollingInterval | Milliseconds between fetching sensor-values from the Hue bridge. A higher value means more delay for the sensor values, but a too low value can cause congestion on the bridge. Optional, the default value is 500. Default value will be considered if the value is lower than 50. Use 0 to disable the polling for sensors. |
|
||||
|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| ipAddress | Network address of the Hue Bridge. **Mandatory**. |
|
||||
| port | Port of the Hue Bridge. Optional, default value is 80 or 443, derived from protocol, otherwise user-defined. |
|
||||
| protocol | Protocol to connect to the Hue Bridge ("http" or "https"), default value is "https"). |
|
||||
| useSelfSignedCertificate | Use self-signed certificate for HTTPS connection to Hue Bridge. **Advanced**, default value is `true`. |
|
||||
| userName | Name of a registered Hue Bridge user, that allows to access the API. **Mandatory** |
|
||||
| pollingInterval | Seconds between fetching light values from the Hue Bridge. Optional, the default value is 10 (min="1", step="1"). |
|
||||
| sensorPollingInterval | Milliseconds between fetching sensor-values from the Hue Bridge. A higher value means more delay for the sensor values, but a too low value can cause congestion on the bridge. Optional, the default value is 500. Default value will be considered if the value is lower than 50. Use 0 to disable the polling for sensors. |
|
||||
|
||||
### Devices
|
||||
|
||||
The devices are identified by the number that the Hue bridge assigns to them (also shown in the Hue App as an identifier).
|
||||
The devices are identified by the number that the Hue Bridge assigns to them (also shown in the Hue App as an identifier).
|
||||
Thus, all it needs for manual configuration is this single value like
|
||||
|
||||
```
|
||||
@ -130,13 +139,13 @@ The following device types also have an optional configuration value to specify
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------------------------------------------------------------------------|
|
||||
| lightId | Number of the device provided by the Hue bridge. **Mandatory** |
|
||||
| lightId | Number of the device provided by the Hue Bridge. **Mandatory** |
|
||||
| fadetime | Fade time in Milliseconds to a new state (min="0", step="100", default="400") |
|
||||
|
||||
|
||||
### Groups
|
||||
|
||||
The groups are identified by the number that the Hue bridge assigns to them.
|
||||
The groups are identified by the number that the Hue Bridge assigns to them.
|
||||
Thus, all it needs for manual configuration is this single value like
|
||||
|
||||
```
|
||||
@ -149,7 +158,7 @@ The group type also have an optional configuration value to specify the fade tim
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------------------------------------------------------------------------|
|
||||
| groupId | Number of the group provided by the Hue bridge. **Mandatory** |
|
||||
| groupId | Number of the group provided by the Hue Bridge. **Mandatory** |
|
||||
| fadetime | Fade time in Milliseconds to a new state (min="0", step="100", default="400") |
|
||||
|
||||
|
||||
@ -179,7 +188,7 @@ The devices support some of the following channels:
|
||||
| last_updated | DateTime | This channel the date and time when the sensor was last updated. | 0820, 0830, 0840, 0850, 0106, 0107, 0302 |
|
||||
| battery_level | Number | This channel shows the battery level. | 0820, 0106, 0107, 0302 |
|
||||
| battery_low | Switch | This channel indicates whether the battery is low or not. | 0820, 0106, 0107, 0302 |
|
||||
| scene | String | This channel activates the scene with the given ID String. The ID String of each scene is assigned by the Hue bridge. | bridge, group |
|
||||
| scene | String | This channel activates the scene with the given ID String. The ID String of each scene is assigned by the Hue Bridge. | bridge, group |
|
||||
|
||||
To load a hue scene inside a rule for example, the ID of the scene will be required.
|
||||
You can list all the scene IDs with the following console commands: `hue <bridgeUID> scenes` and `hue <groupThingUID> scenes`.
|
||||
@ -366,11 +375,3 @@ if (receivedEvent == "1000.0")) {
|
||||
//do stuff
|
||||
}
|
||||
```
|
||||
|
||||
### UPnP Discovery: Inbox 'Grace Period'
|
||||
|
||||
The Hue Bridge can sometimes be late in sending its UPnP 'ssdp:alive' notifications even though it has not really gone offline.
|
||||
This means that the Hue Bridge could be repeatedly removed from, and (re)added to, the InBox.
|
||||
Which would lead to confusion in the UI, and repeated logger messages.
|
||||
To prevent this, the binding tells the OpenHAB core to wait for a further period of time ('grace period') before actually removing the Bridge from the Inbox.
|
||||
The 'grace period' has a default value of 50 seconds, but it can be fine tuned in the main UI via Settings | Bindings | Hue | Configure.
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<feature name="openhab-binding-hue" description="Hue Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-upnp</feature>
|
||||
<feature>openhab-transport-mdns</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.hue/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
|
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class CreateScheduleRequest {
|
||||
private String name;
|
||||
private String description;
|
||||
private ScheduleCommand command;
|
||||
private Date time;
|
||||
|
||||
public CreateScheduleRequest(String name, String description, ScheduleCommand command, Date time) {
|
||||
if (name != null && Util.stringSize(name) > 32) {
|
||||
throw new IllegalArgumentException("Schedule name can be at most 32 characters long");
|
||||
}
|
||||
|
||||
if (description != null && Util.stringSize(description) > 64) {
|
||||
throw new IllegalArgumentException("Schedule description can be at most 64 characters long");
|
||||
}
|
||||
|
||||
if (command == null) {
|
||||
throw new IllegalArgumentException("No schedule command specified");
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.command = command;
|
||||
this.time = time;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Detailed schedule information.
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
public class FullSchedule extends Schedule {
|
||||
private String description;
|
||||
private ScheduleCommand command; // Not really appropriate for exposure
|
||||
private Date time;
|
||||
|
||||
/**
|
||||
* Returns the description of the schedule.
|
||||
*
|
||||
* @return description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scheduled command.
|
||||
*
|
||||
* @return command
|
||||
*/
|
||||
public ScheduleCommand getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time for which the command is scheduled to be ran.
|
||||
*
|
||||
* @return scheduled time
|
||||
*/
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpClient {
|
||||
private int timeout = 1000;
|
||||
private final Logger logger = LoggerFactory.getLogger(HttpClient.class);
|
||||
private final LinkedList<AsyncPutParameters> commandsQueue = new LinkedList<>();
|
||||
private @Nullable Future<?> job;
|
||||
|
||||
@SuppressWarnings({ "null", "unused" })
|
||||
private void executeCommands() {
|
||||
while (true) {
|
||||
try {
|
||||
long delayTime = 0;
|
||||
synchronized (commandsQueue) {
|
||||
AsyncPutParameters payloadCallbackPair = commandsQueue.poll();
|
||||
if (payloadCallbackPair != null) {
|
||||
logger.debug("Async sending put to address: {} delay: {} body: {}", payloadCallbackPair.address,
|
||||
payloadCallbackPair.delay, payloadCallbackPair.body);
|
||||
try {
|
||||
Result result = put(payloadCallbackPair.address, payloadCallbackPair.body);
|
||||
payloadCallbackPair.future.complete(result);
|
||||
} catch (IOException e) {
|
||||
payloadCallbackPair.future.completeExceptionally(e);
|
||||
}
|
||||
delayTime = payloadCallbackPair.delay;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Thread.sleep(delayTime);
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("commandExecutorThread was interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public Result get(String address) throws IOException {
|
||||
return doNetwork(address, "GET");
|
||||
}
|
||||
|
||||
public Result post(String address, String body) throws IOException {
|
||||
return doNetwork(address, "POST", body);
|
||||
}
|
||||
|
||||
public Result put(String address, String body) throws IOException {
|
||||
return doNetwork(address, "PUT", body);
|
||||
}
|
||||
|
||||
public CompletableFuture<Result> putAsync(String address, String body, long delay,
|
||||
ScheduledExecutorService scheduler) {
|
||||
AsyncPutParameters asyncPutParameters = new AsyncPutParameters(address, body, delay);
|
||||
|
||||
synchronized (commandsQueue) {
|
||||
if (commandsQueue.isEmpty()) {
|
||||
commandsQueue.offer(asyncPutParameters);
|
||||
Future<?> localJob = job;
|
||||
if (localJob == null || localJob.isDone()) {
|
||||
job = scheduler.submit(this::executeCommands);
|
||||
}
|
||||
} else {
|
||||
commandsQueue.offer(asyncPutParameters);
|
||||
}
|
||||
}
|
||||
|
||||
return asyncPutParameters.future;
|
||||
}
|
||||
|
||||
public Result delete(String address) throws IOException {
|
||||
return doNetwork(address, "DELETE");
|
||||
}
|
||||
|
||||
protected Result doNetwork(String address, String requestMethod) throws IOException {
|
||||
return doNetwork(address, requestMethod, null);
|
||||
}
|
||||
|
||||
protected Result doNetwork(String address, String requestMethod, @Nullable String body) throws IOException {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(address).openConnection();
|
||||
try {
|
||||
conn.setRequestMethod(requestMethod);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setConnectTimeout(timeout);
|
||||
conn.setReadTimeout(timeout);
|
||||
|
||||
if (body != null && !"".equals(body)) {
|
||||
conn.setDoOutput(true);
|
||||
try (Writer out = new OutputStreamWriter(conn.getOutputStream())) {
|
||||
out.write(body);
|
||||
}
|
||||
}
|
||||
|
||||
try (InputStream in = conn.getInputStream(); ByteArrayOutputStream result = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = in.read(buffer)) != -1) {
|
||||
result.write(buffer, 0, length);
|
||||
}
|
||||
return new Result(result.toString(StandardCharsets.UTF_8.name()), conn.getResponseCode());
|
||||
}
|
||||
} finally {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
private final String body;
|
||||
private final int responseCode;
|
||||
|
||||
public Result(String body, int responseCode) {
|
||||
this.body = body;
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
}
|
||||
|
||||
public final class AsyncPutParameters {
|
||||
public final String address;
|
||||
public final String body;
|
||||
public final CompletableFuture<Result> future;
|
||||
public final long delay;
|
||||
|
||||
public AsyncPutParameters(String address, String body, long delay) {
|
||||
this.address = address;
|
||||
this.body = body;
|
||||
this.future = new CompletableFuture<>();
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
}
|
@ -89,8 +89,6 @@ public class HueBindingConstants {
|
||||
|
||||
// Bridge config properties
|
||||
public static final String HOST = "ipAddress";
|
||||
public static final String PORT = "port";
|
||||
public static final String PROTOCOL = "protocol";
|
||||
public static final String USER_NAME = "userName";
|
||||
|
||||
// Thing configuration properties
|
||||
@ -102,4 +100,11 @@ public class HueBindingConstants {
|
||||
public static final String GROUP_ID = "groupId";
|
||||
|
||||
public static final String NORMALIZE_ID_REGEX = "[^a-zA-Z0-9_]";
|
||||
|
||||
//
|
||||
public static final String TEXT_OFFLINE_COMMUNICATION_ERROR = "@text/offline.communication-error";
|
||||
public static final String TEXT_OFFLINE_CONFIGURATION_ERROR_INVALID_SSL_CERIFICATE = "@text/offline.conf-error-invalid-ssl-certificate";
|
||||
|
||||
// Config status messages
|
||||
public static final String IP_ADDRESS_MISSING = "missing-ip-address-configuration";
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
|
||||
/**
|
||||
* The {@link HueConfigStatusMessage} defines
|
||||
* the keys to be used for {@link ConfigStatusMessage}s.
|
||||
*
|
||||
* @author Alexander Kostadinov - Initial contribution
|
||||
* @author Kai Kreuzer - Changed from enum to interface
|
||||
*
|
||||
*/
|
||||
public interface HueConfigStatusMessage {
|
||||
|
||||
static final String IP_ADDRESS_MISSING = "missing-ip-address-configuration";
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
class PortalDiscoveryResult {
|
||||
public static final Type GSON_TYPE = new TypeToken<List<PortalDiscoveryResult>>() {
|
||||
}.getType();
|
||||
|
||||
private String internalipaddress;
|
||||
|
||||
public String getIPAddress() {
|
||||
return internalipaddress;
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* Information about a scheduled command.
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
public class ScheduleCommand {
|
||||
private String address;
|
||||
private String method;
|
||||
private JsonElement body;
|
||||
|
||||
ScheduleCommand(String address, String method, JsonElement body) {
|
||||
this.address = address;
|
||||
this.method = method;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative request url.
|
||||
*
|
||||
* @return request url
|
||||
*/
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request method.
|
||||
* Can be GET, PUT, POST or DELETE.
|
||||
*
|
||||
* @return request method
|
||||
*/
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request body.
|
||||
*
|
||||
* @return request body
|
||||
*/
|
||||
public String getBody() {
|
||||
return body.toString();
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link LightActions} defines {@link ThingActions} for the hue lights.
|
||||
* The {@link LightActions} defines {@link ThingActions} for the Hue lights.
|
||||
*
|
||||
* @author Jochen Leopold - Initial contribution
|
||||
*/
|
||||
|
@ -26,59 +26,16 @@ public class HueBridgeConfig {
|
||||
public static final String HTTP = "http";
|
||||
public static final String HTTPS = "https";
|
||||
|
||||
private @Nullable String ipAddress;
|
||||
private @Nullable Integer port;
|
||||
private String protocol = HTTP;
|
||||
private @Nullable String userName;
|
||||
private int pollingInterval = 10;
|
||||
private int sensorPollingInterval = 500;
|
||||
|
||||
public @Nullable String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
public @Nullable String ipAddress;
|
||||
public @Nullable Integer port;
|
||||
public String protocol = HTTPS;
|
||||
public boolean useSelfSignedCertificate = true;
|
||||
public @Nullable String userName;
|
||||
public int pollingInterval = 10;
|
||||
public int sensorPollingInterval = 500;
|
||||
|
||||
public int getPort() {
|
||||
Integer thePort = this.port;
|
||||
Integer thePort = port;
|
||||
return (thePort != null) ? thePort.intValue() : HTTPS.equals(protocol) ? 443 : 80;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public @Nullable String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public int getPollingInterval() {
|
||||
return pollingInterval;
|
||||
}
|
||||
|
||||
public void setPollingInterval(int pollingInterval) {
|
||||
this.pollingInterval = pollingInterval;
|
||||
}
|
||||
|
||||
public int getSensorPollingInterval() {
|
||||
return sensorPollingInterval;
|
||||
}
|
||||
|
||||
public void setSensorPollingInterval(int sensorPollingInterval) {
|
||||
this.sensorPollingInterval = sensorPollingInterval;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.io.net.http.PEMTrustManager;
|
||||
import org.openhab.core.io.net.http.PEMTrustManager.CertificateInstantiationException;
|
||||
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
|
||||
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provides a {@link PEMTrustManager} to allow secure connections to any Hue Bridge.
|
||||
*
|
||||
* @author Christoph Weitkamp - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueTlsTrustManagerProvider implements TlsTrustManagerProvider {
|
||||
|
||||
private static final String PEM_FILENAME = "huebridge_cacert.pem";
|
||||
private final String hostname;
|
||||
private final boolean useSelfSignedCertificate;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HueTlsTrustManagerProvider.class);
|
||||
|
||||
public HueTlsTrustManagerProvider(String hostname, boolean useSelfSignedCertificate) {
|
||||
this.hostname = hostname;
|
||||
this.useSelfSignedCertificate = useSelfSignedCertificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHostName() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509ExtendedTrustManager getTrustManager() {
|
||||
try {
|
||||
if (useSelfSignedCertificate) {
|
||||
logger.trace("Use self-signed certificate downloaded from Hue Bridge.");
|
||||
// use self-signed certificate downloaded from Hue Bridge
|
||||
return PEMTrustManager.getInstanceFromServer("https://" + getHostName());
|
||||
} else {
|
||||
logger.trace("Use Signify private CA Certificate for Hue Bridges from resources.");
|
||||
// use Signify private CA Certificate for Hue Bridges from resources
|
||||
return getInstanceFromResource(PEM_FILENAME);
|
||||
}
|
||||
} catch (CertificateException | MalformedURLException e) {
|
||||
logger.error("An unexpected exception occurred - returning a TrustAllTrustManager: {}", e.getMessage(), e);
|
||||
}
|
||||
return TrustAllTrustManager.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PEMTrustManager} instance by reading the PEM certificate from the given file.
|
||||
* This is useful if you have a private CA Certificate stored in a file.
|
||||
*
|
||||
* @param fileName name to the PEM file located in the resources folder
|
||||
* @return a {@link PEMTrustManager} instance
|
||||
* @throws CertificateInstantiationException
|
||||
*/
|
||||
private PEMTrustManager getInstanceFromResource(String fileName) throws CertificateException {
|
||||
String pemCert = readPEMCertificateStringFromResource(fileName);
|
||||
if (pemCert != null) {
|
||||
return new PEMTrustManager(pemCert);
|
||||
}
|
||||
throw new CertificateInstantiationException(
|
||||
String.format("Certificate resource '%s' not found or not accessible.", fileName));
|
||||
}
|
||||
|
||||
private @Nullable String readPEMCertificateStringFromResource(String fileName) {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource(fileName);
|
||||
if (resource != null) {
|
||||
try (InputStream certInputStream = resource.openStream()) {
|
||||
return new String(certInputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
logger.error("An unexpected exception occurred: ", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ public class HueCommandExtension extends AbstractConsoleCommandExtension {
|
||||
|
||||
@Activate
|
||||
public HueCommandExtension(final @Reference ThingRegistry thingRegistry) {
|
||||
super("hue", "Interact with the hue binding.");
|
||||
super("hue", "Interact with the Hue binding.");
|
||||
this.thingRegistry = thingRegistry;
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ public class HueCommandExtension extends AbstractConsoleCommandExtension {
|
||||
console.println("No handler initialized for the thingUID '" + args[0] + "'");
|
||||
printUsage(console);
|
||||
} else if (bridgeHandler == null && groupHandler == null) {
|
||||
console.println("'" + args[0] + "' is neither a Hue bridgeUID nor a Hue groupThingUID");
|
||||
console.println("'" + args[0] + "' is neither a Hue BridgeUID nor a Hue groupThingUID");
|
||||
printUsage(console);
|
||||
} else {
|
||||
switch (args[1]) {
|
||||
@ -87,7 +87,7 @@ public class HueCommandExtension extends AbstractConsoleCommandExtension {
|
||||
String userName = bridgeHandler.getUserName();
|
||||
console.println("Your user name is " + (userName != null ? userName : "undefined"));
|
||||
} else {
|
||||
console.println("'" + args[0] + "' is not a Hue bridgeUID");
|
||||
console.println("'" + args[0] + "' is not a Hue BridgeUID");
|
||||
printUsage(console);
|
||||
}
|
||||
break;
|
||||
|
@ -1,134 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.jupnp.model.meta.DeviceDetails;
|
||||
import org.jupnp.model.meta.ModelDetails;
|
||||
import org.jupnp.model.meta.RemoteDevice;
|
||||
import org.openhab.binding.hue.internal.HueBindingConstants;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||
import org.openhab.core.config.discovery.upnp.internal.UpnpDiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.cm.Configuration;
|
||||
import org.osgi.service.cm.ConfigurationAdmin;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HueBridgeDiscoveryParticipant} is responsible for discovering new and
|
||||
* removed hue bridges. It uses the central {@link UpnpDiscoveryService}.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Thomas Höfer - Added representation
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = UpnpDiscoveryParticipant.class)
|
||||
public class HueBridgeDiscoveryParticipant implements UpnpDiscoveryParticipant {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HueBridgeDiscoveryParticipant.class);
|
||||
|
||||
// Hue bridges have maxAge 100 seconds, so set the default grace period to half of that
|
||||
private long removalGracePeriodSeconds = 50;
|
||||
|
||||
private final ConfigurationAdmin configAdmin;
|
||||
|
||||
@Activate
|
||||
public HueBridgeDiscoveryParticipant(final @Reference ConfigurationAdmin configAdmin) {
|
||||
this.configAdmin = configAdmin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.singleton(THING_TYPE_BRIDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(RemoteDevice device) {
|
||||
ThingUID uid = getThingUID(device);
|
||||
if (uid != null) {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(HOST, device.getDetails().getBaseURL().getHost());
|
||||
properties.put(PORT, device.getDetails().getBaseURL().getPort());
|
||||
properties.put(PROTOCOL, device.getDetails().getBaseURL().getProtocol());
|
||||
String serialNumber = device.getDetails().getSerialNumber();
|
||||
DiscoveryResult result;
|
||||
if (serialNumber != null && !serialNumber.isBlank()) {
|
||||
properties.put(PROPERTY_SERIAL_NUMBER, serialNumber.toLowerCase());
|
||||
|
||||
result = DiscoveryResultBuilder.create(uid).withProperties(properties)
|
||||
.withLabel(device.getDetails().getFriendlyName())
|
||||
.withRepresentationProperty(PROPERTY_SERIAL_NUMBER).build();
|
||||
} else {
|
||||
result = DiscoveryResultBuilder.create(uid).withProperties(properties)
|
||||
.withLabel(device.getDetails().getFriendlyName()).build();
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(RemoteDevice device) {
|
||||
DeviceDetails details = device.getDetails();
|
||||
if (details != null) {
|
||||
ModelDetails modelDetails = details.getModelDetails();
|
||||
String serialNumber = details.getSerialNumber();
|
||||
if (modelDetails != null && serialNumber != null && !serialNumber.isBlank()) {
|
||||
String modelName = modelDetails.getModelName();
|
||||
if (modelName != null) {
|
||||
if (modelName.startsWith("Philips hue bridge")) {
|
||||
return new ThingUID(THING_TYPE_BRIDGE, serialNumber.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemovalGracePeriodSeconds(RemoteDevice device) {
|
||||
try {
|
||||
Configuration conf = configAdmin.getConfiguration("binding.hue");
|
||||
Dictionary<String, @Nullable Object> properties = conf.getProperties();
|
||||
if (properties != null) {
|
||||
Object property = properties.get(HueBindingConstants.REMOVAL_GRACE_PERIOD);
|
||||
if (property != null) {
|
||||
removalGracePeriodSeconds = Long.parseLong(property.toString());
|
||||
}
|
||||
}
|
||||
} catch (IOException | IllegalStateException | NumberFormatException e) {
|
||||
// fall through to pre-initialised (default) value
|
||||
}
|
||||
logger.trace("getRemovalGracePeriodSeconds={}", removalGracePeriodSeconds);
|
||||
return removalGracePeriodSeconds;
|
||||
}
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jmdns.ServiceInfo;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.config.discovery.mdns.internal.MDNSDiscoveryService;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Modified;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HueBridgeMDNSDiscoveryParticipant} is responsible for discovering new and removed Hue Bridges. It uses the
|
||||
* central {@link MDNSDiscoveryService}.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Thomas Höfer - Added representation
|
||||
* @author Christoph Weitkamp - Change discovery protocol to mDNS
|
||||
*/
|
||||
@Component(configurationPid = "discovery.hue")
|
||||
@NonNullByDefault
|
||||
public class HueBridgeMDNSDiscoveryParticipant implements MDNSDiscoveryParticipant {
|
||||
|
||||
private static final String SERVICE_TYPE = "_hue._tcp.local.";
|
||||
private static final String MDNS_PROPERTY_BRIDGE_ID = "bridgeid";
|
||||
private static final String MDNS_PROPERTY_MODEL_ID = "modelid";
|
||||
|
||||
private static final String CONFIG_PROPERTY_REMOVAL_GRACE_PERIOD = "removalGracePeriod";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HueBridgeMDNSDiscoveryParticipant.class);
|
||||
|
||||
private long removalGracePeriod = 0L;
|
||||
|
||||
private boolean isAutoDiscoveryEnabled = true;
|
||||
|
||||
@Activate
|
||||
protected void activate(ComponentContext componentContext) {
|
||||
activateOrModifyService(componentContext);
|
||||
}
|
||||
|
||||
@Modified
|
||||
protected void modified(ComponentContext componentContext) {
|
||||
activateOrModifyService(componentContext);
|
||||
}
|
||||
|
||||
private void activateOrModifyService(ComponentContext componentContext) {
|
||||
Dictionary<String, @Nullable Object> properties = componentContext.getProperties();
|
||||
String autoDiscoveryPropertyValue = (String) properties
|
||||
.get(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY);
|
||||
if (autoDiscoveryPropertyValue != null && !autoDiscoveryPropertyValue.isBlank()) {
|
||||
isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue);
|
||||
}
|
||||
String removalGracePeriodPropertyValue = (String) properties.get(CONFIG_PROPERTY_REMOVAL_GRACE_PERIOD);
|
||||
if (removalGracePeriodPropertyValue != null && !removalGracePeriodPropertyValue.isBlank()) {
|
||||
try {
|
||||
removalGracePeriod = Long.parseLong(removalGracePeriodPropertyValue);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("Configuration property '{}' has invalid value: {}", CONFIG_PROPERTY_REMOVAL_GRACE_PERIOD,
|
||||
removalGracePeriodPropertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return HueBridgeHandler.SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceType() {
|
||||
return SERVICE_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable DiscoveryResult createResult(ServiceInfo service) {
|
||||
if (isAutoDiscoveryEnabled) {
|
||||
ThingUID uid = getThingUID(service);
|
||||
if (uid != null) {
|
||||
String host = service.getHostAddresses()[0];
|
||||
String id = service.getPropertyString(MDNS_PROPERTY_BRIDGE_ID);
|
||||
String friendlyName = String.format("%s (%s)", service.getName(), host);
|
||||
return DiscoveryResultBuilder.create(uid) //
|
||||
.withProperties(Map.of( //
|
||||
HOST, host, //
|
||||
Thing.PROPERTY_MODEL_ID, service.getPropertyString(MDNS_PROPERTY_MODEL_ID), //
|
||||
Thing.PROPERTY_SERIAL_NUMBER, id.toLowerCase())) //
|
||||
.withLabel(friendlyName) //
|
||||
.withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER) //
|
||||
.withTTL(120L) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingUID getThingUID(ServiceInfo service) {
|
||||
String id = service.getPropertyString(MDNS_PROPERTY_BRIDGE_ID);
|
||||
if (id != null && !id.isBlank()) {
|
||||
return new ThingUID(THING_TYPE_BRIDGE, id.toLowerCase());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRemovalGracePeriodSeconds(ServiceInfo service) {
|
||||
return removalGracePeriod;
|
||||
}
|
||||
}
|
@ -13,25 +13,21 @@
|
||||
package org.openhab.binding.hue.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.io.net.http.HttpUtil;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
@ -42,37 +38,29 @@ import com.google.gson.JsonParseException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* The {@link HueBridgeNupnpDiscovery} is responsible for discovering new hue bridges. It uses the 'NUPnP service
|
||||
* The {@link HueBridgeNupnpDiscovery} is responsible for discovering new Hue Bridges. It uses the 'NUPnP service
|
||||
* provided by Philips'.
|
||||
*
|
||||
* @author Awelkiyar Wehabrebi - Initial contribution
|
||||
* @author Christoph Knauf - Refactorings
|
||||
* @author Andre Fuechsel - make {@link #startScan()} asynchronous
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, configurationPid = "discovery.hue")
|
||||
@NonNullByDefault
|
||||
public class HueBridgeNupnpDiscovery extends AbstractDiscoveryService {
|
||||
|
||||
private static final String MODEL_NAME_PHILIPS_HUE = "<modelName>Philips hue";
|
||||
|
||||
private static final String MODEL_NAME_PHILIPS_HUE = "\"name\":\"Philips Hue\"";
|
||||
protected static final String BRIDGE_INDICATOR = "fffe";
|
||||
|
||||
private static final String DISCOVERY_URL = "https://discovery.meethue.com/";
|
||||
|
||||
protected static final String LABEL_PATTERN = "Philips hue (IP)";
|
||||
|
||||
private static final String DESC_URL_PATTERN = "http://HOST/description.xml";
|
||||
|
||||
protected static final String LABEL_PATTERN = "Philips Hue (%s)";
|
||||
private static final String CONFIG_URL_PATTERN = "http://%s/api/0/config";
|
||||
private static final int REQUEST_TIMEOUT = 5000;
|
||||
|
||||
private static final int DISCOVERY_TIMEOUT = 10;
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BRIDGE);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HueBridgeNupnpDiscovery.class);
|
||||
|
||||
public HueBridgeNupnpDiscovery() {
|
||||
super(SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT, false);
|
||||
super(HueBridgeHandler.SUPPORTED_THING_TYPES, DISCOVERY_TIMEOUT, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -87,37 +75,25 @@ public class HueBridgeNupnpDiscovery extends AbstractDiscoveryService {
|
||||
for (BridgeJsonParameters bridge : getBridgeList()) {
|
||||
if (isReachableAndValidHueBridge(bridge)) {
|
||||
String host = bridge.getInternalIpAddress();
|
||||
String serialNumber = bridge.getId().substring(0, 6) + bridge.getId().substring(10);
|
||||
serialNumber = serialNumber.toLowerCase();
|
||||
String serialNumber = bridge.getId().toLowerCase();
|
||||
ThingUID uid = new ThingUID(THING_TYPE_BRIDGE, serialNumber);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid)
|
||||
.withProperties(buildProperties(host, serialNumber))
|
||||
.withLabel(LABEL_PATTERN.replace("IP", host)).withRepresentationProperty(PROPERTY_SERIAL_NUMBER)
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid) //
|
||||
.withProperties(Map.of( //
|
||||
HOST, host, //
|
||||
Thing.PROPERTY_SERIAL_NUMBER, serialNumber)) //
|
||||
.withLabel(String.format(LABEL_PATTERN, host)) //
|
||||
.withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER) //
|
||||
.build();
|
||||
thingDiscovered(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the bridge properties.
|
||||
*
|
||||
* @param host the ip of the bridge
|
||||
* @param serialNumber the id of the bridge
|
||||
* @return the bridge properties
|
||||
*/
|
||||
private Map<String, Object> buildProperties(String host, String serialNumber) {
|
||||
Map<String, Object> properties = new HashMap<>(2);
|
||||
properties.put(HOST, host);
|
||||
properties.put(PROPERTY_SERIAL_NUMBER, serialNumber);
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Bridge is a reachable Hue Bridge with a valid id.
|
||||
*
|
||||
* @param bridge the {@link BridgeJsonParameters}s
|
||||
* @return true if Bridge is a reachable Hue Bridge with a id containing
|
||||
* @return true if Hue Bridge is a reachable Hue Bridge with a id containing
|
||||
* BRIDGE_INDICATOR longer then 10
|
||||
*/
|
||||
private boolean isReachableAndValidHueBridge(BridgeJsonParameters bridge) {
|
||||
@ -136,14 +112,14 @@ public class HueBridgeNupnpDiscovery extends AbstractDiscoveryService {
|
||||
logger.debug("Bridge not discovered: id {} is shorter then 10.", id);
|
||||
return false;
|
||||
}
|
||||
if (!id.substring(6, 10).equals(BRIDGE_INDICATOR)) {
|
||||
if (!BRIDGE_INDICATOR.equals(id.substring(6, 10))) {
|
||||
logger.debug(
|
||||
"Bridge not discovered: id {} does not contain bridge indicator {} or its at the wrong position.",
|
||||
id, BRIDGE_INDICATOR);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
description = doGetRequest(DESC_URL_PATTERN.replace("HOST", host));
|
||||
description = doGetRequest(String.format(CONFIG_URL_PATTERN, host));
|
||||
} catch (IOException e) {
|
||||
logger.debug("Bridge not discovered: Failure accessing description file for ip: {}", host);
|
||||
return false;
|
||||
@ -170,10 +146,10 @@ public class HueBridgeNupnpDiscovery extends AbstractDiscoveryService {
|
||||
return Objects.requireNonNull(bridgeParameters);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Philips Hue NUPnP service not reachable. Can't discover bridges");
|
||||
} catch (JsonParseException je) {
|
||||
} catch (JsonParseException e) {
|
||||
logger.debug("Invalid json respone from Hue NUPnP service. Can't discover bridges");
|
||||
}
|
||||
return new ArrayList<>();
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,6 @@ package org.openhab.binding.hue.internal.discovery;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -26,10 +25,10 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.FullGroup;
|
||||
import org.openhab.binding.hue.internal.FullHueObject;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.FullHueObject;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
|
||||
import org.openhab.binding.hue.internal.handler.HueGroupHandler;
|
||||
import org.openhab.binding.hue.internal.handler.HueLightHandler;
|
||||
@ -43,7 +42,6 @@ import org.openhab.binding.hue.internal.handler.sensors.TemperatureHandler;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
@ -53,8 +51,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link HueBridgeServiceTracker} tracks for hue lights, sensors and groups which are connected
|
||||
* to a paired hue bridge. The default search time for hue is 60 seconds.
|
||||
* The {@link HueBridgeServiceTracker} tracks for Hue lights, sensors and groups which are connected
|
||||
* to a paired Hue Bridge. The default search time for Hue is 60 seconds.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Andre Fuechsel - changed search timeout, changed discovery result creation to support generic thing types;
|
||||
@ -67,16 +65,15 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Laurent Garnier - Added support for groups
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueDeviceDiscoveryService extends AbstractDiscoveryService
|
||||
implements DiscoveryService, ThingHandlerService {
|
||||
public class HueDeviceDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(Stream
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
|
||||
.of(HueLightHandler.SUPPORTED_THING_TYPES.stream(), DimmerSwitchHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
TapSwitchHandler.SUPPORTED_THING_TYPES.stream(), PresenceHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
GeofencePresenceHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
TemperatureHandler.SUPPORTED_THING_TYPES.stream(), LightLevelHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream())
|
||||
.flatMap(i -> i).collect(Collectors.toSet()));
|
||||
.flatMap(i -> i).collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
// @formatter:off
|
||||
private static final Map<String, String> TYPE_TO_ZIGBEE_ID_MAP = Map.ofEntries(
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.regex.Matcher;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
@ -18,9 +18,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
* @author Samuel Leisering - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ApiVersionUtils {
|
||||
public final class ApiVersionUtils {
|
||||
|
||||
private static ApiVersion fullLights = new ApiVersion(1, 11, 0);
|
||||
private static final ApiVersion FULL_LIGHTS = new ApiVersion(1, 11, 0);
|
||||
|
||||
ApiVersionUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from version 1.11, <code>GET</code>ing the Lights always returns {@link FullLight}s instead of
|
||||
@ -29,6 +32,6 @@ public class ApiVersionUtils {
|
||||
* @return
|
||||
*/
|
||||
public static boolean supportsFullLights(ApiVersion version) {
|
||||
return fullLights.compare(version) <= 0;
|
||||
return FULL_LIGHTS.compare(version) <= 0;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
/**
|
||||
* Collection of updates to the bridge configuration.
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@ -21,8 +21,8 @@ import com.google.gson.Gson;
|
||||
* @author Samuel Leisering - changed Command visibility to public
|
||||
*/
|
||||
public class Command {
|
||||
String key;
|
||||
Object value;
|
||||
public String key;
|
||||
public Object value;
|
||||
|
||||
public Command(String key, Object value) {
|
||||
this.key = key;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
@ -26,7 +26,7 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class ConfigUpdate {
|
||||
|
||||
protected final ArrayList<Command> commands = new ArrayList<>();
|
||||
public final ArrayList<Command> commands = new ArrayList<>();
|
||||
|
||||
public ConfigUpdate() {
|
||||
super();
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -18,7 +18,7 @@ package org.openhab.binding.hue.internal;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class CreateUserRequest {
|
||||
public class CreateUserRequest {
|
||||
private String username;
|
||||
private String devicetype;
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
@ -18,11 +18,10 @@ import java.util.List;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
class ErrorResponse {
|
||||
public class ErrorResponse {
|
||||
public static final Type GSON_TYPE = new TypeToken<List<ErrorResponse>>() {
|
||||
}.getType();
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
@ -40,7 +40,7 @@ public class FullGroup extends Group {
|
||||
/**
|
||||
* Test constructor
|
||||
*/
|
||||
FullGroup(String id, String name, String type, State action, List<String> lights, State state) {
|
||||
public FullGroup(String id, String name, String type, State action, List<String> lights, State state) {
|
||||
super(id, name, type);
|
||||
this.action = action;
|
||||
this.lights = lights;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.NORMALIZE_ID_REGEX;
|
||||
|
||||
@ -20,7 +20,7 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Detailed information about an object on the hue bridge
|
||||
* Detailed information about an object on the Hue Bridge
|
||||
*
|
||||
* @author Samuel Leisering - Initial contribution
|
||||
* @author Christoph Weitkamp - Initial contribution
|
||||
@ -53,7 +53,7 @@ public class FullHueObject extends HueObject {
|
||||
/**
|
||||
* Set the type of the object.
|
||||
*/
|
||||
protected void setType(final String type) {
|
||||
public void setType(final String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ public class FullHueObject extends HueObject {
|
||||
/**
|
||||
* Set the model ID of the object.
|
||||
*/
|
||||
protected void setModelID(final String modelId) {
|
||||
public void setModelID(final String modelId) {
|
||||
this.modelid = modelId;
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public class FullHueObject extends HueObject {
|
||||
/**
|
||||
* Sets the unique id of the object.
|
||||
*/
|
||||
protected void setUniqueID(final String uniqueid) {
|
||||
public void setUniqueID(final String uniqueid) {
|
||||
this.uniqueid = uniqueid;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.Duration;
|
||||
@ -18,7 +18,6 @@ import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.dto.Capabilities;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
/**
|
||||
* Basic group information.
|
||||
@ -24,7 +24,7 @@ public class Group {
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
Group() {
|
||||
public Group() {
|
||||
this.id = "0";
|
||||
this.name = "Lightset 0";
|
||||
this.type = "LightGroup";
|
||||
@ -39,11 +39,11 @@ public class Group {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
void setName(String name) {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
void setId(String id) {
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
@ -34,7 +34,7 @@ public class HueObject {
|
||||
HueObject() {
|
||||
}
|
||||
|
||||
void setId(String id) {
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -10,9 +10,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
|
||||
/**
|
||||
* Updates the configuration of a light level sensor
|
@ -10,13 +10,12 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
class NewLightsResponse {
|
||||
public class NewLightsResponse {
|
||||
public String lastscan;
|
||||
}
|
@ -10,9 +10,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
|
||||
/**
|
||||
* Updates the configuration of a presence sensor
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
@ -51,7 +51,7 @@ public class Scene {
|
||||
/**
|
||||
* Test constructor
|
||||
*/
|
||||
Scene(String id, String name, @Nullable String groupId, List<String> lightIds, boolean recycle) {
|
||||
public Scene(String id, String name, @Nullable String groupId, List<String> lightIds, boolean recycle) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.groupId = groupId;
|
||||
@ -63,7 +63,7 @@ public class Scene {
|
||||
return id;
|
||||
}
|
||||
|
||||
void setId(String id) {
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
@ -30,7 +30,7 @@ public class Schedule {
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
void setId(String id) {
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -21,7 +21,7 @@ import java.util.List;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class SearchForLightsRequest {
|
||||
public class SearchForLightsRequest {
|
||||
private List<String> deviceid;
|
||||
|
||||
public SearchForLightsRequest(List<String> deviceid) {
|
@ -10,9 +10,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.CONFIG_ON;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.CONFIG_ON;
|
||||
|
||||
/**
|
||||
* Collection of updates to the sensor configuration.
|
@ -10,9 +10,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -20,9 +24,10 @@ import java.util.List;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
class SetAttributesRequest {
|
||||
private String name;
|
||||
private List<String> lights;
|
||||
@NonNullByDefault
|
||||
public class SetAttributesRequest {
|
||||
private final @Nullable String name;
|
||||
private final @Nullable List<String> lights;
|
||||
|
||||
public SetAttributesRequest(String name) {
|
||||
this(name, null);
|
||||
@ -32,7 +37,7 @@ class SetAttributesRequest {
|
||||
this(null, lights);
|
||||
}
|
||||
|
||||
public SetAttributesRequest(String name, List<HueObject> lights) {
|
||||
public SetAttributesRequest(@Nullable String name, @Nullable List<HueObject> lights) {
|
||||
if (name != null && Util.stringSize(name) > 32) {
|
||||
throw new IllegalArgumentException("Name can be at most 32 characters long");
|
||||
} else if (lights != null && (lights.isEmpty() || lights.size() > 16)) {
|
||||
@ -40,8 +45,6 @@ class SetAttributesRequest {
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
if (lights != null) {
|
||||
this.lights = Util.lightsToIds(lights);
|
||||
}
|
||||
this.lights = lights == null ? null : lights.stream().map(l -> l.getId()).collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
/**
|
||||
* Details of a bridge firmware update.
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@ -23,14 +23,14 @@ import java.util.Arrays;
|
||||
*/
|
||||
public class State {
|
||||
private boolean on;
|
||||
int bri;
|
||||
int hue;
|
||||
int sat;
|
||||
public int bri;
|
||||
public int hue;
|
||||
public int sat;
|
||||
private float[] xy;
|
||||
int ct;
|
||||
public int ct;
|
||||
private String alert;
|
||||
private String effect;
|
||||
String colormode;
|
||||
public String colormode;
|
||||
private boolean reachable;
|
||||
|
||||
public State() {
|
@ -10,11 +10,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import org.openhab.binding.hue.internal.State.AlertMode;
|
||||
import org.openhab.binding.hue.internal.State.Effect;
|
||||
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||
import org.openhab.binding.hue.internal.dto.State.AlertMode;
|
||||
import org.openhab.binding.hue.internal.dto.State.Effect;
|
||||
|
||||
/**
|
||||
* Collection of updates to the state of a light.
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
@ -19,11 +19,10 @@ import java.util.Map;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
class SuccessResponse {
|
||||
public class SuccessResponse {
|
||||
public static final Type GSON_TYPE = new TypeToken<List<SuccessResponse>>() {
|
||||
}.getType();
|
||||
|
@ -10,9 +10,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.CONFIG_LED_INDICATION;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.CONFIG_LED_INDICATION;
|
||||
|
||||
/**
|
||||
* Updates the configuration of a temperature sensor
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -10,11 +10,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.dto;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -22,12 +20,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Q42 - Initial contribution
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class Util {
|
||||
public final class Util {
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
@ -36,28 +34,6 @@ class Util {
|
||||
return str.getBytes(StandardCharsets.UTF_8).length;
|
||||
}
|
||||
|
||||
public static List<HueObject> idsToLights(List<String> ids) {
|
||||
List<HueObject> lights = new ArrayList<>();
|
||||
|
||||
for (String id : ids) {
|
||||
HueObject light = new HueObject();
|
||||
light.setId(id);
|
||||
lights.add(light);
|
||||
}
|
||||
|
||||
return lights;
|
||||
}
|
||||
|
||||
public static List<String> lightsToIds(List<HueObject> lights) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
|
||||
for (HueObject light : lights) {
|
||||
ids.add(light.getId());
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static @Nullable String quickMatch(String needle, String haystack) {
|
||||
Matcher m = Pattern.compile(needle).matcher(haystack);
|
||||
m.find();
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when the API returns an unknown error.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class ApiException extends Exception {
|
||||
public ApiException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when trying to change the state of a light that is off.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class DeviceOffException extends ApiException {
|
||||
public DeviceOffException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when operating on a group, light or user that doesn't exist.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class EntityNotAvailableException extends ApiException {
|
||||
public EntityNotAvailableException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when adding more than 16 groups (excluding all lights group) to a bridge.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class GroupTableFullException extends ApiException {
|
||||
public GroupTableFullException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when scheduling an invalid command.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class InvalidCommandException extends ApiException {
|
||||
public InvalidCommandException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown if the link button hasn't been pressed in the last 30 seconds.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class LinkButtonException extends ApiException {
|
||||
public LinkButtonException() {
|
||||
}
|
||||
|
@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.exceptions;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Thrown when the specified user is no longer whitelisted on the bridge.
|
||||
*
|
||||
@ -19,6 +21,7 @@ package org.openhab.binding.hue.internal.exceptions;
|
||||
* @author Denis Dudnik - moved Jue library source code inside the smarthome Hue binding
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@NonNullByDefault
|
||||
public class UnauthorizedException extends ApiException {
|
||||
public UnauthorizedException() {
|
||||
}
|
||||
|
@ -10,17 +10,17 @@
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
package org.openhab.binding.hue.internal.factory;
|
||||
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
|
||||
import org.openhab.binding.hue.internal.handler.HueGroupHandler;
|
||||
import org.openhab.binding.hue.internal.handler.HueLightHandler;
|
||||
@ -35,6 +35,7 @@ import org.openhab.binding.hue.internal.handler.sensors.TemperatureHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
@ -60,22 +61,25 @@ import org.osgi.service.component.annotations.Reference;
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.hue")
|
||||
public class HueThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.unmodifiableSet(Stream
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
|
||||
.of(HueBridgeHandler.SUPPORTED_THING_TYPES.stream(), HueLightHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
DimmerSwitchHandler.SUPPORTED_THING_TYPES.stream(), TapSwitchHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
PresenceHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
GeofencePresenceHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
TemperatureHandler.SUPPORTED_THING_TYPES.stream(), LightLevelHandler.SUPPORTED_THING_TYPES.stream(),
|
||||
ClipHandler.SUPPORTED_THING_TYPES.stream(), HueGroupHandler.SUPPORTED_THING_TYPES.stream())
|
||||
.flatMap(i -> i).collect(Collectors.toSet()));
|
||||
.flatMap(i -> i).collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final HueStateDescriptionProvider stateDescriptionProvider;
|
||||
private final TranslationProvider i18nProvider;
|
||||
private final LocaleProvider localeProvider;
|
||||
|
||||
@Activate
|
||||
public HueThingHandlerFactory(final @Reference HueStateDescriptionProvider stateDescriptionProvider,
|
||||
public HueThingHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference HueStateDescriptionProvider stateDescriptionProvider,
|
||||
final @Reference TranslationProvider i18nProvider, final @Reference LocaleProvider localeProvider) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
@ -103,7 +107,7 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
return super.createThing(thingTypeUID, configuration, hueGroupUID, bridgeUID);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the hue binding.");
|
||||
throw new IllegalArgumentException("The thing type " + thingTypeUID + " is not supported by the Hue binding.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -149,7 +153,8 @@ public class HueThingHandlerFactory extends BaseThingHandlerFactory {
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
if (HueBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
return new HueBridgeHandler((Bridge) thing, stateDescriptionProvider, i18nProvider, localeProvider);
|
||||
return new HueBridgeHandler((Bridge) thing, httpClient, stateDescriptionProvider, i18nProvider,
|
||||
localeProvider);
|
||||
} else if (HueLightHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
||||
return new HueLightHandler(thing, stateDescriptionProvider);
|
||||
} else if (DimmerSwitchHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
|
@ -15,8 +15,8 @@ package org.openhab.binding.hue.internal.handler;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullGroup;
|
||||
import org.openhab.binding.hue.internal.Scene;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.Scene;
|
||||
|
||||
/**
|
||||
* The {@link GroupStatusListener} is notified when a group status has changed or a group has been removed or added.
|
||||
|
@ -18,7 +18,6 @@ import static org.openhab.core.thing.Thing.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -33,20 +32,21 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.ApiVersionUtils;
|
||||
import org.openhab.binding.hue.internal.Config;
|
||||
import org.openhab.binding.hue.internal.ConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.FullConfig;
|
||||
import org.openhab.binding.hue.internal.FullGroup;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.HueBridge;
|
||||
import org.openhab.binding.hue.internal.HueConfigStatusMessage;
|
||||
import org.openhab.binding.hue.internal.Scene;
|
||||
import org.openhab.binding.hue.internal.State;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.hue.internal.config.HueBridgeConfig;
|
||||
import org.openhab.binding.hue.internal.connection.HueBridge;
|
||||
import org.openhab.binding.hue.internal.connection.HueTlsTrustManagerProvider;
|
||||
import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
|
||||
import org.openhab.binding.hue.internal.dto.ApiVersionUtils;
|
||||
import org.openhab.binding.hue.internal.dto.Config;
|
||||
import org.openhab.binding.hue.internal.dto.ConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullConfig;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.Scene;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.exceptions.ApiException;
|
||||
import org.openhab.binding.hue.internal.exceptions.DeviceOffException;
|
||||
import org.openhab.binding.hue.internal.exceptions.EntityNotAvailableException;
|
||||
@ -54,8 +54,11 @@ import org.openhab.binding.hue.internal.exceptions.LinkButtonException;
|
||||
import org.openhab.binding.hue.internal.exceptions.UnauthorizedException;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
import org.openhab.core.i18n.CommunicationException;
|
||||
import org.openhab.core.i18n.ConfigurationException;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.TlsTrustManagerProvider;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
@ -68,11 +71,13 @@ import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link HueBridgeHandler} is the handler for a hue bridge and connects it to
|
||||
* {@link HueBridgeHandler} is the handler for a Hue Bridge and connects it to
|
||||
* the framework. All {@link HueLightHandler}s use the {@link HueBridgeHandler} to execute the actual commands.
|
||||
*
|
||||
* @author Dennis Nobel - Initial contribution
|
||||
@ -93,12 +98,13 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BRIDGE);
|
||||
|
||||
private static final long BYPASS_MIN_DURATION_BEFORE_CMD = 1500L;
|
||||
|
||||
private static final String DEVICE_TYPE = "EclipseSmartHome";
|
||||
|
||||
private static final long SCENE_POLLING_INTERVAL = TimeUnit.SECONDS.convert(10, TimeUnit.MINUTES);
|
||||
|
||||
private static final String DEVICE_TYPE = "openHAB";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(HueBridgeHandler.class);
|
||||
private @Nullable ServiceRegistration<?> serviceRegistration;
|
||||
private final HttpClient httpClient;
|
||||
private final HueStateDescriptionProvider stateDescriptionOptionProvider;
|
||||
private final TranslationProvider i18nProvider;
|
||||
private final LocaleProvider localeProvider;
|
||||
@ -120,8 +126,8 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
try {
|
||||
pollingLock.lock();
|
||||
if (!lastBridgeConnectionState) {
|
||||
// if user is not set in configuration try to create a new user on Hue bridge
|
||||
if (hueBridgeConfig.getUserName() == null) {
|
||||
// if user is not set in configuration try to create a new user on Hue Bridge
|
||||
if (hueBridgeConfig.userName == null) {
|
||||
hueBridge.getFullConfig();
|
||||
}
|
||||
lastBridgeConnectionState = tryResumeBridgeConnection();
|
||||
@ -132,6 +138,8 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
} catch (ConfigurationException e) {
|
||||
handleConfigurationFailure(e);
|
||||
} catch (UnauthorizedException | IllegalStateException e) {
|
||||
if (isReachable(hueBridge.getIPAddress())) {
|
||||
lastBridgeConnectionState = false;
|
||||
@ -142,7 +150,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
lastBridgeConnectionState = false;
|
||||
onConnectionLost();
|
||||
}
|
||||
} catch (ApiException | IOException e) {
|
||||
} catch (ApiException | CommunicationException | IOException e) {
|
||||
if (hueBridge != null && lastBridgeConnectionState) {
|
||||
logger.debug("Connection to Hue Bridge {} lost: {}", hueBridge.getIPAddress(), e.getMessage(), e);
|
||||
lastBridgeConnectionState = false;
|
||||
@ -165,7 +173,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
|
||||
// If there is no connection, this line will fail
|
||||
hueBridge.authenticate("invalid");
|
||||
} catch (IOException e) {
|
||||
} catch (ConfigurationException | IOException e) {
|
||||
return false;
|
||||
} catch (ApiException e) {
|
||||
String message = e.getMessage();
|
||||
@ -407,9 +415,11 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
|
||||
private List<String> consoleScenesList = new ArrayList<>();
|
||||
|
||||
public HueBridgeHandler(Bridge bridge, HueStateDescriptionProvider stateDescriptionOptionProvider,
|
||||
TranslationProvider i18nProvider, LocaleProvider localeProvider) {
|
||||
public HueBridgeHandler(Bridge bridge, HttpClient httpClient,
|
||||
HueStateDescriptionProvider stateDescriptionOptionProvider, TranslationProvider i18nProvider,
|
||||
LocaleProvider localeProvider) {
|
||||
super(bridge);
|
||||
this.httpClient = httpClient;
|
||||
this.stateDescriptionOptionProvider = stateDescriptionOptionProvider;
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
@ -417,7 +427,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singleton(HueDeviceDiscoveryService.class);
|
||||
return Set.of(HueDeviceDiscoveryService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -596,10 +606,10 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
ScheduledFuture<?> job = lightPollingJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
long lightPollingInterval;
|
||||
int configPollingInterval = hueBridgeConfig.getPollingInterval();
|
||||
int configPollingInterval = hueBridgeConfig.pollingInterval;
|
||||
if (configPollingInterval < 1) {
|
||||
lightPollingInterval = TimeUnit.SECONDS.toSeconds(10);
|
||||
logger.info("Wrong configuration value for polling interval. Using default value: {}s",
|
||||
logger.warn("Wrong configuration value for polling interval. Using default value: {}s",
|
||||
lightPollingInterval);
|
||||
} else {
|
||||
lightPollingInterval = configPollingInterval;
|
||||
@ -621,12 +631,12 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
private void startSensorPolling() {
|
||||
ScheduledFuture<?> job = sensorPollingJob;
|
||||
if (job == null || job.isCancelled()) {
|
||||
int configSensorPollingInterval = hueBridgeConfig.getSensorPollingInterval();
|
||||
int configSensorPollingInterval = hueBridgeConfig.sensorPollingInterval;
|
||||
if (configSensorPollingInterval > 0) {
|
||||
long sensorPollingInterval;
|
||||
if (configSensorPollingInterval < 50) {
|
||||
sensorPollingInterval = TimeUnit.MILLISECONDS.toMillis(500);
|
||||
logger.info("Wrong configuration value for sensor polling interval. Using default value: {}ms",
|
||||
logger.warn("Wrong configuration value for sensor polling interval. Using default value: {}ms",
|
||||
sensorPollingInterval);
|
||||
} else {
|
||||
sensorPollingInterval = configSensorPollingInterval;
|
||||
@ -665,7 +675,7 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Handler disposed.");
|
||||
logger.debug("Disposing Hue Bridge handler ...");
|
||||
Future<?> job = initJob;
|
||||
if (job != null) {
|
||||
job.cancel(true);
|
||||
@ -676,47 +686,51 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
if (hueBridge != null) {
|
||||
hueBridge = null;
|
||||
}
|
||||
ServiceRegistration<?> localServiceRegistration = serviceRegistration;
|
||||
if (localServiceRegistration != null) {
|
||||
// remove trustmanager service
|
||||
localServiceRegistration.unregister();
|
||||
serviceRegistration = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing hue bridge handler.");
|
||||
logger.debug("Initializing Hue Bridge handler ...");
|
||||
hueBridgeConfig = getConfigAs(HueBridgeConfig.class);
|
||||
|
||||
String ip = hueBridgeConfig.getIpAddress();
|
||||
String ip = hueBridgeConfig.ipAddress;
|
||||
if (ip == null || ip.isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-no-ip-address");
|
||||
} else {
|
||||
if (hueBridge == null) {
|
||||
hueBridge = new HueBridge(ip, hueBridgeConfig.getPort(), hueBridgeConfig.getProtocol(), scheduler);
|
||||
hueBridge.setTimeout(5000);
|
||||
if (HueBridgeConfig.HTTPS.equals(hueBridgeConfig.protocol)) {
|
||||
// register trustmanager service
|
||||
HueTlsTrustManagerProvider tlsTrustManagerProvider = new HueTlsTrustManagerProvider(
|
||||
ip + ":" + hueBridgeConfig.getPort(), hueBridgeConfig.useSelfSignedCertificate);
|
||||
serviceRegistration = FrameworkUtil.getBundle(getClass()).getBundleContext()
|
||||
.registerService(TlsTrustManagerProvider.class.getName(), tlsTrustManagerProvider, null);
|
||||
}
|
||||
|
||||
hueBridge = new HueBridge(httpClient, ip, hueBridgeConfig.getPort(), hueBridgeConfig.protocol,
|
||||
scheduler);
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
// Try a first connection that will fail, then try to authenticate,
|
||||
// and finally change the bridge status to ONLINE
|
||||
initJob = scheduler.submit(new PollingRunnable() {
|
||||
@Override
|
||||
protected void doConnectedRun() throws IOException, ApiException {
|
||||
}
|
||||
});
|
||||
}
|
||||
onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable String getUserName() {
|
||||
return hueBridgeConfig == null ? null : hueBridgeConfig.getUserName();
|
||||
return hueBridgeConfig == null ? null : hueBridgeConfig.userName;
|
||||
}
|
||||
|
||||
private synchronized void onUpdate() {
|
||||
if (hueBridge != null) {
|
||||
startLightPolling();
|
||||
startSensorPolling();
|
||||
startScenePolling();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever the connection to the {@link HueBridge} is lost.
|
||||
@ -761,9 +775,9 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
*/
|
||||
private boolean tryResumeBridgeConnection() throws IOException, ApiException {
|
||||
logger.debug("Connection to Hue Bridge {} established.", hueBridge.getIPAddress());
|
||||
if (hueBridgeConfig.getUserName() == null) {
|
||||
if (hueBridgeConfig.userName == null) {
|
||||
logger.warn(
|
||||
"User name for Hue bridge authentication not available in configuration. Setting ThingStatus to OFFLINE.");
|
||||
"User name for Hue Bridge authentication not available in configuration. Setting ThingStatus to OFFLINE.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-no-username");
|
||||
return false;
|
||||
@ -780,21 +794,24 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
* If there is a user name available, it attempts to re-authenticate. Otherwise new authentication credentials will
|
||||
* be requested from the bridge.
|
||||
*
|
||||
* @param bridge the hue bridge the connection is not authorized
|
||||
* @param bridge the Hue Bridge the connection is not authorized
|
||||
* @return returns {@code true} if re-authentication was successful, {@code false} otherwise
|
||||
*/
|
||||
public boolean onNotAuthenticated() {
|
||||
if (hueBridge == null) {
|
||||
return false;
|
||||
}
|
||||
String userName = hueBridgeConfig.getUserName();
|
||||
String userName = hueBridgeConfig.userName;
|
||||
if (userName == null) {
|
||||
createUser();
|
||||
} else {
|
||||
try {
|
||||
hueBridge.authenticate(userName);
|
||||
return true;
|
||||
} catch (ConfigurationException e) {
|
||||
handleConfigurationFailure(e);
|
||||
} catch (Exception e) {
|
||||
logger.trace("", e);
|
||||
handleAuthenticationFailure(e, userName);
|
||||
}
|
||||
}
|
||||
@ -813,10 +830,10 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
}
|
||||
|
||||
private String createUserOnPhysicalBridge() throws IOException, ApiException {
|
||||
logger.info("Creating new user on Hue bridge {} - please press the pairing button on the bridge.",
|
||||
hueBridgeConfig.getIpAddress());
|
||||
logger.info("Creating new user on Hue Bridge {} - please press the pairing button on the bridge.",
|
||||
hueBridgeConfig.ipAddress);
|
||||
String userName = hueBridge.link(DEVICE_TYPE);
|
||||
logger.info("User has been successfully added to Hue bridge.");
|
||||
logger.info("User has been successfully added to Hue Bridge.");
|
||||
return userName;
|
||||
}
|
||||
|
||||
@ -829,26 +846,32 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
hueBridgeConfig = getConfigAs(HueBridgeConfig.class);
|
||||
} catch (IllegalStateException e) {
|
||||
logger.trace("Configuration update failed.", e);
|
||||
logger.warn("Unable to update configuration of Hue bridge.");
|
||||
logger.warn("Unable to update configuration of Hue Bridge.");
|
||||
logger.warn("Please configure the user name manually.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleConfigurationFailure(ConfigurationException ex) {
|
||||
logger.warn(
|
||||
"Invalid certificate for secured connection. You might want to enable the \"Use Self-Signed Certificate\" configuration.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, ex.getRawMessage());
|
||||
}
|
||||
|
||||
private void handleAuthenticationFailure(Exception ex, String userName) {
|
||||
logger.warn("User is not authenticated on Hue bridge {}", hueBridgeConfig.getIpAddress());
|
||||
logger.warn("User is not authenticated on Hue Bridge {}", hueBridgeConfig.ipAddress);
|
||||
logger.warn("Please configure a valid user or remove user from configuration to generate a new one.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-invalid-username");
|
||||
}
|
||||
|
||||
private void handleLinkButtonNotPressed(LinkButtonException ex) {
|
||||
logger.debug("Failed creating new user on Hue bridge: {}", ex.getMessage());
|
||||
logger.debug("Failed creating new user on Hue Bridge: {}", ex.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-press-pairing-button");
|
||||
}
|
||||
|
||||
private void handleExceptionWhileCreatingUser(Exception ex) {
|
||||
logger.warn("Failed creating new user on Hue bridge", ex);
|
||||
logger.warn("Failed creating new user on Hue Bridge", ex);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-creation-username");
|
||||
}
|
||||
@ -1041,10 +1064,10 @@ public class HueBridgeHandler extends ConfigStatusBridgeHandler implements HueCl
|
||||
// Check whether an IP address is provided
|
||||
hueBridgeConfig = getConfigAs(HueBridgeConfig.class);
|
||||
|
||||
String ip = hueBridgeConfig.getIpAddress();
|
||||
String ip = hueBridgeConfig.ipAddress;
|
||||
if (ip == null || ip.isEmpty()) {
|
||||
return List.of(ConfigStatusMessage.Builder.error(HOST)
|
||||
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build());
|
||||
return List.of(ConfigStatusMessage.Builder.error(HOST).withMessageKeySuffix(IP_ADDRESS_MISSING)
|
||||
.withArguments(HOST).build());
|
||||
} else {
|
||||
return List.of();
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.ConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.FullGroup;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
|
||||
import org.openhab.binding.hue.internal.dto.ConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
|
||||
/**
|
||||
* Access to the Hue system for light handlers.
|
||||
|
@ -25,11 +25,11 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.FullGroup;
|
||||
import org.openhab.binding.hue.internal.Scene;
|
||||
import org.openhab.binding.hue.internal.State;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.Scene;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
@ -87,7 +87,7 @@ public class HueGroupHandler extends BaseThingHandler implements HueLightActions
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing hue group handler.");
|
||||
logger.debug("Initializing Hue group handler.");
|
||||
Bridge bridge = getBridge();
|
||||
initializeThing((bridge == null) ? null : bridge.getStatus());
|
||||
}
|
||||
@ -173,13 +173,13 @@ public class HueGroupHandler extends BaseThingHandler implements HueLightActions
|
||||
public void handleCommand(String channel, Command command, long fadeTime) {
|
||||
HueClient bridgeHandler = getHueClient();
|
||||
if (bridgeHandler == null) {
|
||||
logger.debug("hue bridge handler not found. Cannot handle command without bridge.");
|
||||
logger.debug("Hue Bridge handler not found. Cannot handle command without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
FullGroup group = bridgeHandler.getGroupById(groupId);
|
||||
if (group == null) {
|
||||
logger.debug("hue group not known on bridge. Cannot handle command.");
|
||||
logger.debug("Hue group not known on bridge. Cannot handle command.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-wrong-group-id");
|
||||
return;
|
||||
|
@ -24,11 +24,11 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.State;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.Capabilities;
|
||||
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
@ -51,7 +51,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link HueLightHandler} is the handler for a hue light. It uses the {@link HueClient} to execute the actual
|
||||
* {@link HueLightHandler} is the handler for a Hue light. It uses the {@link HueClient} to execute the actual
|
||||
* command.
|
||||
*
|
||||
* @author Dennis Nobel - Initial contribution
|
||||
@ -116,7 +116,7 @@ public class HueLightHandler extends BaseThingHandler implements HueLightActions
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing hue light handler.");
|
||||
logger.debug("Initializing Hue light handler.");
|
||||
Bridge bridge = getBridge();
|
||||
initializeThing((bridge == null) ? null : bridge.getStatus());
|
||||
}
|
||||
@ -234,13 +234,13 @@ public class HueLightHandler extends BaseThingHandler implements HueLightActions
|
||||
public void handleCommand(String channel, Command command, long fadeTime) {
|
||||
HueClient bridgeHandler = getHueClient();
|
||||
if (bridgeHandler == null) {
|
||||
logger.warn("hue bridge handler not found. Cannot handle command without bridge.");
|
||||
logger.warn("Hue Bridge handler not found. Cannot handle command without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
final FullLight light = lastFullLight == null ? bridgeHandler.getLightById(lightId) : lastFullLight;
|
||||
if (light == null) {
|
||||
logger.debug("hue light not known on bridge. Cannot handle command.");
|
||||
logger.debug("Hue light not known on bridge. Cannot handle command.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-wrong-light-id");
|
||||
return;
|
||||
|
@ -12,8 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
import static org.openhab.core.thing.Thing.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@ -27,9 +27,9 @@ import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -72,7 +72,7 @@ public abstract class HueSensorHandler extends BaseThingHandler implements Senso
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing hue sensor handler.");
|
||||
logger.debug("Initializing Hue sensor handler.");
|
||||
Bridge bridge = getBridge();
|
||||
initializeThing((bridge == null) ? null : bridge.getStatus());
|
||||
}
|
||||
@ -167,13 +167,13 @@ public abstract class HueSensorHandler extends BaseThingHandler implements Senso
|
||||
protected void handleCommand(String channel, Command command) {
|
||||
HueClient bridgeHandler = getHueClient();
|
||||
if (bridgeHandler == null) {
|
||||
logger.warn("hue bridge handler not found. Cannot handle command without bridge.");
|
||||
logger.warn("Hue Bridge handler not found. Cannot handle command without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
final FullSensor sensor = lastFullSensor;
|
||||
if (sensor == null) {
|
||||
logger.debug("hue sensor not known on bridge. Cannot handle command.");
|
||||
logger.debug("Hue sensor not known on bridge. Cannot handle command.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-wrong-sensor-id");
|
||||
return;
|
||||
@ -206,13 +206,13 @@ public abstract class HueSensorHandler extends BaseThingHandler implements Senso
|
||||
if (!configUpdate.isEmpty()) {
|
||||
HueClient hueBridge = getHueClient();
|
||||
if (hueBridge == null) {
|
||||
logger.warn("hue bridge handler not found. Cannot handle configuration update without bridge.");
|
||||
logger.warn("Hue Bridge handler not found. Cannot handle configuration update without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
final FullSensor sensor = lastFullSensor;
|
||||
if (sensor == null) {
|
||||
logger.debug("hue sensor not known on bridge. Cannot handle configuration update.");
|
||||
logger.debug("Hue sensor not known on bridge. Cannot handle configuration update.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-wrong-sensor-id");
|
||||
return;
|
||||
|
@ -14,12 +14,12 @@ package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.hue.internal.State;
|
||||
import org.openhab.binding.hue.internal.State.AlertMode;
|
||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.State.Effect;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
import org.openhab.binding.hue.internal.dto.State.AlertMode;
|
||||
import org.openhab.binding.hue.internal.dto.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.dto.State.Effect;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
|
@ -13,7 +13,7 @@
|
||||
package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
|
||||
/**
|
||||
* The {@link LightStatusListener} is notified when a light status has changed or a light has been removed or added.
|
||||
|
@ -13,7 +13,7 @@
|
||||
package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
|
||||
/**
|
||||
* The {@link SensorStatusListener} is notified when a sensor status has changed or a sensor has been removed or added.
|
||||
|
@ -20,8 +20,8 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
@ -21,13 +21,12 @@ import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -43,7 +42,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
@NonNullByDefault
|
||||
public class DimmerSwitchHandler extends HueSensorHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_DIMMER_SWITCH);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_DIMMER_SWITCH);
|
||||
|
||||
public DimmerSwitchHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -12,16 +12,15 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler.sensors;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.STATE_PRESENCE;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.STATE_PRESENCE;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
@ -35,7 +34,8 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class GeofencePresenceHandler extends HueSensorHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_GEOFENCE_SENSOR);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_GEOFENCE_SENSOR);
|
||||
|
||||
public GeofencePresenceHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -12,18 +12,17 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler.sensors;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.LightLevelConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.LightLevelConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -41,7 +40,8 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightLevelHandler extends HueSensorHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_LIGHT_LEVEL_SENSOR);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_LIGHT_LEVEL_SENSOR);
|
||||
|
||||
public LightLevelHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -12,17 +12,16 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler.sensors;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.PresenceConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.PresenceConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueClient;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
@ -43,7 +42,8 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PresenceHandler extends HueSensorHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_PRESENCE_SENSOR);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_PRESENCE_SENSOR);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PresenceHandler.class);
|
||||
|
||||
@ -55,13 +55,13 @@ public class PresenceHandler extends HueSensorHandler {
|
||||
public void handleCommand(String channel, Command command) {
|
||||
HueClient hueBridge = getHueClient();
|
||||
if (hueBridge == null) {
|
||||
logger.warn("hue bridge handler not found. Cannot handle command without bridge.");
|
||||
logger.warn("Hue Bridge handler not found. Cannot handle command without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
final FullSensor sensor = lastFullSensor;
|
||||
if (sensor == null) {
|
||||
logger.debug("hue sensor not known on bridge. Cannot handle command.");
|
||||
logger.debug("Hue sensor not known on bridge. Cannot handle command.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error-wrong-sensor-id");
|
||||
return;
|
||||
|
@ -21,13 +21,12 @@ import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
@ -42,7 +41,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
@NonNullByDefault
|
||||
public class TapSwitchHandler extends HueSensorHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_TAP_SWITCH);
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_TAP_SWITCH);
|
||||
|
||||
public TapSwitchHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -12,18 +12,17 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler.sensors;
|
||||
|
||||
import static org.openhab.binding.hue.internal.FullSensor.*;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.dto.FullSensor.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.FullSensor;
|
||||
import org.openhab.binding.hue.internal.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.TemperatureConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullSensor;
|
||||
import org.openhab.binding.hue.internal.dto.SensorConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.TemperatureConfigUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.HueSensorHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
@ -39,7 +38,8 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class TemperatureHandler extends HueSensorHandler {
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_TEMPERATURE_SENSOR);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_TEMPERATURE_SENSOR);
|
||||
|
||||
public TemperatureHandler(Thing thing) {
|
||||
super(thing);
|
||||
|
@ -6,13 +6,4 @@
|
||||
<name>Hue Binding</name>
|
||||
<description>The Hue Binding integrates the Philips Hue system. It allows to control Hue bulbs.</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="removalGracePeriod" type="integer" min="0" step="1" unit="s">
|
||||
<label>Removal Grace Period</label>
|
||||
<description>Extra grace period (seconds) that UPnP discovery shall wait before removing a lost Bridge from the
|
||||
Inbox. Default is 50 seconds.</description>
|
||||
<default>50</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</binding:binding>
|
||||
|
@ -117,7 +117,7 @@
|
||||
<config-description uri="thing-type:hue:group">
|
||||
<parameter name="groupId" type="text" required="true">
|
||||
<label>Group ID</label>
|
||||
<description>The group identifier identifies one certain hue group or room.</description>
|
||||
<description>The group identifier identifies one certain Hue group or room.</description>
|
||||
</parameter>
|
||||
<parameter name="fadetime" type="integer" min="0" step="100" unit="ms">
|
||||
<label>@text/config.fadetime.label</label>
|
||||
|
@ -3,11 +3,6 @@
|
||||
binding.hue.name = Hue Binding
|
||||
binding.hue.description = The Hue Binding integrates the Philips Hue system. It allows to control Hue bulbs.
|
||||
|
||||
# binding config
|
||||
|
||||
binding.config.hue.removalGracePeriod.label = Removal Grace Period
|
||||
binding.config.hue.removalGracePeriod.description = Extra grace period (seconds) that UPnP discovery shall wait before removing a lost Bridge from the Inbox. Default is 50 seconds.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.hue.0000.label = On/Off Light
|
||||
@ -39,7 +34,7 @@ thing-type.hue.0840.description = A generic sensor object for IP sensor use.
|
||||
thing-type.hue.0850.label = CLIP Generic Flag Sensor
|
||||
thing-type.hue.0850.description = A generic sensor object for IP sensor use.
|
||||
thing-type.hue.bridge.label = Hue Bridge
|
||||
thing-type.hue.bridge.description = The Hue bridge represents the Philips Hue bridge.
|
||||
thing-type.hue.bridge.description = The Hue Bridge represents the Philips Hue Bridge.
|
||||
thing-type.hue.geofencesensor.label = Geofence Sensor
|
||||
thing-type.hue.geofencesensor.description = A sensor providing geofence based presence detection.
|
||||
thing-type.hue.group.label = Hue Group
|
||||
@ -48,17 +43,23 @@ thing-type.hue.group.description = A group of lights or a room that could be swi
|
||||
# thing types config
|
||||
|
||||
thing-type.config.hue.bridge.ipAddress.label = Network Address
|
||||
thing-type.config.hue.bridge.ipAddress.description = Network address of the Hue bridge.
|
||||
thing-type.config.hue.bridge.ipAddress.description = Network address of the Hue Bridge.
|
||||
thing-type.config.hue.bridge.pollingInterval.label = Polling Interval
|
||||
thing-type.config.hue.bridge.pollingInterval.description = Seconds between fetching values from the Hue bridge. Default is 10.
|
||||
thing-type.config.hue.bridge.pollingInterval.description = Seconds between fetching values from the Hue Bridge. Default is 10.
|
||||
thing-type.config.hue.bridge.port.label = Port
|
||||
thing-type.config.hue.bridge.port.description = Port of the Hue bridge.
|
||||
thing-type.config.hue.bridge.port.description = Port of the Hue Bridge.
|
||||
thing-type.config.hue.bridge.protocol.label = Protocol
|
||||
thing-type.config.hue.bridge.protocol.description = Protocol to connect to the Hue Bridge (http or https).
|
||||
thing-type.config.hue.bridge.protocol.option.http = HTTP
|
||||
thing-type.config.hue.bridge.protocol.option.https = HTTPS
|
||||
thing-type.config.hue.bridge.sensorPollingInterval.label = Sensor Polling Interval
|
||||
thing-type.config.hue.bridge.sensorPollingInterval.description = Milliseconds between fetching sensor-values from the Hue bridge. A higher value means more delay for the sensor values, but a too low value can cause congestion on the Hue bridge. Use 0 to disable the polling for sensors. Default is 500.
|
||||
thing-type.config.hue.bridge.sensorPollingInterval.description = Milliseconds between fetching sensor-values from the Hue Bridge. A higher value means more delay for the sensor values, but a too low value can cause congestion on the Hue Bridge. Use 0 to disable the polling for sensors. Default is 500.
|
||||
thing-type.config.hue.bridge.useSelfSignedCertificate.label = Use Self-Signed Certificate
|
||||
thing-type.config.hue.bridge.useSelfSignedCertificate.description = Use self-signed certificate for HTTPS connection to Hue Bridge.
|
||||
thing-type.config.hue.bridge.userName.label = Username
|
||||
thing-type.config.hue.bridge.userName.description = Name of a registered Hue bridge user, that allows to access the API.
|
||||
thing-type.config.hue.bridge.userName.description = Name of a registered Hue Bridge user, that allows to access the API.
|
||||
thing-type.config.hue.group.groupId.label = Group ID
|
||||
thing-type.config.hue.group.groupId.description = The group identifier identifies one certain hue group or room.
|
||||
thing-type.config.hue.group.groupId.description = The group identifier identifies one certain Hue group or room.
|
||||
thing-type.config.hue.lightlevelsensor.tholddark.label = Threshold Dark
|
||||
thing-type.config.hue.lightlevelsensor.tholddark.description = Threshold the user configured to be used in rules to determine insufficient light level (ie below threshold). Default value 16000.
|
||||
thing-type.config.hue.lightlevelsensor.tholdoffset.label = Threshold Offset
|
||||
@ -132,37 +133,39 @@ config.fadetime.description = Fade time in milliseconds for changing values.
|
||||
config.ledindication.label = LED Indication
|
||||
config.ledindication.description = Turns device LED during normal operation on or off. Devices might still indicate exceptional operation (Reset, SW Update, Battery Low).
|
||||
config.lightId.label = Light ID
|
||||
config.lightId.description = The light identifier that is used within the hue bridge.
|
||||
config.lightId.description = The light identifier that is used within the Hue Bridge.
|
||||
config.plugId.label = Plug ID
|
||||
config.plugId.description = The plug identifier that is used within the hue bridge.
|
||||
config.plugId.description = The plug identifier that is used within the Hue Bridge.
|
||||
config.sensorId.label = Sensor ID
|
||||
config.sensorId.description = The sensor identifier that is used within the hue bridge.
|
||||
config.sensorId.description = The sensor identifier that is used within the Hue Bridge.
|
||||
config.on.label = Sensor Status
|
||||
config.on.description = Enables or disables the sensor.
|
||||
|
||||
# config status messages
|
||||
|
||||
config-status.error.missing-ip-address-configuration = No IP address for the Hue bridge has been provided.
|
||||
config-status.error.missing-ip-address-configuration = No IP address for the Hue Bridge has been provided.
|
||||
|
||||
# thing status descriptions
|
||||
|
||||
offline.conf-error-no-ip-address = Cannot connect to Hue bridge. IP address not available in configuration.
|
||||
offline.conf-error-no-username = Cannot connect to Hue bridge. User name for authentication not available in configuration.
|
||||
offline.communication-error = An unexpected exception occurred during execution.
|
||||
offline.conf-error-invalid-ssl-certificate = Invalid certificate for secured connection. You might want to enable the "Use Self-Signed Certificate" configuration.
|
||||
offline.conf-error-no-ip-address = Cannot connect to Hue Bridge. IP address not available in configuration.
|
||||
offline.conf-error-no-username = Cannot connect to Hue Bridge. User name for authentication not available in configuration.
|
||||
offline.conf-error-invalid-username = Authentication failed. Remove user name from configuration to generate a new one.
|
||||
offline.conf-error-press-pairing-button = Not authenticated. Press pairing button on the Hue bridge or set a valid user name in configuration.
|
||||
offline.conf-error-creation-username = Failed to create new user on Hue bridge.
|
||||
offline.bridge-connection-lost = Hue bridge connection lost.
|
||||
offline.conf-error-press-pairing-button = Not authenticated. Press pairing button on the Hue Bridge or set a valid user name in configuration.
|
||||
offline.conf-error-creation-username = Failed to create new user on Hue Bridge.
|
||||
offline.bridge-connection-lost = Hue Bridge connection lost.
|
||||
offline.conf-error-no-light-id = Light ID not available in configuration.
|
||||
offline.conf-error-no-sensor-id = Sensor ID not available in configuration.
|
||||
offline.conf-error-no-group-id = Group ID not available in configuration.
|
||||
offline.conf-error-wrong-light-id = No light with given ID available on Hue bridge.
|
||||
offline.conf-error-wrong-sensor-id = No sensor with given ID available on Hue bridge.
|
||||
offline.conf-error-wrong-group-id = No group with given ID available on Hue bridge.
|
||||
offline.light-not-reachable = Hue bridge reports light as not reachable.
|
||||
offline.sensor-not-reachable = Hue bridge reports sensor as not reachable.
|
||||
offline.light-removed = Hue bridge reports light as removed.
|
||||
offline.sensor-removed = Hue bridge reports sensor as removed.
|
||||
offline.group-removed = Hue bridge reports group as removed.
|
||||
offline.conf-error-wrong-light-id = No light with given ID available on Hue Bridge.
|
||||
offline.conf-error-wrong-sensor-id = No sensor with given ID available on Hue Bridge.
|
||||
offline.conf-error-wrong-group-id = No group with given ID available on Hue Bridge.
|
||||
offline.light-not-reachable = Hue Bridge reports light as not reachable.
|
||||
offline.sensor-not-reachable = Hue Bridge reports sensor as not reachable.
|
||||
offline.light-removed = Hue Bridge reports light as removed.
|
||||
offline.sensor-removed = Hue Bridge reports sensor as removed.
|
||||
offline.group-removed = Hue Bridge reports group as removed.
|
||||
|
||||
# lightactions
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- Hue Bridge -->
|
||||
<bridge-type id="bridge">
|
||||
<label>Hue Bridge</label>
|
||||
<description>The Hue bridge represents the Philips Hue bridge.</description>
|
||||
<description>The Hue Bridge represents the Philips Hue Bridge.</description>
|
||||
|
||||
<channels>
|
||||
<channel id="scene" typeId="scene"/>
|
||||
@ -21,26 +21,41 @@
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Network Address</label>
|
||||
<description>Network address of the Hue bridge.</description>
|
||||
<description>Network address of the Hue Bridge.</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="false" min="1" max="65535">
|
||||
<label>Port</label>
|
||||
<description>Port of the Hue bridge.</description>
|
||||
<description>Port of the Hue Bridge.</description>
|
||||
</parameter>
|
||||
<parameter name="protocol" type="text">
|
||||
<label>Protocol</label>
|
||||
<description>Protocol to connect to the Hue Bridge (http or https).</description>
|
||||
<default>https</default>
|
||||
<options>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="useSelfSignedCertificate" type="boolean">
|
||||
<label>Use Self-Signed Certificate</label>
|
||||
<description>Use self-signed certificate for HTTPS connection to Hue Bridge.</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="userName" type="text">
|
||||
<context>password</context>
|
||||
<label>Username</label>
|
||||
<description>Name of a registered Hue bridge user, that allows to access the API.</description>
|
||||
<description>Name of a registered Hue Bridge user, that allows to access the API.</description>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" min="1" step="1" unit="s">
|
||||
<label>Polling Interval</label>
|
||||
<description>Seconds between fetching values from the Hue bridge. Default is 10.</description>
|
||||
<description>Seconds between fetching values from the Hue Bridge. Default is 10.</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
<parameter name="sensorPollingInterval" type="integer" min="0" step="1" unit="ms">
|
||||
<label>Sensor Polling Interval</label>
|
||||
<description>Milliseconds between fetching sensor-values from the Hue bridge. A higher value means more delay for
|
||||
the sensor values, but a too low value can cause congestion on the Hue bridge. Use 0 to disable the polling for
|
||||
<description>Milliseconds between fetching sensor-values from the Hue Bridge. A higher value means more delay for
|
||||
the sensor values, but a too low value can cause congestion on the Hue Bridge. Use 0 to disable the polling for
|
||||
sensors. Default is 500.</description>
|
||||
<default>500</default>
|
||||
</parameter>
|
||||
|
@ -0,0 +1,14 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMjCCAdigAwIBAgIUO7FSLbaxikuXAljzVaurLXWmFw4wCgYIKoZIzj0EAwIw
|
||||
OTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC1BoaWxpcHMgSHVlMRQwEgYDVQQDDAty
|
||||
b290LWJyaWRnZTAiGA8yMDE3MDEwMTAwMDAwMFoYDzIwMzgwMTE5MDMxNDA3WjA5
|
||||
MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLUGhpbGlwcyBIdWUxFDASBgNVBAMMC3Jv
|
||||
b3QtYnJpZGdlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjNw2tx2AplOf9x86
|
||||
aTdvEcL1FU65QDxziKvBpW9XXSIcibAeQiKxegpq8Exbr9v6LBnYbna2VcaK0G22
|
||||
jOKkTqOBuTCBtjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNV
|
||||
HQ4EFgQUZ2ONTFrDT6o8ItRnKfqWKnHFGmQwdAYDVR0jBG0wa4AUZ2ONTFrDT6o8
|
||||
ItRnKfqWKnHFGmShPaQ7MDkxCzAJBgNVBAYTAk5MMRQwEgYDVQQKDAtQaGlsaXBz
|
||||
IEh1ZTEUMBIGA1UEAwwLcm9vdC1icmlkZ2WCFDuxUi22sYpLlwJY81Wrqy11phcO
|
||||
MAoGCCqGSM49BAMCA0gAMEUCIEBYYEOsa07TH7E5MJnGw557lVkORgit2Rm1h3B2
|
||||
sFgDAiEA1Fj/C3AN5psFMjo0//mrQebo0eKd3aWRx+pQY08mk48=
|
||||
-----END CERTIFICATE-----
|
@ -12,15 +12,16 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.hue.internal.dto.ApiVersion;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Samuel Leisering - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ApiVersionTest {
|
||||
|
||||
@Test
|
||||
|
@ -14,35 +14,47 @@ package org.openhab.binding.hue.internal;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.openhab.binding.hue.internal.HttpClient.Result;
|
||||
import org.openhab.binding.hue.internal.config.HueBridgeConfig;
|
||||
import org.openhab.binding.hue.internal.connection.HueBridge;
|
||||
import org.openhab.binding.hue.internal.dto.Scene;
|
||||
import org.openhab.binding.hue.internal.exceptions.ApiException;
|
||||
import org.openhab.core.i18n.CommunicationException;
|
||||
import org.openhab.core.i18n.ConfigurationException;
|
||||
|
||||
/**
|
||||
* @author Hengrui Jiang - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueBridgeTest {
|
||||
|
||||
@Test
|
||||
public void testGetScenesExcludeRecycleScenes() throws IOException, ApiException {
|
||||
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
|
||||
|
||||
HueBridge hueBridge = new HueBridge("ip", "baseUrl", "username", Executors.newScheduledThreadPool(1),
|
||||
mockHttpClient);
|
||||
|
||||
List<Scene> testScenes = Arrays.asList(new Scene("id1", "name1", "group1", Collections.emptyList(), true), //
|
||||
new Scene("id2", "name2", "group2", Collections.emptyList(), false));
|
||||
when(mockHttpClient.get("baseUrl/username/scenes")).thenReturn(new Result(createMockResponse(testScenes), 200));
|
||||
HueBridge hueBridge = new HueBridge(mock(HttpClient.class), "ip", 443, HueBridgeConfig.HTTPS, "username",
|
||||
Executors.newScheduledThreadPool(1)) {
|
||||
@Override
|
||||
public HueResult get(String address) throws ConfigurationException, CommunicationException {
|
||||
if ("https://ip:443/api/username/lights".equals(address)) {
|
||||
return new HueResult("{}", HttpStatus.OK_200);
|
||||
} else if ("https://ip:443/api/username/scenes".equals(address)) {
|
||||
List<Scene> testScenes = List.of( //
|
||||
new Scene("id1", "name1", "group1", List.of(), true), //
|
||||
new Scene("id2", "name2", "group2", List.of(), false));
|
||||
return new HueResult(createMockResponse(testScenes), HttpStatus.OK_200);
|
||||
}
|
||||
return super.get(address);
|
||||
}
|
||||
};
|
||||
|
||||
List<Scene> scenes = hueBridge.getScenes();
|
||||
assertThat(scenes.size(), is(1));
|
||||
@ -51,15 +63,22 @@ public class HueBridgeTest {
|
||||
|
||||
@Test
|
||||
public void testGetScenesOrderByGroup() throws IOException, ApiException {
|
||||
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
|
||||
|
||||
HueBridge hueBridge = new HueBridge("ip", "baseUrl", "username", Executors.newScheduledThreadPool(1),
|
||||
mockHttpClient);
|
||||
|
||||
List<Scene> testScenes = Arrays.asList(new Scene("id1", "name1", "group1", Collections.emptyList(), false), //
|
||||
new Scene("id2", "name2", "group2", Collections.emptyList(), false),
|
||||
new Scene("id3", "name3", "group1", Collections.emptyList(), false));
|
||||
when(mockHttpClient.get("baseUrl/username/scenes")).thenReturn(new Result(createMockResponse(testScenes), 200));
|
||||
HueBridge hueBridge = new HueBridge(mock(HttpClient.class), "ip", 443, HueBridgeConfig.HTTPS, "username",
|
||||
Executors.newScheduledThreadPool(1)) {
|
||||
@Override
|
||||
public HueResult get(String address) throws ConfigurationException, CommunicationException {
|
||||
if ("https://ip:443/api/username/lights".equals(address)) {
|
||||
return new HueResult("{}", HttpStatus.OK_200);
|
||||
} else if ("https://ip:443/api/username/scenes".equals(address)) {
|
||||
List<Scene> testScenes = List.of( //
|
||||
new Scene("id1", "name1", "group1", List.of(), false), //
|
||||
new Scene("id2", "name2", "group2", List.of(), false), //
|
||||
new Scene("id3", "name3", "group1", List.of(), false));
|
||||
return new HueResult(createMockResponse(testScenes), HttpStatus.OK_200);
|
||||
}
|
||||
return super.get(address);
|
||||
}
|
||||
};
|
||||
|
||||
List<Scene> scenes = hueBridge.getScenes();
|
||||
assertThat(scenes.size(), is(3));
|
||||
@ -92,8 +111,7 @@ public class HueBridgeTest {
|
||||
" \"version\": 2,\n" + //
|
||||
" \"group\": \"%s\"\n" + //
|
||||
" }";
|
||||
String lights = String.join(",",
|
||||
scene.getLightIds().stream().map(id -> "\"" + id + "\"").collect(Collectors.toList()));
|
||||
String lights = scene.getLightIds().stream().map(id -> "\"" + id + "\"").collect(Collectors.joining(","));
|
||||
return String.format(template, scene.getId(), scene.getName(), lights, scene.isRecycle(), scene.getGroupId());
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,12 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.dto.ColorTemperature;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
import org.openhab.binding.hue.internal.dto.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.handler.LightStateConverter;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
@ -31,6 +34,7 @@ import org.openhab.core.library.types.PercentType;
|
||||
* @author Denis Dudnik - switched to internally integrated source of Jue library
|
||||
* @author Markus Rathgeb - migrated to plain Java test
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class LightStateConverterTest {
|
||||
|
||||
@Test
|
||||
|
@ -18,11 +18,16 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.hue.internal.dto.FullGroup;
|
||||
import org.openhab.binding.hue.internal.dto.Scene;
|
||||
import org.openhab.binding.hue.internal.dto.State;
|
||||
|
||||
/**
|
||||
* @author HJiang - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SceneTest {
|
||||
|
||||
private static final State PLACEHOLDER_STATE = new State();
|
||||
|
@ -23,10 +23,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.openhab.binding.hue.internal.FullConfig;
|
||||
import org.openhab.binding.hue.internal.FullLight;
|
||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.StateUpdate;
|
||||
import org.openhab.binding.hue.internal.dto.FullConfig;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.dto.State.ColorMode;
|
||||
import org.openhab.binding.hue.internal.dto.StateUpdate;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
|
@ -12,7 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.handler;
|
||||
|
||||
import org.openhab.binding.hue.internal.State.ColorMode;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.hue.internal.dto.State.ColorMode;
|
||||
|
||||
/**
|
||||
* Builder for the current state of a hue light.
|
||||
@ -22,6 +23,7 @@ import org.openhab.binding.hue.internal.State.ColorMode;
|
||||
* @author Markus Rathgeb - migrated to plain Java test
|
||||
* @author Christoph Weitkamp - Added support for bulbs using CIE XY colormode only
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HueLightState {
|
||||
|
||||
int brightness = 200;
|
||||
|
@ -26,8 +26,6 @@ Fragment-Host: org.openhab.binding.hue
|
||||
jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\
|
||||
org.apache.servicemix.specs.activation-api-1.2.1;version='[1.2.1,1.2.2)',\
|
||||
org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\
|
||||
org.objectweb.asm.commons;version='[9.0.0,9.0.1)',\
|
||||
org.objectweb.asm.tree;version='[9.0.0,9.0.1)',\
|
||||
jakarta.annotation-api;version='[2.0.0,2.0.1)',\
|
||||
jakarta.inject.jakarta.inject-api;version='[2.0.0,2.0.1)',\
|
||||
javax.measure.unit-api;version='[2.1.2,2.1.3)',\
|
||||
@ -45,12 +43,21 @@ Fragment-Host: org.openhab.binding.hue
|
||||
org.apache.felix.scr;version='[2.1.30,2.1.31)',\
|
||||
org.osgi.util.function;version='[1.2.0,1.2.1)',\
|
||||
org.osgi.util.promise;version='[1.2.0,1.2.1)',\
|
||||
org.openhab.binding.hue;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.binding.hue.tests;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.binding.xml;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.core;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.discovery;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.xml;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.io.console;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.io.net;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.test;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.thing;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.thing.xml;version='[3.4.0,3.4.1)',\
|
||||
xstream;version='[1.4.19,1.4.20)',\
|
||||
com.google.gson;version='[2.8.9,2.8.10)',\
|
||||
org.objectweb.asm;version='[9.2.0,9.2.1)',\
|
||||
org.apache.felix.configadmin;version='[1.9.24,1.9.25)',\
|
||||
org.apache.xbean.bundleutils;version='[4.21.0,4.21.1)',\
|
||||
org.apache.xbean.finder;version='[4.21.0,4.21.1)',\
|
||||
org.eclipse.jetty.client;version='[9.4.46,9.4.47)',\
|
||||
org.eclipse.jetty.http;version='[9.4.46,9.4.47)',\
|
||||
org.eclipse.jetty.io;version='[9.4.46,9.4.47)',\
|
||||
@ -63,22 +70,14 @@ Fragment-Host: org.openhab.binding.hue
|
||||
org.eclipse.jetty.websocket.client;version='[9.4.46,9.4.47)',\
|
||||
org.eclipse.jetty.websocket.common;version='[9.4.46,9.4.47)',\
|
||||
org.ops4j.pax.logging.pax-logging-api;version='[2.0.16,2.0.17)',\
|
||||
org.ops4j.pax.web.pax-web-api;version='[7.3.25,7.3.26)',\
|
||||
org.jupnp;version='[2.6.1,2.6.2)',\
|
||||
ch.qos.logback.classic;version='[1.2.11,1.2.12)',\
|
||||
ch.qos.logback.core;version='[1.2.11,1.2.12)',\
|
||||
org.eclipse.jdt.annotation;version='[2.2.100,2.2.101)',\
|
||||
javax.jmdns;version='[3.5.8,3.5.9)',\
|
||||
net.bytebuddy.byte-buddy;version='[1.12.1,1.12.2)',\
|
||||
net.bytebuddy.byte-buddy-agent;version='[1.12.1,1.12.2)',\
|
||||
org.mockito.mockito-core;version='[4.1.0,4.1.1)',\
|
||||
org.objenesis;version='[3.2.0,3.2.1)',\
|
||||
org.openhab.core.config.discovery.mdns;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.io.transport.mdns;version='[3.4.0,3.4.1)',\
|
||||
biz.aQute.tester.junit-platform;version='[6.3.0,6.3.1)',\
|
||||
org.openhab.binding.hue;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.binding.hue.tests;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.binding.xml;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.core;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.discovery;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.discovery.upnp;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.config.xml;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.io.console;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.io.net;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.test;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.thing;version='[3.4.0,3.4.1)',\
|
||||
org.openhab.core.thing.xml;version='[3.4.0,3.4.1)'
|
||||
ch.qos.logback.classic;version='[1.2.11,1.2.12)',\
|
||||
ch.qos.logback.core;version='[1.2.11,1.2.12)'
|
||||
|
@ -15,26 +15,35 @@ package org.openhab.binding.hue.internal;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.hue.internal.config.HueBridgeConfig;
|
||||
import org.openhab.binding.hue.internal.connection.HueBridge;
|
||||
import org.openhab.binding.hue.internal.discovery.HueDeviceDiscoveryService;
|
||||
import org.openhab.binding.hue.internal.dto.FullLight;
|
||||
import org.openhab.binding.hue.internal.exceptions.ApiException;
|
||||
import org.openhab.binding.hue.internal.handler.HueBridgeHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.discovery.DiscoveryListener;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultFlag;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.i18n.CommunicationException;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
@ -54,7 +63,6 @@ import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
|
||||
*/
|
||||
public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent {
|
||||
|
||||
protected HueThingHandlerFactory hueThingHandlerFactory;
|
||||
protected DiscoveryListener discoveryListener;
|
||||
protected ThingRegistry thingRegistry;
|
||||
protected Bridge hueBridge;
|
||||
@ -74,7 +82,8 @@ public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, "1.2.3.4");
|
||||
configuration.put(USER_NAME, "testUserName");
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put("useSelfSignedCertificate", false);
|
||||
|
||||
hueBridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID, BRIDGE_THING_UID, null, "Bridge",
|
||||
configuration);
|
||||
@ -150,45 +159,45 @@ public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startSearchIsCalled() {
|
||||
public void startSearchIsCalled() throws IOException, ApiException {
|
||||
final AtomicBoolean searchHasBeenTriggered = new AtomicBoolean(false);
|
||||
|
||||
MockedHttpClient mockedHttpClient = new MockedHttpClient() {
|
||||
|
||||
HueBridge mockedHueBridge = new HueBridge(mock(HttpClient.class), "ip", 443, HueBridgeConfig.HTTPS, "username",
|
||||
Executors.newScheduledThreadPool(1)) {
|
||||
@Override
|
||||
public Result put(String address, String body) throws IOException {
|
||||
return new Result("", 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result get(String address) throws IOException {
|
||||
public HueResult get(String address) throws CommunicationException {
|
||||
if (address.endsWith("testUserName")) {
|
||||
String body = "{\"lights\":{}}";
|
||||
return new Result(body, 200);
|
||||
return new HueResult(body, HttpStatus.OK_200);
|
||||
} else if (address.endsWith("lights") || address.endsWith("sensors") || address.endsWith("groups")) {
|
||||
String body = "{}";
|
||||
return new Result(body, 200);
|
||||
return new HueResult(body, HttpStatus.OK_200);
|
||||
} else if (address.endsWith("testUserName/config")) {
|
||||
String body = "{ \"apiversion\": \"1.26.0\"}";
|
||||
return new Result(body, 200);
|
||||
String body = "{\"apiversion\": \"1.26.0\"}";
|
||||
return new HueResult(body, HttpStatus.OK_200);
|
||||
} else {
|
||||
return new Result("", 404);
|
||||
return new HueResult("", HttpStatus.NOT_FOUND_404);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result post(String address, String body) throws IOException {
|
||||
public HueResult post(String address, String body) throws CommunicationException {
|
||||
if (address.endsWith("lights")) {
|
||||
String bodyReturn = "{\"success\": {\"/lights\": \"Searching for new devices\"}}";
|
||||
searchHasBeenTriggered.set(true);
|
||||
return new Result(bodyReturn, 200);
|
||||
return new HueResult(bodyReturn, HttpStatus.OK_200);
|
||||
} else {
|
||||
return new Result("", 404);
|
||||
return new HueResult("", HttpStatus.NOT_FOUND_404);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HueResult put(String address, String body) throws CommunicationException {
|
||||
return new HueResult("", HttpStatus.OK_200);
|
||||
}
|
||||
};
|
||||
|
||||
installHttpClientMock(hueBridgeHandler, mockedHttpClient);
|
||||
installHttpClientMock(hueBridgeHandler, mockedHueBridge);
|
||||
|
||||
ThingStatusInfo online = ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build();
|
||||
waitForAssert(() -> {
|
||||
@ -201,19 +210,17 @@ public class HueDeviceDiscoveryServiceOSGiTest extends AbstractHueOSGiTestParent
|
||||
});
|
||||
}
|
||||
|
||||
private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, MockedHttpClient mockedHttpClient) {
|
||||
private void installHttpClientMock(HueBridgeHandler hueBridgeHandler, HueBridge mockedHueBridge) {
|
||||
waitForAssert(() -> {
|
||||
try {
|
||||
// mock HttpClient
|
||||
final Field hueBridgeField = HueBridgeHandler.class.getDeclaredField("hueBridge");
|
||||
hueBridgeField.setAccessible(true);
|
||||
hueBridgeField.set(hueBridgeHandler, mockedHueBridge);
|
||||
|
||||
final Object hueBridgeValue = hueBridgeField.get(hueBridgeHandler);
|
||||
assertThat(hueBridgeValue, is(notNullValue()));
|
||||
|
||||
final Field httpClientField = HueBridge.class.getDeclaredField("http");
|
||||
httpClientField.setAccessible(true);
|
||||
httpClientField.set(hueBridgeValue, mockedHttpClient);
|
||||
|
||||
final Field usernameField = HueBridge.class.getDeclaredField("username");
|
||||
usernameField.setAccessible(true);
|
||||
usernameField.set(hueBridgeValue, hueBridgeHandler.getThing().getConfiguration().get(USER_NAME));
|
||||
|
@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal;
|
||||
|
||||
/**
|
||||
* @author Denis Dudnik - Initial contribution
|
||||
*/
|
||||
public class MockedHttpClient extends HttpClient {
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2022 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.hue.internal.discovery;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.jupnp.model.ValidationException;
|
||||
import org.jupnp.model.meta.DeviceDetails;
|
||||
import org.jupnp.model.meta.ManufacturerDetails;
|
||||
import org.jupnp.model.meta.ModelDetails;
|
||||
import org.jupnp.model.meta.RemoteDevice;
|
||||
import org.jupnp.model.meta.RemoteDeviceIdentity;
|
||||
import org.jupnp.model.meta.RemoteService;
|
||||
import org.jupnp.model.types.DeviceType;
|
||||
import org.jupnp.model.types.UDN;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultFlag;
|
||||
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
|
||||
import org.openhab.core.test.java.JavaOSGiTest;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* Tests for {@link org.openhab.binding.hue.internal.discovery.HueBridgeDiscoveryParticipant}.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Thomas Höfer - Added representation
|
||||
* @author Markus Rathgeb - migrated to plain Java test
|
||||
*/
|
||||
public class HueBridgeDiscoveryParticipantOSGITest extends JavaOSGiTest {
|
||||
|
||||
UpnpDiscoveryParticipant discoveryParticipant;
|
||||
|
||||
RemoteDevice hueDevice;
|
||||
RemoteDevice otherDevice;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
discoveryParticipant = getService(UpnpDiscoveryParticipant.class, HueBridgeDiscoveryParticipant.class);
|
||||
assertThat(discoveryParticipant, is(notNullValue()));
|
||||
|
||||
try {
|
||||
final RemoteService remoteService = null;
|
||||
|
||||
hueDevice = new RemoteDevice(
|
||||
new RemoteDeviceIdentity(new UDN("123"), 60, new URL("http://hue"), null, null),
|
||||
new DeviceType("namespace", "type"),
|
||||
new DeviceDetails(new URL("http://1.2.3.4/"), "Hue Bridge", new ManufacturerDetails("Philips"),
|
||||
new ModelDetails("Philips hue bridge"), "serial123", "upc", null),
|
||||
remoteService);
|
||||
|
||||
otherDevice = new RemoteDevice(
|
||||
new RemoteDeviceIdentity(new UDN("567"), 60, new URL("http://acme"), null, null),
|
||||
new DeviceType("namespace", "type"), new DeviceDetails("Some Device",
|
||||
new ManufacturerDetails("Taiwan"), new ModelDetails("$%&/"), "serial567", "upc"),
|
||||
remoteService);
|
||||
} catch (final ValidationException | MalformedURLException ex) {
|
||||
fail("Internal test error.");
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanUp() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctSupportedTypes() {
|
||||
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().size(), is(1));
|
||||
assertThat(discoveryParticipant.getSupportedThingTypeUIDs().iterator().next(), is(THING_TYPE_BRIDGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void correctThingUID() {
|
||||
assertThat(discoveryParticipant.getThingUID(hueDevice), is(new ThingUID("hue:bridge:serial123")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validDiscoveryResult() {
|
||||
final DiscoveryResult result = discoveryParticipant.createResult(hueDevice);
|
||||
assertThat(result.getFlag(), is(DiscoveryResultFlag.NEW));
|
||||
assertThat(result.getThingUID(), is(new ThingUID("hue:bridge:serial123")));
|
||||
assertThat(result.getThingTypeUID(), is(THING_TYPE_BRIDGE));
|
||||
assertThat(result.getBridgeUID(), is(nullValue()));
|
||||
assertThat(result.getProperties().get(HOST), is("1.2.3.4"));
|
||||
assertThat(result.getProperties().get(PROPERTY_SERIAL_NUMBER), is("serial123"));
|
||||
assertThat(result.getRepresentationProperty(), is(PROPERTY_SERIAL_NUMBER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noThingUIDForUnknownDevice() {
|
||||
assertThat(discoveryParticipant.getThingUID(otherDevice), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDiscoveryResultForUnknownDevice() {
|
||||
assertThat(discoveryParticipant.createResult(otherDevice), is(nullValue()));
|
||||
}
|
||||
}
|
@ -37,7 +37,6 @@ import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Christoph Knauf - Initial contribution
|
||||
* @author Markus Rathgeb - migrated to plain Java test
|
||||
*/
|
||||
@ -51,64 +50,18 @@ public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
|
||||
final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID("hue", "bridge");
|
||||
final String ip1 = "192.168.31.17";
|
||||
final String ip2 = "192.168.30.28";
|
||||
final String sn1 = "00178820057f";
|
||||
final String sn2 = "001788141b41";
|
||||
final String sn1 = "001788fffe20057f";
|
||||
final String sn2 = "001788fffe141b41";
|
||||
final ThingUID BRIDGE_THING_UID_1 = new ThingUID(BRIDGE_THING_TYPE_UID, sn1);
|
||||
final ThingUID BRIDGE_THING_UID_2 = new ThingUID(BRIDGE_THING_TYPE_UID, sn2);
|
||||
final String validBridgeDiscoveryResult = "[{\"id\":\"001788fffe20057f\",\"internalipaddress\":" + ip1
|
||||
+ "},{\"id\":\"001788fffe141b41\",\"internalipaddress\":" + ip2 + "}]";
|
||||
final String validBridgeDiscoveryResult = "[{\"id\":\"" + sn1 + "\",\"internalipaddress\":" + ip1 + "},{\"id\":\""
|
||||
+ sn2 + "\",\"internalipaddress\":" + ip2 + "}]";
|
||||
String discoveryResult;
|
||||
String expBridgeDescription = "" + //
|
||||
"<?xml version=\"1.0\"?>" + //
|
||||
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" + //
|
||||
" <specVersion>" + //
|
||||
" <major>1</major>" + //
|
||||
" <minor>0</minor>" + //
|
||||
" </specVersion>" + //
|
||||
" <URLBase>http://$IP:80/</URLBase>" + //
|
||||
" <device>" + //
|
||||
" <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>" + //
|
||||
" <friendlyName>Philips hue ($IP)</friendlyName>" + //
|
||||
" <manufacturer>Royal Philips Electronics</manufacturer>" + //
|
||||
" <manufacturerURL>http://www.philips.com</manufacturerURL>" + //
|
||||
"<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>" + //
|
||||
"<modelName>Philips hue bridge 2012</modelName>" + //
|
||||
"<modelNumber>1000000000000</modelNumber>" + //
|
||||
"<modelURL>http://www.meethue.com</modelURL>" + //
|
||||
" <serialNumber>93eadbeef13</serialNumber>" + //
|
||||
" <UDN>uuid:01234567-89ab-cdef-0123-456789abcdef</UDN>" + //
|
||||
" <serviceList>" + //
|
||||
" <service>" + //
|
||||
" <serviceType>(null)</serviceType>" + //
|
||||
" <serviceId>(null)</serviceId>" + //
|
||||
" <controlURL>(null)</controlURL>" + //
|
||||
" <eventSubURL>(null)</eventSubURL>" + //
|
||||
" <SCPDURL>(null)</SCPDURL>" + //
|
||||
" </service>" + //
|
||||
" </serviceList>" + //
|
||||
" <presentationURL>index.html</presentationURL>" + //
|
||||
" <iconList>" + //
|
||||
" <icon>" + //
|
||||
" <mimetype>image/png</mimetype>" + //
|
||||
" <height>48</height>" + //
|
||||
" <width>48</width>" + //
|
||||
" <depth>24</depth>" + //
|
||||
" <url>hue_logo_0.png</url>" + //
|
||||
" </icon>" + //
|
||||
" <icon>" + //
|
||||
" <mimetype>image/png</mimetype>" + //
|
||||
" <height>120</height>" + //
|
||||
" <width>120</width>" + //
|
||||
" <depth>24</depth>" + //
|
||||
" <url>hue_logo_3.png</url>" + //
|
||||
" </icon>" + //
|
||||
" </iconList>" + //
|
||||
" </device>" + //
|
||||
"</root>";
|
||||
String expBridgeDescription = "{\"name\":\"Philips Hue\",\"datastoreversion\":\"113\",\"swversion\":\"1948086000\",\"apiversion\":\"1.48.0\",\"mac\":\"00:11:22:33:44\",\"bridgeid\":\"$SN\",\"factorynew\":false,\"replacesbridgeid\":null,\"modelid\":\"BSB002\",\"starterkitid\":\"\"}";
|
||||
|
||||
private void checkDiscoveryResult(DiscoveryResult result, String expIp, String expSn) {
|
||||
assertThat(result.getBridgeUID(), nullValue());
|
||||
assertThat(result.getLabel(), is(HueBridgeNupnpDiscovery.LABEL_PATTERN.replace("IP", expIp)));
|
||||
assertThat(result.getLabel(), is(String.format(HueBridgeNupnpDiscovery.LABEL_PATTERN, expIp)));
|
||||
assertThat(result.getProperties().get("ipAddress"), is(expIp));
|
||||
assertThat(result.getProperties().get("serialNumber"), is(expSn));
|
||||
}
|
||||
@ -132,9 +85,9 @@ public class HueBridgeNupnpDiscoveryOSGITest extends JavaOSGiTest {
|
||||
if (url.contains("meethue")) {
|
||||
return discoveryResult;
|
||||
} else if (url.contains(ip1)) {
|
||||
return expBridgeDescription.replaceAll("$IP", ip1);
|
||||
return expBridgeDescription.replaceAll("$SN", sn1);
|
||||
} else if (url.contains(ip2)) {
|
||||
return expBridgeDescription.replaceAll("$IP", ip2);
|
||||
return expBridgeDescription.replaceAll("$SN", sn2);
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
|
@ -16,19 +16,19 @@ import static org.eclipse.jdt.annotation.Checks.requireNonNull;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.openhab.binding.hue.internal.HueBindingConstants.*;
|
||||
import static org.openhab.binding.hue.internal.config.HueBridgeConfig.HTTP;
|
||||
import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.hue.internal.AbstractHueOSGiTestParent;
|
||||
import org.openhab.binding.hue.internal.HueBridge;
|
||||
import org.openhab.binding.hue.internal.HueConfigStatusMessage;
|
||||
import org.openhab.binding.hue.internal.config.HueBridgeConfig;
|
||||
import org.openhab.binding.hue.internal.connection.HueBridge;
|
||||
import org.openhab.binding.hue.internal.exceptions.ApiException;
|
||||
import org.openhab.binding.hue.internal.exceptions.LinkButtonException;
|
||||
import org.openhab.binding.hue.internal.exceptions.UnauthorizedException;
|
||||
@ -36,6 +36,7 @@ import org.openhab.core.common.ThreadPoolManager;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingRegistry;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
@ -72,13 +73,14 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void assertThatANewUserIsAddedToConfigIfNotExistingYet() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
hueBridgeHandler.thingUpdated(bridge);
|
||||
|
||||
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
|
||||
injectBridge(hueBridgeHandler,
|
||||
new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
|
||||
@Override
|
||||
public String link(String deviceType) throws IOException, ApiException {
|
||||
return TEST_USER_NAME;
|
||||
@ -95,13 +97,14 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(USER_NAME, TEST_USER_NAME);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
hueBridgeHandler.thingUpdated(bridge);
|
||||
|
||||
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
|
||||
injectBridge(hueBridgeHandler,
|
||||
new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
|
||||
@Override
|
||||
public void authenticate(String userName) throws IOException, ApiException {
|
||||
}
|
||||
@ -117,13 +120,14 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(USER_NAME, "notAuthenticatedUser");
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
hueBridgeHandler.thingUpdated(bridge);
|
||||
|
||||
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
|
||||
injectBridge(hueBridgeHandler,
|
||||
new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
|
||||
@Override
|
||||
public void authenticate(String userName) throws IOException, ApiException {
|
||||
throw new UnauthorizedException();
|
||||
@ -141,13 +145,14 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void verifyStatusIfLinkButtonIsNotPressed() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
hueBridgeHandler.thingUpdated(bridge);
|
||||
|
||||
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
|
||||
injectBridge(hueBridgeHandler,
|
||||
new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
|
||||
@Override
|
||||
public String link(String deviceType) throws IOException, ApiException {
|
||||
throw new LinkButtonException();
|
||||
@ -165,13 +170,14 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void verifyStatusIfNewUserCannotBeCreated() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
hueBridgeHandler.thingUpdated(bridge);
|
||||
|
||||
injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
|
||||
injectBridge(hueBridgeHandler,
|
||||
new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
|
||||
@Override
|
||||
public String link(String deviceType) throws IOException, ApiException {
|
||||
throw new ApiException();
|
||||
@ -190,7 +196,7 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void verifyOfflineIsSetWithoutBridgeOfflineStatus() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, DUMMY_HOST);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
@ -206,14 +212,13 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsNull() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, null);
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
|
||||
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
|
||||
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
|
||||
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST).withMessageKeySuffix(IP_ADDRESS_MISSING)
|
||||
.withArguments(HOST).build();
|
||||
|
||||
waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
|
||||
}
|
||||
@ -222,19 +227,19 @@ public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
|
||||
public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsAnEmptyString() {
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.put(HOST, "");
|
||||
configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
|
||||
configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
|
||||
Bridge bridge = createBridgeThing(configuration);
|
||||
|
||||
HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
|
||||
|
||||
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
|
||||
.withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
|
||||
ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST).withMessageKeySuffix(IP_ADDRESS_MISSING)
|
||||
.withArguments(HOST).build();
|
||||
|
||||
waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
|
||||
}
|
||||
|
||||
private Bridge createBridgeThing(Configuration configuration) {
|
||||
configuration.put("useSelfSignedCertificate", false);
|
||||
Bridge bridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID,
|
||||
new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge"), null, "Bridge", configuration);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user