/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.comfoair.internal;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.comfoair.ComfoAirBindingProvider;
import org.openhab.binding.comfoair.datatypes.ComfoAirDataType;
import org.openhab.binding.comfoair.handling.ComfoAirCommand;
import org.openhab.binding.comfoair.handling.ComfoAirCommandType;
import org.openhab.binding.comfoair.handling.ComfoAirConnector;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An active binding which requests a given URL frequently.
*
* @author Holger Hees
* @since 1.3.0
*/
public class ComfoAirBinding extends AbstractActiveBinding<ComfoAirBindingProvider> implements ManagedService {
static final Logger logger = LoggerFactory.getLogger(ComfoAirBinding.class);
/**
* The interval to find new refresh candidates (defaults to 60000
* milliseconds).
*/
private long refreshInterval = 60000L;
private String port;
private ComfoAirConnector connector;
private ScheduledExecutorService scheduler;
/**
* @{inheritDoc
*/
@Override
public void activate() {
scheduler = Executors.newScheduledThreadPool(10);
}
/**
* @{inheritDoc
*/
@Override
public void deactivate() {
for (ComfoAirBindingProvider provider : providers) {
provider.removeBindingChangeListener(this);
}
if (scheduler != null) {
scheduler.shutdown();
try {
scheduler.awaitTermination(5000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.warn("Unable to shutdown scheduler!");
}
}
providers.clear();
if (connector != null) {
connector.close();
}
}
/**
* @{inheritDoc
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* @{inheritDoc
*/
@Override
protected String getName() {
return "ComfoAir Refresh Service";
}
/**
* @{inheritDoc
*/
@Override
public void internalReceiveCommand(String itemName, Command command) {
Set<String> usedKeys = new HashSet<String>();
for (ComfoAirBindingProvider provider : providers) {
usedKeys.addAll(provider.getConfiguredKeys());
}
for (ComfoAirBindingProvider provider : providers) {
String eventType = provider.getConfiguredKeyForItem(itemName);
ComfoAirCommand changeCommand = ComfoAirCommandType.getChangeCommand(eventType, (DecimalType) command);
sendCommand(changeCommand);
Collection<ComfoAirCommand> affectedReadCommands = ComfoAirCommandType.getAffectedReadCommands(eventType,
usedKeys);
if (affectedReadCommands.size() > 0) {
// refresh 3 seconds later all affected items
Runnable updateThread = new AffectedItemsUpdateThread(affectedReadCommands);
scheduler.schedule(updateThread, 3, TimeUnit.SECONDS);
}
}
}
/**
* @{inheritDoc
*/
@Override
public void execute() {
for (ComfoAirBindingProvider provider : providers) {
Collection<ComfoAirCommand> commands = ComfoAirCommandType
.getReadCommandsByEventTypes(provider.getConfiguredKeys());
for (ComfoAirCommand command : commands) {
sendCommand(command);
}
}
}
/**
* send a command and send additional command which are affected by the
* first command
*
* @param command
*/
private void sendCommand(ComfoAirCommand command) {
int[] response = connector.sendCommand(command);
if (response == null) {
return;
}
List<ComfoAirCommandType> commandTypes = ComfoAirCommandType.getCommandTypesByReplyCmd(command.getReplyCmd());
for (ComfoAirCommandType commandType : commandTypes) {
ComfoAirDataType dataType = commandType.getDataType();
State value = dataType.convertToState(response, commandType);
if (value == null) {
logger.error("Unexpected value for DATA: " + ComfoAirConnector.dumpData(response));
} else {
for (ComfoAirBindingProvider provider : providers) {
List<String> items = provider.getItemNamesForCommandKey(commandType.getKey());
for (String item : items) {
eventPublisher.postUpdate(item, value);
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("rawtypes")
public void updated(Dictionary config) throws ConfigurationException {
if (config != null) {
String newPort = (String) config.get("port"); //$NON-NLS-1$
if (StringUtils.isNotBlank(newPort) && !newPort.equals(port)) {
// only do something if the newPort has changed
port = newPort;
String refreshIntervalString = (String) config.get("refresh");
if (StringUtils.isNotBlank(refreshIntervalString)) {
refreshInterval = Long.parseLong(refreshIntervalString);
}
if (connector != null) {
connector.close();
}
connector = new ComfoAirConnector();
try {
connector.open(port);
} catch (InitializationException e) {
logger.error(e.getMessage());
}
setProperlyConfigured(true);
}
}
}
protected void addBindingProvider(ComfoAirBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(ComfoAirBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
private class AffectedItemsUpdateThread extends Thread {
private Collection<ComfoAirCommand> affectedReadCommands;
public AffectedItemsUpdateThread(Collection<ComfoAirCommand> affectedReadCommands) {
this.affectedReadCommands = affectedReadCommands;
}
@Override
public void run() {
for (ComfoAirCommand readCommand : this.affectedReadCommands) {
sendCommand(readCommand);
}
}
}
}