/**
* 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.freebox.internal;
import static org.apache.commons.lang.StringUtils.isBlank;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.List;
import org.matmaul.freeboxos.FreeboxException;
import org.matmaul.freeboxos.FreeboxOsClient;
import org.matmaul.freeboxos.airmedia.AirMediaConfig;
import org.matmaul.freeboxos.call.CallEntry;
import org.matmaul.freeboxos.connection.ConnectionStatus;
import org.matmaul.freeboxos.connection.xDslStatus;
import org.matmaul.freeboxos.ftp.FtpConfig;
import org.matmaul.freeboxos.lan.LanConfig;
import org.matmaul.freeboxos.lan.LanHostsConfig;
import org.matmaul.freeboxos.lcd.LCDConfig;
import org.matmaul.freeboxos.login.Authorize;
import org.matmaul.freeboxos.login.LoginManager;
import org.matmaul.freeboxos.login.TrackAuthorizeStatus;
import org.matmaul.freeboxos.netshare.SambaConfig;
import org.matmaul.freeboxos.system.SystemConfiguration;
import org.matmaul.freeboxos.upnpav.UPnPAVConfig;
import org.matmaul.freeboxos.wifi.WifiGlobalConfig;
import org.openhab.binding.freebox.FreeboxBindingConfig;
import org.openhab.binding.freebox.FreeboxBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.items.Item;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Freebox binding for openHAB
* This implements needed behaviour in order to get connected to the Freebox
* using ad-hoc connection procedure and then enables uses FreeboxAPI to
* gather meaningfull informations or execute actions
*
* @author clinique
* @since 1.5.0
*/
public class FreeboxBinding extends AbstractActiveBinding<FreeboxBindingProvider>implements ManagedService {
private static final Logger logger = LoggerFactory.getLogger(FreeboxBinding.class);
private static String appVersion;
private static String appID;
private static String appName;
private static String deviceName;
private static String appToken;
private static String serverAddress;
private static FreeboxOsClient fbClient;
private static LoginManager loginManager;
private Calendar lastPhoneCheck;
/**
* the refresh interval which is used to poll values from the Freebox
* server (optional, defaults to 60000ms)
*/
private long refreshInterval = 60000;
/**
* @{inheritDoc}
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* @{inheritDoc}
*/
@Override
protected String getName() {
return "Freebox Refresh Service";
}
@Override
public void activate() {
Bundle bundle = FrameworkUtil.getBundle(getClass());
appVersion = String.format("%d.%d", bundle.getVersion().getMajor(), bundle.getVersion().getMinor()); // something
// like 1.5
appID = bundle.getSymbolicName(); // org.openhab.binding.freebox
appName = bundle.getHeaders().get("Bundle-Name"); // "openHAB Freebox Binding"
lastPhoneCheck = Calendar.getInstance();
}
private void setItemValue(Item item, boolean value) {
eventPublisher.postUpdate(item.getName(), value ? OnOffType.ON : OnOffType.OFF);
}
private void setItemValue(Item item, String value) {
eventPublisher.postUpdate(item.getName(), new StringType(value));
}
private void setItemValue(Item item, Long value) {
eventPublisher.postUpdate(item.getName(), new DecimalType(value));
}
private void setDateTimeValue(Item item, long value) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(value * 1000);
eventPublisher.postUpdate(item.getName(), new DateTimeType(c));
}
/**
* @{inheritDoc}
*/
@SuppressWarnings("incomplete-switch")
@Override
protected void execute() {
try {
SystemConfiguration sc = fbClient.getSystemManager().getConfiguration();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case REBOOT:
setItemValue(bindingConfig.item, false);
break;
case CPUB:
setItemValue(bindingConfig.item, sc.getTemp_cpub());
break;
case CPUM:
setItemValue(bindingConfig.item, sc.getTemp_cpum());
break;
case FAN:
setItemValue(bindingConfig.item, sc.getFan_rpm());
break;
case FWVERSION:
setItemValue(bindingConfig.item, sc.getFirmware_version());
break;
case SW:
setItemValue(bindingConfig.item, sc.getTemp_sw());
break;
case UPTIME:
setItemValue(bindingConfig.item, sc.getUptimeVal());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("SystemConfiguration: " + e.getMessage());
}
try {
ConnectionStatus cs = fbClient.getConnectionManager().getStatus();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case BYTESDOWN:
setItemValue(bindingConfig.item, cs.getBytes_down());
break;
case BYTESUP:
setItemValue(bindingConfig.item, cs.getBytes_up());
break;
case IPV4:
setItemValue(bindingConfig.item, cs.getIpv4());
break;
case LINESTATUS:
setItemValue(bindingConfig.item, cs.getState());
break;
case RATEDOWN:
setItemValue(bindingConfig.item, cs.getRate_down());
break;
case RATEUP:
setItemValue(bindingConfig.item, cs.getRate_up());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("ConnectionStatus: " + e.getMessage());
}
try {
WifiGlobalConfig wc = fbClient.getWifiManager().getGlobalConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case WIFISTATUS:
setItemValue(bindingConfig.item, wc.getEnabled());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("WifiGlobalConfig: " + e.getMessage());
}
try {
LCDConfig lcd = fbClient.getLCDManager().getLCDConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case LCDBRIGHTNESS:
setItemValue(bindingConfig.item, (long) lcd.getBrightness());
break;
case LCDORIENTATION:
setItemValue(bindingConfig.item, (long) lcd.getOrientation());
break;
case LCDFORCED:
setItemValue(bindingConfig.item, lcd.getOrientationForced());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("LCDConfig: " + e.getMessage());
}
try {
xDslStatus xdsl = fbClient.getConnectionManager().getxDslStatus();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case XDSLSTATUS:
setItemValue(bindingConfig.item, xdsl.getStatus());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("xDslStatus: " + e.getMessage());
}
try {
FtpConfig fc = fbClient.getFtpManager().getConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case FTPSTATUS:
setItemValue(bindingConfig.item, fc.getEnabled());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("FtpConfig: " + e.getMessage());
}
String mode = null;
try {
LanConfig lc = fbClient.getLanManager().getLanConfig();
mode = lc.getMode();
} catch (FreeboxException e) {
logger.info("LanConfig: " + e.getMessage());
mode = null;
}
if ((mode != null) && !mode.equalsIgnoreCase("bridge")) {
// Only when Freebox Revolution is not in bridge mode
try {
AirMediaConfig ac = fbClient.getAirMediaManager().getConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case AIRMEDIASTATUS:
setItemValue(bindingConfig.item, ac.getEnabled());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("AirMediaConfig: " + e.getMessage());
}
try {
UPnPAVConfig uc = fbClient.getUPnPAVManager().getConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case UPNPAVSTATUS:
setItemValue(bindingConfig.item, uc.getEnabled());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("UPnPAVConfig: " + e.getMessage());
}
}
try {
SambaConfig sac = fbClient.getNetShareManager().getSambaConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case SAMBAFILESTATUS:
setItemValue(bindingConfig.item, sac.getFileShareEnabled());
break;
case SAMBAPRINTERSTATUS:
setItemValue(bindingConfig.item, sac.getPrintShareEnabled());
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("SambaConfig: " + e.getMessage());
}
try {
LanHostsConfig hc = fbClient.getLanManager().getAllLanHostsConfig();
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
switch (bindingConfig.commandType) {
case REACHABLEMAC:
setItemValue(bindingConfig.item, hc.isMacReachable(bindingConfig.commandParam));
break;
case REACHABLEIP:
setItemValue(bindingConfig.item, hc.isIpReachable(bindingConfig.commandParam));
break;
case REACHABLENAME:
setItemValue(bindingConfig.item, hc.isHostNameReachable(bindingConfig.commandParam));
break;
default:
break;
}
}
}
} catch (FreeboxException e) {
logger.info("LanHostsConfig: " + e.getMessage());
}
try {
List<CallEntry> appels = fbClient.getCallManager().getCallEntries();
PhoneCallComparator comparator = new PhoneCallComparator();
Collections.sort(appels, comparator);
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (CallEntry call : appels) {
Calendar callEndTime = call.getTimeStamp();
callEndTime.add(Calendar.SECOND, (int) (call.getDuration()));
if ((call.getDuration() > 0) && callEndTime.after(lastPhoneCheck)) {
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
if (bindingConfig.commandParam == null
|| bindingConfig.commandParam.equalsIgnoreCase(call.getType())) {
switch (bindingConfig.commandType) {
case CALLSTATUS:
setItemValue(bindingConfig.item, call.getType());
break;
case CALLDURATION:
setItemValue(bindingConfig.item, call.getDuration());
break;
case CALLNUMBER:
setItemValue(bindingConfig.item, call.getNumber());
break;
case CALLTIMESTAMP:
setDateTimeValue(bindingConfig.item, call.getDateTime());
break;
case CALLNAME:
setItemValue(bindingConfig.item, call.getName());
break;
}
}
}
lastPhoneCheck = callEndTime;
}
}
}
} catch (FreeboxException e) {
logger.info("CallEntries: " + e.getMessage());
}
}
/**
* @{inheritDoc}
*/
@SuppressWarnings("incomplete-switch")
@Override
protected void internalReceiveCommand(String itemName, Command command) {
if (!isProperlyConfigured()) {
logger.info("Freebox binding is not properly configured. Command is ignored.");
return;
}
for (FreeboxBindingProvider provider : providers) {
FreeboxBindingConfig config = provider.getConfig(itemName);
if (config == null) {
continue;
}
Boolean value = null;
switch (config.commandType) {
case LCDBRIGHTNESS:
if (command instanceof DecimalType) {
try {
LCDConfig lcd = fbClient.getLCDManager().getLCDConfig();
int valeur = ((DecimalType) command).intValue();
lcd.setBrightness(new Integer(valeur));
fbClient.getLCDManager().setLCDConfig(lcd);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
}
break;
case LCDORIENTATION:
if (command instanceof DecimalType) {
try {
LCDConfig lcd = fbClient.getLCDManager().getLCDConfig();
int valeur = ((DecimalType) command).intValue();
lcd.setOrientation(new Integer(valeur));
lcd.setOrientationForced(true);
fbClient.getLCDManager().setLCDConfig(lcd);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
}
break;
case LCDFORCED:
try {
LCDConfig lcd = fbClient.getLCDManager().getLCDConfig();
lcd.setOrientationForced(command.equals(OnOffType.ON) ? true : false);
fbClient.getLCDManager().setLCDConfig(lcd);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case WIFISTATUS:
try {
WifiGlobalConfig wc = new WifiGlobalConfig();
wc.setEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getWifiManager().setGlobalConfig(wc);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
WifiGlobalConfig wc = fbClient.getWifiManager().getGlobalConfig();
value = wc.getEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case REBOOT:
try {
fbClient.getSystemManager().Reboot();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case FTPSTATUS:
try {
FtpConfig fc = new FtpConfig();
fc.setEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getFtpManager().setConfig(fc);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
FtpConfig fc = fbClient.getFtpManager().getConfig();
value = fc.getEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case AIRMEDIASTATUS:
try {
AirMediaConfig ac = new AirMediaConfig();
ac.setEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getAirMediaManager().setConfig(ac);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
AirMediaConfig ac = fbClient.getAirMediaManager().getConfig();
value = ac.getEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case UPNPAVSTATUS:
try {
UPnPAVConfig uc = new UPnPAVConfig();
uc.setEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getUPnPAVManager().setConfig(uc);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
UPnPAVConfig uc = fbClient.getUPnPAVManager().getConfig();
value = uc.getEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case SAMBAFILESTATUS:
try {
SambaConfig sc = new SambaConfig();
sc.setFileShareEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getNetShareManager().setSambaConfig(sc);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
SambaConfig sc = fbClient.getNetShareManager().getSambaConfig();
value = sc.getFileShareEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
case SAMBAPRINTERSTATUS:
try {
SambaConfig sc = new SambaConfig();
sc.setPrintShareEnabled(command.equals(OnOffType.ON) ? true : false);
fbClient.getNetShareManager().setSambaConfig(sc);
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
// Get the current state
try {
SambaConfig sc = fbClient.getNetShareManager().getSambaConfig();
value = sc.getPrintShareEnabled();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
break;
}
// Refresh items with the current ON/OFF state
if (value != null) {
updateOnOffItems(config.commandType, value);
}
}
}
/**
* @{inheritDoc}
*/
@Override
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
if (config != null) {
setProperlyConfigured(false);
deviceName = (String) config.get("device");
if (isBlank(deviceName)) { // The only mandatory parameter is tested first
throw new ConfigurationException("freebox",
"The parameter 'device' is missing! Please refer to your 'openhab.cfg'");
}
String refreshConfig = (String) config.get("refresh");
if (!isBlank(refreshConfig)) {
refreshInterval = Long.parseLong(refreshConfig);
}
serverAddress = (String) config.get("server");
if (isBlank(serverAddress)) {
serverAddress = "mafreebox.freebox.fr";
}
appToken = (String) config.get("apptoken");
try {
authorize();
} catch (FreeboxException e) {
logger.info(e.getMessage());
}
setProperlyConfigured((loginManager != null) && loginManager.isConnected());
logger.info("Freebox binding " + (isProperlyConfigured() ? "" : "not ") + "properly configured");
}
}
/**
* Handles connection to the Freebox, including validation of the Apptoken
* if none is provided in 'openhab.cfg'
*
* @throws FreeboxException
*/
private void authorize() throws FreeboxException {
logger.debug("Appname : " + appName);
logger.debug("AppVersion : " + appVersion);
logger.debug("DeviceName : " + deviceName);
logger.debug("AppID :" + appID);
fbClient = new FreeboxOsClient(appID, serverAddress);
loginManager = fbClient.getLoginManager();
TrackAuthorizeStatus authorizeStatus = TrackAuthorizeStatus.UNKNOWN;
if (isBlank(appToken)) {
Authorize authorize = loginManager.newAuthorize(appName, appVersion, deviceName);
appToken = authorize.getAppToken();
logger.info("####################################################################");
logger.info("# Please accept activation request directly on your freebox #");
logger.info("# Once done, record current Apptoken in your 'openhab.cfg' #");
logger.info("# " + appToken + " #");
logger.info("####################################################################");
do {
try {
Thread.sleep(2000);
authorizeStatus = loginManager.trackAuthorize();
} catch (InterruptedException e) {
logger.info(e.getMessage());
}
} while (authorizeStatus == TrackAuthorizeStatus.PENDING);
} else {
authorizeStatus = TrackAuthorizeStatus.GRANTED;
}
if (authorizeStatus != TrackAuthorizeStatus.GRANTED) {
throw new FreeboxException(authorizeStatus.toString());
}
logger.debug("Apptoken valide : [" + appToken + "]");
loginManager.setAppToken(appToken);
loginManager.openSession();
}
/**
* Update switch items attached to a particular command type
*/
private void updateOnOffItems(CommandType commandType, boolean value) {
for (FreeboxBindingProvider provider : providers) {
Collection<String> items = provider.getItemNames();
for (String itemName : items) {
FreeboxBindingConfig bindingConfig = provider.getConfig(itemName);
if (bindingConfig.commandType == commandType) {
setItemValue(bindingConfig.item, value);
}
}
}
}
/**
* A comparator of phone calls by ascending end date and time
*/
private class PhoneCallComparator implements Comparator<CallEntry> {
@Override
public int compare(CallEntry call1, CallEntry call2) {
int result = 0;
Calendar callEndTime1 = call1.getTimeStamp();
callEndTime1.add(Calendar.SECOND, (int) (call1.getDuration()));
Calendar callEndTime2 = call2.getTimeStamp();
callEndTime2.add(Calendar.SECOND, (int) (call2.getDuration()));
if (callEndTime1.before(callEndTime2)) {
result = -1;
} else if (callEndTime1.after(callEndTime2)) {
result = 1;
}
return result;
}
}
}