[benqprojector] Migrate benqprojector binding to OH3 (#10341)

Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
mlobstein 2021-06-12 05:00:08 -05:00 committed by GitHub
parent c12e189a13
commit e452de8e15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1597 additions and 0 deletions

View File

@ -26,6 +26,7 @@
/bundles/org.openhab.binding.autelis/ @digitaldan /bundles/org.openhab.binding.autelis/ @digitaldan
/bundles/org.openhab.binding.automower/ @maxpg /bundles/org.openhab.binding.automower/ @maxpg
/bundles/org.openhab.binding.avmfritz/ @cweitkamp /bundles/org.openhab.binding.avmfritz/ @cweitkamp
/bundles/org.openhab.binding.benqprojector/ @mlobstein
/bundles/org.openhab.binding.bigassfan/ @mhilbush /bundles/org.openhab.binding.bigassfan/ @mhilbush
/bundles/org.openhab.binding.bluetooth/ @cdjackson @cpmeister /bundles/org.openhab.binding.bluetooth/ @cdjackson @cpmeister
/bundles/org.openhab.binding.bluetooth.airthings/ @paulianttila /bundles/org.openhab.binding.bluetooth.airthings/ @paulianttila

View File

@ -121,6 +121,11 @@
<artifactId>org.openhab.binding.avmfritz</artifactId> <artifactId>org.openhab.binding.avmfritz</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.benqprojector</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.openhab.addons.bundles</groupId> <groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.bigassfan</artifactId> <artifactId>org.openhab.binding.bigassfan</artifactId>

View File

@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@ -0,0 +1,115 @@
# BenQ Projector Binding
This binding is compatible with BenQ projectors that support the control protocol via the built-in ethernet port, serial port or USB to serial adapter.
If your projector does not have built-in networking, you can connect to your projector's serial port via a TCP connection using a serial over IP device or by using`ser2net`.
The control protocol can be found here: https://business-display.benq.com/content/dam/bb/en/product/projector/corporate/lx770/quick-start-guide/lx770-rs232-control-guide-0-windows7-windows8-winxp.pdf
## Supported Things
This binding supports two thing types based on the connection used: `projector-serial` and `projector-tcp`.
## Discovery
The projector thing cannot be auto-discovered, it has to be configured manually.
## Binding Configuration
There are no overall binding configuration settings that need to be set.
All settings are through thing configuration parameters.
## Thing Configuration
The `projector-serial` thing has the following configuration parameters:
| Parameter | Name | Description | Required |
|-----------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
| serialPort | Serial Port | Serial port device name that is connected to the BenQ projector to control, e.g. COM1 on Windows, /dev/ttyS0 on Linux or /dev/tty.PL2303-0000103D on Mac. | yes |
| pollingInterval | Polling Interval | Polling interval in seconds to update channel states, range 5-60 seconds; default 10 seconds. | no |
The `projector-tcp` thing has the following configuration parameters:
| Parameter | Name | Description | Required |
|-----------------|------------------|-------------------------------------------------------------------------------------------------------------|----------|
| host | Host Name | Host Name or IP address for the projector or serial over IP device. | yes |
| port | Port | Port for the projector or serial over IP device, Default 8000 for BenQ projectors with built in networking. | yes |
| pollingInterval | Polling Interval | Polling interval in seconds to update channel states, range 5-60 seconds; default 10 seconds. | no |
Some notes:
* If using a serial port connection, the baud rate in the projector OSD menu must be set to 9600 bps.
* The _source_, _picturemode_ and _aspectratio_ channels include a dropdown with the most commonly used settings.
* Not all pre-defined dropdown options will be usable if your particular projector does support a given option.
* If your projector has an option that is not in the dropdown, the string code to access that option will be displayed by the channel when that option is selected by the remote control.
* By using the sitemap mapping or a rule to send that code back to the channel, any options that are missing in the binding can be accessed.
* On Linux, you may get an error stating the serial port cannot be opened when the benqprojector binding tries to load.
* You can get around this by adding the `openhab` user to the `dialout` group like this: `usermod -a -G dialout openhab`.
* Also on Linux you may have issues with the USB if using two serial USB devices e.g. benqprojector and RFXcom. See the [general documentation about serial port configuration](/docs/administration/serial.html) for more on symlinking the USB ports.
* Here is an example of ser2net.conf you can use to share your serial port /dev/ttyUSB0 on IP port 4444 using [ser2net Linux tool](https://sourceforge.net/projects/ser2net/) (take care, the baud rate is specific to the BenQ projector):
```
4444:raw:0:/dev/ttyUSB0:9600 8DATABITS NONE 1STOPBIT LOCAL
```
## Channels
| Channel | Item Type | Purpose | Values |
| ------------------ | --------- | --------------------------------------------------- | --------- |
| power | Switch | Powers the projector on or off. | |
| source | String | Retrieve or set the input source. | See above |
| picturemode | String | Retrieve or set the picture mode. | See above |
| aspectratio | String | Retrieve or set the aspect ratio. | See above |
| freeze | Switch | Turn the freeze image mode on or off. | |
| blank | Switch | Turn the screen blank mode on or off. | |
| directcmd | String | Send a command directly to the projector. | Send only |
| lamptime | Number | Retrieves the lamp hours. | Read only |
## Full Example
things/benq.things:
```
//serial port connection
benqprojector:projector-serial:hometheater "Projector" [ serialPort="COM5", pollingInterval=10 ]
// serial over IP connection
benqprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=8000, pollingInterval=10 ]
```
items/benq.items
```
Switch benqPower { channel="benqprojector:projector-serial:hometheater:power" }
String benqSource "Source [%s]" { channel="benqprojector:projector-serial:hometheater:source" }
String benqPictureMode "Picture Mode [%s]" { channel="benqprojector:projector-serial:hometheater:picturemode" }
String benqAspectRatio "Aspect Ratio [%s]" { channel="benqprojector:projector-serial:hometheater:aspectratio" }
Switch benqFreeze { channel="benqprojector:projector-serial:hometheater:freeze" }
Switch benqBlank { channel="benqprojector:projector-serial:hometheater:blank" }
String benqDirect { channel="benqprojector:projector-serial:hometheater:directcmd", autoupdate="false" }
Number benqLampTime "Lamp Time [%d h]" <switch> { channel="benqprojector:projector-serial:hometheater:lamptime" }
```
sitemaps/benq.sitemap
```
sitemap benq label="BenQ Projector Demo" {
Frame label="Controls" {
Switch item=benqPower label="Power"
Selection item=benqSource label="Source" mappings=["hdmi"="HDMI", "hdmi2"="HDMI2", "ypbr"="Component", "RGB"="Computer", "vid"="Video", "svid"="S-Video"]
Selection item=benqPictureMode label="Picture Mode"
Selection item=benqAspectRatio label="Aspect Ratio"
Switch item=benqFreeze label="Freeze"
Switch item=benqBlank label="Blank Screen"
Selection item=benqDirect label="Direct Command"
Text item=benqLampTime
}
Frame label="Advanced Controls" {
Switch item=benqDirect label="Image Flip" mappings=["pp=FT"="Front","pp=RE"="Rear","pp=FC"="Front Ceiling","pp=RC"="Rear Ceiling"]
Switch item=benqDirect label="Load Lens Memory" mappings=["lensload=m1"="1","lensload=m2"="2","lensload=m3"="3","lensload=m4"="4"]
Switch item=benqDirect label="Lamp Mode" mappings=["lampm=lnor"="Normal","lampm=eco"="Eco","lampm=seco"="SmartEco"]
Switch item=benqDirect label="Lamp Mode" mappings=["lampm=seco2"="SmartEco2","lampm=seco3"="SmartEco3","lampm=dimming"="Dimming","lampm=custom"="Custom"]
}
}
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.benqprojector</artifactId>
<name>openHAB Add-ons :: Bundles :: BenQ Projector Binding</name>
</project>

View File

@ -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>

View File

@ -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";
}

View File

@ -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);
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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) {
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -58,6 +58,7 @@
<module>org.openhab.binding.autelis</module> <module>org.openhab.binding.autelis</module>
<module>org.openhab.binding.automower</module> <module>org.openhab.binding.automower</module>
<module>org.openhab.binding.avmfritz</module> <module>org.openhab.binding.avmfritz</module>
<module>org.openhab.binding.benqprojector</module>
<module>org.openhab.binding.bigassfan</module> <module>org.openhab.binding.bigassfan</module>
<module>org.openhab.binding.bluetooth</module> <module>org.openhab.binding.bluetooth</module>
<module>org.openhab.binding.bluetooth.airthings</module> <module>org.openhab.binding.bluetooth.airthings</module>