/**
* 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.denon.internal;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import org.openhab.binding.denon.DenonBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;
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;
/**
* Binding that communicates with one or multiple Denon receivers.
*
* @author Jeroen Idserda
* @since 1.7.0
*/
public class DenonBinding extends AbstractActiveBinding<DenonBindingProvider> implements ManagedService {
private static final String CONFIG_REFRESH = "refresh";
private static final String CONFIG_HOST = "host";
private static final String CONFIG_UPDATE_TYPE = "update";
private static final String CONFIG_UPDATE_TYPE_HTTP = "http";
private static final String CONFIG_UPDATE_TYPE_TELNET = "telnet";
private static final String CONFIG_SERVICE_PID = "service.pid";
private static final String SWITCH_INPUT = "SI";
private static final Logger logger = LoggerFactory.getLogger(DenonBinding.class);
private Map<String, DenonConnectionProperties> connections = new HashMap<String, DenonConnectionProperties>();
private int refreshInterval = 5000;
private ItemRegistry itemRegistry;
@Override
public void deactivate() {
for (Entry<String, DenonConnectionProperties> entry : connections.entrySet()) {
entry.getValue().getConnector().disconnect();
}
}
@Override
protected void execute() {
for (Entry<String, DenonConnectionProperties> entry : connections.entrySet()) {
DenonConnectionProperties connection = entry.getValue();
if (connection.isHttp()) {
entry.getValue().getConnector().updateState();
}
}
}
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
@Override
protected String getName() {
return "Denon Refresh Service";
}
public void setItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = itemRegistry;
}
public void unsetItemRegistry(ItemRegistry itemRegistry) {
this.itemRegistry = null;
}
/**
* @{inheritDoc}
*/
@Override
public void bindingChanged(BindingProvider provider, String itemName) {
logger.debug("Denon binding changed for item {}", itemName);
if (provider instanceof DenonBindingProvider) {
DenonBindingConfig config = ((DenonBindingProvider) provider).getConfig(itemName);
if (config != null) {
getConnector(config).updateStateFromCache(config.getProperty());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void allBindingsChanged(BindingProvider provider) {
logger.debug("Denon all bindings changed");
updateInitialState();
}
/**
* {@inheritDoc}
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
DenonBindingConfig config = getConfig(itemName);
getConnector(config).sendCommand(config, command);
}
protected void addBindingProvider(DenonBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(DenonBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* {@inheritDoc}
*/
@Override
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
logger.debug("Denon binding updated");
if (config == null) {
return;
}
Enumeration<String> keys = config.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (CONFIG_SERVICE_PID.equals(key)) {
continue;
}
String[] parts = key.split("\\.");
String value = Objects.toString(config.get(key), null);
if (parts.length == 1) {
String option = parts[0];
if (CONFIG_REFRESH.equals(option)) {
refreshInterval = Integer.valueOf(value);
}
} else {
String instance = parts[0];
DenonConnectionProperties connection = connections.get(instance);
if (connection == null) {
connection = new DenonConnectionProperties();
connection.setInstance(instance);
connections.put(instance, connection);
}
String option = parts[1].trim();
if (CONFIG_HOST.equals(option)) {
connection.setHost(value);
} else if (CONFIG_UPDATE_TYPE.equals(option)) {
connection.setTelnet(value.equals(CONFIG_UPDATE_TYPE_TELNET));
connection.setHttp(value.equals(CONFIG_UPDATE_TYPE_HTTP));
if (!value.equals(CONFIG_UPDATE_TYPE_TELNET) && !value.equals(CONFIG_UPDATE_TYPE_HTTP)) {
logger.warn("Invalid connection type {} for instance {}, using default", value, instance);
}
}
}
}
boolean isActiveBinding = false;
for (Entry<String, DenonConnectionProperties> entry : connections.entrySet()) {
DenonConnectionProperties connection = entry.getValue();
logger.debug("Denon receiver configured at {}", connection.getHost());
DenonConnector connector = new DenonConnector(connection, new DenonPropertyUpdatedCallback() {
@Override
public void updated(String instance, String property, State state) {
processPropertyUpdated(instance, property, state);
}
});
connection.setConnector(connector);
connector.connect();
if (connection.isHttp()) {
isActiveBinding = true;
}
}
setProperlyConfigured(isActiveBinding);
}
private void updateInitialState() {
for (Entry<String, DenonConnectionProperties> entry : connections.entrySet()) {
entry.getValue().getConnector().getInitialState();
}
}
private void processPropertyUpdated(String instance, String property, State state) {
updateIfChanged(instance, property, state);
if (property.startsWith(SWITCH_INPUT)) {
updateInputProperties(instance, property);
}
}
/**
* Update all the different input properties (=properties that start with SI).
* This way, only the currently selected input has state 'ON'.
*/
private void updateInputProperties(String instance, String property) {
for (DenonBindingProvider provider : providers) {
for (String itemName : provider.getItemNames()) {
DenonBindingConfig cfg = provider.getConfig(itemName);
if (cfg.getInstance().equals(instance)) {
if (cfg.getProperty().startsWith(SWITCH_INPUT) && !cfg.getProperty().equals(property)) {
updateIfChanged(cfg.getInstance(), cfg.getProperty(), OnOffType.OFF);
}
}
}
}
}
/**
* Only update the property if newState is different than its current state.
*/
private void updateIfChanged(String instance, String property, State newState) {
DenonBindingProvider firstProvider = getFirstMatchingProvider(instance, property);
if (firstProvider != null) {
DenonBindingConfig config = firstProvider.getConfig(instance, property);
try {
State oldState = itemRegistry.getItem(config.getItemName()).getState();
if (!oldState.equals(newState)) {
eventPublisher.postUpdate(config.getItemName(), newState);
}
} catch (ItemNotFoundException e) {
logger.debug("Cannot find item " + config.getItemName() + " in the registry", e);
}
}
}
private DenonBindingProvider getFirstMatchingProvider(String instance, String property) {
for (DenonBindingProvider provider : providers) {
DenonBindingConfig config = provider.getConfig(instance, property);
if (config != null) {
return provider;
}
}
return null;
}
private DenonBindingConfig getConfig(String itemName) {
for (DenonBindingProvider provider : providers) {
DenonBindingConfig config = provider.getConfig(itemName);
if (config != null) {
return config;
}
}
return null;
}
private DenonConnector getConnector(DenonBindingConfig config) {
return connections.get(config.getInstance()).getConnector();
}
}