import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
- logger.debug("handleCommand channel: {} command: {}", channelUID.getId(), command);
- if (AutelisBindingConstants.CMD_LIGHTS.equals(channelUID.getId())) {
- /*
- * lighting command possible values, but we will let anything
- * through. alloff, allon, csync, cset, cswim, party, romance,
- * caribbean, american, sunset, royalty, blue, green, red, white,
- * magenta, hold, recall
- */
- getUrl(baseURL + "/lights.cgi?val=" + command.toString(), TIMEOUT_SECONDS);
- } else if (AutelisBindingConstants.CMD_REBOOT.equals(channelUID.getId()) && command == OnOffType.ON) {
- getUrl(baseURL + "/userreboot.cgi?do=true" + command.toString(), TIMEOUT_SECONDS);
- updateState(channelUID, OnOffType.OFF);
- } else {
- String[] args = channelUID.getId().split("-");
- if (args.length < 2) {
- logger.warn("Unown channel {} for command {}", channelUID, command);
- return;
- }
- String type = args[0];
- String name = args[1];
-
- if (AutelisBindingConstants.CMD_EQUIPMENT.equals(type)) {
- String cmd = "value";
- int value;
- if (command == OnOffType.OFF) {
- value = 0;
- } else if (command == OnOffType.ON) {
- value = 1;
- } else if (command instanceof DecimalType) {
- value = ((DecimalType) command).intValue();
- if (!isJandy() && value >= 3) {
- // this is a autelis dim type. not sure what 2 does
- cmd = "dim";
- }
- } else {
- logger.error("command type {} is not supported", command);
+ try {
+ logger.debug("handleCommand channel: {} command: {}", channelUID.getId(), command);
+ if (AutelisBindingConstants.CMD_LIGHTS.equals(channelUID.getId())) {
+ /*
+ * lighting command possible values, but we will let anything
+ * through. alloff, allon, csync, cset, cswim, party, romance,
+ * caribbean, american, sunset, royalty, blue, green, red, white,
+ * magenta, hold, recall
+ */
+ getUrl(baseURL + "/lights.cgi?val=" + command.toString(), TIMEOUT_SECONDS);
+ } else if (AutelisBindingConstants.CMD_REBOOT.equals(channelUID.getId()) && command == OnOffType.ON) {
+ getUrl(baseURL + "/userreboot.cgi?do=true" + command.toString(), TIMEOUT_SECONDS);
+ updateState(channelUID, OnOffType.OFF);
+ } else {
+ String[] args = channelUID.getId().split("-");
+ if (args.length < 2) {
+ logger.warn("Unown channel {} for command {}", channelUID, command);
return;
}
- String response = getUrl(baseURL + "/set.cgi?name=" + name + "&" + cmd + "=" + value, TIMEOUT_SECONDS);
- logger.debug("equipment set {} {} {} : result {}", name, cmd, value, response);
- } else if (AutelisBindingConstants.CMD_TEMP.equals(type)) {
- String value;
- if (command == IncreaseDecreaseType.INCREASE) {
- value = "up";
- } else if (command == IncreaseDecreaseType.DECREASE) {
- value = "down";
- } else if (command == OnOffType.OFF) {
- value = "0";
- } else if (command == OnOffType.ON) {
- value = "1";
- } else {
- value = command.toString();
- }
+ String type = args[0];
+ String name = args[1];
+
+ if (AutelisBindingConstants.CMD_EQUIPMENT.equals(type)) {
+ String cmd = "value";
+ int value;
+ if (command == OnOffType.OFF) {
+ value = 0;
+ } else if (command == OnOffType.ON) {
+ value = 1;
+ } else if (command instanceof DecimalType) {
+ value = ((DecimalType) command).intValue();
+ if (!isJandy() && value >= 3) {
+ // this is a autelis dim type. not sure what 2 does
+ cmd = "dim";
+ }
+ } else {
+ logger.error("command type {} is not supported", command);
+ return;
+ }
+ String response = getUrl(baseURL + "/set.cgi?name=" + name + "&" + cmd + "=" + value,
+ TIMEOUT_SECONDS);
+ logger.debug("equipment set {} {} {} : result {}", name, cmd, value, response);
+ } else if (AutelisBindingConstants.CMD_TEMP.equals(type)) {
+ String value;
+ if (command == IncreaseDecreaseType.INCREASE) {
+ value = "up";
+ } else if (command == IncreaseDecreaseType.DECREASE) {
+ value = "down";
+ } else if (command == OnOffType.OFF) {
+ value = "0";
+ } else if (command == OnOffType.ON) {
+ value = "1";
+ } else {
+ value = command.toString();
+ }
- String cmd;
- // name ending in sp are setpoints, ht are heater?
- if (name.endsWith("sp")) {
- cmd = "temp";
- } else if (name.endsWith("ht")) {
- cmd = "hval";
+ String cmd;
+ // name ending in sp are setpoints, ht are heater?
+ if (name.endsWith("sp")) {
+ cmd = "temp";
+ } else if (name.endsWith("ht")) {
+ cmd = "hval";
+ } else {
+ logger.error("Unknown temp type {}", name);
+ return;
+ }
+ String response = getUrl(baseURL + "/set.cgi?wait=1&name=" + name + "&" + cmd + "=" + value,
+ TIMEOUT_SECONDS);
+ logger.debug("temp set name:{} cmd:{} value:{} : result {}", name, cmd, value, response);
+ } else if (AutelisBindingConstants.CMD_CHEM.equals(type)) {
+ String response = getUrl(baseURL + "/set.cgi?name=" + name + "&chem=" + command.toString(),
+ TIMEOUT_SECONDS);
+ logger.debug("chlrp {} {}: result {}", name, command, response);
+ } else if (AutelisBindingConstants.CMD_PUMPS.equals(type)) {
+ String response = getUrl(baseURL + "/set.cgi?name=" + name + "&speed=" + command.toString(),
+ TIMEOUT_SECONDS);
+ logger.debug("pumps {} {}: result {}", name, command, response);
} else {
- logger.error("Unknown temp type {}", name);
- return;
+ logger.error("Unsupported type {}", type);
}
- String response = getUrl(baseURL + "/set.cgi?wait=1&name=" + name + "&" + cmd + "=" + value,
- TIMEOUT_SECONDS);
- logger.debug("temp set name:{} cmd:{} value:{} : result {}", name, cmd, value, response);
- } else if (AutelisBindingConstants.CMD_CHEM.equals(type)) {
- String response = getUrl(baseURL + "/set.cgi?name=" + name + "&chem=" + command.toString(),
- TIMEOUT_SECONDS);
- logger.debug("chlrp {} {}: result {}", name, command, response);
- } else if (AutelisBindingConstants.CMD_PUMPS.equals(type)) {
- String response = getUrl(baseURL + "/set.cgi?name=" + name + "&speed=" + command.toString(),
- TIMEOUT_SECONDS);
- logger.debug("pumps {} {}: result {}", name, command, response);
- } else {
- logger.error("Unsupported type {}", type);
}
+ clearState(true);
+ // reset the schedule for our next poll which at that time will reflect if our command was successful or
+ // not.
+ initPolling(COMMAND_UPDATE_TIME_SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
- clearState(true);
- // reset the schedule for our next poll which at that time will reflect if our command was successful or not.
- initPolling(COMMAND_UPDATE_TIME_SECONDS);
}
/**
* Poll the Autelis controller for updates. This will retrieve various xml documents and update channel states from
* its contents.
*/
- private void pollAutelisController() {
+ private void pollAutelisController() throws InterruptedException {
logger.trace("Connecting to {}", baseURL);
// clear our cached stated IF it is time.
* @param timeout
* @return
*/
- private synchronized String getUrl(String url, int timeout) {
+ private synchronized String getUrl(String url, int timeout) throws InterruptedException {
// throttle commands for a very short time to avoid 'loosing' them
long now = System.currentTimeMillis();
long nextReq = lastRequestTime + THROTTLE_TIME_MILLISECONDS;
if (nextReq > now) {
- try {
- logger.trace("Throttling request for {} mills", nextReq - now);
- Thread.sleep(nextReq - now);
- } catch (InterruptedException ignored) {
- }
+ logger.trace("Throttling request for {} mills", nextReq - now);
+ Thread.sleep(nextReq - now);
}
String getURL = url + (url.contains("?") ? "&" : "?") + "timestamp=" + System.currentTimeMillis();
logger.trace("Getting URL {} ", getURL);
}
lastRequestTime = System.currentTimeMillis();
return response.getContentAsString();
- } catch (Exception e) {
+ } catch (ExecutionException | TimeoutException e) {
logger.debug("Could not make http connection", e);
}
return null;
class DeviceService<TState extends BoschSHCServiceState> {
/**
* Constructor.
- *
+ *
* @param service Service which belongs to the device.
* @param affectedChannels Channels which are affected by the state of this service.
*/
/**
* Returns the unique id of the Bosch device.
- *
+ *
* @return Unique id of the Bosch device.
*/
public @Nullable String getBoschID() {
/**
* Handles the refresh command of all registered services. Override it to handle custom commands (e.g. to update
* states of services).
- *
+ *
* @param channelUID {@link ChannelUID} of the channel to which the command was sent
* @param command {@link Command}
*/
if (deviceService.affectedChannels.contains(channelUID.getIdWithoutGroup())) {
try {
deviceService.service.refreshState();
- } catch (InterruptedException | TimeoutException | ExecutionException | BoschSHCException e) {
+ } catch (TimeoutException | ExecutionException | BoschSHCException e) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
String.format("Error when trying to refresh state from service %s: %s",
deviceService.service.getServiceName(), e.getMessage()));
+ } catch (InterruptedException e) {
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
+ String.format("Interrupted refresh state from service %s: %s",
+ deviceService.service.getServiceName(), e.getMessage()));
+ Thread.currentThread().interrupt();
}
}
}
/**
* Processes an update which is received from the bridge.
- *
+ *
* @param serviceName Name of service the update came from.
* @param stateData Current state of device service. Serialized as JSON.
*/
/**
* Returns the bridge handler for this thing handler.
- *
+ *
* @return Bridge handler for this thing handler. Null if no or an invalid bridge was set in the configuration.
* @throws BoschSHCException If bridge for handler is not set or an invalid bridge is set.
*/
/**
* Query the Bosch Smart Home Controller for the state of the service with the specified name.
- *
+ *
* @note Use services instead of directly requesting a state.
*
* @param stateName Name of the service to query
try {
BoschSHCBridgeHandler bridgeHandler = this.getBridgeHandler();
return bridgeHandler.getState(deviceId, stateName, classOfT);
- } catch (InterruptedException | TimeoutException | ExecutionException | BoschSHCException e) {
+ } catch (TimeoutException | ExecutionException | BoschSHCException e) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
String.format("Error when trying to refresh state from service %s: %s", stateName, e.getMessage()));
return null;
+ } catch (InterruptedException e) {
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
+ String.format("Interrupted refresh state from service %s: %s", stateName, e.getMessage()));
+ Thread.currentThread().interrupt();
+ return null;
}
}
/**
* Creates and registers a new service for this device.
- *
+ *
* @param <TService> Type of service.
* @param <TState> Type of service state.
* @param newService Supplier function to create a new instance of the service.
/**
* Registers a service for this device.
- *
+ *
* @param <TService> Type of service.
* @param <TState> Type of service state.
* @param service Service to register.
/**
* Updates the state of a device service.
* Sets the status of the device to offline if setting the state fails.
- *
+ *
* @param <TService> Type of service.
* @param <TState> Type of service state.
* @param service Service to set state for.
TService service, TState state) {
try {
service.setState(state);
- } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ } catch (TimeoutException | ExecutionException e) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, String.format(
"Error when trying to update state for service %s: %s", service.getServiceName(), e.getMessage()));
+ } catch (InterruptedException e) {
+ this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, String
+ .format("Interrupted update state for service %s: %s", service.getServiceName(), e.getMessage()));
+ Thread.currentThread().interrupt();
}
}
/**
* Registers a service of this device.
- *
+ *
* @param service Service which belongs to this device
* @param affectedChannels Channels which are affected by the state of this
* service
*/
package org.openhab.binding.boschshc.internal.devices.bridge;
-import static org.eclipse.jetty.http.HttpMethod.GET;
-import static org.eclipse.jetty.http.HttpMethod.PUT;
+import static org.eclipse.jetty.http.HttpMethod.*;
import java.lang.reflect.Type;
import java.util.ArrayList;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
-import org.openhab.binding.boschshc.internal.devices.bridge.dto.*;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceStatusUpdate;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
+import org.openhab.binding.boschshc.internal.devices.bridge.dto.Room;
import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
import org.openhab.binding.boschshc.internal.exceptions.LongPollingFailedException;
import org.openhab.binding.boschshc.internal.exceptions.PairingFailedException;
} catch (InterruptedException e) {
this.updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.UNKNOWN.NONE, "@text/offline.interrupted");
+ Thread.currentThread().interrupt();
}
}
/**
* Get a list of connected devices from the Smart-Home Controller
- *
+ *
* @throws InterruptedException in case bridge is stopped
*/
private boolean getDevices() throws InterruptedException {
/**
* Get a list of rooms from the Smart-Home controller
- *
+ *
* @throws InterruptedException in case bridge is stopped
*/
private boolean getRooms() throws InterruptedException {
/**
* Sends a state change for a device to the controller
- *
+ *
* @param deviceId Id of device to change state for
* @param serviceName Name of service of device to change state for
* @param state New state data to set for service
- *
+ *
* @return Response of request
* @throws InterruptedException
* @throws ExecutionException