[benqprojector] Migrate benqprojector binding to OH3 (#10341)
Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.benqprojector-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-benqprojector" description="BenQ Projector Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-serial</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.benqprojector/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link BenqProjectorBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "benqprojector";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_PROJECTOR_SERIAL = new ThingTypeUID(BINDING_ID, "projector-serial");
|
||||
public static final ThingTypeUID THING_TYPE_PROJECTOR_TCP = new ThingTypeUID(BINDING_ID, "projector-tcp");
|
||||
|
||||
// Some Channel types
|
||||
public static final String CHANNEL_TYPE_POWER = "power";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception for BenQ projector command errors.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorCommandException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -8048415193494625295L;
|
||||
|
||||
public BenqProjectorCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import java.io.InvalidClassException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.library.items.StringItem;
|
||||
import org.openhab.core.library.items.SwitchItem;
|
||||
|
||||
/**
|
||||
* Represents all valid command types which could be processed by this
|
||||
* binding.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum BenqProjectorCommandType {
|
||||
POWER("Power", SwitchItem.class),
|
||||
SOURCE("Source", StringItem.class),
|
||||
PICTURE_MODE("PictureMode", StringItem.class),
|
||||
ASPECT_RATIO("AspectRatio", StringItem.class),
|
||||
FREEZE("Freeze", SwitchItem.class),
|
||||
BLANK("Blank", SwitchItem.class),
|
||||
DIRECTCMD("DirectCmd", StringItem.class),
|
||||
LAMP_TIME("LampTime", NumberItem.class);
|
||||
|
||||
private final String text;
|
||||
private Class<? extends Item> itemClass;
|
||||
|
||||
private BenqProjectorCommandType(final String text, Class<? extends Item> itemClass) {
|
||||
this.text = text;
|
||||
this.itemClass = itemClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public Class<? extends Item> getItemClass() {
|
||||
return itemClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Procedure to validate command type string.
|
||||
*
|
||||
* @param commandTypeText
|
||||
* command string e.g. RawData, Command, Brightness
|
||||
* @return true if item is valid.
|
||||
* @throws IllegalArgumentException
|
||||
* Not valid command type.
|
||||
* @throws InvalidClassException
|
||||
* Not valid class for command type.
|
||||
*/
|
||||
public static boolean validateBinding(String commandTypeText, Class<? extends Item> itemClass)
|
||||
throws IllegalArgumentException, InvalidClassException {
|
||||
for (BenqProjectorCommandType c : BenqProjectorCommandType.values()) {
|
||||
if (c.text.equalsIgnoreCase(commandTypeText)) {
|
||||
if (c.getItemClass().equals(itemClass)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new InvalidClassException("Not valid class for command type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not valid command type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Procedure to convert command type string to command type class.
|
||||
*
|
||||
* @param commandTypeText
|
||||
* command string e.g. RawData, Command, Brightness
|
||||
* @return corresponding command type.
|
||||
* @throws InvalidClassException
|
||||
* Not valid class for command type.
|
||||
*/
|
||||
public static BenqProjectorCommandType getCommandType(String commandTypeText) throws IllegalArgumentException {
|
||||
for (BenqProjectorCommandType c : BenqProjectorCommandType.values()) {
|
||||
if (c.text.equalsIgnoreCase(commandTypeText)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not valid command type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.configuration.BenqProjectorConfiguration;
|
||||
import org.openhab.binding.benqprojector.internal.connector.BenqProjectorConnector;
|
||||
import org.openhab.binding.benqprojector.internal.connector.BenqProjectorSerialConnector;
|
||||
import org.openhab.binding.benqprojector.internal.connector.BenqProjectorTcpConnector;
|
||||
import org.openhab.binding.benqprojector.internal.enums.Switch;
|
||||
import org.openhab.core.cache.ExpiringCache;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Provide high level interface to BenQ projector.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorDevice {
|
||||
private static final String UNSUPPORTED_ITM = "Unsupported item";
|
||||
private static final String BLOCK_ITM = "Block item";
|
||||
private static final String ILLEGAL_FMT = "Illegal format";
|
||||
|
||||
private static final int LAMP_REFRESH_WAIT_MINUTES = 5;
|
||||
|
||||
private ExpiringCache<Integer> cachedLampHours = new ExpiringCache<>(Duration.ofMinutes(LAMP_REFRESH_WAIT_MINUTES),
|
||||
this::queryLamp);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BenqProjectorDevice.class);
|
||||
|
||||
private BenqProjectorConnector connection;
|
||||
private boolean connected = false;
|
||||
|
||||
public BenqProjectorDevice(SerialPortManager serialPortManager, BenqProjectorConfiguration config) {
|
||||
connection = new BenqProjectorSerialConnector(serialPortManager, config.serialPort);
|
||||
}
|
||||
|
||||
public BenqProjectorDevice(BenqProjectorConfiguration config) {
|
||||
connection = new BenqProjectorTcpConnector(config.host, config.port);
|
||||
}
|
||||
|
||||
private synchronized String sendQuery(String query) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
logger.debug("Query: '{}'", query);
|
||||
String response = connection.sendMessage(query);
|
||||
|
||||
if (response.length() == 0) {
|
||||
throw new BenqProjectorException("No response received");
|
||||
}
|
||||
|
||||
if (response.contains(UNSUPPORTED_ITM)) {
|
||||
return "UNSUPPORTED";
|
||||
}
|
||||
|
||||
if (response.contains(BLOCK_ITM)) {
|
||||
throw new BenqProjectorCommandException("Block Item received for command: " + query);
|
||||
}
|
||||
|
||||
if (response.contains(ILLEGAL_FMT)) {
|
||||
throw new BenqProjectorCommandException("Illegal Format response received for command: " + query);
|
||||
}
|
||||
|
||||
logger.debug("Response: '{}'", response);
|
||||
|
||||
// example: SOUR=HDMI2
|
||||
String[] responseParts = response.split("=");
|
||||
if (responseParts.length != 2) {
|
||||
throw new BenqProjectorCommandException("Invalid respose for command: " + query);
|
||||
}
|
||||
|
||||
return responseParts[1].toLowerCase();
|
||||
}
|
||||
|
||||
protected void sendCommand(String command) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendQuery(command);
|
||||
}
|
||||
|
||||
protected int queryInt(String query) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
String response = sendQuery(query);
|
||||
return Integer.parseInt(response);
|
||||
}
|
||||
|
||||
protected String queryString(String query) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return sendQuery(query);
|
||||
}
|
||||
|
||||
public void connect() throws BenqProjectorException {
|
||||
connection.connect();
|
||||
connected = true;
|
||||
}
|
||||
|
||||
public void disconnect() throws BenqProjectorException {
|
||||
connection.disconnect();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Power
|
||||
*/
|
||||
public Switch getPowerStatus() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return (queryString("pow=?").contains("on") ? Switch.ON : Switch.OFF);
|
||||
}
|
||||
|
||||
public void setPower(Switch value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(value == Switch.ON ? "pow=on" : "pow=off");
|
||||
}
|
||||
|
||||
/*
|
||||
* Source
|
||||
*/
|
||||
public @Nullable String getSource() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return queryString("sour=?");
|
||||
}
|
||||
|
||||
public void setSource(String value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(String.format("sour=%s", value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Picture Mode
|
||||
*/
|
||||
public @Nullable String getPictureMode() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return queryString("appmod=?");
|
||||
}
|
||||
|
||||
public void setPictureMode(String value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(String.format("appmod=%s", value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Aspect Ratio
|
||||
*/
|
||||
public @Nullable String getAspectRatio() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return queryString("asp=?");
|
||||
}
|
||||
|
||||
public void setAspectRatio(String value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(String.format("asp=%s", value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Blank Screen
|
||||
*/
|
||||
public Switch getBlank() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return (queryString("blank=?").contains("on") ? Switch.ON : Switch.OFF);
|
||||
}
|
||||
|
||||
public void setBlank(Switch value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(String.format("blank=%s", (value == Switch.ON ? "on" : "off")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Freeze
|
||||
*/
|
||||
public Switch getFreeze() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
return (queryString("freeze=?").contains("on") ? Switch.ON : Switch.OFF);
|
||||
}
|
||||
|
||||
public void setFreeze(Switch value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(String.format("freeze=%s", (value == Switch.ON ? "on" : "off")));
|
||||
}
|
||||
|
||||
/*
|
||||
* Direct Command
|
||||
*/
|
||||
public void sendDirectCommand(String value) throws BenqProjectorCommandException, BenqProjectorException {
|
||||
sendCommand(value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lamp Time (hours) - get from cache
|
||||
*/
|
||||
public int getLampTime() throws BenqProjectorCommandException, BenqProjectorException {
|
||||
Integer lampHours = cachedLampHours.getValue();
|
||||
|
||||
if (lampHours != null) {
|
||||
return lampHours.intValue();
|
||||
} else {
|
||||
throw new BenqProjectorCommandException("cachedLampHours returned null");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get Lamp Time
|
||||
*/
|
||||
private @Nullable Integer queryLamp() {
|
||||
try {
|
||||
return Integer.valueOf(queryInt("ltim=?"));
|
||||
} catch (BenqProjectorCommandException | BenqProjectorException e) {
|
||||
logger.debug("Error executing command ltim=?", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception for BenQ projector errors.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -8048415193494625295L;
|
||||
|
||||
public BenqProjectorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BenqProjectorException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal;
|
||||
|
||||
import static org.openhab.binding.benqprojector.internal.BenqProjectorBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.handler.BenqProjectorHandler;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link BenqProjectorHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.benqprojector", service = ThingHandlerFactory.class)
|
||||
public class BenqProjectorHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PROJECTOR_SERIAL,
|
||||
THING_TYPE_PROJECTOR_TCP);
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Activate
|
||||
public BenqProjectorHandlerFactory(final @Reference SerialPortManager serialPortManager) {
|
||||
this.serialPortManager = serialPortManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_PROJECTOR_SERIAL.equals(thingTypeUID) || THING_TYPE_PROJECTOR_TCP.equals(thingTypeUID)) {
|
||||
return new BenqProjectorHandler(thing, serialPortManager);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.configuration;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link BenqProjectorConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorConfiguration {
|
||||
|
||||
/**
|
||||
* Serial port used for communication.
|
||||
*/
|
||||
public String serialPort = "";
|
||||
|
||||
/**
|
||||
* Host or IP address used for communication over a TCP link (if serialPort is not set).
|
||||
*/
|
||||
public String host = "";
|
||||
|
||||
/**
|
||||
* Port used for communication over a TCP link (if serialPort is not set).
|
||||
*/
|
||||
public int port;
|
||||
|
||||
/**
|
||||
* Polling interval to refresh states.
|
||||
*/
|
||||
public int pollingInterval;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.connector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorException;
|
||||
|
||||
/**
|
||||
* Base class for BenQ projector communication.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface BenqProjectorConnector {
|
||||
public static final int TIMEOUT_MS = 5 * 1000;
|
||||
|
||||
public static final String START = "\r*";
|
||||
public static final String END = "#\r";
|
||||
public static final String BLANK = "";
|
||||
|
||||
/**
|
||||
* Procedure for connecting to projector.
|
||||
*
|
||||
* @throws BenqProjectorException
|
||||
*/
|
||||
void connect() throws BenqProjectorException;
|
||||
|
||||
/**
|
||||
* Procedure for disconnecting to projector controller.
|
||||
*
|
||||
* @throws BenqProjectorException
|
||||
*/
|
||||
void disconnect() throws BenqProjectorException;
|
||||
|
||||
/**
|
||||
* Procedure for sending raw data to projector.
|
||||
*
|
||||
* @param data
|
||||
* Message to send.
|
||||
*
|
||||
* @throws BenqProjectorException
|
||||
*/
|
||||
String sendMessage(String data) throws BenqProjectorException;
|
||||
|
||||
/**
|
||||
* Common method called by the Serial or Tcp connector to send the message to the projector, wait for a response and
|
||||
* return it after processing.
|
||||
*
|
||||
* @param data
|
||||
* Message to send.
|
||||
* @param in
|
||||
* The connector's input stream.
|
||||
* @param out
|
||||
* The connector's output stream.
|
||||
*
|
||||
* @throws BenqProjectorException
|
||||
*/
|
||||
default String sendMsgReadResp(String data, @Nullable InputStream in, @Nullable OutputStream out)
|
||||
throws IOException, BenqProjectorException {
|
||||
String resp = BLANK;
|
||||
|
||||
if (in != null && out != null) {
|
||||
out.write((START + data + END).getBytes(StandardCharsets.US_ASCII));
|
||||
out.flush();
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
long elapsedTime = 0;
|
||||
|
||||
while (elapsedTime < TIMEOUT_MS) {
|
||||
int availableBytes = in.available();
|
||||
if (availableBytes > 0) {
|
||||
byte[] tmpData = new byte[availableBytes];
|
||||
int readBytes = in.read(tmpData, 0, availableBytes);
|
||||
resp = resp.concat(new String(tmpData, 0, readBytes, StandardCharsets.US_ASCII));
|
||||
if (resp.contains(END)) {
|
||||
return resp.replaceAll("[\\r\\n*#>]", BLANK).replace(data, BLANK);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new BenqProjectorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
elapsedTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.connector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorException;
|
||||
import org.openhab.core.io.transport.serial.PortInUseException;
|
||||
import org.openhab.core.io.transport.serial.SerialPort;
|
||||
import org.openhab.core.io.transport.serial.SerialPortEvent;
|
||||
import org.openhab.core.io.transport.serial.SerialPortEventListener;
|
||||
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Connector for serial port communication.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorSerialConnector implements BenqProjectorConnector, SerialPortEventListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BenqProjectorSerialConnector.class);
|
||||
private final String serialPortName;
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
private @Nullable InputStream in = null;
|
||||
private @Nullable OutputStream out = null;
|
||||
private @Nullable SerialPort serialPort = null;
|
||||
|
||||
public BenqProjectorSerialConnector(SerialPortManager serialPortManager, String serialPort) {
|
||||
this.serialPortManager = serialPortManager;
|
||||
this.serialPortName = serialPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws BenqProjectorException {
|
||||
try {
|
||||
logger.debug("Open connection to serial port '{}'", serialPortName);
|
||||
|
||||
SerialPortIdentifier serialPortIdentifier = serialPortManager.getIdentifier(serialPortName);
|
||||
|
||||
if (serialPortIdentifier == null) {
|
||||
throw new IOException("Unknown serial port");
|
||||
}
|
||||
SerialPort serialPort = serialPortIdentifier.open(this.getClass().getName(), 2000);
|
||||
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
|
||||
serialPort.enableReceiveThreshold(1);
|
||||
serialPort.disableReceiveTimeout();
|
||||
|
||||
InputStream in = serialPort.getInputStream();
|
||||
OutputStream out = serialPort.getOutputStream();
|
||||
|
||||
if (in != null && out != null) {
|
||||
out.flush();
|
||||
if (in.markSupported()) {
|
||||
in.reset();
|
||||
}
|
||||
|
||||
serialPort.notifyOnDataAvailable(true);
|
||||
|
||||
this.serialPort = serialPort;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
} catch (PortInUseException | UnsupportedCommOperationException | IOException e) {
|
||||
throw new BenqProjectorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() throws BenqProjectorException {
|
||||
InputStream in = this.in;
|
||||
OutputStream out = this.out;
|
||||
SerialPort serialPort = this.serialPort;
|
||||
|
||||
if (out != null) {
|
||||
logger.debug("Close serial out stream");
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error occurred when closing serial out stream: {}", e.getMessage());
|
||||
}
|
||||
this.out = null;
|
||||
}
|
||||
if (in != null) {
|
||||
logger.debug("Close serial in stream");
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error occurred when closing serial in stream: {}", e.getMessage());
|
||||
}
|
||||
this.in = null;
|
||||
}
|
||||
if (serialPort != null) {
|
||||
logger.debug("Close serial port");
|
||||
serialPort.close();
|
||||
serialPort.removeEventListener();
|
||||
this.serialPort = null;
|
||||
}
|
||||
|
||||
logger.debug("Closed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sendMessage(String data) throws BenqProjectorException {
|
||||
InputStream in = this.in;
|
||||
OutputStream out = this.out;
|
||||
|
||||
if (in == null || out == null) {
|
||||
connect();
|
||||
in = this.in;
|
||||
out = this.out;
|
||||
}
|
||||
|
||||
try {
|
||||
if (in != null && out != null) {
|
||||
// flush input stream
|
||||
if (in.markSupported()) {
|
||||
in.reset();
|
||||
} else {
|
||||
while (in.available() > 0) {
|
||||
int availableBytes = in.available();
|
||||
|
||||
if (availableBytes > 0) {
|
||||
byte[] tmpData = new byte[availableBytes];
|
||||
in.read(tmpData, 0, availableBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendMsgReadResp(data, in, out);
|
||||
} else {
|
||||
return BLANK;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IO error occurred...reconnect and resend once: {}", e.getMessage());
|
||||
disconnect();
|
||||
connect();
|
||||
|
||||
try {
|
||||
return sendMsgReadResp(data, in, out);
|
||||
} catch (IOException e1) {
|
||||
throw new BenqProjectorException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialEvent(SerialPortEvent arg0) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.connector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Connector for TCP communication.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorTcpConnector implements BenqProjectorConnector {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BenqProjectorTcpConnector.class);
|
||||
private final String ip;
|
||||
private final int port;
|
||||
|
||||
private @Nullable Socket socket = null;
|
||||
private @Nullable InputStream in = null;
|
||||
private @Nullable OutputStream out = null;
|
||||
|
||||
public BenqProjectorTcpConnector(String ip, int port) {
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws BenqProjectorException {
|
||||
logger.debug("Open connection to address'{}:{}'", ip, port);
|
||||
|
||||
try {
|
||||
Socket socket = new Socket(ip, port);
|
||||
this.socket = socket;
|
||||
in = socket.getInputStream();
|
||||
out = socket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
throw new BenqProjectorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() throws BenqProjectorException {
|
||||
OutputStream out = this.out;
|
||||
|
||||
if (out != null) {
|
||||
logger.debug("Close tcp out stream");
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error occurred when closing tcp out stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
InputStream in = this.in;
|
||||
if (in != null) {
|
||||
logger.debug("Close tcp in stream");
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error occurred when closing tcp in stream: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Socket socket = this.socket;
|
||||
if (socket != null) {
|
||||
logger.debug("Closing socket");
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error occurred when closing tcp socket: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
this.socket = null;
|
||||
this.out = null;
|
||||
this.in = null;
|
||||
|
||||
logger.debug("Closed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sendMessage(String data) throws BenqProjectorException {
|
||||
InputStream in = this.in;
|
||||
OutputStream out = this.out;
|
||||
|
||||
if (in == null || out == null) {
|
||||
connect();
|
||||
in = this.in;
|
||||
out = this.out;
|
||||
}
|
||||
|
||||
try {
|
||||
if (in != null) {
|
||||
// flush input stream
|
||||
if (in.markSupported()) {
|
||||
in.reset();
|
||||
} else {
|
||||
while (in.available() > 0) {
|
||||
int availableBytes = in.available();
|
||||
|
||||
if (availableBytes > 0) {
|
||||
byte[] tmpData = new byte[availableBytes];
|
||||
in.read(tmpData, 0, availableBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sendMsgReadResp(data, in, out);
|
||||
} else {
|
||||
return BLANK;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("IO error occurred...reconnect and resend once: {}", e.getMessage());
|
||||
disconnect();
|
||||
connect();
|
||||
|
||||
try {
|
||||
return sendMsgReadResp(data, in, out);
|
||||
} catch (IOException e1) {
|
||||
throw new BenqProjectorException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.enums;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Valid values for BenQ switch commands.
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum Switch {
|
||||
ON,
|
||||
OFF;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.benqprojector.internal.handler;
|
||||
|
||||
import static org.openhab.binding.benqprojector.internal.BenqProjectorBindingConstants.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorCommandException;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorCommandType;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorDevice;
|
||||
import org.openhab.binding.benqprojector.internal.BenqProjectorException;
|
||||
import org.openhab.binding.benqprojector.internal.configuration.BenqProjectorConfiguration;
|
||||
import org.openhab.binding.benqprojector.internal.enums.Switch;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
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.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BenqProjectorHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* Based on 'epsonprojector' originally by Pauli Anttila & Yannick Schaus
|
||||
*
|
||||
* @author Michael Lobstein - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class BenqProjectorHandler extends BaseThingHandler {
|
||||
private static final int DEFAULT_POLLING_INTERVAL_SEC = 10;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(BenqProjectorHandler.class);
|
||||
private final SerialPortManager serialPortManager;
|
||||
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
private Optional<BenqProjectorDevice> device = Optional.empty();
|
||||
|
||||
private boolean isPowerOn = false;
|
||||
private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
|
||||
|
||||
public BenqProjectorHandler(Thing thing, SerialPortManager serialPortManager) {
|
||||
super(thing);
|
||||
this.serialPortManager = serialPortManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
String channelId = channelUID.getId();
|
||||
if (command instanceof RefreshType) {
|
||||
Channel channel = this.thing.getChannel(channelUID);
|
||||
if (channel != null && getThing().getStatus() == ThingStatus.ONLINE) {
|
||||
updateChannelState(channel);
|
||||
}
|
||||
} else {
|
||||
BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channelId);
|
||||
sendDataToDevice(benqCommand, command);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
BenqProjectorConfiguration config = getConfigAs(BenqProjectorConfiguration.class);
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_PROJECTOR_SERIAL.equals(thingTypeUID)) {
|
||||
device = Optional.of(new BenqProjectorDevice(serialPortManager, config));
|
||||
} else if (THING_TYPE_PROJECTOR_TCP.equals(thingTypeUID)) {
|
||||
device = Optional.of(new BenqProjectorDevice(config));
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
pollingInterval = config.pollingInterval;
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
schedulePollingJob();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the polling job
|
||||
*/
|
||||
private void schedulePollingJob() {
|
||||
cancelPollingJob();
|
||||
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
List<Channel> channels = this.thing.getChannels();
|
||||
for (Channel channel : channels) {
|
||||
// only query power when projector is off
|
||||
if (isPowerOn || channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
|
||||
updateChannelState(channel);
|
||||
}
|
||||
}
|
||||
}, 0, (pollingInterval > 0) ? pollingInterval : DEFAULT_POLLING_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the polling job
|
||||
*/
|
||||
private void cancelPollingJob() {
|
||||
ScheduledFuture<?> pollingJob = this.pollingJob;
|
||||
if (pollingJob != null) {
|
||||
pollingJob.cancel(true);
|
||||
this.pollingJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
cancelPollingJob();
|
||||
closeConnection();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void updateChannelState(Channel channel) {
|
||||
try {
|
||||
if (!isLinked(channel.getUID()) && !channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channel.getUID().getId());
|
||||
|
||||
State state = queryDataFromDevice(benqCommand);
|
||||
|
||||
if (state != null) {
|
||||
if (isLinked(channel.getUID())) {
|
||||
updateState(channel.getUID(), state);
|
||||
}
|
||||
// the first valid response will cause the thing to go ONLINE
|
||||
if (state != UnDefType.UNDEF) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Unknown channel {}", channel.getUID().getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private State queryDataFromDevice(BenqProjectorCommandType commandType) {
|
||||
BenqProjectorDevice remoteController = device.get();
|
||||
|
||||
try {
|
||||
if (!remoteController.isConnected()) {
|
||||
remoteController.connect();
|
||||
}
|
||||
|
||||
switch (commandType) {
|
||||
case POWER:
|
||||
Switch powerStatus = remoteController.getPowerStatus();
|
||||
if (powerStatus == Switch.ON) {
|
||||
isPowerOn = true;
|
||||
return OnOffType.ON;
|
||||
} else {
|
||||
isPowerOn = false;
|
||||
return OnOffType.OFF;
|
||||
}
|
||||
case SOURCE:
|
||||
String source = remoteController.getSource();
|
||||
if (source != null) {
|
||||
return new StringType(source);
|
||||
} else {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
case PICTURE_MODE:
|
||||
String picturemode = remoteController.getPictureMode();
|
||||
if (picturemode != null) {
|
||||
return new StringType(picturemode);
|
||||
} else {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
case ASPECT_RATIO:
|
||||
String aspectratio = remoteController.getAspectRatio();
|
||||
if (aspectratio != null) {
|
||||
return new StringType(aspectratio);
|
||||
} else {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
case FREEZE:
|
||||
Switch freeze = remoteController.getFreeze();
|
||||
return freeze == Switch.ON ? OnOffType.ON : OnOffType.OFF;
|
||||
case BLANK:
|
||||
Switch blank = remoteController.getBlank();
|
||||
return blank == Switch.ON ? OnOffType.ON : OnOffType.OFF;
|
||||
case DIRECTCMD:
|
||||
break;
|
||||
case LAMP_TIME:
|
||||
int lampTime = remoteController.getLampTime();
|
||||
return new DecimalType(lampTime);
|
||||
default:
|
||||
logger.warn("Unknown '{}' command!", commandType);
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
} catch (BenqProjectorCommandException e) {
|
||||
logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
|
||||
return UnDefType.UNDEF;
|
||||
} catch (BenqProjectorException e) {
|
||||
logger.debug("Couldn't execute command '{}', {}", commandType, e.getMessage());
|
||||
closeConnection();
|
||||
return null;
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
private void sendDataToDevice(BenqProjectorCommandType commandType, Command command) {
|
||||
BenqProjectorDevice remoteController = device.get();
|
||||
|
||||
try {
|
||||
if (!remoteController.isConnected()) {
|
||||
remoteController.connect();
|
||||
}
|
||||
|
||||
switch (commandType) {
|
||||
case POWER:
|
||||
if (command == OnOffType.ON) {
|
||||
remoteController.setPower(Switch.ON);
|
||||
isPowerOn = true;
|
||||
} else {
|
||||
remoteController.setPower(Switch.OFF);
|
||||
isPowerOn = false;
|
||||
}
|
||||
break;
|
||||
case SOURCE:
|
||||
remoteController.setSource(command.toString());
|
||||
break;
|
||||
case PICTURE_MODE:
|
||||
remoteController.setPictureMode(command.toString());
|
||||
break;
|
||||
case ASPECT_RATIO:
|
||||
remoteController.setAspectRatio(command.toString());
|
||||
break;
|
||||
case FREEZE:
|
||||
remoteController.setFreeze(command == OnOffType.ON ? Switch.ON : Switch.OFF);
|
||||
break;
|
||||
case BLANK:
|
||||
remoteController.setBlank(command == OnOffType.ON ? Switch.ON : Switch.OFF);
|
||||
break;
|
||||
case DIRECTCMD:
|
||||
remoteController.sendDirectCommand(command.toString());
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown '{}' command!", commandType);
|
||||
break;
|
||||
}
|
||||
} catch (BenqProjectorCommandException e) {
|
||||
logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
|
||||
} catch (BenqProjectorException e) {
|
||||
logger.warn("Couldn't execute command '{}', {}", commandType, e.getMessage());
|
||||
closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
private void closeConnection() {
|
||||
BenqProjectorDevice remoteController = device.get();
|
||||
try {
|
||||
logger.debug("Closing connection to device '{}'", this.thing.getUID());
|
||||
remoteController.disconnect();
|
||||
updateStatus(ThingStatus.OFFLINE);
|
||||
} catch (BenqProjectorException e) {
|
||||
logger.debug("Error occurred when closing connection to device '{}'", this.thing.getUID(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="benqprojector" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>BenQ Projector Binding</name>
|
||||
<description>This binding is compatible with BenQ projectors</description>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,168 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="benqprojector"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="projector-serial">
|
||||
<label>BenQ Projector - Serial</label>
|
||||
<description>A BenQ projector connected via a serial port</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="source" typeId="source"/>
|
||||
<channel id="picturemode" typeId="picturemode"/>
|
||||
<channel id="aspectratio" typeId="aspectratio"/>
|
||||
<channel id="freeze" typeId="freeze"/>
|
||||
<channel id="blank" typeId="blank"/>
|
||||
<channel id="directcmd" typeId="directcmd"/>
|
||||
<channel id="lamptime" typeId="lamptime"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="serialPort" type="text" required="true">
|
||||
<label>Serial Port</label>
|
||||
<context>serial-port</context>
|
||||
<description>Serial Port to Use for Connecting to the BenQ Projector</description>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
|
||||
<label>Polling Interval</label>
|
||||
<description>Configures How Often to Poll the Projector for Updates (5-60; Default 10)</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="projector-tcp">
|
||||
<label>BenQ Projector - TCP/IP</label>
|
||||
<description>A BenQ projector connected via the built-in ethernet port or a serial over
|
||||
IP device</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="source" typeId="source"/>
|
||||
<channel id="picturemode" typeId="picturemode"/>
|
||||
<channel id="aspectratio" typeId="aspectratio"/>
|
||||
<channel id="freeze" typeId="freeze"/>
|
||||
<channel id="blank" typeId="blank"/>
|
||||
<channel id="directcmd" typeId="directcmd"/>
|
||||
<channel id="lamptime" typeId="lamptime"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="host" type="text" required="true">
|
||||
<label>Host</label>
|
||||
<context>network-address</context>
|
||||
<description>IP address for the projector or serial over IP device</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" min="1" max="65535" required="true">
|
||||
<label>Port</label>
|
||||
<description>Port for the projector or serial over IP device</description>
|
||||
<default>8000</default>
|
||||
</parameter>
|
||||
<parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
|
||||
<label>Polling Interval</label>
|
||||
<description>Configures How Often to Poll the Projector for Updates (5-60; Default 10)</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="source">
|
||||
<item-type>String</item-type>
|
||||
<label>Source</label>
|
||||
<description>Retrieve or Set the Input Source</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="hdmi">HDMI</option>
|
||||
<option value="hdmi2">HDMI2</option>
|
||||
<option value="ypbr">Component</option>
|
||||
<option value="rgb">Computer/YPbPr</option>
|
||||
<option value="rgb2">Computer/YPbPr2</option>
|
||||
<option value="vid">Video</option>
|
||||
<option value="svid">S-Video</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="picturemode">
|
||||
<item-type>String</item-type>
|
||||
<label>Picture Mode</label>
|
||||
<description>Retrieve or Set the Picture Mode</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="dynamic">Dynamic</option>
|
||||
<option value="preset">Presentation</option>
|
||||
<option value="srgb">sRGB</option>
|
||||
<option value="bright">Bright</option>
|
||||
<option value="livingroom">Living Room</option>
|
||||
<option value="game">Game</option>
|
||||
<option value="cine">Cinema</option>
|
||||
<option value="std">Standard/Vivid</option>
|
||||
<option value="football">Football</option>
|
||||
<option value="footballbt">Football Bright</option>
|
||||
<option value="user1">User 1</option>
|
||||
<option value="user2">User 2</option>
|
||||
<option value="user3">User 3</option>
|
||||
<option value="isfday">ISF Day</option>
|
||||
<option value="isfnight">ISF Night</option>
|
||||
<option value="threed">3-D</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="aspectratio">
|
||||
<item-type>String</item-type>
|
||||
<label>Aspect Ratio</label>
|
||||
<description>Retrieve or Set the Aspect Ratio</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="4:3">4:3</option>
|
||||
<option value="16:9">16:9</option>
|
||||
<option value="auto">Auto</option>
|
||||
<option value="lbox">Letterbox</option>
|
||||
<option value="wide">Wide</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="freeze">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Freeze Image</label>
|
||||
<description>Turn the Freeze Image Mode On or Off</description>
|
||||
</channel-type>
|
||||
<channel-type id="blank">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Screen Blank</label>
|
||||
<description>Turn the Screen Blank On or Off</description>
|
||||
</channel-type>
|
||||
<channel-type id="directcmd" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Direct Command</label>
|
||||
<description>Send a Command Directly to the Projector</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="mute=on">Mute On</option>
|
||||
<option value="mute=off">Mute Off</option>
|
||||
<option value="vol=+">Volume +</option>
|
||||
<option value="vol=-">Volume -</option>
|
||||
<option value="zoomI">Zoom In</option>
|
||||
<option value="zoomO">Zoom Out</option>
|
||||
<option value="auto">Zoom Auto</option>
|
||||
<option value="menu=on">Menu On</option>
|
||||
<option value="menu=off">Menu Off</option>
|
||||
<option value="up">Up</option>
|
||||
<option value="down">Down</option>
|
||||
<option value="left">Left</option>
|
||||
<option value="right">Right</option>
|
||||
<option value="enter">Enter</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="lamptime">
|
||||
<item-type>Number</item-type>
|
||||
<label>Lamp Time</label>
|
||||
<description>Retrieves the Lamp Hours</description>
|
||||
<state readOnly="true" pattern="%d h"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user