[epsonprojector] Update epsonprojector binding for OH3 (#9021)

* baseline EpsonProjector code from ysc
* Improvements for OH3
* Finish epsonprojector binding for OH3
* improve exception logging
* cleanup exception logging
* Make connection specific thing types

Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
mlobstein 2020-12-03 19:34:23 -06:00 committed by GitHub
parent 48dcb27a61
commit ffe252ccd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 2841 additions and 0 deletions

View File

@ -63,6 +63,7 @@
/bundles/org.openhab.binding.enigma2/ @gdolfen
/bundles/org.openhab.binding.enocean/ @fruggy83
/bundles/org.openhab.binding.enturno/ @klocsson
/bundles/org.openhab.binding.epsonprojector/ @mlobstein
/bundles/org.openhab.binding.etherrain/ @dfad1469
/bundles/org.openhab.binding.evohome/ @Nebula83
/bundles/org.openhab.binding.exec/ @kgoderis

View File

@ -306,6 +306,11 @@
<artifactId>org.openhab.binding.enturno</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.epsonprojector</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.binding.etherrain</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,174 @@
# Epson Projector Binding
This binding is compatible with Epson projectors that support the ESC/VP21 protocol over a serial port or USB to serial adapter.
Alternatively, you can connect to your projector via a TCP connection using a serial over IP device or by using`ser2net`.
## 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:
- _serialPort_: Serial port device name that is connected to the Epson projector to control, e.g. COM1 on Windows, /dev/ttyS0 on Linux or /dev/tty.PL2303-0000103D on Mac
- _pollingInterval_: Polling interval in seconds to update channel states | 5-60 seconds; default 10 seconds
The `projector-tcp` thing has the following configuration parameters:
- _host_: IP address for the serial over IP device
- _port_: Port for the serial over IP device
- _pollingInterval_: Polling interval in seconds to update channel states | 5-60 seconds; default 10 seconds
Some notes:
* The binding should work on all Epson projectors that support the ESC/VP21 protocol, however not all binding channels will be useable on all projectors.
* The _source_ channel includes a dropdown with the most common source inputs.
* If your projector has a source input that is not in the dropdown, the two digit hex code to access that input will be displayed by the _source_ channel when that input is selected by the remote control.
* By using the sitemap mapping or a rule to send the input's code back to the _source_ channel, any source on the projector can be accessed by the binding.
* The following channels _aspectratio_, _colormode_, _luminance_, _gamma_ and _background_ are pre-populated with a full set of options and not every option will be useable on all projectors.
* If your projector has an option in one of the above mentioned channels that is not recognized by the binding, the channel will display 'UNKNOWN' if that un-recognized option is selected by the remote control.
* If the projector power is switched to off in the middle of a polling operation, some of the channel values may become undefined until the projector is switched on again.
* On Linux, you may get an error stating the serial port cannot be opened when the epsonprojector 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. epsonprojector 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 Epson 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. | |
| powerstate | String | Retrieves the textual power state of the projector. | Read only |
| source | String | Retrieve or set the input source. | See above |
| aspectratio | String | Retrieve or set the aspect ratio. | See above |
| colormode | String | Retrieve or set the color mode. | See above |
| freeze | Switch | Turn the freeze screen mode on or off. | |
| mute | Switch | Turn the AV mute on or off. | |
| volume | Number | Retrieve or set the volume. | 0 - +20 |
| luminance | String | Retrieve or set the lamp mode. | See above |
| brightness | Number | Retrieve or set the brightness. | -24 - +24 |
| contrast | Number | Retrieve or set the contrast. | -24 - +24 |
| density | Number | Retrieve or set the density (color saturation). | -32 - +32 |
| tint | Number | Retrieve or set the tint. | -32 - +32 |
| colortemperature | Number | Retrieve or set the color temperature. | 0 - +9 |
| fleshtemperature | Number | Retrieve or set the flesh temperature. | 0 - +6 |
| gamma | String | Retrieve or set the gamma setting. | See above |
| autokeystone | Switch | Turn the auto keystone mode on or off. | |
| verticalkeystone | Number | Retrieve or set the vertical keystone. | -30 - +30 |
| horizontalkeystone | Number | Retrieve or set the horizontal keystone. | -30 - +30 |
| verticalposition | Number | Retrieve or set the vertical position. | -8 - +10 |
| horizontalposition | Number | Retrieve or set the horizontal position. | -23 - +26 |
| verticalreverse | Switch | Turn the vertical reverse mode on or off. | |
| horizontalreverse | Switch | Turn the horizontal reverse mode on or off. | |
| background | String | Retrieve or set the background color/logo. | See above |
| keycode | Number | Send a key operation command to the projector. | Send only |
| lamptime | Number | Retrieves the lamp hours. | Read only |
| errcode | Number | Retrieves the last error code. | Read only |
| errmessage | String | Retrieves the description of the last error. | Read only |
## Full Example
things/epson.things:
```java
//serial port connection
epsonprojector:projector-serial:hometheater "Projector" [ serialPort="COM5", pollingInterval=10 ]
// serial over IP connection
epsonprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=4444, pollingInterval=10 ]
```
items/epson.items
```
Switch epsonPower { channel="epsonprojector:projector:hometheater:power" }
String epsonSource "Source [%s]" { channel="epsonprojector:projector:hometheater:source" }
String epsonAspectRatio "Aspect Ratio [%s]" { channel="epsonprojector:projector:hometheater:aspectratio" }
String epsonColorMode "Color Mode [%s]" { channel="epsonprojector:projector:hometheater:colormode" }
Switch epsonFreeze { channel="epsonprojector:projector:hometheater:freeze" }
Switch epsonMute { channel="epsonprojector:projector:hometheater:mute" }
Number epsonVolume { channel="epsonprojector:projector:hometheater:volume" }
String epsonLuminance "Lamp Mode [%s]" { channel="epsonprojector:projector:hometheater:luminance" }
Number epsonBrightness { channel="epsonprojector:projector:hometheater:brightness" }
Number epsonContrast { channel="epsonprojector:projector:hometheater:contrast" }
Number epsonDensity { channel="epsonprojector:projector:hometheater:density" }
Number epsonTint { channel="epsonprojector:projector:hometheater:tint" }
Number epsonColorTemperature { channel="epsonprojector:projector:hometheater:colortemperature" }
Number epsonFleshTemperature { channel="epsonprojector:projector:hometheater:fleshtemperature" }
String epsonGamma "Gamma [%s]" { channel="epsonprojector:projector:hometheater:gamma" }
Switch epsonAutokeystone { channel="epsonprojector:projector:hometheater:autokeystone" }
Number epsonVerticalKeystone { channel="epsonprojector:projector:hometheater:verticalkeystone" }
Number epsonHorizontalKeystone { channel="epsonprojector:projector:hometheater:horizontalkeystone" }
Number epsonVerticalPosition { channel="epsonprojector:projector:hometheater:verticalposition" }
Number epsonHorizontalPosition { channel="epsonprojector:projector:hometheater:horizontalposition" }
Switch epsonVerticalReverse { channel="epsonprojector:projector:hometheater:verticalreverse" }
Switch epsonHorizontalReverse { channel="epsonprojector:projector:hometheater:horizontalreverse" }
String epsonBackground "Background [%s]" { channel="epsonprojector:projector:hometheater:background" }
String epsonPowerState "Power State [%s]" <switch> { channel="epsonprojector:projector:hometheater:powerstate" }
Number epsonLampTime "Lamp Time [%d h]" <switch> { channel="epsonprojector:projector:hometheater:lamptime" }
Number epsonErrCode "Error Code [%d]" <"siren-on"> { channel="epsonprojector:projector:hometheater:errcode" }
String epsonErrMessage "Error Message [%s]" <"siren-off"> { channel="epsonprojector:projector:hometheater:errmessage" }
```
sitemaps/epson.sitemap
```
sitemap epson label="Epson Projector Demo"
{
Frame label="Controls" {
Switch item=epsonPower label="Power"
Selection item=epsonSource label="Source" mappings=["30"="HDMI1", "A0"="HDMI2", "14"="Component", "20"="PC DSUB", "41"="Video", "42"="S-Video"]
Switch item=epsonFreeze label="Freeze"
Switch item=epsonMute label="AV Mute"
Setpoint item=epsonVolume label="Volume"
}
Frame label="Adjust Image" {
Setpoint item=epsonBrightness label="Brightness"
Setpoint item=epsonContrast label="Contrast"
Setpoint item=epsonDensity label="Color Saturation"
Setpoint item=epsonTint label="Tint"
Switch item=epsonAutokeystone label="Auto Keystone"
Setpoint item=epsonVerticalKeystone label="Vertical Keystone"
Setpoint item=epsonHorizontalKeystone label="Horizontal Keystone"
Setpoint item=epsonVerticalPosition label="Vertical Position"
Setpoint item=epsonHorizontalPosition label="Horizontal Position"
Selection item=epsonBackground label="Background"
}
Frame label="Flip Projection" {
Switch item=epsonVerticalReverse label="Vertical Reverse"
Switch item=epsonHorizontalReverse label="Horizontal Reverse"
}
Frame label="Info" {
Text item=epsonAspectRatio
Text item=epsonColorMode
Text item=epsonColorTemperature label="Color Temperature"
Text item=epsonFleshTemperature label="Flesh Temperature"
Text item=epsonGamma
Text item=epsonLuminance
Text item=epsonPowerState
Text item=epsonLampTime
Text item=epsonErrCode
Text item=epsonErrMessage
}
}
```

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.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.epsonprojector</artifactId>
<name>openHAB Add-ons :: Bundles :: Epson Projector Binding</name>
</project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.epsonprojector-${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-epsonprojector" description="Epson 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.epsonprojector/${project.version}</bundle>
</feature>
</features>

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link EpsonProjectorBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Yannick Schaus - Initial contribution
*/
@NonNullByDefault
public class EpsonProjectorBindingConstants {
private static final String BINDING_ID = "epsonprojector";
// 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";
public static final String CHANNEL_TYPE_POWERSTATE = "powerstate";
public static final String CHANNEL_TYPE_LAMPTIME = "lamptime";
}

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Exception for Epson projector command errors.
*
* @author Michael Lobstein - Initial contribution
*/
@NonNullByDefault
public class EpsonProjectorCommandException extends Exception {
private static final long serialVersionUID = -8048415193494625295L;
public EpsonProjectorCommandException(String message) {
super(message);
}
}

View File

@ -0,0 +1,121 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.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 Pauli Anttila - Initial contribution
*/
@NonNullByDefault
public enum EpsonProjectorCommandType {
POWER("Power", SwitchItem.class),
POWER_STATE("PowerState", StringItem.class),
LAMP_TIME("LampTime", NumberItem.class),
KEY_CODE("KeyCode", NumberItem.class),
VKEYSTONE("VerticalKeystone", NumberItem.class),
HKEYSTONE("HorizontalKeystone", NumberItem.class),
AKEYSTONE("AutoKeystone", SwitchItem.class),
FREEZE("Freeze", SwitchItem.class),
ASPECT_RATIO("AspectRatio", StringItem.class),
LUMINANCE("Luminance", StringItem.class),
SOURCE("Source", StringItem.class),
BRIGHTNESS("Brightness", NumberItem.class),
CONTRAST("Contrast", NumberItem.class),
DENSITY("Density", NumberItem.class),
TINT("Tint", NumberItem.class),
COLOR_TEMP("ColorTemperature", NumberItem.class),
FLESH_TEMP("FleshTemperature", NumberItem.class),
COLOR_MODE("ColorMode", StringItem.class),
HPOSITION("HorizontalPosition", NumberItem.class),
VPOSITION("VerticalPosition", NumberItem.class),
GAMMA("Gamma", StringItem.class),
VOLUME("Volume", NumberItem.class),
MUTE("Mute", SwitchItem.class),
HREVERSE("HorizontalReverse", SwitchItem.class),
VREVERSE("VerticalReverse", SwitchItem.class),
BACKGROUND("Background", StringItem.class),
ERR_CODE("ErrCode", NumberItem.class),
ERR_MESSAGE("ErrMessage", StringItem.class),;
private final String text;
private Class<? extends Item> itemClass;
private EpsonProjectorCommandType(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 (EpsonProjectorCommandType c : EpsonProjectorCommandType.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 EpsonProjectorCommandType getCommandType(String commandTypeText) throws IllegalArgumentException {
for (EpsonProjectorCommandType c : EpsonProjectorCommandType.values()) {
if (c.text.equalsIgnoreCase(commandTypeText)) {
return c;
}
}
throw new IllegalArgumentException("Not valid command type");
}
}

View File

@ -0,0 +1,626 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal;
import java.time.Duration;
import java.util.concurrent.ScheduledExecutorService;
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.epsonprojector.internal.configuration.EpsonProjectorConfiguration;
import org.openhab.binding.epsonprojector.internal.connector.EpsonProjectorConnector;
import org.openhab.binding.epsonprojector.internal.connector.EpsonProjectorSerialConnector;
import org.openhab.binding.epsonprojector.internal.connector.EpsonProjectorTcpConnector;
import org.openhab.binding.epsonprojector.internal.enums.AspectRatio;
import org.openhab.binding.epsonprojector.internal.enums.Background;
import org.openhab.binding.epsonprojector.internal.enums.ColorMode;
import org.openhab.binding.epsonprojector.internal.enums.ErrorMessage;
import org.openhab.binding.epsonprojector.internal.enums.Gamma;
import org.openhab.binding.epsonprojector.internal.enums.Luminance;
import org.openhab.binding.epsonprojector.internal.enums.PowerStatus;
import org.openhab.binding.epsonprojector.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 Epson projector.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public class EpsonProjectorDevice {
private static final int[] MAP64 = new int[] { 0, 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, 66,
70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126, 129, 133, 137, 141, 145, 149, 153, 157,
161, 165, 169, 173, 177, 181, 185, 189, 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240,
244, 248, 252 };
private static final int[] MAP60 = new int[] { 0, 4, 8, 12, 16, 20, 25, 29, 33, 37, 41, 46, 50, 54, 58, 62, 67, 71,
75, 79, 83, 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, 142, 146, 151, 155, 159, 163, 167,
172, 176, 180, 184, 188, 193, 197, 201, 205, 209, 214, 218, 222, 226, 230, 235, 239, 243, 247, 251 };
private static final int[] MAP49 = new int[] { 0, 5, 10, 15, 20, 25, 30, 35, 40, 46, 51, 56, 61, 66, 71, 76, 81, 87,
92, 97, 102, 107, 112, 117, 122, 128, 133, 138, 143, 148, 153, 158, 163, 168, 174, 179, 184, 189, 194, 199,
204, 209, 215, 220, 225, 230, 235, 240, 245, 250 };
private static final int[] MAP48 = new int[] { 0, 5, 10, 15, 20, 26, 31, 36, 41, 47, 52, 57, 62, 67, 73, 78, 83, 88,
94, 99, 104, 109, 114, 120, 125, 130, 135, 141, 146, 151, 156, 161, 167, 172, 177, 182, 188, 193, 198, 203,
208, 214, 219, 224, 229, 235, 240, 245, 250 };
private static final int[] MAP20 = new int[] { 0, 12, 24, 36, 48, 60, 73, 85, 97, 109, 121, 134, 146, 158, 170, 182,
195, 207, 219, 231, 243 };
private static final int[] MAP18 = new int[] { 0, 13, 26, 40, 53, 67, 80, 94, 107, 121, 134, 148, 161, 175, 188,
202, 215, 229, 242 };
private static final int[] MAP_COLOR_TEMP = new int[] { 0, 25, 51, 76, 102, 128, 153, 179, 204, 230 };
private static final int[] MAP_FLESH_COLOR = new int[] { 0, 36, 73, 109, 146, 182, 219 };
private static final int DEFAULT_TIMEOUT = 5 * 1000;
private static final int POWER_ON_TIMEOUT = 100 * 1000;
private static final int POWER_OFF_TIMEOUT = 130 * 1000;
private static final int LAMP_REFRESH_WAIT_MINUTES = 5;
private static final String ON = "ON";
private static final String ERR = "ERR";
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorDevice.class);
private @Nullable ScheduledExecutorService scheduler = null;
private @Nullable ScheduledFuture<?> timeoutJob;
private EpsonProjectorConnector connection;
private ExpiringCache<Integer> cachedLampHours = new ExpiringCache<>(Duration.ofMinutes(LAMP_REFRESH_WAIT_MINUTES),
this::queryLamp);
private boolean connected = false;
private boolean ready = true;
public EpsonProjectorDevice(SerialPortManager serialPortManager, EpsonProjectorConfiguration config) {
connection = new EpsonProjectorSerialConnector(serialPortManager, config.serialPort);
}
public EpsonProjectorDevice(EpsonProjectorConfiguration config) {
connection = new EpsonProjectorTcpConnector(config.host, config.port);
}
public boolean isReady() {
return ready;
}
public void setScheduler(ScheduledExecutorService scheduler) {
this.scheduler = scheduler;
}
private synchronized @Nullable String sendQuery(String query, int timeout)
throws EpsonProjectorCommandException, EpsonProjectorException {
logger.debug("Query: '{}'", query);
String response = connection.sendMessage(query, timeout);
if (response.length() == 0) {
throw new EpsonProjectorException("No response received");
}
response = response.replace("\r:", "");
logger.debug("Response: '{}'", response);
if (ERR.equals(response)) {
throw new EpsonProjectorCommandException("Error response received for command: " + query);
}
if ("PWR OFF".equals(query) && ":".equals(response)) {
// When PWR OFF command is sent, next command can be sent 10 seconds after the colon is received
logger.debug("Refusing further commands for 10 seconds to power OFF completion");
ready = false;
ScheduledExecutorService scheduler = this.scheduler;
if (scheduler != null) {
timeoutJob = scheduler.schedule(() -> {
ready = true;
}, 10, TimeUnit.SECONDS);
}
}
return response;
}
private String splitResponse(@Nullable String response) throws EpsonProjectorException {
if (response != null && !"".equals(response)) {
String[] pieces = response.split("=");
if (pieces.length < 2) {
throw new EpsonProjectorException("Invalid response from projector: " + response);
}
return pieces[1].trim();
} else {
throw new EpsonProjectorException("No response received");
}
}
protected void sendCommand(String command, int timeout)
throws EpsonProjectorCommandException, EpsonProjectorException {
sendQuery(command, timeout);
}
protected void sendCommand(String command) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(command, DEFAULT_TIMEOUT);
}
protected int queryInt(String query, int timeout, int radix)
throws EpsonProjectorCommandException, EpsonProjectorException {
String response = sendQuery(query, timeout);
String str = splitResponse(response);
// if the response has two number groups, get the first one (Aspect Ratio does this)
if (str.contains(" ")) {
String[] subStr = str.split(" ");
str = subStr[0];
}
return Integer.parseInt(str, radix);
}
protected int queryInt(String query, int timeout) throws EpsonProjectorCommandException, EpsonProjectorException {
return queryInt(query, timeout, 10);
}
protected int queryInt(String query) throws EpsonProjectorCommandException, EpsonProjectorException {
return queryInt(query, DEFAULT_TIMEOUT, 10);
}
protected int queryHexInt(String query, int timeout)
throws EpsonProjectorCommandException, EpsonProjectorException {
return queryInt(query, timeout, 16);
}
protected int queryHexInt(String query) throws EpsonProjectorCommandException, EpsonProjectorException {
return queryInt(query, DEFAULT_TIMEOUT, 16);
}
protected String queryString(String query) throws EpsonProjectorCommandException, EpsonProjectorException {
String response = sendQuery(query, DEFAULT_TIMEOUT);
return splitResponse(response);
}
public void connect() throws EpsonProjectorException {
connection.connect();
connected = true;
}
public void disconnect() throws EpsonProjectorException {
connection.disconnect();
connected = false;
ScheduledFuture<?> timeoutJob = this.timeoutJob;
if (timeoutJob != null) {
timeoutJob.cancel(true);
this.timeoutJob = null;
}
}
public boolean isConnected() {
return connected;
}
/*
* Power
*/
public PowerStatus getPowerStatus() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryInt("PWR?");
return PowerStatus.forValue(val);
}
public void setPower(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("PWR %s", value.name()), value == Switch.ON ? POWER_ON_TIMEOUT : POWER_OFF_TIMEOUT);
}
/*
* Key code
*/
public void sendKeyCode(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("KEY %02X", value));
}
/*
* Vertical Keystone
*/
public int getVerticalKeystone() throws EpsonProjectorCommandException, EpsonProjectorException {
int vkey = queryInt("VKEYSTONE?");
for (int i = 0; i < MAP60.length; i++) {
if (vkey == MAP60[i]) {
return i - 30;
}
}
return 0;
}
public void setVerticalKeystone(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 30;
if (value >= 0 && value <= 60) {
sendCommand(String.format("VKEYSTONE %d", MAP60[value]));
}
}
/*
* Horizontal Keystone
*/
public int getHorizontalKeystone() throws EpsonProjectorCommandException, EpsonProjectorException {
int hkey = queryInt("HKEYSTONE?");
for (int i = 0; i < MAP60.length; i++) {
if (hkey == MAP60[i]) {
return i - 30;
}
}
return 0;
}
public void setHorizontalKeystone(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 30;
if (value >= 0 && value <= 60) {
sendCommand(String.format("HKEYSTONE %d", MAP60[value]));
}
}
/*
* Auto Keystone
*/
public Switch getAutoKeystone() throws EpsonProjectorCommandException, EpsonProjectorException {
String val = queryString("AUTOKEYSTONE?");
return val.equals(ON) ? Switch.ON : Switch.OFF;
}
public void setAutoKeystone(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("AUTOKEYSTONE %s", value.name()), DEFAULT_TIMEOUT);
}
/*
* Freeze
*/
public Switch getFreeze() throws EpsonProjectorCommandException, EpsonProjectorException {
String val = queryString("FREEZE?");
return val.equals(ON) ? Switch.ON : Switch.OFF;
}
public void setFreeze(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("FREEZE %s", value.name()), DEFAULT_TIMEOUT);
}
/*
* Aspect Ratio
*/
public AspectRatio getAspectRatio() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryHexInt("ASPECT?");
return AspectRatio.forValue(val);
}
public void setAspectRatio(AspectRatio value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("ASPECT %02X", value.toInt()));
}
/*
* Luminance
*/
public Luminance getLuminance() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryHexInt("LUMINANCE?");
return Luminance.forValue(val);
}
public void setLuminance(Luminance value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("LUMINANCE %02X", value.toInt()));
}
/*
* Source
*/
public String getSource() throws EpsonProjectorCommandException, EpsonProjectorException {
return queryString("SOURCE?");
}
public void setSource(String value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("SOURCE %s", value));
}
/*
* Brightness
*/
public int getBrightness() throws EpsonProjectorCommandException, EpsonProjectorException {
int brt = queryInt("BRIGHT?");
for (int i = 0; i < MAP48.length; i++) {
if (brt == MAP48[i]) {
return i - 24;
}
}
return 0;
}
public void setBrightness(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 24;
if (value >= 0 && value <= 48) {
sendCommand(String.format("BRIGHT %d", MAP48[value]));
}
}
/*
* Contrast
*/
public int getContrast() throws EpsonProjectorCommandException, EpsonProjectorException {
int con = queryInt("CONTRAST?");
for (int i = 0; i < MAP48.length; i++) {
if (con == MAP48[i]) {
return i - 24;
}
}
return 0;
}
public void setContrast(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 24;
if (value >= 0 && value <= 48) {
sendCommand(String.format("CONTRAST %d", MAP48[value]));
}
}
/*
* Density
*/
public int getDensity() throws EpsonProjectorCommandException, EpsonProjectorException {
int den = queryInt("DENSITY?");
for (int i = 0; i < MAP64.length; i++) {
if (den == MAP64[i]) {
return i - 32;
}
}
return 0;
}
public void setDensity(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 32;
if (value >= 0 && value <= 64) {
sendCommand(String.format("DENSITY %d", MAP64[value]));
}
}
/*
* Tint
*/
public int getTint() throws EpsonProjectorCommandException, EpsonProjectorException {
int tint = queryInt("TINT?");
for (int i = 0; i < MAP64.length; i++) {
if (tint == MAP64[i]) {
return i - 32;
}
}
return 0;
}
public void setTint(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 32;
if (value >= 0 && value <= 64) {
sendCommand(String.format("TINT %d", MAP64[value]));
}
}
/*
* Color Temperature
*/
public int getColorTemperature() throws EpsonProjectorCommandException, EpsonProjectorException {
int ctemp = queryInt("CTEMP?");
for (int i = 0; i < MAP_COLOR_TEMP.length; i++) {
if (ctemp == MAP_COLOR_TEMP[i]) {
return i;
}
}
return 0;
}
public void setColorTemperature(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
if (value >= 0 && value <= 9) {
sendCommand(String.format("CTEMP %d", MAP_COLOR_TEMP[value]));
}
}
/*
* Flesh Color
*/
public int getFleshColor() throws EpsonProjectorCommandException, EpsonProjectorException {
int fclr = queryInt("FCOLOR?");
for (int i = 0; i < MAP_FLESH_COLOR.length; i++) {
if (fclr == MAP_FLESH_COLOR[i]) {
return i;
}
}
return 0;
}
public void setFleshColor(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
if (value >= 0 && value <= 6) {
sendCommand(String.format("FCOLOR %d", MAP_FLESH_COLOR[value]));
}
}
/*
* Color Mode
*/
public ColorMode getColorMode() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryHexInt("CMODE?");
return ColorMode.forValue(val);
}
public void setColorMode(ColorMode value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("CMODE %02X", value.toInt()));
}
/*
* Horizontal Position
*/
public int getHorizontalPosition() throws EpsonProjectorCommandException, EpsonProjectorException {
int hpos = queryInt("HPOS?");
for (int i = 0; i < MAP49.length; i++) {
if (hpos == MAP49[i]) {
return i - 23;
}
}
return 0;
}
public void setHorizontalPosition(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 23;
if (value >= 0 && value <= 49) {
sendCommand(String.format("HPOS %d", MAP49[value]));
}
}
/*
* Vertical Position
*/
public int getVerticalPosition() throws EpsonProjectorCommandException, EpsonProjectorException {
int vpos = queryInt("VPOS?");
for (int i = 0; i < MAP18.length; i++) {
if (vpos == MAP18[i]) {
return i - 8;
}
}
return 0;
}
public void setVerticalPosition(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
value = value + 8;
if (value >= 0 && value <= 18) {
sendCommand(String.format("VPOS %d", MAP18[value]));
}
}
/*
* Gamma
*/
public Gamma getGamma() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryHexInt("GAMMA?");
return Gamma.forValue(val);
}
public void setGamma(Gamma value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("GAMMA %02X", value.toInt()));
}
/*
* Volume
*/
public int getVolume() throws EpsonProjectorCommandException, EpsonProjectorException {
int vol = queryInt("VOL?");
for (int i = 0; i < MAP20.length; i++) {
if (vol == MAP20[i]) {
return i;
}
}
return 0;
}
public void setVolume(int value) throws EpsonProjectorCommandException, EpsonProjectorException {
if (value >= 0 && value <= 20) {
sendCommand(String.format("VOL %d", MAP20[value]));
}
}
/*
* AV Mute
*/
public Switch getMute() throws EpsonProjectorCommandException, EpsonProjectorException {
String val = queryString("MUTE?");
return val.equals(ON) ? Switch.ON : Switch.OFF;
}
public void setMute(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("MUTE %s", value.name()));
}
/*
* Horizontal Reverse
*/
public Switch getHorizontalReverse() throws EpsonProjectorCommandException, EpsonProjectorException {
String val = queryString("HREVERSE?");
return val.equals(ON) ? Switch.ON : Switch.OFF;
}
public void setHorizontalReverse(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("HREVERSE %s", value.name()));
}
/*
* Vertical Reverse
*/
public Switch getVerticalReverse() throws EpsonProjectorCommandException, EpsonProjectorException {
String val = queryString("VREVERSE?");
return val.equals(ON) ? Switch.ON : Switch.OFF;
}
public void setVerticalReverse(Switch value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("VREVERSE %s", value.name()));
}
/*
* Background Select for AV Mute
*/
public Background getBackground() throws EpsonProjectorCommandException, EpsonProjectorException {
int val = queryHexInt("MSEL?");
return Background.forValue(val);
}
public void setBackground(Background value) throws EpsonProjectorCommandException, EpsonProjectorException {
sendCommand(String.format("MSEL %02X", value.toInt()));
}
/*
* Lamp Time (hours) - get from cache
*/
public int getLampTime() throws EpsonProjectorCommandException, EpsonProjectorException {
Integer lampHours = cachedLampHours.getValue();
if (lampHours != null) {
return lampHours.intValue();
} else {
throw new EpsonProjectorCommandException("cachedLampHours returned null");
}
}
/*
* Get Lamp Time
*/
private @Nullable Integer queryLamp() {
try {
return Integer.valueOf(queryInt("LAMP?"));
} catch (EpsonProjectorCommandException | EpsonProjectorException e) {
logger.debug("Error executing command LAMP?", e);
return null;
}
}
/*
* Error Code
*/
public int getError() throws EpsonProjectorCommandException, EpsonProjectorException {
return queryHexInt("ERR?");
}
/*
* Error Code Description
*/
public String getErrorString() throws EpsonProjectorCommandException, EpsonProjectorException {
int err = queryInt("ERR?");
return ErrorMessage.forCode(err);
}
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Exception for Epson projector errors.
*
* @author Pauli Anttila - Initial contribution
*/
@NonNullByDefault
public class EpsonProjectorException extends Exception {
private static final long serialVersionUID = -8048415193494625295L;
public EpsonProjectorException(String message) {
super(message);
}
public EpsonProjectorException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal;
import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.epsonprojector.internal.handler.EpsonProjectorHandler;
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 EpsonProjectorHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Yannick Schaus - Initial contribution
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
@Component(configurationPid = "binding.epsonprojector", service = ThingHandlerFactory.class)
public class EpsonProjectorHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
Stream.of(THING_TYPE_PROJECTOR_SERIAL, THING_TYPE_PROJECTOR_TCP).collect(Collectors.toSet()));
private final SerialPortManager serialPortManager;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Activate
public EpsonProjectorHandlerFactory(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 EpsonProjectorHandler(thing, serialPortManager);
}
return null;
}
}

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link EpsonProjectorConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Yannick Schaus - Initial contribution
*/
@NonNullByDefault
public class EpsonProjectorConfiguration {
/**
* 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,52 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.connector;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorException;
/**
* Base class for Epson projector communication.
*
* @author Pauli Anttila - Initial contribution
*/
@NonNullByDefault
public interface EpsonProjectorConnector {
/**
* Procedure for connecting to projector.
*
* @throws EpsonProjectorException
*/
void connect() throws EpsonProjectorException;
/**
* Procedure for disconnecting to projector controller.
*
* @throws EpsonProjectorException
*/
void disconnect() throws EpsonProjectorException;
/**
* Procedure for send raw data to projector.
*
* @param data
* Message to send.
*
* @param timeout
* timeout to wait response in milliseconds.
*
* @throws EpsonProjectorException
*/
String sendMessage(String data, int timeout) throws EpsonProjectorException;
}

View File

@ -0,0 +1,209 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.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.epsonprojector.internal.EpsonProjectorException;
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 Pauli Anttila - Initial contribution
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public class EpsonProjectorSerialConnector implements EpsonProjectorConnector, SerialPortEventListener {
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorSerialConnector.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 EpsonProjectorSerialConnector(SerialPortManager serialPortManager, String serialPort) {
this.serialPortManager = serialPortManager;
this.serialPortName = serialPort;
}
@Override
public void connect() throws EpsonProjectorException {
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 EpsonProjectorException(e);
}
}
@Override
public void disconnect() throws EpsonProjectorException {
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, int timeout) throws EpsonProjectorException {
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 sendMmsg(data, timeout);
} else {
return "";
}
} catch (IOException e) {
logger.debug("IO error occurred...reconnect and resend once: {}", e.getMessage());
disconnect();
connect();
try {
return sendMmsg(data, timeout);
} catch (IOException e1) {
throw new EpsonProjectorException(e);
}
}
}
@Override
public void serialEvent(SerialPortEvent arg0) {
}
private String sendMmsg(String data, int timeout) throws IOException, EpsonProjectorException {
String resp = "";
InputStream in = this.in;
OutputStream out = this.out;
if (in != null && out != null) {
out.write(data.getBytes(StandardCharsets.US_ASCII));
out.write("\r\n".getBytes(StandardCharsets.US_ASCII));
out.flush();
long startTime = System.currentTimeMillis();
long elapsedTime = 0;
while (elapsedTime < timeout) {
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(":")) {
return resp;
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new EpsonProjectorException(e);
}
}
elapsedTime = System.currentTimeMillis() - startTime;
}
}
return resp;
}
}

