added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.neeo-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-neeo" description="NEEO Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.neeo/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.neeo.internal.models.ExecuteResult;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoBrain;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoForwardActions;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.binding.neeo.internal.net.HttpRequest;
|
||||
import org.openhab.binding.neeo.internal.net.HttpResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The class provides the API for communicating with a NEEO brain
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoBrainApi implements AutoCloseable {
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoBrainApi.class);
|
||||
|
||||
/** The gson used in communications */
|
||||
private final Gson gson = NeeoUtil.getGson();
|
||||
|
||||
/** The {@link HttpRequest} used for making requests */
|
||||
private final AtomicReference<HttpRequest> request = new AtomicReference<>(new HttpRequest());
|
||||
|
||||
/** The IP address of the neeo brain */
|
||||
private final NeeoUrlBuilder urlBuilder;
|
||||
|
||||
/**
|
||||
* Instantiates the API using the specified IP Address
|
||||
*
|
||||
* @param ipAddress the non-empty ip address
|
||||
*/
|
||||
public NeeoBrainApi(String ipAddress) {
|
||||
NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
|
||||
|
||||
this.urlBuilder = new NeeoUrlBuilder(
|
||||
NeeoConstants.PROTOCOL + ipAddress + ":" + NeeoConstants.DEFAULT_BRAIN_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link NeeoBrain}
|
||||
*
|
||||
* @return the non-null {@link NeeoBrain}
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public NeeoBrain getBrain() throws IOException {
|
||||
final String url = urlBuilder.append(NeeoConstants.PROJECTS_HOME).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), NeeoBrain.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link NeeoRoom} from the brain for the specified room key
|
||||
*
|
||||
* @param roomKey the non-empty room key
|
||||
* @return the {@link NeeoRoom}
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public NeeoRoom getRoom(String roomKey) throws IOException {
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
|
||||
final String url = urlBuilder.append(NeeoConstants.GET_ROOM).sub("roomkey", roomKey).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), NeeoRoom.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the specified recipe in the specified room
|
||||
*
|
||||
* @param roomKey the non-empty room key
|
||||
* @param recipeKey the non-empty recipe key
|
||||
* @return the execute result
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
ExecuteResult executeRecipe(String roomKey, String recipeKey) throws IOException {
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final String url = urlBuilder.append(NeeoConstants.EXECUTE_RECIPE).sub("roomkey", roomKey)
|
||||
.sub("recipekey", recipeKey).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), ExecuteResult.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the specified scenario in the specified room
|
||||
*
|
||||
* @param roomKey the non-empty room key
|
||||
* @param scenarioKey the non-empty scenario key
|
||||
* @return the execute result
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
ExecuteResult stopScenario(String roomKey, String scenarioKey) throws IOException {
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final String url = urlBuilder.append(NeeoConstants.STOP_SCENARIO).sub("roomkey", roomKey)
|
||||
.sub("scenariokey", scenarioKey).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), ExecuteResult.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active scenarios.
|
||||
*
|
||||
* @return the non-null, possibly empty list of active scenarios keys
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public String[] getActiveScenarios() throws IOException {
|
||||
final String url = urlBuilder.append(NeeoConstants.GET_ACTIVESCENARIOS).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), String[].class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger macro on a specified device in the specified room.
|
||||
*
|
||||
* @param roomKey the non-null room key
|
||||
* @param deviceKey the non-null device key
|
||||
* @param macroKey the non-null macro key
|
||||
* @return the execute result
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
ExecuteResult triggerMacro(String roomKey, String deviceKey, String macroKey) throws IOException {
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(deviceKey, "deviceKey cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(macroKey, "macroKey cannot be empty");
|
||||
|
||||
final String url = urlBuilder.append(NeeoConstants.TRIGGER_MACRO).sub("roomkey", roomKey)
|
||||
.sub("devicekey", deviceKey).sub("macrokey", macroKey).toString();
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendGetCommand(url);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
|
||||
return gson.fromJson(resp.getContent(), ExecuteResult.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our API with the brain's forward actions.
|
||||
*
|
||||
* @param url the non-null URL to register to
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public void registerForwardActions(URL url) throws IOException {
|
||||
Objects.requireNonNull(url, "url cannot be null");
|
||||
|
||||
final String brainUrl = urlBuilder.append(NeeoConstants.FORWARD_ACTIONS).toString();
|
||||
|
||||
logger.debug("Registering forward actions {} using callback {}", brainUrl, url.toExternalForm());
|
||||
|
||||
final String forwardActions = gson.toJson(new NeeoForwardActions(url.getHost(), url.getPort(), url.getPath()));
|
||||
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendPostJsonCommand(brainUrl, forwardActions);
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister our API with the brain's forward actions.
|
||||
*
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public void deregisterForwardActions() throws IOException {
|
||||
final String brainUrl = urlBuilder.append(NeeoConstants.FORWARD_ACTIONS).toString();
|
||||
|
||||
logger.debug("Deregistering forward actions callback {}", brainUrl);
|
||||
final HttpRequest rqst = request.get();
|
||||
final HttpResponse resp = rqst.sendPostJsonCommand(brainUrl, "");
|
||||
if (resp.getHttpCode() != HttpStatus.OK_200) {
|
||||
throw resp.createException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
NeeoUtil.close(request.getAndSet(new HttpRequest()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to help build NEEO URLs
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
private class NeeoUrlBuilder {
|
||||
/** The current url */
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* Create a new class from the given URL
|
||||
*
|
||||
* @param url a non-null, non-empty URL
|
||||
*/
|
||||
NeeoUrlBuilder(final String url) {
|
||||
NeeoUtil.requireNotEmpty(url, "url cannot be empty");
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes '{key}' into value from the URL and returns a new {@link NeeoUrlBuilder} with the new URL
|
||||
*
|
||||
* @param key a non-null, non-empty key
|
||||
* @param value a non-null, non-empty key
|
||||
* @return a non-null {@link NeeoUrlBuilder} with the new URL
|
||||
*/
|
||||
NeeoUrlBuilder sub(String key, String value) {
|
||||
NeeoUtil.requireNotEmpty(key, "key cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(value, "value cannot be empty");
|
||||
|
||||
final String newUrl = url.replace("{" + key + "}", value);
|
||||
return new NeeoUrlBuilder(newUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified value to the URL and returns the new {@link NeeoUrlBuilder} with the new URL
|
||||
*
|
||||
* @param value a non-null, non-empty value
|
||||
* @return a non-null {@link NeeoUrlBuilder} with the new URL
|
||||
*/
|
||||
NeeoUrlBuilder append(String value) {
|
||||
NeeoUtil.requireNotEmpty(value, "value cannot be empty");
|
||||
|
||||
return new NeeoUrlBuilder(url + (value.startsWith("/") ? value : ("/" + value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.handler.NeeoBrainHandler;
|
||||
|
||||
/**
|
||||
* The configuration class a neeo brain and used by {@link NeeoBrainHandler}
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoBrainConfig {
|
||||
|
||||
/** The ip address */
|
||||
@Nullable
|
||||
private String ipAddress;
|
||||
|
||||
/** Whether to enable forward actions */
|
||||
private boolean enableForwardActions;
|
||||
|
||||
/** The forward actions chain (comma delimited) */
|
||||
@Nullable
|
||||
private String forwardChain;
|
||||
|
||||
/** Whether to discover empty rooms or not */
|
||||
private boolean discoverEmptyRooms;
|
||||
|
||||
/** The check status interval (in seconds) */
|
||||
private int checkStatusInterval;
|
||||
|
||||
/**
|
||||
* Gets the ip address.
|
||||
*
|
||||
* @return the ip address
|
||||
*/
|
||||
@Nullable
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ip address.
|
||||
*
|
||||
* @param ipAddress the new ip address
|
||||
*/
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if forward actions is enabled
|
||||
*
|
||||
* @return true for enabled, false otherwise
|
||||
*/
|
||||
public boolean isEnableForwardActions() {
|
||||
return enableForwardActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to enable forward actions
|
||||
*
|
||||
* @param enableForwardActions true to enable, false otherwise
|
||||
*/
|
||||
public void setEnableForwardActions(boolean enableForwardActions) {
|
||||
this.enableForwardActions = enableForwardActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the forward chain
|
||||
*
|
||||
* @return the forward chain
|
||||
*/
|
||||
@Nullable
|
||||
public String getForwardChain() {
|
||||
return forwardChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the forward change
|
||||
*
|
||||
* @param forwardChain the forward chain
|
||||
*/
|
||||
public void setForwardChain(String forwardChain) {
|
||||
this.forwardChain = forwardChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether empty rooms should be discovered or not
|
||||
*
|
||||
* @return true to discover empty rooms, false otherwise
|
||||
*/
|
||||
public boolean isDiscoverEmptyRooms() {
|
||||
return discoverEmptyRooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's whether to discover empty rooms
|
||||
*
|
||||
* @param discoverEmptyRooms true to discover, false otherwise
|
||||
*/
|
||||
public void setDiscoverEmptyRooms(boolean discoverEmptyRooms) {
|
||||
this.discoverEmptyRooms = discoverEmptyRooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interval (in seconds) to check the brain status
|
||||
*
|
||||
* @return the check status interval (negative to disable)
|
||||
*/
|
||||
public int getCheckStatusInterval() {
|
||||
return checkStatusInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interval (in seconds) to check the brain status
|
||||
*
|
||||
* @param checkStatusInterval return the check status interval (negative to disable)
|
||||
*/
|
||||
public void setCheckStatusInterval(int checkStatusInterval) {
|
||||
this.checkStatusInterval = checkStatusInterval;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link NeeoConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoConstants {
|
||||
|
||||
/** The main binding */
|
||||
public static final String BINDING_ID = "neeo";
|
||||
|
||||
/** The various bridge/thing UIDs */
|
||||
public static final ThingTypeUID BRIDGE_TYPE_BRAIN = new ThingTypeUID(BINDING_ID, "brain");
|
||||
public static final ThingTypeUID BRIDGE_TYPE_ROOM = new ThingTypeUID(BINDING_ID, "room");
|
||||
public static final ThingTypeUID THING_TYPE_DEVICE = new ThingTypeUID(BINDING_ID, "device");
|
||||
|
||||
/** The MDNS type for the NEEO brain */
|
||||
public static final String NEEO_MDNS_TYPE = "_neeo._tcp.local.";
|
||||
|
||||
/** Various config related */
|
||||
public static final String CONFIG_IPADDRESS = "ipAddress";
|
||||
public static final String CONFIG_ENABLEFORWARDACTIONS = "enableForwardActions";
|
||||
public static final String CONFIG_REFRESH_POLLING = "refreshPolling";
|
||||
public static final String CONFIG_DEVICEKEY = "deviceKey";
|
||||
public static final String CONFIG_ROOMKEY = "roomKey";
|
||||
public static final String CONFIG_EXCLUDE_THINGS = "excludeThings";
|
||||
|
||||
/** Brain channels */
|
||||
public static final String CHANNEL_BRAIN_FOWARDACTIONS = "forwardActions";
|
||||
|
||||
/** The various room channel constants */
|
||||
public static final String ROOM_CHANNEL_NAME = "name";
|
||||
public static final String ROOM_CHANNEL_TYPE = "type";
|
||||
public static final String ROOM_CHANNEL_ENABLED = "enabled";
|
||||
public static final String ROOM_CHANNEL_CONFIGURED = "configured";
|
||||
public static final String ROOM_CHANNEL_STATUS = "status";
|
||||
public static final String ROOM_CHANNEL_CURRENTSTEP = "currentStep";
|
||||
public static final String ROOM_GROUP_STATE_ID = "state";
|
||||
public static final String ROOM_GROUP_SCENARIO_ID = "scenario";
|
||||
public static final String ROOM_GROUP_RECIPE_ID = "recipe";
|
||||
public static final ChannelTypeUID ROOM_STATE_CURRENTSTEP_UID = new ChannelTypeUID(BINDING_ID,
|
||||
"room-state-currentstep");
|
||||
public static final ChannelTypeUID ROOM_SCENARIO_NAME_UID = new ChannelTypeUID(BINDING_ID, "room-scenario-name");
|
||||
public static final ChannelTypeUID ROOM_SCENARIO_CONFIGURED_UID = new ChannelTypeUID(BINDING_ID,
|
||||
"room-scenario-configured");
|
||||
public static final ChannelTypeUID ROOM_SCENARIO_STATUS_UID = new ChannelTypeUID(BINDING_ID,
|
||||
"room-scenario-status");
|
||||
public static final ChannelTypeUID ROOM_RECIPE_NAME_UID = new ChannelTypeUID(BINDING_ID, "room-recipe-name");
|
||||
public static final ChannelTypeUID ROOM_RECIPE_TYPE_UID = new ChannelTypeUID(BINDING_ID, "room-recipe-type");
|
||||
public static final ChannelTypeUID ROOM_RECIPE_ENABLED_UID = new ChannelTypeUID(BINDING_ID, "room-recipe-enabled");
|
||||
public static final ChannelTypeUID ROOM_RECIPE_STATUS_UID = new ChannelTypeUID(BINDING_ID, "room-recipe-status");
|
||||
|
||||
/** The various device channel constants */
|
||||
public static final String DEVICE_CHANNEL_STATUS = "status";
|
||||
public static final String DEVICE_GROUP_MACROS_ID = "macros";
|
||||
public static final ChannelTypeUID DEVICE_MACRO_STATUS_UID = new ChannelTypeUID(BINDING_ID, "device-macros-status");
|
||||
|
||||
public static final String WEBAPP_FORWARDACTIONS = "/neeo/binding/{brainid}/forwardactions";
|
||||
|
||||
/** The default port the brain listens on. */
|
||||
public static final int DEFAULT_BRAIN_PORT = 3000;
|
||||
public static final int DEFAULT_BRAIN_HTTP_PORT = 8080;
|
||||
|
||||
/** The default protocol for the brain. */
|
||||
public static final String PROTOCOL = "http://";
|
||||
|
||||
/** The brain API constants */
|
||||
private static final String NEEO_VERSION = "/v1";
|
||||
public static final String FORWARD_ACTIONS = NEEO_VERSION + "/forwardactions";
|
||||
public static final String PROJECTS_HOME = NEEO_VERSION + "/projects/home";
|
||||
public static final String GET_ACTIVESCENARIOS = PROJECTS_HOME + "/activescenariokeys";
|
||||
public static final String GET_ROOM = PROJECTS_HOME + "/rooms/{roomkey}";
|
||||
public static final String EXECUTE_RECIPE = PROJECTS_HOME + "/rooms/{roomkey}/recipes/{recipekey}/execute";
|
||||
public static final String STOP_SCENARIO = PROJECTS_HOME + "/rooms/{roomkey}/scenarios/{scenariokey}/poweroff";
|
||||
public static final String TRIGGER_MACRO = PROJECTS_HOME
|
||||
+ "/rooms/{roomkey}/devices/{devicekey}/macros/{macrokey}/trigger";
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.handler.NeeoDeviceHandler;
|
||||
|
||||
/**
|
||||
* THe configuration class for the device used by {@link NeeoDeviceHandler}
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceConfig {
|
||||
|
||||
/** The NEEO device key */
|
||||
@Nullable
|
||||
private String deviceKey;
|
||||
|
||||
/**
|
||||
* Gets the device key
|
||||
*
|
||||
* @return the device key
|
||||
*/
|
||||
@Nullable
|
||||
public String getDeviceKey() {
|
||||
return deviceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the device key.
|
||||
*
|
||||
* @param deviceKey the new device key
|
||||
*/
|
||||
public void setDeviceKey(String deviceKey) {
|
||||
this.deviceKey = deviceKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevice;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevices;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoMacro;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This protocol class for a Neeo Device
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceProtocol {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoDeviceProtocol.class);
|
||||
|
||||
/** The {@link NeeoHandlerCallback} */
|
||||
private final NeeoHandlerCallback callback;
|
||||
|
||||
/** The room key */
|
||||
private final String roomKey;
|
||||
|
||||
/** The device key */
|
||||
private final String deviceKey;
|
||||
|
||||
/** The {@link NeeoDevice} in the room */
|
||||
private final NeeoDevice neeoDevice;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo device protocol.
|
||||
*
|
||||
* @param callback the non-null callback
|
||||
* @param roomKey the non-empty room key
|
||||
* @param deviceKey the non-empty device key
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public NeeoDeviceProtocol(NeeoHandlerCallback callback, String roomKey, String deviceKey) throws IOException {
|
||||
Objects.requireNonNull(callback, "callback cannot be null");
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(deviceKey, "deviceKey cannot be empty");
|
||||
|
||||
this.roomKey = roomKey;
|
||||
this.callback = callback;
|
||||
this.deviceKey = deviceKey;
|
||||
|
||||
final NeeoBrainApi api = callback.getApi();
|
||||
if (api == null) {
|
||||
throw new IllegalArgumentException("NeeoBrainApi cannot be null");
|
||||
}
|
||||
|
||||
final NeeoRoom neeoRoom = api.getRoom(roomKey);
|
||||
|
||||
final NeeoDevices devices = neeoRoom.getDevices();
|
||||
final NeeoDevice device = devices.getDevice(deviceKey);
|
||||
if (device == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Device (" + deviceKey + ") was not found in the NEEO Brain for room (" + roomKey + ")");
|
||||
}
|
||||
|
||||
neeoDevice = device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback being used
|
||||
*
|
||||
* @return the non-null callback
|
||||
*/
|
||||
public NeeoHandlerCallback getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the macro status.
|
||||
*
|
||||
* @param macroKey the non-null macro key
|
||||
*/
|
||||
public void refreshMacroStatus(String macroKey) {
|
||||
NeeoUtil.requireNotEmpty(macroKey, "macroKey cannot be empty");
|
||||
|
||||
final NeeoMacro macro = neeoDevice.getMacros().getMacro(macroKey);
|
||||
if (macro != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.DEVICE_GROUP_MACROS_ID,
|
||||
NeeoConstants.DEVICE_CHANNEL_STATUS, macroKey), OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the macro status. If the status is true, the macro will be triggered. If false, nothing occurs
|
||||
*
|
||||
* @param macroKey the non-null macro key
|
||||
* @param start whether to start (true) or stop (false) the macro
|
||||
*/
|
||||
public void setMacroStatus(String macroKey, boolean start) {
|
||||
NeeoUtil.requireNotEmpty(macroKey, "macroKey cannot be empty");
|
||||
|
||||
final NeeoBrainApi api = callback.getApi();
|
||||
if (api == null) {
|
||||
logger.debug("API is null [likely bridge is offline]");
|
||||
} else {
|
||||
try {
|
||||
if (start) {
|
||||
api.triggerMacro(roomKey, deviceKey, macroKey);
|
||||
|
||||
// NEEO macros are not what we generally think of for macros
|
||||
// Trigger a NEEO macro is simply asking the brain to send an IR pulse
|
||||
// for whatever the macro is linked up to (POWER ON would send the IR
|
||||
// pulse for the specified device). Because of this, the execution of the
|
||||
// macro will never take more than 100ms to complete. Since we get no
|
||||
// feedback from the brain whether the macro has executed or completed
|
||||
// AND it's impossible to tell if any macro is executing or not (no equivalent
|
||||
// API to poll for), we simply refresh the status back to OFF after 500ms
|
||||
callback.scheduleTask(() -> {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.DEVICE_GROUP_MACROS_ID,
|
||||
NeeoConstants.DEVICE_CHANNEL_STATUS, macroKey), OnOffType.OFF);
|
||||
}, 500);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Some macros have issues executing on the NEEO Brain (depends on the firmware)
|
||||
// and IO exception will be thrown if the macro encounters an issue
|
||||
// (mostly it depends on the state of the brain - if it's starting up or in the process
|
||||
// of executing a long scenario - the macro will likely timeout or simply throw an exception)
|
||||
// Because of this, we simply log the error versus taking the binding offline
|
||||
logger.warn(
|
||||
"Exception occurred during execution of a macro (may need to update the brain firmware): {}",
|
||||
e.getMessage(), e);
|
||||
// callback.statusChanged(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
*
|
||||
* This interface is used to provide a callback mechanism between a @link {@link ThingHandler} and the assoicated
|
||||
* protocol.
|
||||
* This is necessary since the status and state of a bridge/thing is private and the protocol handler cannot access it
|
||||
* directly.
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface NeeoHandlerCallback {
|
||||
/**
|
||||
* Callback to the bridge/thing to update the status of the bridge/thing.
|
||||
*
|
||||
* @param status a non-null {@link ThingStatus}
|
||||
* @param detail a non-null {@link ThingStatusDetail}
|
||||
* @param msg a possibly null, possibly empty message
|
||||
*/
|
||||
void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg);
|
||||
|
||||
/**
|
||||
* Callback to the bridge/thing to update the state of a channel in the bridge/thing.
|
||||
*
|
||||
* @param channelId the non-null, non-empty channel id
|
||||
* @param state the new non-null {@State}
|
||||
*/
|
||||
void stateChanged(String channelId, State state);
|
||||
|
||||
/**
|
||||
* Callback to set a property for the thing.
|
||||
*
|
||||
* @param propertyName a non-null, non-empty property name
|
||||
* @param propertyValue a non-null, possibly empty property value
|
||||
*/
|
||||
void setProperty(String propertyName, String propertyValue);
|
||||
|
||||
/**
|
||||
* Schedule a task to be executed in the future
|
||||
*
|
||||
* @param task the non-null task
|
||||
* @param milliSeconds the milliseconds (>0)
|
||||
*/
|
||||
void scheduleTask(Runnable task, long milliSeconds);
|
||||
|
||||
/**
|
||||
* Callback to trigger an event
|
||||
*
|
||||
* @param channelID a non-null, non-empty channel id
|
||||
* @param event a possibly null, possibly empty event
|
||||
*/
|
||||
void triggerEvent(String channelID, String event);
|
||||
|
||||
/**
|
||||
* Callback to retrieve the current {@link NeeoBrainApi}
|
||||
*
|
||||
* @return a possibly null {@link NeeoBrainApi}
|
||||
*/
|
||||
@Nullable
|
||||
NeeoBrainApi getApi();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.handler.NeeoRoomHandler;
|
||||
|
||||
/**
|
||||
* THe configuration class for the room used by {@link NeeoRoomHandler}
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoomConfig {
|
||||
|
||||
/** The NEEO room key */
|
||||
@Nullable
|
||||
private String roomKey;
|
||||
|
||||
/** The refresh polling (in seconds) */
|
||||
private int refreshPolling;
|
||||
|
||||
/** Whether to exclude things */
|
||||
private boolean excludeThings;
|
||||
|
||||
/**
|
||||
* Gets the room key
|
||||
*
|
||||
* @return the room key
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomKey() {
|
||||
return roomKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the room key.
|
||||
*
|
||||
* @param roomKey the new room key
|
||||
*/
|
||||
public void setRoomKey(String roomKey) {
|
||||
this.roomKey = roomKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the refresh polling (in seconds)
|
||||
*
|
||||
* @return the refresh polling
|
||||
*/
|
||||
public int getRefreshPolling() {
|
||||
return refreshPolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's the refresh polling
|
||||
*
|
||||
* @param refreshPolling the refresh polling
|
||||
*/
|
||||
public void setRefreshPolling(int refreshPolling) {
|
||||
this.refreshPolling = refreshPolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to exclude things or not
|
||||
*
|
||||
* @return true to exclude, false otherwise
|
||||
*/
|
||||
public boolean isExcludeThings() {
|
||||
return excludeThings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to exclude things
|
||||
*
|
||||
* @param excludeThings true to exclude, false otherwise
|
||||
*/
|
||||
public void setExcludeThings(boolean excludeThings) {
|
||||
this.excludeThings = excludeThings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.models.ExecuteResult;
|
||||
import org.openhab.binding.neeo.internal.models.ExecuteStep;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoAction;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipe;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipes;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoScenario;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This protocol class for a Neeo Room
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoomProtocol {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoRoomProtocol.class);
|
||||
|
||||
/** The {@link NeeoHandlerCallback} */
|
||||
private final NeeoHandlerCallback callback;
|
||||
|
||||
/** The room key */
|
||||
private final String roomKey;
|
||||
|
||||
/** The {@link NeeoRoom} */
|
||||
private final NeeoRoom neeoRoom;
|
||||
|
||||
/** The currently active scenarios */
|
||||
private final AtomicReference<String[]> activeScenarios = new AtomicReference<>(new String[0]);
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo room protocol.
|
||||
*
|
||||
* @param callback the non-null callback
|
||||
* @param roomKey the non-empty room key
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public NeeoRoomProtocol(NeeoHandlerCallback callback, String roomKey) throws IOException {
|
||||
Objects.requireNonNull(callback, "callback cannot be null");
|
||||
NeeoUtil.requireNotEmpty(roomKey, "roomKey cannot be empty");
|
||||
|
||||
this.callback = callback;
|
||||
this.roomKey = roomKey;
|
||||
|
||||
final NeeoBrainApi api = callback.getApi();
|
||||
if (api == null) {
|
||||
throw new IllegalArgumentException("NeeoBrainApi cannot be null");
|
||||
}
|
||||
|
||||
neeoRoom = api.getRoom(roomKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback being used
|
||||
*
|
||||
* @return the non-null callback
|
||||
*/
|
||||
public NeeoHandlerCallback getCallback() {
|
||||
return callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the action if it applies to this room
|
||||
*
|
||||
* @param action a non-null action to process
|
||||
*/
|
||||
public void processAction(NeeoAction action) {
|
||||
Objects.requireNonNull(action, "action cannot be null");
|
||||
|
||||
final NeeoRecipes recipes = neeoRoom.getRecipes();
|
||||
final boolean launch = StringUtils.equalsIgnoreCase(NeeoRecipe.LAUNCH, action.getAction());
|
||||
final boolean poweroff = StringUtils.equalsIgnoreCase(NeeoRecipe.POWEROFF, action.getAction());
|
||||
|
||||
// Can't be both true but if both false - it's neither one
|
||||
if (launch == poweroff) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String recipeName = action.getRecipe();
|
||||
final NeeoRecipe recipe = recipeName == null ? null : recipes.getRecipeByName(recipeName);
|
||||
final String scenarioKey = recipe == null ? null : recipe.getScenarioKey();
|
||||
|
||||
if (scenarioKey != null && StringUtils.isNotEmpty(scenarioKey)) {
|
||||
processScenarioChange(scenarioKey, launch);
|
||||
} else {
|
||||
logger.debug("Could not find a recipe named '{}' for the action {}", recipeName, action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a change to the scenario (whether it's been launched or not)
|
||||
*
|
||||
* @param scenarioKey a non-null, non-empty scenario key
|
||||
* @param launch true if the scenario was launched, false otherwise
|
||||
*/
|
||||
private void processScenarioChange(String scenarioKey, boolean launch) {
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final String[] activeScenarios = this.activeScenarios.get();
|
||||
final int idx = ArrayUtils.indexOf(activeScenarios, scenarioKey);
|
||||
|
||||
// already set that way
|
||||
if ((idx < 0 && !launch) || (idx >= 0 && launch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] newScenarios = idx >= 0 ? (String[]) ArrayUtils.remove(activeScenarios, idx)
|
||||
: (String[]) ArrayUtils.add(activeScenarios, scenarioKey);
|
||||
|
||||
this.activeScenarios.set(newScenarios);
|
||||
|
||||
refreshScenarioStatus(scenarioKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh state of the room - currently only refreshes the active scenarios via {@link #refreshActiveScenarios()}
|
||||
*/
|
||||
public void refreshState() {
|
||||
refreshActiveScenarios();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the recipe name
|
||||
*
|
||||
* @param recipeKey the non-empty recipe key
|
||||
*/
|
||||
public void refreshRecipeName(String recipeKey) {
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipe(recipeKey);
|
||||
if (recipe != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_NAME, recipeKey), new StringType(recipe.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the recipe type
|
||||
*
|
||||
* @param recipeKey the non-empty recipe key
|
||||
*/
|
||||
public void refreshRecipeType(String recipeKey) {
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipe(recipeKey);
|
||||
if (recipe != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_TYPE, recipeKey), new StringType(recipe.getType()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh whether the recipe is enabled
|
||||
*
|
||||
* @param recipeKey the non-null recipe key
|
||||
*/
|
||||
public void refreshRecipeEnabled(String recipeKey) {
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipe(recipeKey);
|
||||
if (recipe != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_ENABLED, recipeKey), recipe.isEnabled() ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the recipe status.
|
||||
*
|
||||
* @param recipeKey the non-null recipe key
|
||||
*/
|
||||
public void refreshRecipeStatus(String recipeKey) {
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipe(recipeKey);
|
||||
if (recipe != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_STATUS, recipeKey), OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the scenario name.
|
||||
*
|
||||
* @param scenarioKey the non-null scenario key
|
||||
*/
|
||||
public void refreshScenarioName(String scenarioKey) {
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final NeeoScenario scenario = neeoRoom.getScenarios().getScenario(scenarioKey);
|
||||
if (scenario != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_NAME, scenarioKey), new StringType(scenario.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh whether the scenario is configured.
|
||||
*
|
||||
* @param scenarioKey the non-null scenario key
|
||||
*/
|
||||
public void refreshScenarioConfigured(String scenarioKey) {
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final NeeoScenario scenario = neeoRoom.getScenarios().getScenario(scenarioKey);
|
||||
if (scenario != null) {
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_SCENARIO_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_ENABLED, scenarioKey),
|
||||
scenario.isConfigured() ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the scenario status.
|
||||
*
|
||||
* @param scenarioKey the non-null scenario key
|
||||
*/
|
||||
public void refreshScenarioStatus(String scenarioKey) {
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final NeeoScenario scenario = neeoRoom.getScenarios().getScenario(scenarioKey);
|
||||
if (scenario != null) {
|
||||
final String[] active = activeScenarios.get();
|
||||
final boolean isActive = ArrayUtils.contains(active, scenarioKey);
|
||||
callback.stateChanged(UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_SCENARIO_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_STATUS, scenarioKey), isActive ? OnOffType.ON : OnOffType.OFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh active scenarios
|
||||
*/
|
||||
private void refreshActiveScenarios() {
|
||||
final NeeoBrainApi api = callback.getApi();
|
||||
if (api == null) {
|
||||
logger.debug("API is null [likely bridge is offline]");
|
||||
} else {
|
||||
try {
|
||||
final String[] activeScenarios = api.getActiveScenarios();
|
||||
final String[] oldScenarios = this.activeScenarios.getAndSet(activeScenarios);
|
||||
|
||||
if (!ArrayUtils.isEquals(activeScenarios, oldScenarios)) {
|
||||
for (String scenario : activeScenarios) {
|
||||
refreshScenarioStatus(scenario);
|
||||
}
|
||||
|
||||
for (String oldScenario : oldScenarios) {
|
||||
if (!ArrayUtils.contains(activeScenarios, oldScenario)) {
|
||||
refreshScenarioStatus(oldScenario);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception requesting active scenarios: {}", e.getMessage(), e);
|
||||
// callback.statusChanged(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the trigger for the current step
|
||||
*
|
||||
* @param step a possibly null, possibly empty step to send
|
||||
*/
|
||||
private void sendCurrentStepTrigger(@Nullable String step) {
|
||||
callback.triggerEvent(
|
||||
UidUtils.createChannelId(NeeoConstants.ROOM_GROUP_STATE_ID, NeeoConstants.ROOM_CHANNEL_CURRENTSTEP),
|
||||
step == null || StringUtils.isEmpty(step) ? "" : step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the given recipe key
|
||||
*
|
||||
* @param recipeKey the non-null recipe key
|
||||
*/
|
||||
public void startRecipe(String recipeKey) {
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
final NeeoBrainApi api = callback.getApi();
|
||||
if (api == null) {
|
||||
logger.debug("API is null [likely bridge is offline] - cannot start recipe: {}", recipeKey);
|
||||
} else {
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipe(recipeKey);
|
||||
final String scenarioKey = recipe == null ? null : recipe.getScenarioKey();
|
||||
|
||||
if (recipe != null) {
|
||||
if (recipe.isEnabled()) {
|
||||
final boolean isLaunch = StringUtils.equalsIgnoreCase(NeeoRecipe.LAUNCH, recipe.getType());
|
||||
|
||||
try {
|
||||
if (isLaunch || scenarioKey == null || StringUtils.isEmpty(scenarioKey)) {
|
||||
handleExecuteResult(scenarioKey, recipeKey, true, api.executeRecipe(roomKey, recipeKey));
|
||||
} else {
|
||||
handleExecuteResult(scenarioKey, recipeKey, false, api.stopScenario(roomKey, scenarioKey));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception occurred during execution: {}", e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
logger.debug("recipe for key {} was not enabled, cannot start or stop", recipeKey);
|
||||
}
|
||||
} else {
|
||||
logger.debug("recipe key {} was not found", recipeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scenario status.
|
||||
*
|
||||
* @param scenarioKey the non-null scenario key
|
||||
* @param start whether to start (true) or stop (false) the scenario
|
||||
*/
|
||||
public void setScenarioStatus(String scenarioKey, boolean start) {
|
||||
NeeoUtil.requireNotEmpty(scenarioKey, "scenarioKey cannot be empty");
|
||||
|
||||
final NeeoRecipe recipe = neeoRoom.getRecipes().getRecipeByScenarioKey(scenarioKey,
|
||||
start ? NeeoRecipe.LAUNCH : NeeoRecipe.POWEROFF);
|
||||
final String recipeKey = recipe == null ? null : recipe.getKey();
|
||||
|
||||
if (recipe != null && recipeKey != null && StringUtils.isNotEmpty(recipeKey)) {
|
||||
if (recipe.isEnabled()) {
|
||||
startRecipe(recipeKey);
|
||||
} else {
|
||||
logger.debug("Recipe ({}) found for scenario {} but was not enabled", recipe.getKey(), scenarioKey);
|
||||
}
|
||||
} else {
|
||||
logger.debug("No recipe found for scenario {} to start ({})", scenarioKey, start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the {@link ExecuteResult} from a call
|
||||
*
|
||||
* @param scenarioKey the possibly null scenario key being changed
|
||||
* @param recipeKey the non-null recipe key being used
|
||||
* @param launch whether the recipe launches the scenario (true) or not (false)
|
||||
* @param result the non-null result (null will do nothing)
|
||||
*/
|
||||
private void handleExecuteResult(@Nullable String scenarioKey, String recipeKey, boolean launch,
|
||||
ExecuteResult result) {
|
||||
Objects.requireNonNull(result, "result cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(recipeKey, "recipeKey cannot be empty");
|
||||
|
||||
int nextStep = 0;
|
||||
if (scenarioKey != null && StringUtils.isNotEmpty(scenarioKey)) {
|
||||
callback.scheduleTask(() -> {
|
||||
processScenarioChange(scenarioKey, launch);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
for (final ExecuteStep step : result.getSteps()) {
|
||||
callback.scheduleTask(() -> {
|
||||
sendCurrentStepTrigger(step.getText());
|
||||
}, nextStep);
|
||||
nextStep += step.getDuration();
|
||||
}
|
||||
|
||||
callback.scheduleTask(() -> {
|
||||
sendCurrentStepTrigger(null);
|
||||
refreshRecipeStatus(recipeKey);
|
||||
}, nextStep);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevices;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevicesDeserializer;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoMacros;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoMacrosDeserializer;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipes;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipesDeserializer;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRooms;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoomsDeserializer;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoScenarios;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoScenariosDeserializer;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
* Various utility functions used by the NEEO binding
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoUtil {
|
||||
|
||||
/**
|
||||
* Builds and returns a {@link Gson}. The gson has adapters registered for {@link NeeoRooms}, {@link NeeoRecipes}
|
||||
* and {@link NeeoScenarios}
|
||||
*
|
||||
* @return a non-null {@link Gson} to use
|
||||
*/
|
||||
public static Gson getGson() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(NeeoRooms.class, new NeeoRoomsDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(NeeoRecipes.class, new NeeoRecipesDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(NeeoScenarios.class, new NeeoScenariosDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(NeeoDevices.class, new NeeoDevicesDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(NeeoMacros.class, new NeeoMacrosDeserializer());
|
||||
return gsonBuilder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to close a {@link AutoCloseable} and log any exception thrown.
|
||||
*
|
||||
* @param closeable a possibly null {@link AutoCloseable}. If null, no action is done.
|
||||
*/
|
||||
public static void close(@Nullable AutoCloseable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception e) {
|
||||
LoggerFactory.getLogger(NeeoUtil.class).debug("Exception closing: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current thread has been interrupted and throws {@link InterruptedException} if it's been
|
||||
* interrupted
|
||||
*
|
||||
* @throws InterruptedException the interrupted exception
|
||||
*/
|
||||
public static void checkInterrupt() throws InterruptedException {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw new InterruptedException("thread interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the specified {@link Future}
|
||||
*
|
||||
* @param future a possibly null future. If null, no action is done
|
||||
*/
|
||||
public static void cancel(@Nullable Future<?> future) {
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Require the specified value to be a non-null, non-empty string
|
||||
*
|
||||
* @param value the value to check
|
||||
* @param msg the msg to use when throwing an exception
|
||||
* @throws NullPointerException if value is null
|
||||
* @throws IllegalArgumentException if value is an empty string
|
||||
*/
|
||||
public static void requireNotEmpty(@Nullable String value, String msg) {
|
||||
Objects.requireNonNull(value, msg);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevice;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDeviceDetails;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
||||
/**
|
||||
* Utility class for generating some UIDs.
|
||||
*
|
||||
* @author Tim Roberts - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class UidUtils {
|
||||
|
||||
/** The delimiter to separate 'parts' of an UID */
|
||||
private static final char DELIMITER = '-';
|
||||
|
||||
/**
|
||||
* Determines if the specified device is an openhab thing or not. The determination is made if the adapter name for
|
||||
* the device is a valid {@link ThingUID}
|
||||
*
|
||||
* @param device a possibly null device
|
||||
* @return true if a thing, false otherwise
|
||||
*/
|
||||
public static boolean isThing(NeeoDevice device) {
|
||||
final NeeoDeviceDetails details = device.getDetails();
|
||||
if (details == null) {
|
||||
return false;
|
||||
}
|
||||
final String adapterName = details.getAdapterName();
|
||||
if (adapterName == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
new ThingUID(adapterName);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the channel id/key in the {@link ChannelUID} and will return a non-null, non-empty string array
|
||||
* representing the parts. The first element will always be the channelID itself. If there is a second element, the
|
||||
* second element will the the channel key
|
||||
*
|
||||
* @param uid the non-null channel uid
|
||||
* @return the non-null, non empty (only 1 or 2 element) list of parts
|
||||
*/
|
||||
public static String[] parseChannelId(ChannelUID uid) {
|
||||
Objects.requireNonNull(uid, "uid cannot be null");
|
||||
|
||||
final String channelId = uid.getIdWithoutGroup();
|
||||
final int idx = channelId.indexOf(DELIMITER);
|
||||
if (idx < 0 || idx == channelId.length() - 1) {
|
||||
return new String[] { channelId };
|
||||
}
|
||||
return new String[] { channelId.substring(0, idx), channelId.substring(idx + 1) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the channel id from the group/channel
|
||||
*
|
||||
* @param groupId the not empty group id
|
||||
* @param channelId the not empty channel id
|
||||
* @return the full channel id
|
||||
*/
|
||||
public static String createChannelId(String groupId, String channelId) {
|
||||
NeeoUtil.requireNotEmpty(groupId, "groupId cannot be empty");
|
||||
NeeoUtil.requireNotEmpty(channelId, "channelId cannot be empty");
|
||||
|
||||
return createChannelId(groupId, channelId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the channel id from the group, channel id, and key
|
||||
*
|
||||
* @param groupId the possibly empty/null group id
|
||||
* @param channelId the not empty channel id
|
||||
* @param channelKey the possibly empty/null channel key
|
||||
* @return the full channel id
|
||||
*/
|
||||
public static String createChannelId(@Nullable String groupId, String channelId, @Nullable String channelKey) {
|
||||
NeeoUtil.requireNotEmpty(channelId, "channelId cannot be empty");
|
||||
|
||||
return (StringUtils.isEmpty(groupId) ? "" : (groupId + "#"))
|
||||
+ (StringUtils.isEmpty(channelKey) ? channelId : (channelId + DELIMITER + channelKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link ChannelUID} for a give thingUID, group ID and channel ID
|
||||
*
|
||||
* @param thingUid a non-null thing UID
|
||||
* @param groupId a non-null, non-empty group ID
|
||||
* @param channelId a non-null, non-empty channel ID
|
||||
* @return a non-null {@link ChannelUID}
|
||||
*/
|
||||
public static ChannelUID createChannelUID(ThingUID thingUid, String groupId, String channelId) {
|
||||
return createChannelUID(thingUid, groupId, channelId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link ChannelUID} for a give thingUID, group ID, channel ID and channel key
|
||||
*
|
||||
* @param thingUid a non-null thing UID
|
||||
* @param groupId a non-null, non-empty group ID
|
||||
* @param channelId a non-null, non-empty channel ID
|
||||
* @param channelKey a potentially null, potentially empty channel KEY
|
||||
* @return a non-null {@link ChannelUID}
|
||||
*/
|
||||
public static ChannelUID createChannelUID(ThingUID thingUid, String groupId, String channelId,
|
||||
@Nullable String channelKey) {
|
||||
return new ChannelUID(thingUid, groupId,
|
||||
channelId + (StringUtils.isEmpty(channelKey) ? "" : (DELIMITER + channelKey)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.neeo.internal.NeeoConstants.BRIDGE_TYPE_BRAIN;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.jmdns.ServiceInfo;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implementation of {@link MDNSDiscoveryParticipant} that will discover NEEO brain(s).
|
||||
*
|
||||
* @author Tim Roberts - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true)
|
||||
public class NeeoBrainDiscovery implements MDNSDiscoveryParticipant {
|
||||
|
||||
/** The logger */
|
||||
private Logger logger = LoggerFactory.getLogger(NeeoBrainDiscovery.class);
|
||||
|
||||
@Override
|
||||
public Set<@Nullable ThingTypeUID> getSupportedThingTypeUIDs() {
|
||||
return Collections.singleton(BRIDGE_TYPE_BRAIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceType() {
|
||||
return NeeoConstants.NEEO_MDNS_TYPE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DiscoveryResult createResult(@Nullable ServiceInfo service) {
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ThingUID uid = getThingUID(service);
|
||||
if (uid == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("createResult is evaluating: {}", service);
|
||||
|
||||
final Map<String, Object> properties = new HashMap<>(2);
|
||||
|
||||
final InetAddress ip = getIpAddress(service);
|
||||
if (ip == null) {
|
||||
logger.debug("Application not 'neeo' in MDNS serviceinfo: {}", service);
|
||||
return null;
|
||||
}
|
||||
final String inetAddress = ip.getHostAddress();
|
||||
|
||||
final String id = uid.getId();
|
||||
final String label = service.getName() + " (" + id + ")";
|
||||
|
||||
properties.put(NeeoConstants.CONFIG_IPADDRESS, inetAddress);
|
||||
properties.put(NeeoConstants.CONFIG_ENABLEFORWARDACTIONS, true);
|
||||
|
||||
logger.debug("Adding NEEO Brain to inbox: {} at {}", id, inetAddress);
|
||||
return DiscoveryResultBuilder.create(uid).withProperties(properties).withLabel(label).build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ThingUID getThingUID(@Nullable ServiceInfo service) {
|
||||
if (service == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug("getThingUID is evaluating: {}", service);
|
||||
if (!StringUtils.equals("neeo", service.getApplication())) {
|
||||
logger.debug("Application not 'neeo' in MDNS serviceinfo: {}", service);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (getIpAddress(service) == null) {
|
||||
logger.debug("No IP address found in MDNS serviceinfo: {}", service);
|
||||
return null;
|
||||
}
|
||||
|
||||
String model = service.getPropertyString("hon"); // model
|
||||
if (model == null) {
|
||||
final String server = service.getServer(); // NEEO-xxxxx.local.
|
||||
if (server != null) {
|
||||
final int idx = server.indexOf(".");
|
||||
if (idx >= 0) {
|
||||
model = server.substring(0, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (model == null || model.length() <= 5 || !model.toLowerCase().startsWith("neeo")) {
|
||||
logger.debug("No 'hon' found in MDNS serviceinfo: {}", service);
|
||||
return null;
|
||||
}
|
||||
|
||||
final String id = model.substring(5);
|
||||
logger.debug("NEEO Brain Found: {}", id);
|
||||
|
||||
return new ThingUID(BRIDGE_TYPE_BRAIN, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ip address found in the {@link ServiceInfo}
|
||||
*
|
||||
* @param service a non-null service
|
||||
* @return the ip address of the service or null if none found.
|
||||
*/
|
||||
@Nullable
|
||||
private InetAddress getIpAddress(ServiceInfo service) {
|
||||
Objects.requireNonNull(service, "service cannot be null");
|
||||
|
||||
for (String addr : service.getHostAddresses()) {
|
||||
try {
|
||||
return InetAddress.getByName(addr);
|
||||
} catch (UnknownHostException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
for (InetAddress addr : service.getInet4Addresses()) {
|
||||
return addr;
|
||||
}
|
||||
// Fallback for Inet6addresses
|
||||
for (InetAddress addr : service.getInet6Addresses()) {
|
||||
return addr;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainApi;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.NeeoRoomConfig;
|
||||
import org.openhab.binding.neeo.internal.UidUtils;
|
||||
import org.openhab.binding.neeo.internal.handler.NeeoRoomHandler;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevice;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
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.thing.Bridge;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AbstractDiscoveryService} that will discover the devices in a NEEO room;
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoDeviceDiscoveryService.class);
|
||||
|
||||
/** The device thing type we support */
|
||||
private static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Collections
|
||||
.singleton(NeeoConstants.THING_TYPE_DEVICE);
|
||||
|
||||
/** The timeout (in seconds) for searching the room */
|
||||
private static final int SEARCH_TIME = 10;
|
||||
|
||||
/** The room handler to search */
|
||||
private final NeeoRoomHandler roomHandler;
|
||||
|
||||
/**
|
||||
* Constructs the discovery service from the room handler
|
||||
*
|
||||
* @param roomHandler a non-null room handler
|
||||
*/
|
||||
public NeeoDeviceDiscoveryService(NeeoRoomHandler roomHandler) {
|
||||
super(DISCOVERABLE_THING_TYPES_UIDS, SEARCH_TIME);
|
||||
Objects.requireNonNull(roomHandler, "roomHandler cannot be null");
|
||||
this.roomHandler = roomHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
final Bridge roomBridge = roomHandler.getThing();
|
||||
final ThingUID roomUid = roomBridge.getUID();
|
||||
|
||||
final String brainId = roomHandler.getNeeoBrainId();
|
||||
if (brainId == null || StringUtils.isEmpty(brainId)) {
|
||||
logger.debug("Unknown brain ID for roomHandler: {}", roomHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
final NeeoBrainApi api = roomHandler.getNeeoBrainApi();
|
||||
if (api == null) {
|
||||
logger.debug("Brain API was not available for {} - skipping", brainId);
|
||||
return;
|
||||
}
|
||||
|
||||
final NeeoRoomConfig config = roomBridge.getConfiguration().as(NeeoRoomConfig.class);
|
||||
final String roomKey = config.getRoomKey();
|
||||
if (roomKey == null || StringUtils.isEmpty(roomKey)) {
|
||||
logger.debug("RoomKey wasn't configured for {} - skipping", brainId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final NeeoRoom room = api.getRoom(roomKey);
|
||||
final NeeoDevice[] devices = room.getDevices().getDevices();
|
||||
|
||||
if (devices.length == 0) {
|
||||
logger.debug("Room {} found - but there were no devices - skipping", room.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Room {} found, scanning {} devices in it", room.getName(), devices.length);
|
||||
for (NeeoDevice device : devices) {
|
||||
final String deviceKey = device.getKey();
|
||||
if (deviceKey == null || StringUtils.isEmpty(deviceKey)) {
|
||||
logger.debug("Device key wasn't found for device: {}", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.isExcludeThings() && UidUtils.isThing(device)) {
|
||||
logger.debug("Found openHAB thing but ignoring per configuration: {}", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug("Device #{} found - {}", deviceKey, device.getName());
|
||||
|
||||
final ThingUID thingUID = new ThingUID(NeeoConstants.THING_TYPE_DEVICE, roomUid, deviceKey);
|
||||
|
||||
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||
.withProperty(NeeoConstants.CONFIG_DEVICEKEY, deviceKey).withBridge(roomUid)
|
||||
.withLabel(device.getName() + " (NEEO " + brainId + ")").build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException occurred getting brain info ({}): {}", brainId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainApi;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainConfig;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.handler.NeeoBrainHandler;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoBrain;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
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.thing.Bridge;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AbstractDiscoveryService} that will discover the rooms in a NEEO brain;
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoomDiscoveryService extends AbstractDiscoveryService {
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoRoomDiscoveryService.class);
|
||||
|
||||
/** The room bridge type we support */
|
||||
private static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Collections
|
||||
.singleton(NeeoConstants.BRIDGE_TYPE_ROOM);
|
||||
|
||||
/** The timeout (in seconds) for searching the brain */
|
||||
private static final int SEARCH_TIME = 10;
|
||||
|
||||
/** The brain handler that we will use */
|
||||
private final NeeoBrainHandler brainHandler;
|
||||
|
||||
/**
|
||||
* Constructs the discover service from the brain handler
|
||||
*
|
||||
* @param brainHandler a non-null brain handler
|
||||
*/
|
||||
public NeeoRoomDiscoveryService(NeeoBrainHandler brainHandler) {
|
||||
super(DISCOVERABLE_THING_TYPES_UIDS, SEARCH_TIME);
|
||||
Objects.requireNonNull(brainHandler, "brainHandler cannot be null");
|
||||
this.brainHandler = brainHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
final String brainId = brainHandler.getNeeoBrainId();
|
||||
|
||||
final Bridge brainBridge = brainHandler.getThing();
|
||||
final ThingUID brainUid = brainBridge.getUID();
|
||||
|
||||
final NeeoBrainApi api = brainHandler.getNeeoBrainApi();
|
||||
if (api == null) {
|
||||
logger.debug("Brain API was not available for {} - skipping", brainId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final NeeoBrain brain = api.getBrain();
|
||||
final NeeoBrainConfig config = brainBridge.getConfiguration().as(NeeoBrainConfig.class);
|
||||
final NeeoRoom[] rooms = brain.getRooms().getRooms();
|
||||
|
||||
if (rooms.length == 0) {
|
||||
logger.debug("Brain {} ({}) found - but there were no rooms - skipping", brain.getName(), brainId);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Brain {} ({}) found, scanning {} rooms in it", brain.getName(), brainId, rooms.length);
|
||||
for (NeeoRoom room : rooms) {
|
||||
final String roomKey = room.getKey();
|
||||
if (roomKey == null || StringUtils.isEmpty(roomKey)) {
|
||||
logger.debug("Room didn't have a room key: {}", room);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (room.getDevices().getDevices().length == 0 && room.getRecipes().getRecipes().length == 0
|
||||
&& !config.isDiscoverEmptyRooms()) {
|
||||
logger.debug("Room {} ({}) found but has no devices or recipes, ignoring - {}", roomKey, brainId,
|
||||
room.getName());
|
||||
continue;
|
||||
}
|
||||
|
||||
final ThingUID thingUID = new ThingUID(NeeoConstants.BRIDGE_TYPE_ROOM, brainUid, roomKey);
|
||||
|
||||
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
|
||||
.withProperty(NeeoConstants.CONFIG_ROOMKEY, roomKey)
|
||||
.withProperty(NeeoConstants.CONFIG_EXCLUDE_THINGS, true).withBridge(brainUid)
|
||||
.withLabel(room.getName() + " (NEEO " + brainId + ")").build();
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException occurred getting brain info ({}): {}", brainId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.UidUtils;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevice;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoMacro;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipe;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRecipes;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoScenario;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoScenarios;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
|
||||
/**
|
||||
* Utility class for generating channels
|
||||
*
|
||||
* @author Tim Roberts - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class ChannelUtils {
|
||||
/**
|
||||
* Generates a list of {@link Channel} s for a specific device. This implementation will generate a channel for each
|
||||
* macro found on the device.
|
||||
*
|
||||
* @param thingUid a non-null thingUID
|
||||
* @param device a non-null device
|
||||
* @return a non-null but possibly empty list of {@link Channel} s
|
||||
*/
|
||||
static List<Channel> generateChannels(ThingUID thingUid, NeeoDevice device) {
|
||||
Objects.requireNonNull(thingUid, "thingUid cannot be null");
|
||||
Objects.requireNonNull(device, "device cannot be null");
|
||||
|
||||
final List<Channel> channels = new ArrayList<>();
|
||||
for (NeeoMacro macro : device.getMacros().getMacros()) {
|
||||
final String key = macro.getKey();
|
||||
if (key != null && StringUtils.isNotEmpty(key)) {
|
||||
final String label = StringUtils.isEmpty(macro.getName()) ? macro.getLabel() : macro.getName();
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.DEVICE_GROUP_MACROS_ID,
|
||||
NeeoConstants.DEVICE_CHANNEL_STATUS, key), "Switch")
|
||||
.withLabel(label == null || StringUtils.isEmpty(label) ? key : label)
|
||||
.withType(NeeoConstants.DEVICE_MACRO_STATUS_UID).build());
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of {@link Channel} s for a specific room. This implementation will generate multiple channels
|
||||
* for each scenario and recipe in the room (in addition to the current step channel)
|
||||
*
|
||||
* @param thingUid a non-null thingUID
|
||||
* @param room a non-null room
|
||||
* @return a non-null but possibly empty list of {@link Channel} s
|
||||
*/
|
||||
static List<Channel> generateChannels(ThingUID thingUid, NeeoRoom room) {
|
||||
Objects.requireNonNull(thingUid, "thingUid cannot be null");
|
||||
Objects.requireNonNull(room, "room cannot be null");
|
||||
|
||||
final List<Channel> channels = new ArrayList<>();
|
||||
channels.addAll(generateStateChannels(thingUid));
|
||||
channels.addAll(generateScenarioChannels(thingUid, room.getScenarios()));
|
||||
channels.addAll(generateRecipeChannels(thingUid, room.getRecipes()));
|
||||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate state channels (the current step)
|
||||
*
|
||||
* @param thingUid a non-null thingUID
|
||||
* @return a non-null but possibly empty list of {@link Channel} s
|
||||
*/
|
||||
private static List<Channel> generateStateChannels(ThingUID thingUid) {
|
||||
Objects.requireNonNull(thingUid, "thingUid cannot be null");
|
||||
|
||||
final List<Channel> channels = new ArrayList<>();
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_STATE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_CURRENTSTEP), "String")
|
||||
.withType(NeeoConstants.ROOM_STATE_CURRENTSTEP_UID).build());
|
||||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate scenario channels
|
||||
*
|
||||
* @param thingUid a non-null thingUID
|
||||
* @param scenarios the non-null scenarios
|
||||
* @return a non-null but possibly empty list of {@link Channel} s
|
||||
*/
|
||||
private static List<Channel> generateScenarioChannels(ThingUID thingUid, NeeoScenarios scenarios) {
|
||||
Objects.requireNonNull(thingUid, "thingUid cannot be null");
|
||||
Objects.requireNonNull(scenarios, "scenarios cannot be null");
|
||||
|
||||
final List<Channel> channels = new ArrayList<>();
|
||||
for (NeeoScenario scenario : scenarios.getScenarios()) {
|
||||
final String key = scenario.getKey();
|
||||
if (key != null && StringUtils.isNotEmpty(key)) {
|
||||
final String scenarioLabel = StringUtils.isEmpty(scenario.getName()) ? null : scenario.getName();
|
||||
final String nameLabel = (scenarioLabel == null || StringUtils.isEmpty(scenarioLabel) ? key
|
||||
: scenarioLabel) + " Name";
|
||||
final String configuredLabel = (scenarioLabel == null || StringUtils.isEmpty(scenarioLabel) ? key
|
||||
: scenarioLabel) + " Configured";
|
||||
final String statusLabel = (scenarioLabel == null || StringUtils.isEmpty(scenarioLabel) ? key
|
||||
: scenarioLabel) + " Status";
|
||||
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_SCENARIO_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_NAME, key), "String")
|
||||
.withLabel(nameLabel).withType(NeeoConstants.ROOM_SCENARIO_NAME_UID).build());
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_SCENARIO_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_CONFIGURED, key), "Switch")
|
||||
.withLabel(configuredLabel).withType(NeeoConstants.ROOM_SCENARIO_CONFIGURED_UID).build());
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_SCENARIO_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_STATUS, key), "Switch")
|
||||
.withLabel(statusLabel).withType(NeeoConstants.ROOM_SCENARIO_STATUS_UID).build());
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate recipe channels
|
||||
*
|
||||
* @param thingUid a non-null thingUID
|
||||
* @param recipes the non-null recipes
|
||||
* @return a non-null but possibly empty list of {@link Channel} s
|
||||
*/
|
||||
private static List<Channel> generateRecipeChannels(ThingUID thingUid, NeeoRecipes recipes) {
|
||||
Objects.requireNonNull(thingUid, "thingUid cannot be null");
|
||||
Objects.requireNonNull(recipes, "recipes cannot be null");
|
||||
|
||||
final List<Channel> channels = new ArrayList<>();
|
||||
for (NeeoRecipe recipe : recipes.getRecipes()) {
|
||||
final String key = recipe.getKey();
|
||||
if (key != null && StringUtils.isNotEmpty(key)) {
|
||||
final String recipeLabel = StringUtils.isEmpty(recipe.getName()) ? null : recipe.getName();
|
||||
final String nameLabel = (recipeLabel == null || StringUtils.isEmpty(recipeLabel) ? key : recipeLabel)
|
||||
+ " Name (" + recipe.getType() + ")";
|
||||
final String typeLabel = (recipeLabel == null || StringUtils.isEmpty(recipeLabel) ? key : recipeLabel)
|
||||
+ " Type (" + recipe.getType() + ")";
|
||||
final String enabledLabel = (recipeLabel == null || StringUtils.isEmpty(recipeLabel) ? key
|
||||
: recipeLabel) + " Enabled (" + recipe.getType() + ")";
|
||||
final String statusLabel = (recipeLabel == null || StringUtils.isEmpty(recipeLabel) ? key : recipeLabel)
|
||||
+ " Status (" + recipe.getType() + ")";
|
||||
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_NAME, key), "String")
|
||||
.withLabel(nameLabel).withType(NeeoConstants.ROOM_RECIPE_NAME_UID).build());
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_TYPE, key), "String")
|
||||
.withLabel(enabledLabel).withType(NeeoConstants.ROOM_RECIPE_TYPE_UID).build());
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_ENABLED, key), "Switch")
|
||||
.withLabel(typeLabel).withType(NeeoConstants.ROOM_RECIPE_ENABLED_UID).build());
|
||||
channels.add(ChannelBuilder
|
||||
.create(UidUtils.createChannelUID(thingUid, NeeoConstants.ROOM_GROUP_RECIPE_ID,
|
||||
NeeoConstants.ROOM_CHANNEL_STATUS, key), "Switch")
|
||||
.withLabel(statusLabel).withType(NeeoConstants.ROOM_RECIPE_STATUS_UID).build());
|
||||
}
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainApi;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainConfig;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.NeeoUtil;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoAction;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoBrain;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.osgi.service.http.HttpService;
|
||||
import org.osgi.service.http.NamespaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* A subclass of {@link BaseBridgeHandler} is responsible for handling commands and discovery for a
|
||||
* {@link NeeoBrain}
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoBrainHandler extends BaseBridgeHandler {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoBrainHandler.class);
|
||||
|
||||
/** The {@link HttpService} to register callbacks */
|
||||
private final HttpService httpService;
|
||||
|
||||
/** The {@link NetworkAddressService} to use */
|
||||
private final NetworkAddressService networkAddressService;
|
||||
|
||||
/** GSON implementation - only used to deserialize {@link NeeoAction} */
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
/** The port the HTTP service is listening on */
|
||||
private final int servicePort;
|
||||
|
||||
/**
|
||||
* The initialization task (null until set by {@link #initializeTask()} and set back to null in {@link #dispose()}
|
||||
*/
|
||||
private final AtomicReference<@Nullable Future<?>> initializationTask = new AtomicReference<>();
|
||||
|
||||
/** The check status task (not-null when connecting, null otherwise) */
|
||||
private final AtomicReference<@Nullable Future<?>> checkStatus = new AtomicReference<>();
|
||||
|
||||
/** The lock that protected multi-threaded access to the state variables */
|
||||
private final ReadWriteLock stateLock = new ReentrantReadWriteLock();
|
||||
|
||||
/** The {@link NeeoBrainApi} (null until set by {@link #initializationTask}) */
|
||||
@Nullable
|
||||
private NeeoBrainApi neeoBrainApi;
|
||||
|
||||
/** The path to the forward action servlet - will be null if not enabled */
|
||||
@Nullable
|
||||
private String servletPath;
|
||||
|
||||
/** The servlet for forward actions - will be null if not enabled */
|
||||
@Nullable
|
||||
private NeeoForwardActionsServlet forwardActionServlet;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo brain handler from the {@link Bridge}, service port, {@link HttpService} and
|
||||
* {@link NetworkAddressService}.
|
||||
*
|
||||
* @param bridge the non-null {@link Bridge}
|
||||
* @param servicePort the service port the http service is listening on
|
||||
* @param httpService the non-null {@link HttpService}
|
||||
* @param networkAddressService the non-null {@link NetworkAddressService}
|
||||
*/
|
||||
NeeoBrainHandler(Bridge bridge, int servicePort, HttpService httpService,
|
||||
NetworkAddressService networkAddressService) {
|
||||
super(bridge);
|
||||
|
||||
Objects.requireNonNull(bridge, "bridge cannot be null");
|
||||
Objects.requireNonNull(httpService, "httpService cannot be null");
|
||||
Objects.requireNonNull(networkAddressService, "networkAddressService cannot be null");
|
||||
|
||||
this.servicePort = servicePort;
|
||||
this.httpService = httpService;
|
||||
this.networkAddressService = networkAddressService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any {@Commands} sent - this bridge has no commands and does nothing
|
||||
*
|
||||
* @see
|
||||
* org.openhab.core.thing.binding.ThingHandler#handleCommand(org.openhab.core.thing.ChannelUID,
|
||||
* org.openhab.core.types.Command)
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply cancels any existing initialization tasks and schedules a new task
|
||||
*
|
||||
* @see org.openhab.core.thing.binding.BaseThingHandler#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(scheduler.submit(() -> {
|
||||
initializeTask();
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bridge by connecting to the configuration ip address and parsing the results. Properties will be
|
||||
* set and the thing will go online.
|
||||
*/
|
||||
private void initializeTask() {
|
||||
final Lock writerLock = stateLock.writeLock();
|
||||
writerLock.lock();
|
||||
try {
|
||||
NeeoUtil.checkInterrupt();
|
||||
|
||||
final NeeoBrainConfig config = getBrainConfig();
|
||||
final String ipAddress = config.getIpAddress();
|
||||
if (ipAddress == null || StringUtils.isEmpty(ipAddress)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Brain IP Address must be specified");
|
||||
return;
|
||||
}
|
||||
final NeeoBrainApi api = new NeeoBrainApi(ipAddress);
|
||||
final NeeoBrain brain = api.getBrain();
|
||||
final String brainId = getNeeoBrainId();
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
neeoBrainApi = api;
|
||||
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
addProperty(properties, "Name", brain.getName());
|
||||
addProperty(properties, "Version", brain.getVersion());
|
||||
addProperty(properties, "Label", brain.getLabel());
|
||||
addProperty(properties, "Is Configured", String.valueOf(brain.isConfigured()));
|
||||
addProperty(properties, "Key", brain.getKey());
|
||||
addProperty(properties, "AirKey", brain.getAirkey());
|
||||
addProperty(properties, "Last Change", String.valueOf(brain.getLastChange()));
|
||||
updateProperties(properties);
|
||||
|
||||
if (config.isEnableForwardActions()) {
|
||||
NeeoUtil.checkInterrupt();
|
||||
|
||||
forwardActionServlet = new NeeoForwardActionsServlet(scheduler,
|
||||
new NeeoForwardActionsServlet.Callback() {
|
||||
@Override
|
||||
public void post(String json) {
|
||||
triggerChannel(NeeoConstants.CHANNEL_BRAIN_FOWARDACTIONS, json);
|
||||
|
||||
final NeeoAction action = gson.fromJson(json, NeeoAction.class);
|
||||
|
||||
for (final Thing child : getThing().getThings()) {
|
||||
final ThingHandler th = child.getHandler();
|
||||
if (th instanceof NeeoRoomHandler) {
|
||||
((NeeoRoomHandler) th).processAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, config.getForwardChain());
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
try {
|
||||
servletPath = NeeoConstants.WEBAPP_FORWARDACTIONS.replace("{brainid}", brainId);
|
||||
|
||||
httpService.registerServlet(servletPath, forwardActionServlet, new Hashtable<>(),
|
||||
httpService.createDefaultHttpContext());
|
||||
|
||||
final URL callbackURL = createCallbackUrl(brainId, config);
|
||||
if (callbackURL == null) {
|
||||
logger.debug(
|
||||
"Unable to create a callback URL because there is no primary address specified (please set the primary address in the configuration)");
|
||||
} else {
|
||||
final URL url = new URL(callbackURL, servletPath);
|
||||
api.registerForwardActions(url);
|
||||
}
|
||||
} catch (NamespaceException | ServletException e) {
|
||||
logger.debug("Error registering forward actions to {}: {}", servletPath, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
NeeoUtil.checkInterrupt();
|
||||
if (config.getCheckStatusInterval() > 0) {
|
||||
NeeoUtil.cancel(checkStatus.getAndSet(scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
NeeoUtil.checkInterrupt();
|
||||
checkStatus(ipAddress);
|
||||
} catch (InterruptedException e) {
|
||||
// do nothing - we were interrupted and should stop
|
||||
}
|
||||
}, config.getCheckStatusInterval(), config.getCheckStatusInterval(), TimeUnit.SECONDS)));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Exception occurred connecting to brain: {}", e.getMessage(), e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Exception occurred connecting to brain: " + e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Initializtion was interrupted", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Initialization was interrupted");
|
||||
} finally {
|
||||
writerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to add a property to the properties map if the value is not null
|
||||
*
|
||||
* @param properties a non-null properties map
|
||||
* @param key a non-null, non-empty key
|
||||
* @param value a possibly null, possibly empty key
|
||||
*/
|
||||
private void addProperty(Map<String, String> properties, String key, @Nullable String value) {
|
||||
Objects.requireNonNull(properties, "properties cannot be null");
|
||||
NeeoUtil.requireNotEmpty(key, "key cannot be empty");
|
||||
if (value != null && StringUtils.isNotEmpty(value)) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link NeeoBrainApi} used by this bridge
|
||||
*
|
||||
* @return a possibly null {@link NeeoBrainApi}
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoBrainApi getNeeoBrainApi() {
|
||||
final Lock readerLock = stateLock.readLock();
|
||||
readerLock.lock();
|
||||
try {
|
||||
return neeoBrainApi;
|
||||
} finally {
|
||||
readerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the brain id used by this bridge
|
||||
*
|
||||
* @return a non-null, non-empty brain id
|
||||
*/
|
||||
public String getNeeoBrainId() {
|
||||
return getThing().getUID().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the {@link NeeoBrainConfig}
|
||||
*
|
||||
* @return the {@link NeeoBrainConfig}
|
||||
*/
|
||||
private NeeoBrainConfig getBrainConfig() {
|
||||
return getConfigAs(NeeoBrainConfig.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the status of the brain via a quick socket connection. If the status is unavailable and we are
|
||||
* {@link ThingStatus#ONLINE}, then we go {@link ThingStatus#OFFLINE}. If the status is available and we are
|
||||
* {@link ThingStatus#OFFLINE}, we go {@link ThingStatus#ONLINE}.
|
||||
*
|
||||
* @param ipAddress a non-null, non-empty IP address
|
||||
*/
|
||||
private void checkStatus(String ipAddress) {
|
||||
NeeoUtil.requireNotEmpty(ipAddress, "ipAddress cannot be empty");
|
||||
|
||||
try {
|
||||
try (Socket soc = new Socket()) {
|
||||
soc.connect(new InetSocketAddress(ipAddress, NeeoConstants.DEFAULT_BRAIN_PORT), 5000);
|
||||
}
|
||||
logger.debug("Checking connectivity to {}:{} - successful", ipAddress, NeeoConstants.DEFAULT_BRAIN_PORT);
|
||||
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
logger.debug("Checking connectivity to {}:{} - unsuccessful - going offline: {}", ipAddress,
|
||||
NeeoConstants.DEFAULT_BRAIN_PORT, e.getMessage(), e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Exception occurred connecting to brain: " + e.getMessage());
|
||||
} else {
|
||||
logger.debug("Checking connectivity to {}:{} - unsuccessful - still offline", ipAddress,
|
||||
NeeoConstants.DEFAULT_BRAIN_PORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of the bridge by closing/removing the {@link #neeoBrainApi} and canceling/removing any pending
|
||||
* {@link #initializeTask()}
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
final Lock writerLock = stateLock.writeLock();
|
||||
writerLock.lock();
|
||||
try {
|
||||
final NeeoBrainApi api = neeoBrainApi;
|
||||
neeoBrainApi = null;
|
||||
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(null));
|
||||
NeeoUtil.cancel(checkStatus.getAndSet(null));
|
||||
|
||||
if (forwardActionServlet != null) {
|
||||
forwardActionServlet = null;
|
||||
|
||||
if (api != null) {
|
||||
try {
|
||||
api.deregisterForwardActions();
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException occurred deregistering the forward actions: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (servletPath != null) {
|
||||
httpService.unregister(servletPath);
|
||||
servletPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
NeeoUtil.close(api);
|
||||
} finally {
|
||||
writerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the URL the brain should callback. Note: if there is multiple interfaces, we try to prefer the one on the
|
||||
* same subnet as the brain
|
||||
*
|
||||
* @param brainId the non-null, non-empty brain identifier
|
||||
* @param config the non-null brain configuration
|
||||
* @return the callback URL
|
||||
* @throws MalformedURLException if the URL is malformed
|
||||
*/
|
||||
@Nullable
|
||||
private URL createCallbackUrl(String brainId, NeeoBrainConfig config) throws MalformedURLException {
|
||||
NeeoUtil.requireNotEmpty(brainId, "brainId cannot be empty");
|
||||
Objects.requireNonNull(config, "config cannot be null");
|
||||
|
||||
final String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
|
||||
if (ipAddress == null) {
|
||||
logger.debug("No network interface could be found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new URL("http://" + ipAddress + ":" + servicePort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainApi;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.NeeoDeviceConfig;
|
||||
import org.openhab.binding.neeo.internal.NeeoDeviceProtocol;
|
||||
import org.openhab.binding.neeo.internal.NeeoHandlerCallback;
|
||||
import org.openhab.binding.neeo.internal.NeeoRoomConfig;
|
||||
import org.openhab.binding.neeo.internal.NeeoUtil;
|
||||
import org.openhab.binding.neeo.internal.UidUtils;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDevice;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDeviceDetails;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoDeviceDetailsTiming;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An extension of {@link BaseThingHandler} that is responsible for handling commands for a device
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceHandler extends BaseThingHandler {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoDeviceHandler.class);
|
||||
|
||||
/**
|
||||
* The initialization task (null until set by {@link #initializeTask()} and set back to null in {@link #dispose()}
|
||||
*/
|
||||
private final AtomicReference<@Nullable Future<?>> initializationTask = new AtomicReference<>(null);
|
||||
|
||||
/**
|
||||
* The refresh task (null until set by {@link #initializeTask()} and set back to null in {@link #dispose()}
|
||||
*/
|
||||
private final AtomicReference<@Nullable ScheduledFuture<?>> refreshTask = new AtomicReference<>(null);
|
||||
|
||||
/** The {@link NeeoDeviceProtocol} (null until set by {@link #initializationTask}) */
|
||||
private final AtomicReference<@Nullable NeeoDeviceProtocol> deviceProtocol = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo device handler.
|
||||
*
|
||||
* @param thing the non-null thing
|
||||
*/
|
||||
NeeoDeviceHandler(Thing thing) {
|
||||
super(thing);
|
||||
Objects.requireNonNull(thing, "thing cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Objects.requireNonNull(channelUID, "channelUID cannot be null");
|
||||
Objects.requireNonNull(command, "command cannot be null");
|
||||
|
||||
final NeeoDeviceProtocol protocol = deviceProtocol.get();
|
||||
if (protocol == null) {
|
||||
logger.debug("Protocol is null - ignoring update: {}", channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] channelIds = UidUtils.parseChannelId(channelUID);
|
||||
if (channelIds.length == 0) {
|
||||
logger.debug("Bad group declaration: {}", channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
final String localGroupId = channelUID.getGroupId();
|
||||
final String groupId = localGroupId == null || StringUtils.isEmpty(localGroupId) ? "" : localGroupId;
|
||||
final String channelId = channelIds[0];
|
||||
final String channelKey = channelIds.length > 1 ? channelIds[1] : "";
|
||||
|
||||
if (StringUtils.isEmpty(groupId)) {
|
||||
logger.debug("GroupID for channel is null - ignoring command: {}", channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannel(protocol, groupId, channelId, channelKey);
|
||||
} else {
|
||||
switch (groupId) {
|
||||
case NeeoConstants.DEVICE_GROUP_MACROS_ID:
|
||||
switch (channelId) {
|
||||
case NeeoConstants.DEVICE_CHANNEL_STATUS:
|
||||
if (command instanceof OnOffType) {
|
||||
protocol.setMacroStatus(channelKey, command == OnOffType.ON);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown channel to set: {}", channelUID);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown group to set: {}", channelUID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the specified channel section, key and id using the specified protocol
|
||||
*
|
||||
* @param protocol a non-null protocol to use
|
||||
* @param groupId the non-empty groupId
|
||||
* @param channelId the non-empty channel id
|
||||
* @param channelKey the non-empty channel key
|
||||
*/
|
||||
private void refreshChannel(NeeoDeviceProtocol protocol, String groupId, String channelId, String channelKey) {
|
||||
Objects.requireNonNull(protocol, "protocol cannot be null");
|
||||
NeeoUtil.requireNotEmpty(groupId, "groupId must not be empty");
|
||||
NeeoUtil.requireNotEmpty(channelId, "channelId must not be empty");
|
||||
NeeoUtil.requireNotEmpty(channelKey, "channelKey must not be empty");
|
||||
|
||||
switch (groupId) {
|
||||
case NeeoConstants.DEVICE_GROUP_MACROS_ID:
|
||||
switch (channelId) {
|
||||
case NeeoConstants.DEVICE_CHANNEL_STATUS:
|
||||
protocol.refreshMacroStatus(channelKey);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(scheduler.submit(() -> {
|
||||
initializeTask();
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the task be creating the {@link NeeoDeviceProtocol}, going online and then scheduling the refresh
|
||||
* task.
|
||||
*/
|
||||
private void initializeTask() {
|
||||
final NeeoDeviceConfig config = getConfigAs(NeeoDeviceConfig.class);
|
||||
|
||||
final String roomKey = getRoomKey();
|
||||
if (roomKey == null || StringUtils.isEmpty(roomKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Room key (from the parent room bridge) was not found");
|
||||
return;
|
||||
}
|
||||
|
||||
final String deviceKey = config.getDeviceKey();
|
||||
if (deviceKey == null || StringUtils.isEmpty(deviceKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Device key was not found or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
NeeoUtil.checkInterrupt();
|
||||
final NeeoBrainApi brainApi = getNeeoBrainApi();
|
||||
if (brainApi == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Cannot find the NEEO Brain API");
|
||||
return;
|
||||
}
|
||||
|
||||
final NeeoRoom room = brainApi.getRoom(roomKey);
|
||||
|
||||
final NeeoDevice device = room.getDevices().getDevice(deviceKey);
|
||||
if (device == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Device (" + config.getDeviceKey() + ") was not found in room (" + roomKey + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
final ThingUID thingUid = getThing().getUID();
|
||||
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
final NeeoDeviceDetails details = device.getDetails();
|
||||
if (details != null) {
|
||||
/** The following properties have matches in org.openhab.io.neeo.OpenHabToDeviceConverter.java */
|
||||
addProperty(properties, "Source Name", details.getSourceName());
|
||||
addProperty(properties, "Adapter Name", details.getAdapterName());
|
||||
addProperty(properties, "Type", details.getType());
|
||||
addProperty(properties, "Manufacturer", details.getManufacturer());
|
||||
addProperty(properties, "Name", details.getName());
|
||||
|
||||
final NeeoDeviceDetailsTiming timing = details.getTiming();
|
||||
if (timing != null) {
|
||||
properties.put("Standby Command Delay", toString(timing.getStandbyCommandDelay()));
|
||||
properties.put("Source Switch Delay", toString(timing.getSourceSwitchDelay()));
|
||||
properties.put("Shutdown Delay", toString(timing.getShutdownDelay()));
|
||||
}
|
||||
|
||||
properties.put("Device Capabilities", StringUtils.join(details.getDeviceCapabilities(), ','));
|
||||
}
|
||||
|
||||
final ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withLabel(device.getName() + " (NEEO " + brainApi.getBrain().getKey() + ")")
|
||||
.withProperties(properties).withChannels(ChannelUtils.generateChannels(thingUid, device));
|
||||
updateThing(thingBuilder.build());
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
final NeeoDeviceProtocol protocol = new NeeoDeviceProtocol(new NeeoHandlerCallback() {
|
||||
|
||||
@Override
|
||||
public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
|
||||
updateStatus(status, detail, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(String channelId, State state) {
|
||||
updateState(channelId, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) {
|
||||
getThing().setProperty(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleTask(Runnable task, long milliSeconds) {
|
||||
scheduler.schedule(task, milliSeconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerEvent(String channelID, String event) {
|
||||
triggerChannel(channelID, event);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoBrainApi getApi() {
|
||||
return getNeeoBrainApi();
|
||||
}
|
||||
}, roomKey, deviceKey);
|
||||
deviceProtocol.getAndSet(protocol);
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException during initialization", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Room " + roomKey + " couldn't be found");
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Initialization was interrupted", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Initialization was interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to add a property to the properties map if the value is not null
|
||||
*
|
||||
* @param properties a non-null properties map
|
||||
* @param key a non-null, non-empty key
|
||||
* @param value a possibly null, possibly empty key
|
||||
*/
|
||||
private void addProperty(Map<String, String> properties, String key, @Nullable String value) {
|
||||
Objects.requireNonNull(properties, "properties cannot be null");
|
||||
NeeoUtil.requireNotEmpty(key, "key cannot be empty");
|
||||
if (value != null && StringUtils.isNotEmpty(value)) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the room key from the parent bridge (which should be a room)
|
||||
*
|
||||
* @return a non-null, non-empty room key if found, null if not found
|
||||
*/
|
||||
@Nullable
|
||||
private String getRoomKey() {
|
||||
final Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
final BridgeHandler handler = bridge.getHandler();
|
||||
if (handler instanceof NeeoRoomHandler) {
|
||||
return handler.getThing().getConfiguration().as(NeeoRoomConfig.class).getRoomKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to simply create a string from an integer
|
||||
*
|
||||
* @param i the integer
|
||||
* @return the resulting string representation
|
||||
*/
|
||||
private static String toString(@Nullable Integer i) {
|
||||
if (i == null) {
|
||||
return "";
|
||||
}
|
||||
return i.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to return the {@link NeeoRoomHandler} associated with this handler
|
||||
*
|
||||
* @return a possibly null {@link NeeoRoomHandler}
|
||||
*/
|
||||
@Nullable
|
||||
private NeeoRoomHandler getRoomHandler() {
|
||||
final Bridge parent = getBridge();
|
||||
if (parent != null) {
|
||||
final BridgeHandler handler = parent.getHandler();
|
||||
if (handler instanceof NeeoRoomHandler) {
|
||||
return ((NeeoRoomHandler) handler);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NeeoBrainApi} associated with this handler.
|
||||
*
|
||||
* @return the {@link NeeoBrainApi} or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
private NeeoBrainApi getNeeoBrainApi() {
|
||||
final NeeoRoomHandler handler = getRoomHandler();
|
||||
return handler == null ? null : handler.getNeeoBrainApi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the brain ID associated with this handler.
|
||||
*
|
||||
* @return the brain ID or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public String getNeeoBrainId() {
|
||||
final NeeoRoomHandler handler = getRoomHandler();
|
||||
return handler == null ? null : handler.getNeeoBrainId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(null));
|
||||
NeeoUtil.cancel(refreshTask.getAndSet(null));
|
||||
deviceProtocol.getAndSet(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.openhab.binding.neeo.internal.net.HttpRequest;
|
||||
import org.openhab.binding.neeo.internal.net.HttpResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This servlet handles the forward actions events from the NEEO Brain. The forward actions will be posted to the
|
||||
* callback and then will be forwarded on to any URLs lised in {@link #forwardChain}
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@SuppressWarnings("serial")
|
||||
public class NeeoForwardActionsServlet extends HttpServlet {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoForwardActionsServlet.class);
|
||||
|
||||
/** The event publisher */
|
||||
private final Callback callback;
|
||||
|
||||
/** The forwarding chain */
|
||||
@Nullable
|
||||
private final String forwardChain;
|
||||
|
||||
/** The scheduler to use to schedule recipe execution */
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
/**
|
||||
* Creates the servlet the will process foward action events from the NEEO brain.
|
||||
*
|
||||
* @param scheduler a non-null {@link ScheduledExecutorService} to schedule forward actions
|
||||
* @param callback a non-null {@link Callback}
|
||||
* @param forwardChain a possibly null, possibly empty forwarding chain
|
||||
*/
|
||||
NeeoForwardActionsServlet(ScheduledExecutorService scheduler, Callback callback, @Nullable String forwardChain) {
|
||||
super();
|
||||
|
||||
Objects.requireNonNull(scheduler, "scheduler cannot be null");
|
||||
Objects.requireNonNull(callback, "callback cannot be null");
|
||||
|
||||
this.scheduler = scheduler;
|
||||
this.callback = callback;
|
||||
this.forwardChain = forwardChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the post action from the NEEO brain. Simply get's the specified json and then forwards it on (if
|
||||
* needed)
|
||||
*
|
||||
* @param req the non-null request
|
||||
* @param resp the non-null response
|
||||
*/
|
||||
@Override
|
||||
protected void doPost(@Nullable HttpServletRequest req, @Nullable HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
Objects.requireNonNull(req, "req cannot be null");
|
||||
Objects.requireNonNull(resp, "resp cannot be null");
|
||||
|
||||
final String json = IOUtils.toString(req.getReader());
|
||||
logger.debug("handleForwardActions {}", json);
|
||||
|
||||
callback.post(json);
|
||||
|
||||
final String fc = forwardChain;
|
||||
if (fc != null && StringUtils.isNotEmpty(fc)) {
|
||||
scheduler.execute(() -> {
|
||||
try (final HttpRequest request = new HttpRequest()) {
|
||||
for (final String forwardUrl : fc.split(",")) {
|
||||
if (StringUtils.isNotEmpty(forwardUrl)) {
|
||||
final HttpResponse httpResponse = request.sendPostJsonCommand(forwardUrl, json);
|
||||
if (httpResponse.getHttpCode() != HttpStatus.OK_200) {
|
||||
logger.debug("Cannot forward event {} to {}: {}", json, forwardUrl,
|
||||
httpResponse.getHttpCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
void post(String json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.discovery.NeeoDeviceDiscoveryService;
|
||||
import org.openhab.binding.neeo.internal.discovery.NeeoRoomDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.http.HttpService;
|
||||
|
||||
/**
|
||||
* The {@link NeeoHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.neeo")
|
||||
public class NeeoHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
/** The {@link HttpService} used to register callbacks */
|
||||
@NonNullByDefault({})
|
||||
private HttpService httpService;
|
||||
|
||||
/** The {@link NetworkAddressService} used for ip lookup */
|
||||
@NonNullByDefault({})
|
||||
private NetworkAddressService networkAddressService;
|
||||
|
||||
/** The discovery services created by this class (one per room and one for each device) */
|
||||
private final ConcurrentMap<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Sets the {@link HttpService}.
|
||||
*
|
||||
* @param httpService the non-null {@link HttpService} to use
|
||||
*/
|
||||
@Reference
|
||||
protected void setHttpService(HttpService httpService) {
|
||||
Objects.requireNonNull(httpService, "httpService cannot be null");
|
||||
this.httpService = httpService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the {@link HttpService}
|
||||
*
|
||||
* @param httpService the {@link HttpService} (not used in this implementation)
|
||||
*/
|
||||
protected void unsetHttpService(HttpService httpService) {
|
||||
this.httpService = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link NetworkAddressService}.
|
||||
*
|
||||
* @param networkAddressService the non-null {@link NetworkAddressService} to use
|
||||
*/
|
||||
@Reference
|
||||
protected void setNetworkAddressService(NetworkAddressService networkAddressService) {
|
||||
Objects.requireNonNull(networkAddressService, "networkAddressService cannot be null");
|
||||
this.networkAddressService = networkAddressService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the {@link NetworkAddressService}
|
||||
*
|
||||
* @param networkAddressService the {@link NetworkAddressService} (not used in this implementation)
|
||||
*/
|
||||
protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) {
|
||||
this.networkAddressService = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
Objects.requireNonNull(thingTypeUID, "thingTypeUID cannot be null");
|
||||
|
||||
return NeeoConstants.BINDING_ID.equals(thingTypeUID.getBindingId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
Objects.requireNonNull(thing, "thing cannot be null");
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(NeeoConstants.BRIDGE_TYPE_BRAIN)) {
|
||||
final HttpService localHttpService = httpService;
|
||||
final NetworkAddressService localNetworkAddressService = networkAddressService;
|
||||
|
||||
Objects.requireNonNull(localHttpService, "HttpService cannot be null");
|
||||
Objects.requireNonNull(localNetworkAddressService, "networkAddressService cannot be null");
|
||||
|
||||
final int port = HttpServiceUtil.getHttpServicePort(this.bundleContext);
|
||||
|
||||
final NeeoBrainHandler handler = new NeeoBrainHandler((Bridge) thing,
|
||||
port < 0 ? NeeoConstants.DEFAULT_BRAIN_HTTP_PORT : port, localHttpService,
|
||||
localNetworkAddressService);
|
||||
registerRoomDiscoveryService(handler);
|
||||
return handler;
|
||||
} else if (thingTypeUID.equals(NeeoConstants.BRIDGE_TYPE_ROOM)) {
|
||||
final NeeoRoomHandler handler = new NeeoRoomHandler((Bridge) thing);
|
||||
registerDeviceDiscoveryService(handler);
|
||||
return handler;
|
||||
} else if (thingTypeUID.equals(NeeoConstants.THING_TYPE_DEVICE)) {
|
||||
return new NeeoDeviceHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to register the room discovery service
|
||||
*
|
||||
* @param handler a non-null brain handler
|
||||
*/
|
||||
private void registerRoomDiscoveryService(NeeoBrainHandler handler) {
|
||||
Objects.requireNonNull(handler, "handler cannot be null");
|
||||
final NeeoRoomDiscoveryService discoveryService = new NeeoRoomDiscoveryService(handler);
|
||||
this.discoveryServiceRegs.put(handler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to register the device discovery service
|
||||
*
|
||||
* @param handler a non-null room handler
|
||||
*/
|
||||
private void registerDeviceDiscoveryService(NeeoRoomHandler handler) {
|
||||
Objects.requireNonNull(handler, "handler cannot be null");
|
||||
final NeeoDeviceDiscoveryService discoveryService = new NeeoDeviceDiscoveryService(handler);
|
||||
this.discoveryServiceRegs.put(handler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeHandler(ThingHandler thingHandler) {
|
||||
final ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
serviceReg.unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.neeo.internal.NeeoBrainApi;
|
||||
import org.openhab.binding.neeo.internal.NeeoConstants;
|
||||
import org.openhab.binding.neeo.internal.NeeoHandlerCallback;
|
||||
import org.openhab.binding.neeo.internal.NeeoRoomConfig;
|
||||
import org.openhab.binding.neeo.internal.NeeoRoomProtocol;
|
||||
import org.openhab.binding.neeo.internal.NeeoUtil;
|
||||
import org.openhab.binding.neeo.internal.UidUtils;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoAction;
|
||||
import org.openhab.binding.neeo.internal.models.NeeoRoom;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.BridgeHandler;
|
||||
import org.openhab.core.thing.binding.builder.ThingBuilder;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A subclass of {@link BaseBridgeHandler} that is responsible for handling commands for a room
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoomHandler extends BaseBridgeHandler {
|
||||
|
||||
/** The logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(NeeoRoomHandler.class);
|
||||
|
||||
/**
|
||||
* The initialization task (null until set by {@link #initializeTask()} and set back to null in {@link #dispose()}
|
||||
*/
|
||||
private final AtomicReference<@Nullable Future<?>> initializationTask = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* The refresh task (null until set by {@link #initializeTask()} and set back to null in {@link #dispose()}
|
||||
*/
|
||||
private final AtomicReference<@Nullable Future<?>> refreshTask = new AtomicReference<>();
|
||||
|
||||
/** The {@link NeeoRoomProtocol} (null until set by {@link #initializationTask}) */
|
||||
private final AtomicReference<@Nullable NeeoRoomProtocol> roomProtocol = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo room handler.
|
||||
*
|
||||
* @param bridge the non-null bridge
|
||||
*/
|
||||
NeeoRoomHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
Objects.requireNonNull(bridge, "bridge cannot be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
Objects.requireNonNull(channelUID, "channelUID cannot be null");
|
||||
Objects.requireNonNull(command, "command cannot be null");
|
||||
|
||||
final NeeoRoomProtocol protocol = roomProtocol.get();
|
||||
if (protocol == null) {
|
||||
logger.debug("Protocol is null - ignoring update: {}", channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
final String[] channelIds = UidUtils.parseChannelId(channelUID);
|
||||
if (channelIds.length == 0) {
|
||||
logger.debug("Bad group declaration: {}", channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
final String localGroupId = channelUID.getGroupId();
|
||||
final String groupId = localGroupId == null || StringUtils.isEmpty(localGroupId) ? "" : localGroupId;
|
||||
final String channelId = channelIds[0];
|
||||
final String channelKey = channelIds.length > 1 ? channelIds[1] : "";
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannel(protocol, groupId, channelKey, channelId);
|
||||
} else {
|
||||
switch (groupId) {
|
||||
case NeeoConstants.ROOM_GROUP_RECIPE_ID:
|
||||
switch (channelId) {
|
||||
case NeeoConstants.ROOM_CHANNEL_STATUS:
|
||||
// Ignore OFF status updates
|
||||
if (command == OnOffType.ON) {
|
||||
protocol.startRecipe(channelKey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NeeoConstants.ROOM_GROUP_SCENARIO_ID:
|
||||
switch (channelId) {
|
||||
case NeeoConstants.ROOM_CHANNEL_STATUS:
|
||||
if (command instanceof OnOffType) {
|
||||
protocol.setScenarioStatus(channelKey, command == OnOffType.ON);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown channel to set: {}", channelUID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the specified channel section, key and id using the specified protocol
|
||||
*
|
||||
* @param protocol a non-null protocol to use
|
||||
* @param groupId the non-empty channel section
|
||||
* @param channelKey the non-empty channel key
|
||||
* @param channelId the non-empty channel id
|
||||
*/
|
||||
private void refreshChannel(NeeoRoomProtocol protocol, String groupId, String channelKey, String channelId) {
|
||||
Objects.requireNonNull(protocol, "protocol cannot be null");
|
||||
NeeoUtil.requireNotEmpty(groupId, "groupId must not be empty");
|
||||
NeeoUtil.requireNotEmpty(channelId, "channelId must not be empty");
|
||||
|
||||
switch (groupId) {
|
||||
case NeeoConstants.ROOM_GROUP_RECIPE_ID:
|
||||
NeeoUtil.requireNotEmpty(channelKey, "channelKey must not be empty");
|
||||
switch (channelId) {
|
||||
case NeeoConstants.ROOM_CHANNEL_NAME:
|
||||
protocol.refreshRecipeName(channelKey);
|
||||
break;
|
||||
case NeeoConstants.ROOM_CHANNEL_TYPE:
|
||||
protocol.refreshRecipeType(channelKey);
|
||||
break;
|
||||
case NeeoConstants.ROOM_CHANNEL_ENABLED:
|
||||
protocol.refreshRecipeEnabled(channelKey);
|
||||
break;
|
||||
case NeeoConstants.ROOM_CHANNEL_STATUS:
|
||||
protocol.refreshRecipeStatus(channelKey);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NeeoConstants.ROOM_GROUP_SCENARIO_ID:
|
||||
NeeoUtil.requireNotEmpty(channelKey, "channelKey must not be empty");
|
||||
switch (channelId) {
|
||||
case NeeoConstants.ROOM_CHANNEL_NAME:
|
||||
protocol.refreshScenarioName(channelKey);
|
||||
break;
|
||||
case NeeoConstants.ROOM_CHANNEL_CONFIGURED:
|
||||
protocol.refreshScenarioConfigured(channelKey);
|
||||
break;
|
||||
case NeeoConstants.ROOM_CHANNEL_STATUS:
|
||||
protocol.refreshScenarioStatus(channelKey);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(scheduler.submit(() -> {
|
||||
initializeTask();
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the task be creating the {@link NeeoRoomProtocol}, going online and then scheduling the refresh task.
|
||||
*/
|
||||
private void initializeTask() {
|
||||
final NeeoRoomConfig config = getConfigAs(NeeoRoomConfig.class);
|
||||
|
||||
final String roomKey = config.getRoomKey();
|
||||
if (roomKey == null || StringUtils.isEmpty(roomKey)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Room key (from the parent room bridge) was not found");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
NeeoUtil.checkInterrupt();
|
||||
final NeeoBrainApi brainApi = getNeeoBrainApi();
|
||||
if (brainApi == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, "Cannot find the NEEO Brain API");
|
||||
return;
|
||||
}
|
||||
|
||||
final NeeoRoom room = brainApi.getRoom(roomKey);
|
||||
|
||||
final ThingUID thingUid = getThing().getUID();
|
||||
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put("Key", roomKey);
|
||||
|
||||
final ThingBuilder thingBuilder = editThing();
|
||||
thingBuilder.withLabel(room.getName() + " (NEEO " + brainApi.getBrain().getKey() + ")")
|
||||
.withProperties(properties).withChannels(ChannelUtils.generateChannels(thingUid, room));
|
||||
updateThing(thingBuilder.build());
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
final NeeoRoomProtocol protocol = new NeeoRoomProtocol(new NeeoHandlerCallback() {
|
||||
|
||||
@Override
|
||||
public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
|
||||
updateStatus(status, detail, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(String channelId, State state) {
|
||||
updateState(channelId, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) {
|
||||
getThing().setProperty(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleTask(Runnable task, long milliSeconds) {
|
||||
scheduler.schedule(task, milliSeconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerEvent(String channelID, String event) {
|
||||
triggerChannel(channelID, event);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoBrainApi getApi() {
|
||||
return getNeeoBrainApi();
|
||||
}
|
||||
}, roomKey);
|
||||
roomProtocol.getAndSet(protocol);
|
||||
|
||||
NeeoUtil.checkInterrupt();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
if (config.getRefreshPolling() > 0) {
|
||||
NeeoUtil.checkInterrupt();
|
||||
NeeoUtil.cancel(refreshTask.getAndSet(scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
refreshState();
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Refresh State was interrupted", e);
|
||||
}
|
||||
}, 0, config.getRefreshPolling(), TimeUnit.SECONDS)));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IOException during initialization", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Room " + config.getRoomKey() + " couldn't be found");
|
||||
} catch (InterruptedException e) {
|
||||
logger.debug("Initialization was interrupted", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"Initialization was interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the action if it applies to this room
|
||||
*
|
||||
* @param action a non-null action to process
|
||||
*/
|
||||
void processAction(NeeoAction action) {
|
||||
Objects.requireNonNull(action, "action cannot be null");
|
||||
final NeeoRoomProtocol protocol = roomProtocol.get();
|
||||
if (protocol != null) {
|
||||
protocol.processAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the state of the room by calling {@link NeeoRoomProtocol#refreshState()}
|
||||
*
|
||||
* @throws InterruptedException if the call is interrupted
|
||||
*/
|
||||
private void refreshState() throws InterruptedException {
|
||||
NeeoUtil.checkInterrupt();
|
||||
final NeeoRoomProtocol protocol = roomProtocol.get();
|
||||
if (protocol != null) {
|
||||
NeeoUtil.checkInterrupt();
|
||||
protocol.refreshState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to return the {@link NeeoBrainHandler} associated with this handler
|
||||
*
|
||||
* @return a possibly null {@link NeeoBrainHandler}
|
||||
*/
|
||||
@Nullable
|
||||
private NeeoBrainHandler getBrainHandler() {
|
||||
final Bridge parent = getBridge();
|
||||
if (parent != null) {
|
||||
final BridgeHandler handler = parent.getHandler();
|
||||
if (handler instanceof NeeoBrainHandler) {
|
||||
return ((NeeoBrainHandler) handler);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NeeoBrainApi} associated with this handler.
|
||||
*
|
||||
* @return the {@link NeeoBrainApi} or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoBrainApi getNeeoBrainApi() {
|
||||
final NeeoBrainHandler handler = getBrainHandler();
|
||||
return handler == null ? null : handler.getNeeoBrainApi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the brain ID associated with this handler.
|
||||
*
|
||||
* @return the brain ID or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public String getNeeoBrainId() {
|
||||
final NeeoBrainHandler handler = getBrainHandler();
|
||||
return handler == null ? null : handler.getNeeoBrainId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
NeeoUtil.cancel(initializationTask.getAndSet(null));
|
||||
NeeoUtil.cancel(refreshTask.getAndSet(null));
|
||||
roomProtocol.getAndSet(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an error response (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ErrorResponse {
|
||||
|
||||
/** The error */
|
||||
@Nullable
|
||||
private String error;
|
||||
|
||||
/** The message */
|
||||
@Nullable
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* Gets the error.
|
||||
*
|
||||
* @return the error
|
||||
*/
|
||||
@Nullable
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message.
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ErrorResponse [error=" + error + ", message=" + message + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an execute result (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ExecuteResult {
|
||||
|
||||
/** The estimated duration */
|
||||
private int estimatedDuration;
|
||||
|
||||
/** The name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The start time */
|
||||
private long startTime;
|
||||
|
||||
/** The steps */
|
||||
private ExecuteStep @Nullable [] steps;
|
||||
|
||||
/** The type */
|
||||
@Nullable
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* Gets the estimated duration.
|
||||
*
|
||||
* @return the estimated duration
|
||||
*/
|
||||
public int getEstimatedDuration() {
|
||||
return estimatedDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start time.
|
||||
*
|
||||
* @return the start time
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the steps.
|
||||
*
|
||||
* @return the steps
|
||||
*/
|
||||
public ExecuteStep[] getSteps() {
|
||||
final ExecuteStep @Nullable [] localSteps = steps;
|
||||
return localSteps == null ? new ExecuteStep[0] : localSteps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExecuteResult [estimatedDuration=" + estimatedDuration + ", name=" + name + ", startTime=" + startTime
|
||||
+ ", steps=" + Arrays.toString(steps) + ", type=" + type + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an execute setp (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ExecuteStep {
|
||||
|
||||
/** The duration of the step */
|
||||
private int duration;
|
||||
|
||||
/** The text describing the step */
|
||||
@Nullable
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Gets the duration of the step
|
||||
*
|
||||
* @return the duration
|
||||
*/
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text describing the step
|
||||
*
|
||||
* @return the text
|
||||
*/
|
||||
@Nullable
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExecuteStep [duration=" + duration + ", text=" + text + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* The model representing an forward actions result (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoAction {
|
||||
/** The action - can be null */
|
||||
@Nullable
|
||||
private String action;
|
||||
|
||||
/** The action parameter - generally null */
|
||||
@Nullable
|
||||
@SerializedName("actionparameter")
|
||||
private String actionParameter;
|
||||
|
||||
/** The recipe name - only valid on launch of recipe */
|
||||
@Nullable
|
||||
private String recipe;
|
||||
|
||||
/** The device name - usually filled */
|
||||
@Nullable
|
||||
private String device;
|
||||
|
||||
/** The room name - usually filled */
|
||||
@Nullable
|
||||
private String room;
|
||||
|
||||
/**
|
||||
* Returns the action
|
||||
*
|
||||
* @return a possibly null, possibly empty action
|
||||
*/
|
||||
@Nullable
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action parameter
|
||||
*
|
||||
* @return a possibly null, possibly empty action parameter
|
||||
*/
|
||||
@Nullable
|
||||
public String getActionParameter() {
|
||||
return actionParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recipe name
|
||||
*
|
||||
* @return a possibly null, possibly empty recipe name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRecipe() {
|
||||
return recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the device name
|
||||
*
|
||||
* @return a possibly null, possibly empty device name
|
||||
*/
|
||||
@Nullable
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the room name
|
||||
*
|
||||
* @return a possibly null, possibly room name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoAction [action=" + action + ", actionParameter=" + actionParameter + ", recipe=" + recipe
|
||||
+ ", device=" + device + ", room=" + room + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Brain(serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoBrain {
|
||||
|
||||
/** The brain name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The version of the brain */
|
||||
@Nullable
|
||||
private String version;
|
||||
|
||||
/** The brain's label */
|
||||
@Nullable
|
||||
private String label;
|
||||
|
||||
/** Whether the brain has been configured */
|
||||
private boolean configured;
|
||||
|
||||
/** The brain key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** ?? The brain airkey ?? */
|
||||
@Nullable
|
||||
private String airkey;
|
||||
|
||||
/** Last time the brain was changed */
|
||||
private long lastchange;
|
||||
|
||||
/** The rooms in the brain */
|
||||
@Nullable
|
||||
private NeeoRooms rooms;
|
||||
|
||||
/**
|
||||
* Gets the brain name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version of the brain
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
@Nullable
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the brain's label
|
||||
*
|
||||
* @return the label
|
||||
*/
|
||||
@Nullable
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the brain is configured
|
||||
*
|
||||
* @return true, if is configured
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the brain key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the brain's airkey
|
||||
*
|
||||
* @return the airkey
|
||||
*/
|
||||
@Nullable
|
||||
public String getAirkey() {
|
||||
return airkey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last time the brain was changed
|
||||
*
|
||||
* @return the lastchange
|
||||
*/
|
||||
public long getLastChange() {
|
||||
return lastchange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rooms in the brain
|
||||
*
|
||||
* @return the rooms
|
||||
*/
|
||||
public NeeoRooms getRooms() {
|
||||
final NeeoRooms localRooms = rooms;
|
||||
return localRooms == null ? new NeeoRooms(new NeeoRoom[0]) : localRooms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoBrain [name=" + name + ", version=" + version + ", label=" + label + ", configured=" + configured
|
||||
+ ", key=" + key + ", airkey=" + airkey + ", lastchange=" + lastchange + ", rooms=" + rooms + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an NEEO Device (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDevice {
|
||||
|
||||
/** The device name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The associated room name */
|
||||
@Nullable
|
||||
private String roomName;
|
||||
|
||||
/** The associated room key */
|
||||
@Nullable
|
||||
private String roomKey;
|
||||
|
||||
/** The adapter device id */
|
||||
@Nullable
|
||||
private String adapterDeviceId;
|
||||
|
||||
/** The device key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** The macros for the device */
|
||||
@Nullable
|
||||
private NeeoMacros macros;
|
||||
|
||||
/** The device details */
|
||||
@Nullable
|
||||
private NeeoDeviceDetails details;
|
||||
|
||||
/**
|
||||
* Gets the device name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room name
|
||||
*
|
||||
* @return the room name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room key
|
||||
*
|
||||
* @return the room key
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomKey() {
|
||||
return roomKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the adapter device id
|
||||
*
|
||||
* @return the adapter device id
|
||||
*/
|
||||
@Nullable
|
||||
public String getAdapterDeviceId() {
|
||||
return adapterDeviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macro key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macros for the device
|
||||
*
|
||||
* @return the macros
|
||||
*/
|
||||
public NeeoMacros getMacros() {
|
||||
final NeeoMacros localMacros = macros;
|
||||
return localMacros == null ? new NeeoMacros(new NeeoMacro[0]) : localMacros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the details for the device
|
||||
*
|
||||
* @return the details
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoDeviceDetails getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoDevice [name=" + name + ", roomName=" + roomName + ", roomKey=" + roomKey + ", adapterDeviceId="
|
||||
+ adapterDeviceId + ", key=" + key + ", macros=" + macros + ", details=" + details + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Device Details (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceDetails {
|
||||
|
||||
/** The source name (neeo-deviceadapter or sdk name) */
|
||||
@Nullable
|
||||
private String sourceName;
|
||||
|
||||
/** The adapter name (name given by source) */
|
||||
@Nullable
|
||||
private String adapterName;
|
||||
|
||||
/** The NEEO type */
|
||||
@Nullable
|
||||
private String type;
|
||||
|
||||
/** The manufacture */
|
||||
@Nullable
|
||||
private String manufacturer;
|
||||
|
||||
/** The name of the device given by source */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The timings of the device */
|
||||
@Nullable
|
||||
private NeeoDeviceDetailsTiming timing;
|
||||
|
||||
/** The device capabilities */
|
||||
private String @Nullable [] deviceCapabilities;
|
||||
|
||||
/**
|
||||
* The device source name
|
||||
*
|
||||
* @return the device source name
|
||||
*/
|
||||
@Nullable
|
||||
public String getSourceName() {
|
||||
return sourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The device adapter name (given by the source)
|
||||
*
|
||||
* @return the adapter name
|
||||
*/
|
||||
@Nullable
|
||||
public String getAdapterName() {
|
||||
return adapterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The NEEO device type
|
||||
*
|
||||
* @return the NEEO device type
|
||||
*/
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The manufacturer of the device
|
||||
*
|
||||
* @return the manufacturer
|
||||
*/
|
||||
@Nullable
|
||||
public String getManufacturer() {
|
||||
return manufacturer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the device (given by the source)
|
||||
*
|
||||
* @return the device name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The device timing
|
||||
*
|
||||
* @return the timings
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoDeviceDetailsTiming getTiming() {
|
||||
return timing;
|
||||
}
|
||||
|
||||
/**
|
||||
* The device capabilities
|
||||
*
|
||||
* @return the capabilities
|
||||
*/
|
||||
public String[] getDeviceCapabilities() {
|
||||
final String[] localCapabilities = deviceCapabilities;
|
||||
return localCapabilities == null ? new String[0] : localCapabilities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoDeviceDetails [sourceName=" + sourceName + ", adapterName=" + adapterName + ", type=" + type
|
||||
+ ", manufacturer=" + manufacturer + ", name=" + name + ", timing=" + timing + ", deviceCapabilities="
|
||||
+ StringUtils.join(deviceCapabilities, ',') + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Device Details Timings (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDeviceDetailsTiming {
|
||||
|
||||
/** Standby delay in ms (time to turn on device) */
|
||||
@Nullable
|
||||
private Integer standbyCommandDelay;
|
||||
|
||||
/** Source switch in ms (time to switch inputs) */
|
||||
@Nullable
|
||||
private Integer sourceSwitchDelay;
|
||||
|
||||
/** Shutdown delay in ms (time to shutdown device) */
|
||||
@Nullable
|
||||
private Integer shutdownDelay;
|
||||
|
||||
/**
|
||||
* The time (in ms) to turn on device. May be null if not supported
|
||||
*
|
||||
* @return a possibly null time to turn on device
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getStandbyCommandDelay() {
|
||||
return standbyCommandDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time (in ms) to switch inputs. May be null if not supported
|
||||
*
|
||||
* @return a possibly null time to switch inputs
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getSourceSwitchDelay() {
|
||||
return sourceSwitchDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time (in ms) to shutdown device. May be null if not supported
|
||||
*
|
||||
* @return a possibly null time to shut down device
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getShutdownDelay() {
|
||||
return shutdownDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoDeviceDetailsTiming [standbyCommandDelay=" + standbyCommandDelay + ", sourceSwitchDelay="
|
||||
+ sourceSwitchDelay + ", shutdownDelay=" + shutdownDelay + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing Neeo Devices (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDevices {
|
||||
|
||||
/** The devices. */
|
||||
private final NeeoDevice @Nullable [] devices;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo devices.
|
||||
*
|
||||
* @param devices the devices
|
||||
*/
|
||||
NeeoDevices(NeeoDevice[] devices) {
|
||||
Objects.requireNonNull(devices, "devices cannot be null");
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the devices.
|
||||
*
|
||||
* @return the devices
|
||||
*/
|
||||
public NeeoDevice[] getDevices() {
|
||||
final NeeoDevice[] localDevices = devices;
|
||||
return localDevices == null ? new NeeoDevice[0] : localDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the device.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the device
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoDevice getDevice(String key) {
|
||||
for (NeeoDevice device : getDevices()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, device.getKey())) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoDevice [devices=" + Arrays.toString(devices) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The implementation of {@link JsonDeserializer} to deserialize a {@link NeeoDevices} class
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoDevicesDeserializer implements JsonDeserializer<@Nullable NeeoDevices> {
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoDevices deserialize(@Nullable JsonElement jsonElement, @Nullable Type type,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
Objects.requireNonNull(jsonElement, "jsonElement cannot be null");
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
|
||||
if (jsonElement instanceof JsonObject) {
|
||||
final List<NeeoDevice> scenarios = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> entry : ((JsonObject) jsonElement).entrySet()) {
|
||||
final NeeoDevice device = context.deserialize(entry.getValue(), NeeoDevice.class);
|
||||
scenarios.add(device);
|
||||
}
|
||||
|
||||
return new NeeoDevices(scenarios.toArray(new NeeoDevice[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an forward actions request (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoForwardActions {
|
||||
/** The host to forward actions to */
|
||||
@Nullable
|
||||
private final String host;
|
||||
|
||||
/** The port to use */
|
||||
private final int port;
|
||||
|
||||
/** The path the actions should go to */
|
||||
@Nullable
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Creates the forward actions from the given parms
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @param path the path
|
||||
*/
|
||||
public NeeoForwardActions(String host, int port, String path) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host name to forward actions to
|
||||
*
|
||||
* @return the hostname
|
||||
*/
|
||||
@Nullable
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port number
|
||||
*
|
||||
* @return the port number
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to use
|
||||
*
|
||||
* @return the path
|
||||
*/
|
||||
@Nullable
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoForwardActions [host=" + host + ", port=" + port + ", path=" + path + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Macro (serialize/deserialize json use only)
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoMacro {
|
||||
|
||||
/** The macro key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** The component type */
|
||||
@Nullable
|
||||
private String componentType;
|
||||
|
||||
/** The macro name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The macro label */
|
||||
@Nullable
|
||||
private String label;
|
||||
|
||||
/** The associated device name */
|
||||
@Nullable
|
||||
private String deviceName;
|
||||
|
||||
/** The associated room name */
|
||||
@Nullable
|
||||
private String roomName;
|
||||
|
||||
/**
|
||||
* Gets the macro key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component type
|
||||
*
|
||||
* @return the component type
|
||||
*/
|
||||
@Nullable
|
||||
public String getComponentType() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macro name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macro label
|
||||
*
|
||||
* @return the label
|
||||
*/
|
||||
@Nullable
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated device name
|
||||
*
|
||||
* @return the device name
|
||||
*/
|
||||
@Nullable
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room name
|
||||
*
|
||||
* @return the room name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoMacro [key=" + key + ", componentType=" + componentType + ", name=" + name + ", label=" + label
|
||||
+ ", deviceName=" + deviceName + ", roomName=" + roomName + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing Neeo Macros (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoMacros {
|
||||
|
||||
/** The macros. */
|
||||
private final NeeoMacro @Nullable [] macros;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo macros.
|
||||
*
|
||||
* @param macros the macros
|
||||
*/
|
||||
NeeoMacros(NeeoMacro[] macros) {
|
||||
Objects.requireNonNull(macros, "macros cannot be null");
|
||||
this.macros = macros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macros.
|
||||
*
|
||||
* @return the macros
|
||||
*/
|
||||
public NeeoMacro[] getMacros() {
|
||||
final NeeoMacro @Nullable [] localMacros = macros;
|
||||
return localMacros == null ? new NeeoMacro[0] : localMacros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the macro.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the macro
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoMacro getMacro(String key) {
|
||||
for (NeeoMacro macro : getMacros()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, macro.getKey())) {
|
||||
return macro;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoMacro [macros=" + Arrays.toString(macros) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The implementation of {@link JsonDeserializer} to deserialize a {@link NeeoMacros} class
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoMacrosDeserializer implements JsonDeserializer<@Nullable NeeoMacros> {
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoMacros deserialize(@Nullable JsonElement jsonElement, @Nullable Type type,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
Objects.requireNonNull(jsonElement, "jsonElement cannot be null");
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
|
||||
if (jsonElement instanceof JsonObject) {
|
||||
final List<NeeoMacro> scenarios = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> entry : ((JsonObject) jsonElement).entrySet()) {
|
||||
final NeeoMacro macro = context.deserialize(entry.getValue(), NeeoMacro.class);
|
||||
scenarios.add(macro);
|
||||
}
|
||||
|
||||
return new NeeoMacros(scenarios.toArray(new NeeoMacro[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Recipe (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRecipe {
|
||||
|
||||
/** Recipe type for launching the recipe */
|
||||
public static final String LAUNCH = "launch";
|
||||
|
||||
/** Recipe type for powering off the recipe */
|
||||
public static final String POWEROFF = "poweroff";
|
||||
|
||||
/** The recipe key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** The type of recipe (generally launch/poweroff) */
|
||||
@Nullable
|
||||
private String type;
|
||||
|
||||
/** The name of the recipe */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** Whether the recipe is enabled */
|
||||
private boolean enabled;
|
||||
|
||||
/** ?? whether the recipe is dirty ?? */
|
||||
private boolean dirty;
|
||||
|
||||
// May be used in the future...
|
||||
// private NeeoStep[] steps;
|
||||
// private NeeoCondition[] conditions;
|
||||
// private NeeoTrigger trigger;
|
||||
|
||||
/** The associated room key */
|
||||
@Nullable
|
||||
private String roomKey;
|
||||
|
||||
/** The associated room name. */
|
||||
@Nullable
|
||||
private String roomName;
|
||||
|
||||
/** The scenario key recipe is linked to */
|
||||
@Nullable
|
||||
private String scenarioKey;
|
||||
|
||||
/** Whether the recipe is hidden or not */
|
||||
private boolean isHiddenRecipe;
|
||||
|
||||
/** ?? whether this is a custom recipe ?? */
|
||||
private boolean isCustom;
|
||||
|
||||
/**
|
||||
* Gets the recipe key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipe type
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipe name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the recipe is enabled
|
||||
*
|
||||
* @return true, if is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the recipe is dirty
|
||||
*
|
||||
* @return true, if is dirty
|
||||
*/
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room key.
|
||||
*
|
||||
* @return the room key
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomKey() {
|
||||
return roomKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room name.
|
||||
*
|
||||
* @return the room name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated scenario key.
|
||||
*
|
||||
* @return the scenario key
|
||||
*/
|
||||
@Nullable
|
||||
public String getScenarioKey() {
|
||||
return scenarioKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the recipe is hidden
|
||||
*
|
||||
* @return true, if is hidden recipe
|
||||
*/
|
||||
public boolean isHiddenRecipe() {
|
||||
return isHiddenRecipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if its a custom recipe
|
||||
*
|
||||
* @return true, if is custom
|
||||
*/
|
||||
public boolean isCustom() {
|
||||
return isCustom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoRecipe [key=" + key + ", type=" + type + ", name=" + name + ", enabled=" + enabled + ", dirty="
|
||||
+ dirty + ", roomKey=" + roomKey + ", roomName=" + roomName + ", scenarioKey=" + scenarioKey
|
||||
+ ", isHiddenRecipe=" + isHiddenRecipe + ", isCustom=" + isCustom + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing Neeo Recipes (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRecipes {
|
||||
|
||||
/** The recipes. */
|
||||
private NeeoRecipe @Nullable [] recipes;
|
||||
|
||||
/**
|
||||
* Creates the recipes from the given recipes
|
||||
*
|
||||
* @param recipes the recipes
|
||||
*/
|
||||
NeeoRecipes(NeeoRecipe[] recipes) {
|
||||
Objects.requireNonNull(recipes, "recipes cannot be null");
|
||||
this.recipes = recipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipes.
|
||||
*
|
||||
* @return the recipes
|
||||
*/
|
||||
public NeeoRecipe[] getRecipes() {
|
||||
final NeeoRecipe[] localRecipes = recipes;
|
||||
return localRecipes == null ? new NeeoRecipe[0] : localRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipe by key
|
||||
*
|
||||
* @param key the key
|
||||
* @return the recipe or null if none found
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoRecipe getRecipe(String key) {
|
||||
if (recipes == null || StringUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (NeeoRecipe recipe : getRecipes()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, recipe.getKey())) {
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipe by a scenario key and recipe type
|
||||
*
|
||||
* @param key the key
|
||||
* @param type the recipe type
|
||||
* @return the recipe or null if none found
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoRecipe getRecipeByScenarioKey(String key, String type) {
|
||||
if (recipes == null || StringUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (NeeoRecipe recipe : getRecipes()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, recipe.getScenarioKey())
|
||||
&& StringUtils.equalsIgnoreCase(type, recipe.getType())) {
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipe by name
|
||||
*
|
||||
* @param name the recipe name
|
||||
* @return the recipe or null if none found
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoRecipe getRecipeByName(String name) {
|
||||
if (recipes == null || StringUtils.isEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (NeeoRecipe recipe : getRecipes()) {
|
||||
if (StringUtils.equalsIgnoreCase(name, recipe.getName())) {
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoRecipes [recipes=" + Arrays.toString(recipes) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The implementation of {@link JsonDeserializer} to deserialize a {@link NeeoRecipes} class
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRecipesDeserializer implements JsonDeserializer<@Nullable NeeoRecipes> {
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoRecipes deserialize(@Nullable JsonElement jsonElement, @Nullable Type type,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
Objects.requireNonNull(jsonElement, "jsonElement cannot be null");
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
|
||||
if (jsonElement instanceof JsonObject) {
|
||||
final List<NeeoRecipe> recipes = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> entry : ((JsonObject) jsonElement).entrySet()) {
|
||||
final NeeoRecipe recipe = context.deserialize(entry.getValue(), NeeoRecipe.class);
|
||||
recipes.add(recipe);
|
||||
}
|
||||
|
||||
return new NeeoRecipes(recipes.toArray(new NeeoRecipe[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Room (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoom {
|
||||
|
||||
/** The room name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The room key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** The devices in the room */
|
||||
@Nullable
|
||||
private NeeoDevices devices;
|
||||
|
||||
/** The scenarios in the room */
|
||||
@Nullable
|
||||
private NeeoScenarios scenarios;
|
||||
|
||||
/** The recipes in the room */
|
||||
@Nullable
|
||||
private NeeoRecipes recipes;
|
||||
|
||||
/**
|
||||
* Gets the room name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the recipes in the room
|
||||
*
|
||||
* @return the recipes
|
||||
*/
|
||||
public NeeoRecipes getRecipes() {
|
||||
final NeeoRecipes localRecipes = recipes;
|
||||
return localRecipes == null ? new NeeoRecipes(new NeeoRecipe[0]) : localRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the devices in the room
|
||||
*
|
||||
* @return the devices
|
||||
*/
|
||||
public NeeoDevices getDevices() {
|
||||
final NeeoDevices localDevices = devices;
|
||||
return localDevices == null ? new NeeoDevices(new NeeoDevice[0]) : localDevices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scenarios in the room
|
||||
*
|
||||
* @return the scenarios
|
||||
*/
|
||||
public NeeoScenarios getScenarios() {
|
||||
final NeeoScenarios localScenarios = scenarios;
|
||||
return localScenarios == null ? new NeeoScenarios(new NeeoScenario[0]) : localScenarios;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoRoom [name=" + name + ", key=" + key + ", scenarios=" + scenarios + ", devices=" + devices
|
||||
+ ", recipes=" + recipes + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing Neeo Rooms (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRooms {
|
||||
|
||||
/** The rooms. */
|
||||
private final NeeoRoom @Nullable [] rooms;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo rooms.
|
||||
*
|
||||
* @param rooms the rooms
|
||||
*/
|
||||
NeeoRooms(NeeoRoom[] rooms) {
|
||||
Objects.requireNonNull(rooms, "rooms cannot be null");
|
||||
this.rooms = rooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rooms.
|
||||
*
|
||||
* @return the rooms
|
||||
*/
|
||||
public NeeoRoom[] getRooms() {
|
||||
final NeeoRoom @Nullable [] localRooms = rooms;
|
||||
return localRooms == null ? new NeeoRoom[0] : localRooms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the room
|
||||
*/
|
||||
NeeoRoom getRoom(String key) {
|
||||
for (NeeoRoom room : getRooms()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, room.getKey())) {
|
||||
return room;
|
||||
}
|
||||
}
|
||||
return new NeeoRoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoRooms [rooms=" + Arrays.toString(rooms) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The implementation of {@link JsonDeserializer} to deserialize a {@link NeeoRooms} class
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoRoomsDeserializer implements JsonDeserializer<@Nullable NeeoRooms> {
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoRooms deserialize(@Nullable JsonElement jsonElement, @Nullable Type type,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
Objects.requireNonNull(jsonElement, "jsonElement cannot be null");
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
if (jsonElement instanceof JsonObject) {
|
||||
final List<NeeoRoom> recipes = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> entry : ((JsonObject) jsonElement).entrySet()) {
|
||||
final NeeoRoom room = context.deserialize(entry.getValue(), NeeoRoom.class);
|
||||
recipes.add(room);
|
||||
}
|
||||
|
||||
return new NeeoRooms(recipes.toArray(new NeeoRoom[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing an Neeo Scenario (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoScenario {
|
||||
|
||||
/** The scenario name */
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
/** The main device key */
|
||||
@Nullable
|
||||
private String mainDeviceKey;
|
||||
|
||||
/** The volume device key */
|
||||
@Nullable
|
||||
private String volumeDeviceKey;
|
||||
|
||||
/** The scenario key */
|
||||
@Nullable
|
||||
private String key;
|
||||
|
||||
/** Whether the scenario is pending configuration */
|
||||
private boolean configured;
|
||||
|
||||
/** The associated room key */
|
||||
@Nullable
|
||||
private String roomKey;
|
||||
|
||||
/** The associated room name */
|
||||
@Nullable
|
||||
private String roomName;
|
||||
|
||||
/** The devices that make up the scenario */
|
||||
private String @Nullable [] devices;
|
||||
|
||||
// may be used in the future
|
||||
// private final NeeoShortcut[] shortcuts;
|
||||
// @Nullable private String[] deviceInputMacroNames;
|
||||
// private final NeeoCapability[] capabilities;
|
||||
|
||||
/**
|
||||
* Gets the scenario name
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the main device key
|
||||
*
|
||||
* @return the main device key
|
||||
*/
|
||||
@Nullable
|
||||
public String getMainDeviceKey() {
|
||||
return mainDeviceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the volume device key
|
||||
*
|
||||
* @return the volume device key
|
||||
*/
|
||||
@Nullable
|
||||
public String getVolumeDeviceKey() {
|
||||
return volumeDeviceKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scenario key
|
||||
*
|
||||
* @return the key
|
||||
*/
|
||||
@Nullable
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the scenario is pending configuration
|
||||
*
|
||||
* @return true, if is configured
|
||||
*/
|
||||
public boolean isConfigured() {
|
||||
return configured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room key
|
||||
*
|
||||
* @return the room key
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomKey() {
|
||||
return roomKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated room name
|
||||
*
|
||||
* @return the room name
|
||||
*/
|
||||
@Nullable
|
||||
public String getRoomName() {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the devices
|
||||
*
|
||||
* @return the devices
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getDevices() {
|
||||
final String @Nullable [] localDevices = devices;
|
||||
return localDevices == null ? new String[0] : localDevices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoScenario [name=" + name + ", mainDeviceKey=" + mainDeviceKey + ", volumeDeviceKey="
|
||||
+ volumeDeviceKey + ", key=" + key + ", configured=" + configured + ", roomKey=" + roomKey
|
||||
+ ", roomName=" + roomName + ", devices=" + Arrays.toString(devices) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The model representing Neeo Scenarios (serialize/deserialize json use only).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoScenarios {
|
||||
|
||||
/** The scenarios. */
|
||||
private final NeeoScenario @Nullable [] scenarios;
|
||||
|
||||
/**
|
||||
* Instantiates a new neeo scenarios.
|
||||
*
|
||||
* @param scenarios the scenarios
|
||||
*/
|
||||
NeeoScenarios(NeeoScenario[] scenarios) {
|
||||
Objects.requireNonNull(scenarios, "scenarios cannot be null");
|
||||
this.scenarios = scenarios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scenarios.
|
||||
*
|
||||
* @return the scenarios
|
||||
*/
|
||||
public NeeoScenario[] getScenarios() {
|
||||
final NeeoScenario @Nullable [] localScenarios = scenarios;
|
||||
return localScenarios == null ? new NeeoScenario[0] : localScenarios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scenario.
|
||||
*
|
||||
* @param key the key
|
||||
* @return the scenario
|
||||
*/
|
||||
@Nullable
|
||||
public NeeoScenario getScenario(String key) {
|
||||
for (NeeoScenario scenario : getScenarios()) {
|
||||
if (StringUtils.equalsIgnoreCase(key, scenario.getKey())) {
|
||||
return scenario;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NeeoScenarios [scenarios=" + Arrays.toString(scenarios) + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.models;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* The implementation of {@link JsonDeserializer} to deserialize a {@link NeeoScenarios} class
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class NeeoScenariosDeserializer implements JsonDeserializer<@Nullable NeeoScenarios> {
|
||||
@Nullable
|
||||
@Override
|
||||
public NeeoScenarios deserialize(@Nullable JsonElement jsonElement, @Nullable Type jtype,
|
||||
@Nullable JsonDeserializationContext context) throws JsonParseException {
|
||||
Objects.requireNonNull(jsonElement, "jsonElement cannot be null");
|
||||
Objects.requireNonNull(context, "context cannot be null");
|
||||
if (jsonElement instanceof JsonObject) {
|
||||
final List<NeeoScenario> scenarios = new ArrayList<>();
|
||||
for (Map.Entry<String, JsonElement> entry : ((JsonObject) jsonElement).entrySet()) {
|
||||
final NeeoScenario scenario = context.deserialize(entry.getValue(), NeeoScenario.class);
|
||||
scenarios.add(scenario);
|
||||
}
|
||||
|
||||
return new NeeoScenarios(scenarios.toArray(new NeeoScenario[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.Invocation.Builder;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.glassfish.jersey.filter.LoggingFilter;
|
||||
import org.openhab.binding.neeo.internal.NeeoUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class represents an HTTP session with a client
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpRequest implements AutoCloseable {
|
||||
|
||||
/** the logger */
|
||||
private final Logger logger = LoggerFactory.getLogger(HttpRequest.class);
|
||||
|
||||
/** The client to use */
|
||||
private final Client client;
|
||||
|
||||
/**
|
||||
* Instantiates a new request
|
||||
*/
|
||||
public HttpRequest() {
|
||||
client = ClientBuilder.newClient();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
client.register(new LoggingFilter(new Slf4LoggingAdapter(logger), true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a get command to the specified uri
|
||||
*
|
||||
* @param uri the non-null uri
|
||||
* @return the {@link HttpResponse}
|
||||
*/
|
||||
public HttpResponse sendGetCommand(String uri) {
|
||||
NeeoUtil.requireNotEmpty(uri, "uri cannot be empty");
|
||||
try {
|
||||
final Builder request = client.target(uri).request();
|
||||
|
||||
final Response content = request.get();
|
||||
|
||||
try {
|
||||
return new HttpResponse(content);
|
||||
} finally {
|
||||
content.close();
|
||||
}
|
||||
} catch (IOException | IllegalStateException e) {
|
||||
return new HttpResponse(HttpStatus.SERVICE_UNAVAILABLE_503, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send post JSON command using the body
|
||||
*
|
||||
* @param uri the non empty uri
|
||||
* @param body the non-null, possibly empty body
|
||||
* @return the {@link HttpResponse}
|
||||
*/
|
||||
public HttpResponse sendPostJsonCommand(String uri, String body) {
|
||||
NeeoUtil.requireNotEmpty(uri, "uri cannot be empty");
|
||||
Objects.requireNonNull(body, "body cannot be null");
|
||||
|
||||
try {
|
||||
final Builder request = client.target(uri).request(MediaType.APPLICATION_JSON);
|
||||
|
||||
final Response content = request.post(Entity.entity(body, MediaType.APPLICATION_JSON));
|
||||
|
||||
try {
|
||||
return new HttpResponse(content);
|
||||
} finally {
|
||||
content.close();
|
||||
}
|
||||
// IllegalArgumentException/ProcessingException catches issues with the URI being invalid
|
||||
// as well
|
||||
} catch (IOException | IllegalStateException | IllegalArgumentException | ProcessingException e) {
|
||||
return new HttpResponse(HttpStatus.SERVICE_UNAVAILABLE_503, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This class represents an {@link HttpRequest} response
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpResponse {
|
||||
|
||||
/** The http status */
|
||||
private final int httpStatus;
|
||||
|
||||
/** The http reason */
|
||||
private final String httpReason;
|
||||
|
||||
/** The http headers */
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
|
||||
/** The contents as a raw byte array */
|
||||
private final byte @Nullable [] contents;
|
||||
|
||||
/**
|
||||
* Instantiates a new http response from the {@link Response}.
|
||||
*
|
||||
* @param response the non-null response
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
HttpResponse(Response response) throws IOException {
|
||||
Objects.requireNonNull(response, "response cannot be null");
|
||||
|
||||
httpStatus = response.getStatus();
|
||||
httpReason = response.getStatusInfo().getReasonPhrase();
|
||||
|
||||
if (response.hasEntity()) {
|
||||
InputStream is = response.readEntity(InputStream.class);
|
||||
contents = IOUtils.toByteArray(is);
|
||||
} else {
|
||||
contents = null;
|
||||
}
|
||||
|
||||
for (String key : response.getHeaders().keySet()) {
|
||||
headers.put(key, response.getHeaderString(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new http response.
|
||||
*
|
||||
* @param httpCode the http code
|
||||
* @param msg the msg
|
||||
*/
|
||||
HttpResponse(int httpCode, String msg) {
|
||||
httpStatus = httpCode;
|
||||
httpReason = msg;
|
||||
contents = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the HTTP status code.
|
||||
*
|
||||
* @return the HTTP status code
|
||||
*/
|
||||
public int getHttpCode() {
|
||||
return httpStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content.
|
||||
*
|
||||
* @return the content
|
||||
*/
|
||||
public String getContent() {
|
||||
final byte[] localContents = contents;
|
||||
if (localContents == null || localContents.length == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return new String(localContents, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link IOException} from the {@link #httpReason}
|
||||
*
|
||||
* @return the IO exception
|
||||
*/
|
||||
public IOException createException() {
|
||||
return new IOException(httpReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getHttpCode() + " (" + (contents == null ? ("http reason: " + httpReason) : getContent()) + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.neeo.internal.net;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Logging adapter to use for Slf4j
|
||||
*
|
||||
* @author Tim Roberts - Initial Contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class Slf4LoggingAdapter extends java.util.logging.Logger {
|
||||
|
||||
/** The logger. */
|
||||
private final Logger logger;
|
||||
|
||||
/**
|
||||
* Creates the logging adapter from the given logger
|
||||
*
|
||||
* @param logger a non-null logger to use
|
||||
*/
|
||||
protected Slf4LoggingAdapter(Logger logger) {
|
||||
super("jersey", null);
|
||||
Objects.requireNonNull(logger, "logger cannot be null");
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(@Nullable LogRecord record) {
|
||||
logger.debug("{}", record == null ? "" : record.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="neeo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Neeo Binding</name>
|
||||
<description>This binding allows you to discover NEEO Brain(s), discover the Rooms within and the Devices within those
|
||||
Rooms. The binding then will expose that information to openHAB and allow you to execute Recipes/Scenarios and Macros
|
||||
on the Devices</description>
|
||||
<author>Tim Roberts</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,184 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="neeo"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<!-- The brain bridge -->
|
||||
<bridge-type id="brain">
|
||||
<label>Neeo Brain</label>
|
||||
<description>The NEEO Brain</description>
|
||||
<channels>
|
||||
<channel id="forwardActions" typeId="brain-forwardactions"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>IP or Host Name</label>
|
||||
<description>The IP or host name of the NEEO Brain</description>
|
||||
</parameter>
|
||||
<parameter name="discoverEmptyRooms" type="boolean">
|
||||
<label>Discover Empty Rooms</label>
|
||||
<description>Whether to discover rooms with no devices or not</description>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="enableForwardActions" type="boolean">
|
||||
<label>Enable Forward Actions</label>
|
||||
<description>Whether to have the NEEO Brain forward actions to openHAB</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="forwardChain" type="text">
|
||||
<label>Forward Chaining</label>
|
||||
<description>Comma delimited list of URLs to forward NEEO brain actions to</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="checkStatusInterval" type="integer">
|
||||
<label>Check Status Interval</label>
|
||||
<description>The interval (in seconds) to check the status of the brain (specify <=0 to disable)</description>
|
||||
<default>10</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
<bridge-type id="room">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="brain"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Neeo Room</label>
|
||||
<description>Neeo Room</description>
|
||||
<channel-groups>
|
||||
<channel-group id="state" typeId="room-state"/>
|
||||
<channel-group id="scenario" typeId="room-scenario"/>
|
||||
<channel-group id="recipe" typeId="room-recipe"/>
|
||||
</channel-groups>
|
||||
<config-description>
|
||||
<parameter name="roomKey" type="text" required="true">
|
||||
<label>Room Key</label>
|
||||
<description>Unique key of the room</description>
|
||||
</parameter>
|
||||
<parameter name="excludeThings" type="boolean">
|
||||
<label>Exclude Things</label>
|
||||
<description>Exclude openHAB things (from NEEO transport)</description>
|
||||
<default>true</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="refreshPolling" type="integer">
|
||||
<label>Refresh Polling</label>
|
||||
<description>The time (in seconds) to refresh state (<= 0 to disable)</description>
|
||||
<default>120</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
<thing-type id="device">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="room"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Neeo Device</label>
|
||||
<description>Neeo Device</description>
|
||||
<channel-groups>
|
||||
<channel-group id="macros" typeId="device-macros"/>
|
||||
</channel-groups>
|
||||
<config-description>
|
||||
<parameter name="deviceKey" type="text" required="true">
|
||||
<label>Device Key</label>
|
||||
<description>Unique key of the device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
<!-- Channel types for the NEEO Brain -->
|
||||
<channel-type id="brain-forwardactions">
|
||||
<kind>trigger</kind>
|
||||
<label>Forward Actions</label>
|
||||
<description>The forward Actions</description>
|
||||
<event>
|
||||
</event>
|
||||
</channel-type>
|
||||
<!-- Channel Types for the NEEO Room -->
|
||||
<channel-group-type id="room-state">
|
||||
<label>Room State</label>
|
||||
<description>The room's state</description>
|
||||
<channels>
|
||||
<channel id="currentStep" typeId="room-state-currentstep"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-type id="room-state-currentstep">
|
||||
<kind>trigger</kind>
|
||||
<label>Current Step</label>
|
||||
<description>The current step executing</description>
|
||||
<event>
|
||||
</event>
|
||||
</channel-type>
|
||||
<channel-group-type id="room-recipe">
|
||||
<label>Recipes</label>
|
||||
<description>The room recipes</description>
|
||||
<channels>
|
||||
<channel id="name" typeId="room-recipe-name"/>
|
||||
<channel id="type" typeId="room-recipe-type"/>
|
||||
<channel id="enabled" typeId="room-recipe-enabled"/>
|
||||
<channel id="status" typeId="room-recipe-status"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-type id="room-recipe-name" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>The recipe name</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="room-recipe-type" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>The type of recipe</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="room-recipe-enabled" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>Whether the recipe is enabled or not.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="room-recipe-status">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>Send ON to execute the recipe</description>
|
||||
</channel-type>
|
||||
<channel-group-type id="room-scenario">
|
||||
<label>Scenarios</label>
|
||||
<description>The room scenarios</description>
|
||||
<channels>
|
||||
<channel id="name" typeId="room-scenario-name"/>
|
||||
<channel id="configured" typeId="room-scenario-configured"/>
|
||||
<channel id="status" typeId="room-scenario-status"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-type id="room-scenario-name" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>The scenario name</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="room-scenario-configured" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>Whether the scenario is configured or not</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="room-scenario-status">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>Whether the scenario is running or not (send ON to turn on the scenario, OFF to turn off the scenario)</description>
|
||||
</channel-type>
|
||||
<channel-group-type id="device-macros">
|
||||
<label>Macros</label>
|
||||
<description>Executable macros</description>
|
||||
<channels>
|
||||
<channel id="status" typeId="device-macros-status"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-type id="device-macros-status">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Label Dynamically Generated</label>
|
||||
<description>Send ON to trigger the macro</description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user