/**
* 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.fritzaha.internal;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.fritzaha.FritzahaBindingProvider;
import org.openhab.binding.fritzaha.internal.hardware.FritzahaWebInterface;
import org.openhab.binding.fritzaha.internal.hardware.interfaces.FritzahaDevice;
import org.openhab.binding.fritzaha.internal.hardware.interfaces.FritzahaOutletMeter;
import org.openhab.binding.fritzaha.internal.hardware.interfaces.FritzahaSwitchedOutlet;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.library.types.OnOffType;
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;
/**
* This class facilitates the communication between AVM home automation devices
* and the openHAB event bus
*
* @author Christian Brauers
* @since 1.3.0
*/
public class FritzahaBinding extends AbstractActiveBinding<FritzahaBindingProvider>implements ManagedService {
private static final Logger logger = LoggerFactory.getLogger(FritzahaBinding.class);
/**
* the refresh interval which is used to poll values from the fritzaha
* server (optional, defaults to 10000ms)
*/
private long refreshInterval = 10000;
private static final Pattern DEVICES_PATTERN = Pattern
.compile("^(.*?)\\.(host|port|protocol|username|password|synctimeout|asynctimeout)$");
protected Map<String, Host> hostCache = new HashMap<String, Host>();
@Override
public void setEventPublisher(EventPublisher eventPublisher) {
super.setEventPublisher(eventPublisher);
for (Host currentHostData : hostCache.values()) {
currentHostData.eventPublisher = eventPublisher;
FritzahaWebInterface currentHost = currentHostData.getConnection();
currentHost.setEventPublisher(eventPublisher);
}
}
@Override
public void unsetEventPublisher(EventPublisher eventPublisher) {
super.unsetEventPublisher(eventPublisher);
for (Host currentHostData : hostCache.values()) {
currentHostData.eventPublisher = null;
FritzahaWebInterface currentHost = currentHostData.getConnection();
currentHost.unsetEventPublisher(eventPublisher);
}
}
public FritzahaBinding() {
}
@Override
public void activate() {
}
@Override
public void deactivate() {
}
/**
* @{inheritDoc
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* @{inheritDoc
*/
@Override
protected String getName() {
return "FritzAHA Refresh Service";
}
/**
* @{inheritDoc
*/
@Override
protected void execute() {
logger.debug("execute() method is called!");
for (FritzahaBindingProvider currentProvider : providers) {
for (String currentItem : currentProvider.getItemNames()) {
FritzahaDevice currentDevice = currentProvider.getDeviceConfig(currentItem);
String currentHostId = currentDevice.getHost();
if (!hostCache.containsKey(currentHostId)) {
continue;
}
FritzahaWebInterface currentHost = hostCache.get(currentHostId).getConnection();
if (currentDevice instanceof FritzahaSwitchedOutlet) {
FritzahaSwitchedOutlet currentSwitch = (FritzahaSwitchedOutlet) currentDevice;
currentSwitch.updateSwitchState(currentItem, currentHost);
} else if (currentDevice instanceof FritzahaOutletMeter) {
FritzahaOutletMeter currentMeter = (FritzahaOutletMeter) currentDevice;
currentMeter.updateMeterValue(currentItem, currentHost);
}
}
}
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
logger.debug("internalReceiveCommand() is called!");
FritzahaBindingProvider commandProvider = null;
FritzahaSwitchedOutlet switchDevice = null;
for (FritzahaBindingProvider currentProvider : providers) {
if (!currentProvider.getItemNames().contains(itemName)) {
continue;
}
FritzahaDevice device = currentProvider.getDeviceConfig(itemName);
if (!(device instanceof FritzahaSwitchedOutlet)) {
continue;
}
switchDevice = (FritzahaSwitchedOutlet) device;
commandProvider = currentProvider;
break;
}
if (commandProvider == null || switchDevice == null) {
logger.error("No provider found for item " + itemName);
return;
}
if (command instanceof OnOffType) {
String deviceHostID = switchDevice.getHost();
Host host = hostCache.get(deviceHostID);
if (host != null) {
FritzahaWebInterface deviceHost = host.getConnection();
boolean valueToSet = (command == OnOffType.ON);
switchDevice.setSwitchState(valueToSet, itemName, deviceHost);
}
} else {
logger.debug("Unsupported command type for item " + itemName);
}
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveUpdate(String itemName, State newState) {
// No action required on received updates
}
/**
* @{inheritDoc
*/
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
if (config != null) {
// Based on SamsungTv parsing mechanism
Enumeration<String> keys = config.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
// the config-key enumeration contains additional keys that we
// don't want to process here ...
if ("service.pid".equals(key)) {
continue;
}
// to override the default refresh interval one has to add a
// parameter to openhab.cfg like
// <bindingName>:refresh=<intervalInMs>
if ("refresh".equals(key)) {
refreshInterval = Long.parseLong((String) config.get("refresh"));
continue;
}
Matcher matcher = DEVICES_PATTERN.matcher(key);
if (!matcher.matches()) {
logger.debug("given config key '" + key
+ "' does not follow the expected pattern '<id>.<host|port|protocol|username|password>'");
continue;
}
matcher.reset();
matcher.find();
String hostId = matcher.group(1);
Host host = hostCache.get(hostId);
if (host == null) {
host = new Host(hostId);
host.eventPublisher = eventPublisher;
hostCache.put(hostId, host);
logger.debug("Created new FritzAHA host " + hostId);
}
String configKey = matcher.group(2);
String value = (String) config.get(key);
if ("host".equals(configKey)) {
host.host = value;
} else if ("port".equals(configKey)) {
host.port = Integer.valueOf(value);
} else if ("protocol".equals(configKey)) {
host.protocol = value;
} else if ("username".equals(configKey)) {
host.username = value;
} else if ("password".equals(configKey)) {
host.password = value;
} else if ("synctimeout".equals(configKey)) {
host.synctimeout = Integer.parseInt(value);
} else if ("asynctimeout".equals(configKey)) {
host.asynctimeout = Integer.parseInt(value);
} else {
throw new ConfigurationException(configKey, "the given configKey '" + configKey + "' is unknown");
}
}
setProperlyConfigured(true);
logger.debug("FritzAHA Binding configured");
}
}
/**
* Internal data structure which carries the connection details of one
* device (there could be several)
*/
static class Host {
String host = "fritz.box";
int port = -1;
String protocol = "http";
String username = "";
String password = "";
int synctimeout = 2000;
int asynctimeout = 4000;
FritzahaWebInterface connection;
String hostId;
EventPublisher eventPublisher;
public Host(String hostId) {
this.hostId = hostId;
}
@Override
public String toString() {
return "Host [id=" + hostId + ", host=" + host + ", port=" + port + ", username=" + username + "]";
}
/**
* Creates connection if necessary, uses previously created connection
* if not.
*
* @return Connection
*/
FritzahaWebInterface getConnection() {
if (connection == null) {
logger.debug(
"New connection to " + host + " with timeouts (" + synctimeout + "|" + asynctimeout + ").");
connection = new FritzahaWebInterface(host, port, protocol, username, password, synctimeout,
asynctimeout);
connection.setEventPublisher(eventPublisher);
}
return connection;
}
}
}