View File

@ -0,0 +1,183 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.connector;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Connector for TCP communication.
*
* @author Pauli Anttila - Initial contribution
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public class EpsonProjectorTcpConnector implements EpsonProjectorConnector {
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorTcpConnector.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 EpsonProjectorTcpConnector(String ip, int port) {
this.ip = ip;
this.port = port;
}
@Override
public void connect() throws EpsonProjectorException {
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 EpsonProjectorException(e);
}
}
@Override
public void disconnect() throws EpsonProjectorException {
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, int timeout) throws EpsonProjectorException {
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 sendMmsg(data, timeout);
} else {
return "";
}
} catch (IOException e) {
logger.debug("IO error occurred...reconnect and resend once: {}", e.getMessage());
disconnect();
connect();
try {
return sendMmsg(data, timeout);
} catch (IOException e1) {
throw new EpsonProjectorException(e);
}
}
}
private String sendMmsg(String data, int timeout) throws IOException, EpsonProjectorException {
String resp = "";
InputStream in = this.in;
OutputStream out = this.out;
if (in != null && out != null) {
out.write(data.getBytes(StandardCharsets.US_ASCII));
out.write("\r\n".getBytes(StandardCharsets.US_ASCII));
out.flush();
long startTime = System.currentTimeMillis();
long elapsedTime = 0;
while (elapsedTime < timeout) {
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(":")) {
return resp;
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new EpsonProjectorException(e);
}
}
elapsedTime = System.currentTimeMillis() - startTime;
}
}
return resp;
}
}

