]> git.basschouten.com Git - openhab-addons.git/commitdiff
[epsonprojector] Add configurable volume channel (#10988)
authormlobstein <michael.lobstein@gmail.com>
Sat, 17 Jul 2021 20:34:03 +0000 (15:34 -0500)
committerGitHub <noreply@github.com>
Sat, 17 Jul 2021 20:34:03 +0000 (22:34 +0200)
Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
bundles/org.openhab.binding.epsonprojector/README.md
bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/EpsonProjectorCommandType.java
bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/EpsonProjectorDevice.java
bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/configuration/EpsonProjectorConfiguration.java
bundles/org.openhab.binding.epsonprojector/src/main/java/org/openhab/binding/epsonprojector/internal/handler/EpsonProjectorHandler.java
bundles/org.openhab.binding.epsonprojector/src/main/resources/OH-INF/thing/thing-types.xml

index 267218070792e910d76cfb466fedde4a6cb394e1..e121b4016e5ef1c1cd5e0b142a1b79ec64ef5246 100644 (file)
@@ -25,14 +25,16 @@ The `projector-serial` thing has the following configuration parameters:
 |-----------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
 | serialPort      | Serial Port      | 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. | yes      |
 | pollingInterval | Polling Interval | Polling interval in seconds to update channel states, range 5-60 seconds; default 10 seconds.                                                              | no       |
+| maxVolume       | Max Volume Range | Set to the maximum volume level available in the projector's OSD to select the correct range for the volume control. e.g. 20 or 40; default 20             | 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 3629 for projectors with built-in ethernet connector or Wi-Fi. | yes      |
-| pollingInterval | Polling Interval | Polling interval in seconds to update channel states, range 5-60 seconds; default 10 seconds.                           | no       |
+| 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 3629 for projectors with built-in ethernet connector or Wi-Fi.                        | yes      |
+| pollingInterval | Polling Interval | Polling interval in seconds to update channel states, range 5-60 seconds; default 10 seconds.                                                  | no       |
+| maxVolume       | Max Volume Range | Set to the maximum volume level available in the projector's OSD to select the correct range for the volume control. e.g. 20 or 40; default 20 | no       |
 
 Some notes:
 
@@ -40,8 +42,9 @@ Some notes:
 * 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.
+* The following channels _aspectratio_, _colormode_, _luminance_, _gamma_ and _background_ are pre-populated with a full set of options but 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.
+* The volume channel is a dimmer (0-100%) that is scaled to the range on the projector, either 0-20 or 0-40 per the maxVolume configuration setting. If your projector uses a different range, then the volume channel will not work.
 * 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.
 * If the binding fails to connect to the projector using the direct IP connection, ensure that no password is configured on the projctor.
 
@@ -56,36 +59,36 @@ Some notes:
 
 ## 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            | String    | Send a key operation command to the projector. (2 character code) | 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 | 
+| 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             | Dimmer    | Retrieve or set the volume. Scaled to 0-20 or 0-40 on the projector per maxVolume setting. | 0% - 100% | 
+| 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            | String    | Send a key operation command to the projector. (2 character code)                          | 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
 
@@ -93,10 +96,10 @@ things/epson.things:
 
 ```
 // serial port connection
-epsonprojector:projector-serial:hometheater "Projector" [ serialPort="COM5", pollingInterval=10 ]
+epsonprojector:projector-serial:hometheater "Projector" [ serialPort="COM5", pollingInterval=10, maxVolume=20 ]
 
 // direct IP or serial over IP connection
-epsonprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=3629, pollingInterval=10 ]
+epsonprojector:projector-tcp:hometheater "Projector" [ host="192.168.0.10", port=3629, pollingInterval=10, maxVolume=20 ]
 
 ```
 
@@ -109,7 +112,7 @@ String epsonAspectRatio  "Aspect Ratio [%s]"           { channel="epsonprojector
 String epsonColorMode    "Color Mode [%s]"             { channel="epsonprojector:projector-serial:hometheater:colormode" }
 Switch epsonFreeze                                     { channel="epsonprojector:projector-serial:hometheater:freeze" }
 Switch epsonMute                                       { channel="epsonprojector:projector-serial:hometheater:mute" }
-Number epsonVolume                                     { channel="epsonprojector:projector-serial:hometheater:volume" }
+Dimmer epsonVolume                                     { channel="epsonprojector:projector-serial:hometheater:volume" }
 String epsonLuminance    "Lamp Mode [%s]"              { channel="epsonprojector:projector-serial:hometheater:luminance" }
 
 Number epsonBrightness                                 { channel="epsonprojector:projector-serial:hometheater:brightness" }
@@ -139,14 +142,15 @@ String epsonErrMessage  "Error Message [%s]" <"siren-off">  { channel="epsonproj
 sitemaps/epson.sitemap
 
 ```
-sitemap epson label="Epson Projector Demo"
+sitemap epson label="Epson Projector"
 {
     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"
+        // Volume can be a Setpoint also
+        Slider     item=epsonVolume  label="Volume" minValue=0 maxValue=100 step=1 icon="soundvolume"
         Switch     item=epsonKeyCode label="Navigation" icon="screen" mappings=["03"="Menu", "35"="˄", "36"="˅", "37"="<", "38"=">", "16"="Enter"]
     }
     Frame label="Adjust Image" {
index 0f9156b6c0604ef3d763dc3422f383f32678be75..7a9ac00ef8259b836f22e027119734512500148f 100644 (file)
@@ -16,6 +16,7 @@ import java.io.InvalidClassException;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.openhab.core.items.Item;
+import org.openhab.core.library.items.DimmerItem;
 import org.openhab.core.library.items.NumberItem;
 import org.openhab.core.library.items.StringItem;
 import org.openhab.core.library.items.SwitchItem;
@@ -49,7 +50,7 @@ public enum EpsonProjectorCommandType {
     HPOSITION("HorizontalPosition", NumberItem.class),
     VPOSITION("VerticalPosition", NumberItem.class),
     GAMMA("Gamma", StringItem.class),
-    VOLUME("Volume", NumberItem.class),
+    VOLUME("Volume", DimmerItem.class),
     MUTE("Mute", SwitchItem.class),
     HREVERSE("HorizontalReverse", SwitchItem.class),
     VREVERSE("VerticalReverse", SwitchItem.class),
index f1679c5c8bc476beeae519b51e17572dab91a945..6c6277497640fe06bd753e0a57f26bec00f2aba1 100644 (file)
@@ -62,6 +62,10 @@ public class EpsonProjectorDevice {
             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[] MAP40 = new int[] { 0, 6, 12, 18, 24, 31, 37, 43, 49, 56, 62, 68, 74, 81, 87, 93, 99,
+            106, 112, 118, 124, 131, 137, 143, 149, 156, 162, 168, 174, 181, 187, 193, 199, 206, 212, 218, 224, 231,
+            237, 243, 249 };
+
     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 };
 
@@ -78,6 +82,7 @@ public class EpsonProjectorDevice {
 
     private static final String ON = "ON";
     private static final String ERR = "ERR";
+    private static final String IMEVENT = "IMEVENT";
 
     private final Logger logger = LoggerFactory.getLogger(EpsonProjectorDevice.class);
 
@@ -118,7 +123,7 @@ public class EpsonProjectorDevice {
         response = response.replace("\r:", "");
         logger.debug("Response: '{}'", response);
 
-        if (ERR.equals(response)) {
+        if (ERR.equals(response) || response.startsWith(IMEVENT)) {
             throw new EpsonProjectorCommandException("Error response received for command: " + query);
         }
 
@@ -522,19 +527,36 @@ public class EpsonProjectorDevice {
     /*
      * Volume
      */
-    public int getVolume() throws EpsonProjectorCommandException, EpsonProjectorException {
-        int vol = queryInt("VOL?");
-        for (int i = 0; i < MAP20.length; i++) {
-            if (vol == MAP20[i]) {
+    public int getVolume(int maxVolume) throws EpsonProjectorCommandException, EpsonProjectorException {
+        int vol = this.queryInt("VOL?");
+        switch (maxVolume) {
+            case 20:
+                return this.getMappingValue(MAP20, vol);
+            case 40:
+                return this.getMappingValue(MAP40, vol);
+        }
+        return 0;
+    }
+
+    private int getMappingValue(int[] map, int value) {
+        for (int i = 0; i < map.length; i++) {
+            if (value == map[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]));
+    public void setVolume(int value, int maxVolume) throws EpsonProjectorCommandException, EpsonProjectorException {
+        if (value >= 0 && value <= maxVolume) {
+            switch (maxVolume) {
+                case 20:
+                    this.sendCommand(String.format("VOL %d", MAP20[value]));
+                    return;
+                case 40:
+                    this.sendCommand(String.format("VOL %d", MAP40[value]));
+                    return;
+            }
         }
     }
 
index 35d53278576258cd523a37930edcf2cde4d5900a..78e2d4bf0f20496ae07494d2ae6f2e1ba4ab7e04 100644 (file)
@@ -41,4 +41,9 @@ public class EpsonProjectorConfiguration {
      * Polling interval to refresh states.
      */
     public int pollingInterval;
+
+    /**
+     * Maximum volume setting of this projector, ie 20, 40, etc.
+     */
+    public int maxVolume = 20;
 }
index 2d8cc2ca414972d4ae5057777d93c954ff7397f5..30cd57c77775dc0b9f090de978ff7cfa0f32b6a8 100644 (file)
@@ -14,6 +14,7 @@ package org.openhab.binding.epsonprojector.internal.handler;
 
 import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.ScheduledFuture;
@@ -36,6 +37,7 @@ 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.PercentType;
 import org.openhab.core.library.types.StringType;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
@@ -69,6 +71,8 @@ public class EpsonProjectorHandler extends BaseThingHandler {
     private Optional<EpsonProjectorDevice> device = Optional.empty();
 
     private boolean isPowerOn = false;
+    private int maxVolume = 20;
+    private int curVolumeStep = -1;
     private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
 
     public EpsonProjectorHandler(Thing thing, SerialPortManager serialPortManager) {
@@ -103,6 +107,7 @@ public class EpsonProjectorHandler extends BaseThingHandler {
             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
         }
 
+        maxVolume = config.maxVolume;
         pollingInterval = config.pollingInterval;
         device.ifPresent(dev -> dev.setScheduler(scheduler));
         updateStatus(ThingStatus.UNKNOWN);
@@ -267,8 +272,17 @@ public class EpsonProjectorHandler extends BaseThingHandler {
                     int vKeystone = remoteController.getVerticalKeystone();
                     return new DecimalType(vKeystone);
                 case VOLUME:
-                    int volume = remoteController.getVolume();
-                    return new DecimalType(volume);
+                    // Each volume step falls within several percentage values, only change the UI if the polled step is
+                    // different than the step of the current percent. Without this logic the UI would snap back to the
+                    // closest whole % value for that step. e.g., UI set to 51% would snap back to 50% on the next
+                    // polling update.
+                    int volumeStep = remoteController.getVolume(maxVolume);
+                    if (curVolumeStep != volumeStep) {
+                        curVolumeStep = volumeStep;
+                        return new PercentType(
+                                BigDecimal.valueOf(Math.round(curVolumeStep / (double) maxVolume * 100.0)));
+                    }
+                    return null;
                 case VPOSITION:
                     int vPosition = remoteController.getVerticalPosition();
                     return new DecimalType(vPosition);
@@ -387,7 +401,11 @@ public class EpsonProjectorHandler extends BaseThingHandler {
                     remoteController.setVerticalKeystone(((DecimalType) command).intValue());
                     break;
                 case VOLUME:
-                    remoteController.setVolume(((DecimalType) command).intValue());
+                    int newVolumeStep = (int) Math.round(((PercentType) command).doubleValue() / 100.0 * maxVolume);
+                    if (curVolumeStep != newVolumeStep) {
+                        curVolumeStep = newVolumeStep;
+                        remoteController.setVolume(curVolumeStep, maxVolume);
+                    }
                     break;
                 case VPOSITION:
                     remoteController.setVerticalPosition(((DecimalType) command).intValue());
index 6fb3e30778b52e9014074fcf39bce0378dcc74d2..afcc25e589b4b8e559350402c8f4d64077c2f400 100644 (file)
@@ -9,14 +9,14 @@
                <description>An Epson projector which supports the ESC/VP21 protocol via a serial port connection</description>
 
                <channels>
-                       <channel id="power" typeId="power"/>
+                       <channel id="power" typeId="system.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="volume" typeId="system.volume"/>
                        <channel id="luminance" typeId="luminance"/>
                        <channel id="brightness" typeId="brightness"/>
                        <channel id="contrast" typeId="contrast"/>
                                <description>Configures How Often to Poll the Projector for Updates (5-60; Default 10)</description>
                                <default>10</default>
                        </parameter>
+                       <parameter name="maxVolume" type="integer">
+                               <label>Volume Range</label>
+                               <description>Set to Match the Volume Range Seen in the Projector's OSD</description>
+                               <limitToOptions>true</limitToOptions>
+                               <options>
+                                       <option value="20">Volume range is 0-20</option>
+                                       <option value="40">Volume range is 0-40</option>
+                               </options>
+                               <default>20</default>
+                       </parameter>
                </config-description>
 
        </thing-type>
                        IP connection</description>
 
                <channels>
-                       <channel id="power" typeId="power"/>
+                       <channel id="power" typeId="system.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="volume" typeId="system.volume"/>
                        <channel id="luminance" typeId="luminance"/>
                        <channel id="brightness" typeId="brightness"/>
                        <channel id="contrast" typeId="contrast"/>
                                <description>Configures How Often to Poll the Projector for Updates (5-60; Default 10)</description>
                                <default>10</default>
                        </parameter>
+                       <parameter name="maxVolume" type="integer">
+                               <label>Volume Range</label>
+                               <description>Set to Match the Volume Range Seen in the Projector's OSD</description>
+                               <limitToOptions>true</limitToOptions>
+                               <options>
+                                       <option value="20">Volume range is 0-20</option>
+                                       <option value="40">Volume range is 0-40</option>
+                               </options>
+                               <default>20</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>
                        </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>