/**
* 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.benqprojector.internal;
import java.util.Dictionary;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.benqprojector.BenqProjectorBindingProvider;
import org.openhab.binding.benqprojector.internal.transport.BenqProjectorNetworkTransport;
import org.openhab.binding.benqprojector.internal.transport.BenqProjectorSerialTransport;
import org.openhab.binding.benqprojector.internal.transport.BenqProjectorTransport;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This binding allows interaction with BenQ Projectors supporting and RS232
* interface
*
* @author Paul Hampson (cyclingengineer)
* @since 1.6.0
*/
public class BenqProjectorBinding extends AbstractActiveBinding<BenqProjectorBindingProvider>implements ManagedService {
private static final Logger logger = LoggerFactory.getLogger(BenqProjectorBinding.class);
/**
* Transport for communicating with projector
*/
BenqProjectorTransport transport;
/**
* the refresh interval which is used to poll values from the BenqProjector
* server (optional, defaults to 60000ms)
*
*/
private long refreshInterval = 60000;
/**
* Min & Max volume limits
*/
private final int MAX_VOLUME = 10;
private final int MIN_VOLUME = 0;
public BenqProjectorBinding() {
}
@Override
public void activate() {
}
@Override
public void deactivate() {
transport.closeConnection();
}
/**
* @{inheritDoc
*/
@Override
protected long getRefreshInterval() {
return refreshInterval;
}
/**
* @{inheritDoc
*/
@Override
protected String getName() {
return "BenqProjector Refresh Service";
}
/**
* @{inheritDoc
*/
@Override
protected void execute() {
for (BenqProjectorBindingProvider binding : super.providers) {
for (String itemName : binding.getItemNames()) {
logger.debug("Polling projector status for " + itemName);
BenqProjectorBindingConfig cfg = binding.getConfigForItemName(itemName);
State s = queryProjector(cfg);
if (!(s instanceof UnDefType)) {
eventPublisher.postUpdate(itemName, s);
logger.debug(itemName + " status is " + s);
} else {
logger.debug(itemName + " not updated as result was undefined");
}
}
}
}
/**
* @{inheritDoc
*/
@Override
protected void internalReceiveCommand(String itemName, Command command) {
for (BenqProjectorBindingProvider binding : super.providers) {
if (binding.providesBindingFor(itemName)) {
logger.debug("Process command " + command + " for " + itemName);
BenqProjectorBindingConfig cfg = binding.getConfigForItemName(itemName);
String resp = sendCommandToProjector(cfg, command);
State s = cfg.mode.parseResponse(resp);
if (!(s instanceof UnDefType)) {
eventPublisher.postUpdate(itemName, s);
logger.debug(itemName + " status is " + s);
} else {
logger.debug(itemName + " not updated as result was undefined");
}
}
}
}
protected void addBindingProvider(BenqProjectorBindingProvider bindingProvider) {
super.addBindingProvider(bindingProvider);
}
protected void removeBindingProvider(BenqProjectorBindingProvider bindingProvider) {
super.removeBindingProvider(bindingProvider);
}
/**
* {@inheritDoc}
*/
@Override
public void updated(Dictionary<String, ?> config) throws ConfigurationException {
if (config != null) {
// to override the default refresh interval one has to add a
// parameter to openhab.cfg like
// <bindingName>:refresh=<intervalInMs>
String refreshIntervalString = (String) config.get("refresh");
if (StringUtils.isNotBlank(refreshIntervalString)) {
refreshInterval = Long.parseLong(refreshIntervalString);
}
/* decide which transport to use - default is network */
String modeString = (String) config.get("mode");
if (StringUtils.isNotBlank(modeString)) {
if (modeString.equalsIgnoreCase("serial")) {
transport = new BenqProjectorSerialTransport();
} else {
/* default to network */
transport = new BenqProjectorNetworkTransport();
}
} else {
transport = new BenqProjectorNetworkTransport();
}
String deviceIdString = (String) config.get("deviceId");
if (StringUtils.isNotBlank(deviceIdString)) {
setProperlyConfigured(transport.setupConnection(deviceIdString));
}
}
}
/**
* Run query on the projector
*
* @param cfg
* Configuration of item to run query on
*/
private State queryProjector(BenqProjectorBindingConfig cfg) {
String resp = transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandQueryString());
return cfg.mode.parseResponse(resp);
}
/**
* Send the command to the projector via configured transport and return the
* response string
*
* @param cfg
* Item binding configuration
* @param c
* command to be sent
* @return Response string from projector
*/
private String sendCommandToProjector(BenqProjectorBindingConfig cfg, Command c) {
Boolean cmdSent = false;
String response = "";
switch (cfg.mode) {
case POWER:
case MUTE:
if (c instanceof OnOffType) {
if ((OnOffType) c == OnOffType.ON) {
response = transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("ON"));
cmdSent = true;
} else if ((OnOffType) c == OnOffType.OFF) {
response = transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("OFF"));
cmdSent = true;
}
}
break;
case VOLUME:
if (c instanceof DecimalType) {
/* get current volume */
State currentVolState = queryProjector(cfg);
int currentVol = ((DecimalType) currentVolState).intValue();
int volLevel = ((DecimalType) c).intValue();
if (volLevel > this.MAX_VOLUME) {
volLevel = this.MAX_VOLUME;
} else if (volLevel < this.MIN_VOLUME) {
volLevel = this.MIN_VOLUME;
}
if (currentVol == volLevel) {
cmdSent = true;
}
while (currentVol != volLevel) {
if (currentVol < volLevel) {
transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("+"));
currentVol++;
cmdSent = true;
} else {
transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("-"));
currentVol--;
cmdSent = true;
}
}
} else if (c instanceof IncreaseDecreaseType) {
if ((IncreaseDecreaseType) c == IncreaseDecreaseType.INCREASE) {
transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("+"));
cmdSent = true;
} else if ((IncreaseDecreaseType) c == IncreaseDecreaseType.DECREASE) {
transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString("-"));
cmdSent = true;
}
}
/* get final volume */
response = transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandQueryString());
break;
case LAMP_HOURS:
logger.warn("Cannot send command to set lamp hours - not a valid operation!");
break;
case SOURCE_NUMBER:
if (c instanceof DecimalType) {
DecimalType sourceIdx = (DecimalType) c;
String cmd = BenqProjectorSourceMapping.getStringFromMapping(sourceIdx.intValue());
if (cmd.isEmpty() == false) {
response = transport.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString(cmd));
cmdSent = true;
}
}
break;
case SOURCE_STRING:
if (c instanceof StringType) {
StringType sourceStr = (StringType) c;
int mappingIdx = BenqProjectorSourceMapping.getMappingFromString(sourceStr.toString());
if (mappingIdx != -1) // double check this is a valid mapping
{
response = transport
.sendCommandExpectResponse(cfg.mode.getItemModeCommandSetString(sourceStr.toString()));
cmdSent = true;
}
}
break;
default:
logger.error("Unexpected Item Mode!");
break;
}
if (cmdSent == false) {
logger.error("Unable to convert item command to projector state: Command=" + c);
}
return response;
}
}