View File

@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for AspectRatio.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum AspectRatio {
NORMAL(0x00),
RATIO4X3(0x10),
ZOOM4X3(0x12),
RATIO16X9(0x20),
UP16X9(0x21),
DOWN16X9(0x22),
AUTO(0x30),
FULL(0x40),
ZOOM(0x50),
REAL(0x60),
WIDE(0x70),
ANAMORPHIC(0x80),
SQUEEZE(0x90),
UNKNOWN(0xFF);
private final int value;
AspectRatio(int value) {
this.value = value;
}
public static AspectRatio forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for Background.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum Background {
BLACK(0x00),
BLUE(0x01),
LOGO(0x02),
UNKNOWN(0xFF);
private final int value;
Background(int value) {
this.value = value;
}
public static Background forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,74 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for ColorMode.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum ColorMode {
SRGB(0x01),
NORMAL(0x02),
MEETING(0x03),
PRESENTATION(0x04),
CINEMANIGHT(0x05),
DYNAMIC(0x06),
NATURAL(0x07),
SPORTS(0x08),
HD(0x09),
CUSTOM(0x10),
BLACKBOARD(0x11),
WHITEBOARD(0x12),
THX(0x13),
PHOTO(0x14),
CINEMA(0x15),
UNKNOWN16(0x16),
CINEMA3D(0x17),
DYNAMIC3D(0x18),
THX3D(0x19),
BWCINEMA(0x20),
UNKNOWN21(0x21),
DIGITALCINEMA(0x22),
SILVER(0x0A),
XVCOLOR(0x0B),
LIVINGROOM(0x0C),
DICOMSIM(0x0F),
UNKNOWN(0xFF);
private final int value;
ColorMode(int value) {
this.value = value;
}
public static ColorMode forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Messages for documented error codes.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum ErrorMessage {
NO_ERROR(0, "No error"),
ERROR1(1, "Fan error"),
ERROR3(3, "Lamp failure at power on"),
ERROR4(4, "High internal temperature error"),
ERROR6(6, "Lamp error"),
ERROR7(7, "Open Lamp cover door error"),
ERROR8(8, "Cinema filter error"),
ERROR9(9, "Electric dual-layered capacitor is disconnected"),
ERROR10(10, "Auto iris error"),
ERROR11(11, "Subsystem error"),
ERROR12(12, "Low air flow error"),
ERROR13(13, "Air filter air flow sensor error"),
ERROR14(14, "Power supply unit error (ballast)"),
ERROR15(15, "Shutter error"),
ERROR16(16, "Cooling system error (peltier element)"),
ERROR17(17, "Cooling system error (pump)"),
ERROR18(18, "Static iris error"),
ERROR19(19, "Power supply unit error (disagreement of ballast)"),
ERROR20(20, "Exhaust shutter error"),
ERROR21(21, "Obstacle detection error"),
ERROR22(22, "IF board discernment error");
private final int code;
private final String message;
ErrorMessage(int code, String message) {
this.code = code;
this.message = message;
}
public String getMessage() {
return message;
}
public static String forCode(int code) {
try {
return Arrays.stream(values()).filter(e -> e.code == code).findFirst().get().getMessage();
} catch (NoSuchElementException e) {
return "Unknown error code: " + code;
}
}
}

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for Gamma.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum Gamma {
G2_0(0x20),
G2_1(0x21),
G2_2(0x22),
G2_3(0x23),
G2_4(0x24),
CUSTOM(0xF0),
UNKNOWN(0xFF);
private final int value;
Gamma(int value) {
this.value = value;
}
public static Gamma forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for Luminance.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum Luminance {
NORMAL(0x00),
ECO(0x01),
MEDIUM(0x02),
UNKNOWN(0xFF);
private final int value;
Luminance(int value) {
this.value = value;
}
public static Luminance forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for PowerStatus.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public enum PowerStatus {
STANDBY(0x00),
ON(0x01),
WARMUP(0x02),
COOLDOWN(0x03),
STANDBYNETWORKON(0x04),
ABNORMALSTANDBY(0x05),
UNKNOWN(0xFF);
private final int value;
PowerStatus(int value) {
this.value = value;
}
public static PowerStatus forValue(int value) {
try {
return Arrays.stream(values()).filter(e -> e.value == value).findFirst().get();
} catch (NoSuchElementException e) {
return UNKNOWN;
}
}
public int toInt() {
return value;
}
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Valid values for Epson switch commands.
*
* @author Pauli Anttila - Initial contribution
* @author Yannick Schaus - Refactoring
*/
@NonNullByDefault
public enum Switch {
ON,
OFF;
}

View File

@ -0,0 +1,417 @@
/**
* Copyright (c) 2010-2020 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.epsonprojector.internal.handler;
import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
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.epsonprojector.internal.EpsonProjectorCommandException;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorCommandType;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorException;
import org.openhab.binding.epsonprojector.internal.configuration.EpsonProjectorConfiguration;
import org.openhab.binding.epsonprojector.internal.enums.AspectRatio;
import org.openhab.binding.epsonprojector.internal.enums.Background;
import org.openhab.binding.epsonprojector.internal.enums.ColorMode;
import org.openhab.binding.epsonprojector.internal.enums.Gamma;
import org.openhab.binding.epsonprojector.internal.enums.Luminance;
import org.openhab.binding.epsonprojector.internal.enums.PowerStatus;
import org.openhab.binding.epsonprojector.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 EpsonProjectorHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Pauli Anttila, Yannick Schaus - Initial contribution
* @author Michael Lobstein - Improvements for OH3
*/
@NonNullByDefault
public class EpsonProjectorHandler extends BaseThingHandler {
private static final int DEFAULT_POLLING_INTERVAL_SEC = 10;
private final Logger logger = LoggerFactory.getLogger(EpsonProjectorHandler.class);
private final SerialPortManager serialPortManager;
private @Nullable ScheduledFuture<?> pollingJob;
private Optional<EpsonProjectorDevice> device = Optional.empty();
private boolean isPowerOn = false;
private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
public EpsonProjectorHandler(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) {
updateChannelState(channel);
}
} else {
EpsonProjectorCommandType epsonCommand = EpsonProjectorCommandType.getCommandType(channelId);
sendDataToDevice(epsonCommand, command);
}
}
@Override
public void initialize() {
EpsonProjectorConfiguration config = getConfigAs(EpsonProjectorConfiguration.class);
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_PROJECTOR_SERIAL.equals(thingTypeUID)) {
device = Optional.of(new EpsonProjectorDevice(serialPortManager, config));
} else if (THING_TYPE_PROJECTOR_TCP.equals(thingTypeUID)) {
device = Optional.of(new EpsonProjectorDevice(config));
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
pollingInterval = config.pollingInterval;
device.ifPresent(dev -> dev.setScheduler(scheduler));
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 & lamp time when projector is off
if (isPowerOn || (channel.getUID().getId().equals(CHANNEL_TYPE_POWER)
|| channel.getUID().getId().equals(CHANNEL_TYPE_LAMPTIME))) {
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;
}
EpsonProjectorCommandType epsonCommand = EpsonProjectorCommandType.getCommandType(channel.getUID().getId());
State state = queryDataFromDevice(epsonCommand);
if (state != null) {
updateStatus(ThingStatus.ONLINE);
if (isLinked(channel.getUID())) {
updateState(channel.getUID(), state);
}
}
} catch (IllegalArgumentException e) {
logger.warn("Unknown channel {}", channel.getUID().getId());
}
}
@Nullable
private State queryDataFromDevice(EpsonProjectorCommandType commandType) {
EpsonProjectorDevice remoteController = device.get();
try {
if (!remoteController.isConnected()) {
remoteController.connect();
}
if (!remoteController.isReady()) {
logger.debug("Refusing command {} while not ready", commandType.toString());
return null;
}
switch (commandType) {
case AKEYSTONE:
Switch autoKeystone = remoteController.getAutoKeystone();
return autoKeystone == Switch.ON ? OnOffType.ON : OnOffType.OFF;
case ASPECT_RATIO:
AspectRatio aspectRatio = remoteController.getAspectRatio();
return new StringType(aspectRatio.toString());
case BACKGROUND:
Background background = remoteController.getBackground();
return new StringType(background.toString());
case BRIGHTNESS:
int brightness = remoteController.getBrightness();
return new DecimalType(brightness);
case COLOR_MODE:
ColorMode colorMode = remoteController.getColorMode();
return new StringType(colorMode.toString());
case COLOR_TEMP:
int ctemp = remoteController.getColorTemperature();
return new DecimalType(ctemp);
case CONTRAST:
int contrast = remoteController.getContrast();
return new DecimalType(contrast);
case DENSITY:
int density = remoteController.getDensity();
return new DecimalType(density);
case ERR_CODE:
int err = remoteController.getError();
return new DecimalType(err);
case ERR_MESSAGE:
String errString = remoteController.getErrorString();
return new StringType(errString);
case FLESH_TEMP:
int fleshColor = remoteController.getFleshColor();
return new DecimalType(fleshColor);
case FREEZE:
Switch freeze = remoteController.getFreeze();
return freeze == Switch.ON ? OnOffType.ON : OnOffType.OFF;
case GAMMA:
Gamma gamma = remoteController.getGamma();
return new StringType(gamma.toString());
case HKEYSTONE:
int hKeystone = remoteController.getHorizontalKeystone();
return new DecimalType(hKeystone);
case HPOSITION:
int hPosition = remoteController.getHorizontalPosition();
return new DecimalType(hPosition);
case HREVERSE:
Switch hReverse = remoteController.getHorizontalReverse();
return hReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
case KEY_CODE:
break;
case LAMP_TIME:
int lampTime = remoteController.getLampTime();
return new DecimalType(lampTime);
case LUMINANCE:
Luminance luminance = remoteController.getLuminance();
return new StringType(luminance.toString());
case MUTE:
Switch mute = remoteController.getMute();
return mute == Switch.ON ? OnOffType.ON : OnOffType.OFF;
case POWER:
PowerStatus powerStatus = remoteController.getPowerStatus();
if (isLinked(CHANNEL_TYPE_POWERSTATE)) {
updateState(CHANNEL_TYPE_POWERSTATE, new StringType(powerStatus.toString()));
}
if (powerStatus == PowerStatus.ON || powerStatus == PowerStatus.WARMUP) {
isPowerOn = true;
return OnOffType.ON;
} else {
isPowerOn = false;
return OnOffType.OFF;
}
case POWER_STATE:
return null;
case SOURCE:
return new StringType(remoteController.getSource());
case TINT:
int tint = remoteController.getTint();
return new DecimalType(tint);
case VKEYSTONE:
int vKeystone = remoteController.getVerticalKeystone();
return new DecimalType(vKeystone);
case VOLUME:
int volume = remoteController.getVolume();
return new DecimalType(volume);
case VPOSITION:
int vPosition = remoteController.getVerticalPosition();
return new DecimalType(vPosition);
case VREVERSE:
Switch vReverse = remoteController.getVerticalReverse();
return vReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
default:
logger.warn("Unknown '{}' command!", commandType);
return UnDefType.UNDEF;
}
} catch (EpsonProjectorCommandException e) {
logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
return UnDefType.UNDEF;
} catch (EpsonProjectorException e) {
logger.debug("Couldn't execute command '{}', {}", commandType, e.getMessage());
closeConnection();
return null;
}
return UnDefType.UNDEF;
}
private void sendDataToDevice(EpsonProjectorCommandType commandType, Command command) {
EpsonProjectorDevice remoteController = device.get();
try {
if (!remoteController.isConnected()) {
remoteController.connect();
}
if (!remoteController.isReady()) {
logger.debug("Refusing command '{}' while not ready", commandType.toString());
return;
}
switch (commandType) {
case AKEYSTONE:
remoteController.setAutoKeystone((command == OnOffType.ON ? Switch.ON : Switch.OFF));
break;
case ASPECT_RATIO:
remoteController.setAspectRatio(AspectRatio.valueOf(command.toString()));
break;
case BACKGROUND:
remoteController.setBackground(Background.valueOf(command.toString()));
break;
case BRIGHTNESS:
remoteController.setBrightness(((DecimalType) command).intValue());
break;
case COLOR_MODE:
remoteController.setColorMode(ColorMode.valueOf(command.toString()));
break;
case COLOR_TEMP:
remoteController.setColorTemperature(((DecimalType) command).intValue());
break;
case CONTRAST:
remoteController.setContrast(((DecimalType) command).intValue());
break;
case DENSITY:
remoteController.setDensity(((DecimalType) command).intValue());
break;
case ERR_CODE:
logger.warn("'{}' is read only parameter", commandType);
break;
case ERR_MESSAGE:
logger.warn("'{}' is read only parameter", commandType);
break;
case FLESH_TEMP:
remoteController.setFleshColor(((DecimalType) command).intValue());
break;
case FREEZE:
remoteController.setFreeze(command == OnOffType.ON ? Switch.ON : Switch.OFF);
break;
case GAMMA:
remoteController.setGamma(Gamma.valueOf(command.toString()));
break;
case HKEYSTONE:
remoteController.setHorizontalKeystone(((DecimalType) command).intValue());
break;
case HPOSITION:
remoteController.setHorizontalPosition(((DecimalType) command).intValue());
break;
case HREVERSE:
remoteController.setHorizontalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
break;
case KEY_CODE:
remoteController.sendKeyCode(((DecimalType) command).intValue());
break;
case LAMP_TIME:
logger.warn("'{}' is read only parameter", commandType);
break;
case LUMINANCE:
remoteController.setLuminance(Luminance.valueOf(command.toString()));
break;
case MUTE:
remoteController.setMute((command == OnOffType.ON ? Switch.ON : Switch.OFF));
break;
case POWER:
if (command == OnOffType.ON) {
remoteController.setPower(Switch.ON);
isPowerOn = true;
} else {
remoteController.setPower(Switch.OFF);
isPowerOn = false;
}
break;
case POWER_STATE:
logger.warn("'{}' is read only parameter", commandType);
break;
case SOURCE:
remoteController.setSource(command.toString());
break;
case TINT:
remoteController.setTint(((DecimalType) command).intValue());
break;
case VKEYSTONE:
remoteController.setVerticalKeystone(((DecimalType) command).intValue());
break;
case VOLUME:
remoteController.setVolume(((DecimalType) command).intValue());
break;
case VPOSITION:
remoteController.setVerticalPosition(((DecimalType) command).intValue());
break;
case VREVERSE:
remoteController.setVerticalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
break;
default:
logger.warn("Unknown '{}' command!", commandType);
break;
}
} catch (EpsonProjectorCommandException e) {
logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
} catch (EpsonProjectorException e) {
logger.warn("Couldn't execute command '{}', {}", commandType, e.getMessage());
closeConnection();
}
}
private void closeConnection() {
EpsonProjectorDevice remoteController = device.get();
try {
logger.debug("Closing connection to device '{}'", this.thing.getUID());
remoteController.disconnect();
updateStatus(ThingStatus.OFFLINE);
} catch (EpsonProjectorException 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="epsonprojector" 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>Epson Projector Binding</name>
<description>This binding is compatible with Epson projectors which support the ESC/VP21 protocol</description>
</binding:binding>

View File

@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="epsonprojector"
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>Epson Projector - Serial</label>
<description>An Epson projector which supports the ESC/VP21 protocol via a serial port connection</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="powerstate" typeId="powerstate"/>
<channel id="source" typeId="source"/>
<channel id="aspectratio" typeId="aspectratio"/>
<channel id="colormode" typeId="colormode"/>
<channel id="freeze" typeId="freeze"/>
<channel id="mute" typeId="mute"/>
<channel id="volume" typeId="volume"/>
<channel id="luminance" typeId="luminance"/>
<channel id="brightness" typeId="brightness"/>
<channel id="contrast" typeId="contrast"/>
<channel id="density" typeId="density"/>
<channel id="tint" typeId="tint"/>
<channel id="colortemperature" typeId="colortemperature"/>
<channel id="fleshtemperature" typeId="fleshtemperature"/>
<channel id="gamma" typeId="gamma"/>
<channel id="autokeystone" typeId="autokeystone"/>
<channel id="verticalkeystone" typeId="verticalkeystone"/>
<channel id="horizontalkeystone" typeId="horizontalkeystone"/>
<channel id="verticalposition" typeId="verticalposition"/>
<channel id="horizontalposition" typeId="horizontalposition"/>
<channel id="verticalreverse" typeId="verticalreverse"/>
<channel id="horizontalreverse" typeId="horizontalreverse"/>
<channel id="background" typeId="background"/>
<channel id="keycode" typeId="keycode"/>
<channel id="lamptime" typeId="lamptime"/>
<channel id="errcode" typeId="errcode"/>
<channel id="errmessage" typeId="errmessage"/>
</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 Epson 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>Epson Projector - TCP/IP</label>
<description>An Epson projector which supports the ESC/VP21 protocol via a serial over IP connection</description>
<channels>
<channel id="power" typeId="power"/>
<channel id="powerstate" typeId="powerstate"/>
<channel id="source" typeId="source"/>
<channel id="aspectratio" typeId="aspectratio"/>
<channel id="colormode" typeId="colormode"/>
<channel id="freeze" typeId="freeze"/>
<channel id="mute" typeId="mute"/>
<channel id="volume" typeId="volume"/>
<channel id="luminance" typeId="luminance"/>
<channel id="brightness" typeId="brightness"/>
<channel id="contrast" typeId="contrast"/>
<channel id="density" typeId="density"/>
<channel id="tint" typeId="tint"/>
<channel id="colortemperature" typeId="colortemperature"/>
<channel id="fleshtemperature" typeId="fleshtemperature"/>
<channel id="gamma" typeId="gamma"/>
<channel id="autokeystone" typeId="autokeystone"/>
<channel id="verticalkeystone" typeId="verticalkeystone"/>
<channel id="horizontalkeystone" typeId="horizontalkeystone"/>
<channel id="verticalposition" typeId="verticalposition"/>
<channel id="horizontalposition" typeId="horizontalposition"/>
<channel id="verticalreverse" typeId="verticalreverse"/>
<channel id="horizontalreverse" typeId="horizontalreverse"/>
<channel id="background" typeId="background"/>
<channel id="keycode" typeId="keycode"/>
<channel id="lamptime" typeId="lamptime"/>
<channel id="errcode" typeId="errcode"/>
<channel id="errmessage" typeId="errmessage"/>
</channels>
<config-description>
<parameter name="host" type="text" required="true">
<label>Host</label>
<context>network-address</context>
<description>IP address for the serial over IP device</description>
</parameter>
<parameter name="port" type="integer" min="1" max="65535" required="true">
<label>Port</label>
<description>Port for the serial over IP device</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>
<channel-type id="power">
<item-type>Switch</item-type>
<label>Power</label>
<description>Powers the Projector On or Off</description>
</channel-type>
<channel-type id="powerstate">
<item-type>String</item-type>
<label>Power State</label>
<description>Retrieves the Textual Power State of the Projector</description>
<state readOnly="true"/>
</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>
<channel-type id="keycode" advanced="true">
<item-type>Number</item-type>
<label>KeyCode</label>
<description>Send a KEY Operation Command to the Projector</description>
</channel-type>
<channel-type id="verticalkeystone" advanced="true">
<item-type>Number</item-type>
<label>Vertical Keystone</label>
<description>Retrieve or Set the Vertical Keystone</description>
<state min="-30" max="30" step="1" pattern="%d"/>
</channel-type>
<channel-type id="horizontalkeystone" advanced="true">
<item-type>Number</item-type>
<label>Horizontal Keystone</label>
<description>Retrieve or Set the Horizontal Keystone</description>
<state min="-30" max="30" step="1" pattern="%d"/>
</channel-type>
<channel-type id="autokeystone" advanced="true">
<item-type>Switch</item-type>
<label>Auto Keystone</label>
<description>Turn the Auto Keystone On or Off</description>
</channel-type>
<channel-type id="freeze">
<item-type>Switch</item-type>
<label>Freeze Image</label>
<description>Turn the Freeze Screen Mode On or Off</description>
</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="NORMAL">NORMAL</option>
<option value="AUTO">AUTO</option>
<option value="FULL">FULL</option>
<option value="ZOOM">ZOOM</option>
<option value="WIDE">WIDE</option>
<option value="ANAMORPHIC">ANAMORPHIC</option>
<option value="SQUEEZE">SQUEEZE</option>
<option value="RATIO4X3">RATIO4X3</option>
<option value="ZOOM4X3">ZOOM4X3</option>
<option value="RATIO16X9">RATIO16X9</option>
<option value="UP16X9">UP16X9</option>
<option value="DOWN16X9">DOWN16X9</option>
<option value="REAL">REAL</option>
</options>
</state>
</channel-type>
<channel-type id="luminance">
<item-type>String</item-type>
<label>Luminance</label>
<description>Retrieve or Set the Lamp Mode</description>
<state>
<options>
<option value="NORMAL">NORMAL</option>
<option value="ECO">ECO</option>
<option value="MEDIUM">MEDIUM</option>
</options>
</state>
</channel-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="30">HDMI1</option>
<option value="A0">HDMI2</option>
<option value="14">Component</option>
<option value="20">PC DSUB</option>
<option value="41">Video</option>
<option value="42">S-Video</option>
</options>
</state>
</channel-type>
<channel-type id="brightness" advanced="true">
<item-type>Number</item-type>
<label>Brightness</label>
<description>Retrieve or Set the Brightness</description>
<state min="-24" max="24" step="1" pattern="%d"/>
</channel-type>
<channel-type id="contrast" advanced="true">
<item-type>Number</item-type>
<label>Contrast</label>
<description>Retrieve or Set the Contrast</description>
<state min="-24" max="24" step="1" pattern="%d"/>
</channel-type>
<channel-type id="density" advanced="true">
<item-type>Number</item-type>
<label>Density (Color Saturation)</label>
<description>Retrieve or Set the Density</description>
<state min="-32" max="32" step="1" pattern="%d"/>
</channel-type>
<channel-type id="tint" advanced="true">
<item-type>Number</item-type>
<label>Tint</label>
<description>Retrieve or Set the Tint</description>
<state min="-32" max="32" step="1" pattern="%d"/>
</channel-type>
<channel-type id="colortemperature" advanced="true">
<item-type>Number</item-type>
<label>Color Temperature</label>
<description>Retrieve or Set the Color Temperature</description>
<state min="0" max="9" step="1" pattern="%d"/>
</channel-type>
<channel-type id="fleshtemperature" advanced="true">
<item-type>Number</item-type>
<label>Flesh Temperature</label>
<description>Retrieve or Set the Flesh Temperature</description>
<state min="0" max="6" step="1" pattern="%d"/>
</channel-type>
<channel-type id="colormode">
<item-type>String</item-type>
<label>Color Mode</label>
<description>Retrieve or Set the Color Mode</description>
<state>
<options>
<option value="DYNAMIC">DYNAMIC</option>
<option value="LIVINGROOM">LIVINGROOM</option>
<option value="NATURAL">NATURAL</option>
<option value="CINEMA">CINEMA</option>
<option value="CINEMANIGHT">CINEMANIGHT</option>
<option value="BWCINEMA">BWCINEMA</option>
<option value="DIGITALCINEMA">DIGITALCINEMA</option>
<option value="HD">HD</option>
<option value="THX">THX</option>
<option value="CINEMA3D">CINEMA3D</option>
<option value="DYNAMIC3D">DYNAMIC3D</option>
<option value="THX3D">THX3D</option>
<option value="SRGB">SRGB</option>
<option value="NORMAL">NORMAL</option>
<option value="MEETING">MEETING</option>
<option value="PRESENTATION">PRESENTATION</option>
<option value="SPORTS">SPORTS</option>
<option value="CUSTOM">CUSTOM</option>
<option value="BLACKBOARD">BLACKBOARD</option>
<option value="WHITEBOARD">WHITEBOARD</option>
<option value="PHOTO">PHOTO</option>
<option value="SILVER">SILVER</option>
<option value="XVCOLOR">XVCOLOR</option>
<option value="DICOMSIM">DICOMSIM</option>
</options>
</state>
</channel-type>
<channel-type id="horizontalposition" advanced="true">
<item-type>Number</item-type>
<label>Horizontal Position</label>
<description>Retrieve or Set the Horizontal Position</description>
<state min="-23" max="26" step="1" pattern="%d"/>
</channel-type>
<channel-type id="verticalposition" advanced="true">
<item-type>Number</item-type>
<label>Vertical Position</label>
<description>Retrieve or Set the Vertical Position</description>
<state min="-8" max="10" step="1" pattern="%d"/>
</channel-type>
<channel-type id="gamma" advanced="true">
<item-type>String</item-type>
<label>Gamma</label>
<description>Retrieve or Set the Gamma Setting</description>
<state>
<options>
<option value="G2_0">G2_0</option>
<option value="G2_1">G2_1</option>
<option value="G2_2">G2_2</option>
<option value="G2_3">G2_3</option>
<option value="G2_4">G2_4</option>
<option value="CUSTOM">CUSTOM</option>
</options>
</state>
</channel-type>
<channel-type id="volume">
<item-type>Number</item-type>
<label>Volume</label>
<description>Retrieve or Set the Volume</description>
<state min="0" max="20" step="1" pattern="%d"/>
</channel-type>
<channel-type id="mute">
<item-type>Switch</item-type>
<label>AV Mute</label>
<description>Turn the AV Mute On or Off</description>
</channel-type>
<channel-type id="horizontalreverse" advanced="true">
<item-type>Switch</item-type>
<label>Horizontal Reverse</label>
<description>Turn the Horizontal Reverse On or Off</description>
</channel-type>
<channel-type id="verticalreverse" advanced="true">
<item-type>Switch</item-type>
<label>Vertical Reverse</label>
<description>Turn the Vertical Reverse On or Off</description>
</channel-type>
<channel-type id="background" advanced="true">
<item-type>String</item-type>
<label>Background</label>
<description>Select the Background Color/Logo</description>
<state>
<options>
<option value="BLACK">BLACK</option>
<option value="BLUE">BLUE</option>
<option value="LOGO">LOGO</option>
</options>
</state>
</channel-type>
<channel-type id="errcode" advanced="true">
<item-type>Number</item-type>
<label>ErrCode</label>
<description>Retrieves the Last Error Code</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="errmessage" advanced="true">
<item-type>String</item-type>
<label>ErrMessage</label>
<description>Retrieves the Description of the Last Error</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>

View File

@ -95,6 +95,7 @@
<module>org.openhab.binding.enigma2</module>
<module>org.openhab.binding.enocean</module>
<module>org.openhab.binding.enturno</module>
<module>org.openhab.binding.epsonprojector</module>
<module>org.openhab.binding.etherrain</module>
<module>org.openhab.binding.evohome</module>
<module>org.openhab.binding.exec</module>