/** * 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.daikin.internal; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import org.openhab.binding.daikin.DaikinBindingProvider; import org.openhab.core.binding.AbstractActiveBinding; import org.openhab.core.library.types.DecimalType; 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; import net.jonathangiles.daikin.DaikinFactory; import net.jonathangiles.daikin.IDaikin; import net.jonathangiles.daikin.enums.Fan; import net.jonathangiles.daikin.enums.FanDirection; import net.jonathangiles.daikin.enums.Mode; import net.jonathangiles.daikin.enums.Timer; /** * An active binding which requests the state of a Daikin heat pump via the * KKRP01A online controller and can sends commands as well * * - Commands supported: * - Power * - Mode Auto, Cool, Dry, Heat, OnlyFun, Night * - Temp between 10C - 32C * - Fan Fun1, Fun2, Fun3, Fun4, Fun5 (speeds), FAuto (auto) * - Swing Ud (up/down), Off * * @author Ben Jones * @author Jos schering * @since 1.5.0 */ public class DaikinBinding extends AbstractActiveBinding<DaikinBindingProvider>implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(DaikinBinding.class); private static final String CONFIG_KEY_REFRESH = "refresh"; private Long refreshInterval = 60000L; private Map<String, IDaikin> hosts = new HashMap<String, IDaikin>(); /** * @{inheritDoc} */ @Override protected long getRefreshInterval() { return refreshInterval; } @Override protected String getName() { return "Daikin Refresh Service"; } @Override public void activate() { super.activate(); } /** * @{inheritDoc} */ @Override public void internalReceiveCommand(String itemName, Command command) { // update the internal state for the associated host and send the // new state (all values) to the controller to update for (DaikinBindingProvider provider : providers) { if (!provider.providesBindingFor(itemName)) { continue; } DaikinBindingConfig bindingConfig = provider.getBindingConfig(itemName); if (!hosts.containsKey(bindingConfig.getId())) { continue; } IDaikin host = hosts.get(bindingConfig.getId()); DaikinCommandType commandType = bindingConfig.getCommandType(); if (!commandType.isExecutable()) { logger.warn("Attempting to send a command to '{}' which is not executable ({}). Ignoring.", itemName, commandType); continue; } setState(host, commandType, command); } } /** * @{inheritDoc} */ @Override public void execute() { // refresh the state for each host and then check for any item // bindings that are associated with this host, and update for (IDaikin host : hosts.values()) { host.readDaikinState(); for (DaikinBindingProvider provider : providers) { for (String itemName : provider.getItemNames()) { DaikinBindingConfig bindingConfig = provider.getBindingConfig(itemName); if (!bindingConfig.getId().equals(host.getId())) { continue; } DaikinCommandType commandType = bindingConfig.getCommandType(); eventPublisher.postUpdate(itemName, getState(host, commandType)); } } } } private boolean isInteger(String s) { try { Integer.parseInt(s); } catch (NumberFormatException e) { return false; } catch (NullPointerException e) { return false; } // only got here if we didn't return false return true; } private Mode getModeEnum(String command) { if (isInteger(command)) { int modeValue = Integer.parseInt(command); for (Mode mode : Mode.values()) { if (mode.ordinal() == modeValue) { return mode; } } } throw new IllegalArgumentException("Invalid or unsupported Daikin mode: " + command); } private Fan getFanEnum(String command) { if (isInteger(command)) { int fanValue = Integer.parseInt(command); for (Fan fan : Fan.values()) { if (fan.ordinal() == fanValue) { return fan; } } } throw new IllegalArgumentException("Invalid or unsupported Daikin Fan: " + command); } private FanDirection getFanDirectionEnum(String command) { if (isInteger(command)) { int fanDirectionValue = Integer.parseInt(command); for (FanDirection fanDirection : FanDirection.values()) { if (fanDirection.ordinal() == fanDirectionValue) { return fanDirection; } } } throw new IllegalArgumentException("Invalid or unsupported Daikin FanDirection: " + command); } private Timer getTimerEnum(String command) { if (isInteger(command)) { int timerValue = Integer.parseInt(command); for (Timer timer : Timer.values()) { if (timer.ordinal() == timerValue) { return timer; } } } throw new IllegalArgumentException("Invalid or unsupported Daikin FanDirection: " + command); } private State getState(IDaikin host, DaikinCommandType commandType) { switch (commandType) { case POWER: return host.isOn() ? OnOffType.ON : OnOffType.OFF; case MODE: return new DecimalType(host.getMode().ordinal()); case TEMP: return new DecimalType(host.getTargetTemperature()); case FAN: return new DecimalType(host.getFan().ordinal()); case SWING: return new DecimalType(host.getFanDirection().ordinal()); case TEMPIN: return new DecimalType(host.getInsideTemperature()); case TIMER: return new DecimalType(host.getTimer().ordinal()); case TEMPOUT: return new DecimalType(host.getOutsideTemperature()); case HUMIDITYIN: return new DecimalType(host.getTargetHumidity()); default: throw new RuntimeException("Unsupported command type: " + commandType); } } private void setState(IDaikin host, DaikinCommandType commandType, Command value) { switch (commandType) { case POWER: host.setOn(value.equals(OnOffType.ON)); break; case MODE: host.setMode(getModeEnum(value.toString())); break; case TEMP: host.setTargetTemperature(((DecimalType) value).floatValue()); break; case FAN: host.setFan(getFanEnum(value.toString())); break; case SWING: host.setFanDirection(getFanDirectionEnum(value.toString())); break; case TIMER: host.setTimer(getTimerEnum(value.toString())); break; default: throw new RuntimeException("Unsupported command type: " + commandType); } } protected void addBindingProvider(DaikinBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(DaikinBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } /** * {@inheritDoc} */ @Override @SuppressWarnings("rawtypes") public void updated(Dictionary config) throws ConfigurationException { if (config != null) { Enumeration keys = config.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); String value = (String) config.get(key); // the config-key enumeration contains additional keys that we // don't want to process here ... if ("service.pid".equals(key)) { continue; } if (key.equals(CONFIG_KEY_REFRESH)) { refreshInterval = Long.parseLong(value); continue; } String[] keyParts = key.split("\\."); String hostId = keyParts[0]; if (!hosts.containsKey(hostId)) { int index = value.indexOf("@"); String connectionType = value.substring(0, index); String host = value.substring(index + 1); if (connectionType.toUpperCase().equals("WIRELESS")) { IDaikin newHost = DaikinFactory.createWirelessDaikin(hostId, host, 0); hosts.put(hostId, newHost); } else if (connectionType.toUpperCase().equals("WIRED")) { IDaikin newHost = DaikinFactory.createWiredDaikin(hostId, host, 0); hosts.put(hostId, newHost); } else { throw new RuntimeException("Unsupported connectionType: " + connectionType); } } } // start the refresh thread setProperlyConfigured(true); } } }