added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.pioneeravr</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

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,291 @@
# Pioneer AVR Binding
## Binding configuration
The binding can auto-discover the Pioneer AVRs present on your local network.
The auto-discovery is enabled by default.
To disable it, you can create a file in the services directory called pioneeravr.cfg with the following content:
```
#Put your configuration here
org.openhab.pioneeravr:enableAutoDiscovery=false
```
This configuration parameter only control the PioneerAVR auto-discovery process, not the openHAB auto-discovery.
Moreover, if the openHAB auto-discovery is disabled, the PioneerAVR auto-discovery is disabled too.
## Thing configuration
In the things folder, create a file called pioneeravr.things (or any other name) and configure your AVRs inside.
The binding can control AVRs through the local network (ipAvr/ipAvrUnsupported thing type) or through a Serial connection (serialAvr) if the AVR is directly connected to your computer.
Configuration of ipAvr/ipAvrUnsupported:
* address: the hostname/ipAddress of the AVR on the local network. (mandatory)
* tcpPort: the port number to use to connect to the AVR. (optional, default to 23)
Configuration of serialAvr:
* serialPort: the name of the serial port on your computer. (mandatory)
Example:
```
pioneeravr:ipAvr:vsx921IP [ address="192.168.1.25", tcpPort="23" ]
pioneeravr:serialAvr:vsx921Serial [ serialPort="COM9" ]
```
## Channels
* power: power On/Off the AVR. Receive power events.
* volumeDimmer: Increase/Decrease the volume on the AVR or set the volume as %. Receive volume change events (in %).
* volumeDb: Set the volume of the AVR in dB (from -80.0 to 12 with 0.5 dB steps). Receive volume change events (in dB).
* mute: Mute/Unmute the AVR. Receive mute events.
* setInputSource: Set the input source of the AVR. See input source mapping for more details. Receive source input change events with the input source ID.
* displayInformation: Receive display events. Reflect the display on the AVR front panel.
## Input Source Mapping
Here after are the ID values of the input sources (depending on you AVR input sources might not be available):
* 04: DVD
* 25: BD
* 05: TV/SAT
* 06: SAT/CBL
* 15: DVR/BDR
* 10: VIDEO 1(VIDEO)
* 14: VIDEO 2
* 19: HDMI 1
* 20: HDMI 2
* 21: HDMI 3
* 22: HDMI 4
* 23: HDMI 5
* 24: HDMI 6
* 34: HDMI 7
* 35: HDMI 8
* 26: HOME MEDIA GALLERY(Internet Radio)
* 44: Media Server
* 38: Internet Radio
* 17: iPod/USB
* 48: MHL
* 01: CD
* 03: CD-R/TAPE
* 02: TUNER
* 00: PHONO
* 13: USB-DAC
* 12: MULTI CH IN
* 33: ADAPTER PORT (BT)
* 18: XM RADIO
* 27: SIRIUS
* 40: SiriusXM
* 41: PANDORA
* 45: Favourites
* 57: Spotify
* 31: HDMI (cyclic)
## Listening Modes
The *Listening Mode* is set by user to instruct the AVR how to treat the audio signal and do upscaling, downscaling and amplification. This settings corresponds to the settings made with the remote control or front panel. What the AVR actually does with each setting/input-signal-combination can be read out using the *Playing Listening Mode* channel.
* 0001: STEREO (cyclic)
* 0010: STANDARD (cyclic)
* 0009: STEREO (direct set)
* 0011: (2ch source)
* 0013: PRO LOGIC2 MOVIE
* 0018: PRO LOGIC2x MOVIE
* 0014: PRO LOGIC2 MUSIC
* 0019: PRO LOGIC2x MUSIC
* 0015: PRO LOGIC2 GAME
* 0020: PRO LOGIC2x GAME
* 0031: PRO LOGIC2z HEIGHT
* 0032: WIDE SURROUND MOVIE
* 0033: WIDE SURROUND MUSIC
* 0012: PRO LOGIC
* 0016: Neo:6 CINEMA
* 0017: Neo:6 MUSIC
* 0037: Neo:X CINEMA
* 0038: Neo:X MUSIC
* 0039: Neo:X GAME
* 0040: Dolby Surround
* 0041: EXTENDED STEREO
* 0021: (Multi ch source) Channel base straight decode
* 0022: (Multi ch source)+DOLBY EX
* 0023: (Multi ch source)+PRO LOGIC2x MOVIE
* 0024: (Multi ch source)+PRO LOGIC2x MUSIC
* 0034: (Multi-ch Source)+PRO LOGIC2z HEIGHT
* 0035: (Multi-ch Source)+WIDE SURROUND MOVIE
* 0036: (Multi-ch Source)+WIDE SURROUND MUSIC
* 0025: (Multi ch source)DTS-ES Neo:6
* 0026: (Multi ch source)DTS-ES matrix
* 0027: (Multi ch source)DTS-ES discrete
* 0030: (Multi ch source)DTS-ES 8ch discrete
* 0043: (Multi ch source)+Neo:X CINEMA
* 0044: (Multi ch source)+Neo:X MUSIC
* 0045: (Multi ch source)+Neo:X GAME
* 0050: (Multi ch source)+Dolby Surround
* 0100: ADVANCED SURROUND (cyclic)
* 0101: ACTION
* 0103: DRAMA
* 0118: ADVANCED GAME
* 0117: SPORTS
* 0107: CLASSICAL
* 0110: ROCK/POP
* 0003: Front Stage Surround Advance
* 0200: ECO MODE (cyclic)
* 0212: ECO MODE 1
* 0213: ECO MODE 2
* 0153: RETRIEVER AIR
* 0113: PHONES SURROUND
* 0005: AUTO SURR/STREAM DIRECT (cyclic)
* 0006: AUTO SURROUND
* 0151: Auto Level Control (A.L.C.)
* 0007: DIRECT
* 0008: PURE DIRECT
* 0152: OPTIMUM SURROUND
## Playing Listening Modes
The *Playing Listening Mode* is the Listening Mode that is actually playing as opposed to the *Listening Mode* set by the user. The *Playing Listening Mode* is what the display on the device shows.
* 0101: [)(]PLIIx MOVIE
* 0102: [)(]PLII MOVIE
* 0103: [)(]PLIIx MUSIC
* 0104: [)(]PLII MUSIC
* 0105: [)(]PLIIx GAME
* 0106: [)(]PLII GAME
* 0107: [)(]PROLOGIC
* 0108: Neo:6 CINEMA
* 0109: Neo:6 MUSIC
* 010c: 2ch Straight Decode
* 010d: [)(]PLIIz HEIGHT
* 010e: WIDE SURR MOVIE
* 010f: WIDE SURR MUSIC
* 0110: STEREO
* 0111: Neo:X CINEMA
* 0112: Neo:X MUSIC
* 0113: Neo:X GAME
* 0117: Dolby Surround
* 0118: EXTENDED STEREO
* 1101: [)(]PLIIx MOVIE
* 1102: [)(]PLIIx MUSIC
* 1103: [)(]DIGITAL EX
* 1104: DTS Neo:6
* 1105: ES MATRIX
* 1106: ES DISCRETE
* 1107: DTS-ES 8ch
* 1108: multi ch Channel base Straight Decode
* 1109: [)(]PLIIz HEIGHT
* 110a: WIDE SURR MOVIE
* 110b: WIDE SURR MUSIC
* 110c: Neo:X CINEMA
* 110d: Neo:X MUSIC
* 110e: Neo:X GAME
* 110f: Dolby Surround
* 0201: ACTION
* 0202: DRAMA
* 0208: ADVANCEDGAME
* 0209: SPORTS
* 020a: CLASSICAL
* 020b: ROCK/POP
* 020e: PHONES SURR.
* 020f: FRONT STAGE SURROUND ADVANCE
* 0211: SOUND RETRIEVER AIR
* 0212: ECO MODE 1
* 0213: ECO MODE 2
* 0401: STEREO
* 0402: [)(]PLII MOVIE
* 0403: [)(]PLIIx MOVIE
* 0405: AUTO SURROUND Straight Decode
* 0406: [)(]DIGITAL EX
* 0407: [)(]PLIIx MOVIE
* 0408: DTS +Neo:6
* 0409: ES MATRIX
* 040a: ES DISCRETE
* 040b: DTS-ES 8ch
* 040e: RETRIEVER AIR
* 040f: Neo:X CINEMA
* 0411: Dolby Surround
* 0501: STEREO
* 0502: [)(]PLII MOVIE
* 0503: [)(]PLIIx MOVIE
* 0504: DTS/DTS-HD
* 0505: ALC Straight Decode
* 0506: [)(]DIGITAL EX
* 0507: [)(]PLIIx MOVIE
* 0508: DTS +Neo:6
* 0509: ES MATRIX
* 050a: ES DISCRETE
* 050b: DTS-ES 8ch
* 050e: RETRIEVER AIR
* 050f: Neo:X CINEMA
* 0601: STEREO
* 0602: [)(]PLII MOVIE
* 0603: [)(]PLIIx MOVIE
* 0604: Neo:6 CINEMA
* 0605: STREAM DIRECT NORMAL Straight Decode
* 0606: [)(]DIGITAL EX
* 0607: [)(]PLIIx MOVIE
* 0609: ES MATRIX
* 060a: ES DISCRETE
* 060b: DTS-ES 8ch
* 060c: Neo:X CINEMA
* 060e: NORMAL DIRECT Dolby Surround
* 0701: STREAM DIRECT PURE 2ch
* 0702: [)(]PLII MOVIE
* 0703: [)(]PLIIx MOVIE
* 0704: Neo:6 CINEMA
* 0705: STREAM DIRECT PURE Straight Decode
* 0706: [)(]DIGITAL EX
* 0707: [)(]PLIIx MOVIE
* 0708: (nothing)
* 0709: ES MATRIX
* 070a: ES DISCRETE
* 070b: DTS-ES 8ch
* 070c: Neo:X CINEMA
* 070e: PURE DIRECT Dolby Surround
* 0881: OPTIMUM
* 0e01: HDMI THROUGH
* 0f01: MULTI CH IN
## Example
*demo.Things:
```
pioneeravr:ipAvr:vsx921 [ address="192.168.188.89" ]
```
*demo.items:
```
/* Pioneer AVR Items */
Switch vsx921PowerSwitch "Power" (All) { channel="pioneeravr:ipAvr:vsx921:power" }
Switch vsx921MuteSwitch "Mute" <none> (All) { channel="pioneeravr:ipAvr:vsx921:mute" }
Dimmer vsx921VolumeDimmer "Volume [%.1f] %" <none> (All) { channel="pioneeravr:ipAvr:vsx921:volumeDimmer" }
Number vsx921VolumeNumber "Volume [%.1f] dB" <none> (All) { channel="pioneeravr:ipAvr:vsx921:volumeDb" }
String vsx921InputSourceSet "Input" <none> (All) { channel="pioneeravr:ipAvr:vsx921:setInputSource" }
String vsx921InformationDisplay "Information [%s]" <none> (All) { channel="pioneeravr:ipAvr:vsx921:displayInformation" }
String vsx921ListeningMode "Listening Mode [%s]" <none> (All) { channel="pioneeravr:ipAvr:vsx921:listeningMode" }
String vsx921PlayingListeningMode "Playing Listening Mode [%s]" <none> (All) { channel="pioneeravr:ipAvr:vsx921:playingListeningMode" }
```
*demo.sitemap:
```
sitemap demo label="Main Menu"
{
Frame label="Pioneer AVR" {
Switch item=vsx921PowerSwitch
Switch item=vsx921MuteSwitch mappings=[ON="Mute", OFF="Un-Mute"]
Slider item=vsx921VolumeDimmer
Setpoint item=vsx921VolumeNumber minValue="-80" maxValue="12" step="0.5"
Switch item=vsx921InputSourceSet mappings=[04="DVD", 15="DVR/BDR", 25="BD"]
Text item=vsx921InformationDisplay
Switch item=vsx921ListeningMode mappings=["0009"="Stereo", "0040"="Dolby Surround", "0010"="next"]
Text item=vsx921PlayingListeningMode
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.pioneeravr</artifactId>
<name>openHAB Add-ons :: Bundles :: PioneerAvr Binding</name>
</project>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.pioneeravr-${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-pioneeravr" description="PioneerAVR Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-serial</feature>
<feature>openhab-transport-upnp</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.pioneeravr/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,107 @@
/**
* 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.pioneeravr.internal;
import java.util.Collections;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link PioneerAvrBinding} class defines common constants, which are used across the whole binding.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
@NonNullByDefault
public class PioneerAvrBindingConstants {
public static final String BINDING_ID = "pioneeravr";
public static final Set<String> SUPPORTED_DEVICE_MODELS = Collections
.unmodifiableSet(Stream.of("SC-57", "SC-LX85", "SC-55", "SC-1526", "SC-LX75", "VSX-53", "VSX-1326",
"VSX-LX55", "VSX-2021", "VSA-LX55", "VSX-52", "VSX-1126", "VSX-1121", "VSX-51", "VSX-1021",
"VSX-1026", "VSA-1021", "VSX-50", "VSX-926", "VSX-921", "VSA-921").collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2014 = Collections
.unmodifiableSet(Stream.of("SC-LX87", "SC-LX77", "SC-LX57", "SC-2023", "SC-1223", "VSX-1123", "VSX-923")
.collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2015 = Collections.unmodifiableSet(
Stream.of("SC-89", "SC-LX88", "SC-87", "SC-LX78", "SC-85", "SC-LX58", "SC-82", "SC-2024", "SC-81", "VSX-80")
.collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2016 = Collections
.unmodifiableSet(Stream.of("SC-99", "SC-LX89", "SC-97", "SC-LX79", "SC-95", "SC-LX59", "SC-92", "SC-91",
"VSX-90", "VSX-45", "VSX-830", "VSX-930", "VSX-1130").collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2017 = Collections.unmodifiableSet(
Stream.of("VSX-531", "VSX-531D", "VSX-831", "VSX-1131", "VSX-LX101", "VSX-LX301", "VSX-LX501")
.collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2018 = Collections.unmodifiableSet(Stream
.of("VSX-532", "VSX-832", "VSX-932", "VSX-LX102", "VSX-LX302", "VSX-LX502").collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2019 = Collections.unmodifiableSet(
Stream.of("VSX-833", "VSX-933", "VSX-LX103", "VSX-LX303", "VSX-LX503").collect(Collectors.toSet()));
public static final Set<String> SUPPORTED_DEVICE_MODELS2020 = Collections.unmodifiableSet(Stream
.of("VSX-534", "VSX-534D", "VSX-934", "VSX-LX104", "VSX-LX304", "VSX-LX504").collect(Collectors.toSet()));
// List of all Thing Type UIDs
public static final ThingTypeUID IP_AVR_THING_TYPE = new ThingTypeUID(BINDING_ID, "ipAvr");
public static final ThingTypeUID IP_AVR_THING_TYPE2014 = new ThingTypeUID(BINDING_ID, "ipAvr2014");
public static final ThingTypeUID IP_AVR_THING_TYPE2015 = new ThingTypeUID(BINDING_ID, "ipAvr2015");
public static final ThingTypeUID IP_AVR_THING_TYPE2016 = new ThingTypeUID(BINDING_ID, "ipAvr2016");
public static final ThingTypeUID IP_AVR_THING_TYPE2017 = new ThingTypeUID(BINDING_ID, "ipAvr2017");
public static final ThingTypeUID IP_AVR_THING_TYPE2018 = new ThingTypeUID(BINDING_ID, "ipAvr2018");
public static final ThingTypeUID IP_AVR_THING_TYPE2019 = new ThingTypeUID(BINDING_ID, "ipAvr2019");
public static final ThingTypeUID IP_AVR_THING_TYPE2020 = new ThingTypeUID(BINDING_ID, "ipAvr2020");
public static final ThingTypeUID IP_AVR_UNSUPPORTED_THING_TYPE = new ThingTypeUID(BINDING_ID, "ipAvrUnsupported");
public static final ThingTypeUID SERIAL_AVR_THING_TYPE = new ThingTypeUID(BINDING_ID, "serialAvr");
// List of thing parameters names
public static final String PROTOCOL_PARAMETER = "protocol";
public static final String HOST_PARAMETER = "address";
public static final String TCP_PORT_PARAMETER = "tcpPort";
public static final String SERIAL_PORT_PARAMETER = "serialPort";
public static final String IP_PROTOCOL_NAME = "IP";
public static final String SERIAL_PROTOCOL_NAME = "serial";
// List of all Channel names
public static final String POWER_CHANNEL = "power";
public static final String VOLUME_DIMMER_CHANNEL = "volumeDimmer";
public static final String VOLUME_DB_CHANNEL = "volumeDb";
public static final String MUTE_CHANNEL = "mute";
public static final String SET_INPUT_SOURCE_CHANNEL = "setInputSource";
public static final String LISTENING_MODE_CHANNEL = "listeningMode";
public static final String PLAYING_LISTENING_MODE_CHANNEL = "playingListeningMode";
public static final String DISPLAY_INFORMATION_CHANNEL = "displayInformation#displayInformation";
public static final String GROUP_CHANNEL_PATTERN = "zone%s#%s";
public static final Pattern GROUP_CHANNEL_ZONE_PATTERN = Pattern.compile("zone([0-4])#.*");
// Used for Discovery service
public static final String MANUFACTURER = "PIONEER";
public static final String UPNP_DEVICE_TYPE = "MediaRenderer";
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Stream.of(IP_AVR_THING_TYPE,
IP_AVR_THING_TYPE2020, IP_AVR_THING_TYPE2019, IP_AVR_THING_TYPE2018, IP_AVR_THING_TYPE2017,
IP_AVR_THING_TYPE2016, IP_AVR_THING_TYPE2015, IP_AVR_THING_TYPE2014, IP_AVR_UNSUPPORTED_THING_TYPE)
.collect(Collectors.toSet());
}

View File

@@ -0,0 +1,156 @@
/**
* 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.pioneeravr.internal.discovery;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.jupnp.model.meta.RemoteDevice;
import org.openhab.binding.pioneeravr.internal.PioneerAvrBindingConstants;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.upnp.UpnpDiscoveryParticipant;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An UpnpDiscoveryParticipant which allows to discover Pioneer AVRs.
*
* @author Antoine Besnard - Initial contribution
*/
@Component(immediate = true)
public class PioneerAvrDiscoveryParticipant implements UpnpDiscoveryParticipant {
private final Logger logger = LoggerFactory.getLogger(PioneerAvrDiscoveryParticipant.class);
private boolean isAutoDiscoveryEnabled;
private Set<ThingTypeUID> supportedThingTypes;
public PioneerAvrDiscoveryParticipant() {
this.isAutoDiscoveryEnabled = true;
this.supportedThingTypes = PioneerAvrBindingConstants.SUPPORTED_THING_TYPES_UIDS;
}
/**
* Called at the service activation.
*
* @param componentContext
*/
@Activate
protected void activate(ComponentContext componentContext) {
if (componentContext.getProperties() != null) {
String autoDiscoveryPropertyValue = (String) componentContext.getProperties().get("enableAutoDiscovery");
if (StringUtils.isNotEmpty(autoDiscoveryPropertyValue)) {
isAutoDiscoveryEnabled = Boolean.valueOf(autoDiscoveryPropertyValue);
}
}
supportedThingTypes = isAutoDiscoveryEnabled ? PioneerAvrBindingConstants.SUPPORTED_THING_TYPES_UIDS
: new HashSet<>();
}
@Override
public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
return supportedThingTypes;
}
@Override
public DiscoveryResult createResult(RemoteDevice device) {
DiscoveryResult result = null;
ThingUID thingUid = getThingUID(device);
if (thingUid != null) {
String label = StringUtils.isEmpty(device.getDetails().getFriendlyName()) ? device.getDisplayString()
: device.getDetails().getFriendlyName();
Map<String, Object> properties = new HashMap<>(2, 1);
properties.put(PioneerAvrBindingConstants.HOST_PARAMETER,
device.getIdentity().getDescriptorURL().getHost());
properties.put(PioneerAvrBindingConstants.PROTOCOL_PARAMETER, PioneerAvrBindingConstants.IP_PROTOCOL_NAME);
result = DiscoveryResultBuilder.create(thingUid).withLabel(label).withProperties(properties).build();
}
return result;
}
@Override
public ThingUID getThingUID(RemoteDevice device) {
ThingUID result = null;
if (isAutoDiscoveryEnabled) {
if (StringUtils.containsIgnoreCase(device.getDetails().getManufacturerDetails().getManufacturer(),
PioneerAvrBindingConstants.MANUFACTURER)) {
logger.debug("Manufacturer matched: search: {}, device value: {}.",
PioneerAvrBindingConstants.MANUFACTURER,
device.getDetails().getManufacturerDetails().getManufacturer());
if (StringUtils.containsIgnoreCase(device.getType().getType(),
PioneerAvrBindingConstants.UPNP_DEVICE_TYPE)) {
logger.debug("Device type matched: search: {}, device value: {}.",
PioneerAvrBindingConstants.UPNP_DEVICE_TYPE, device.getType().getType());
String deviceModel = device.getDetails().getModelDetails() != null
? device.getDetails().getModelDetails().getModelName()
: null;
ThingTypeUID thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE;
if (isSupportedDeviceModel(deviceModel, PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2020)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2020;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2019)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2019;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2018)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2018;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2017)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2017;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2016)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2016;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2015)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2015;
} else if (isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS2014)) {
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_THING_TYPE2014;
} else if (!isSupportedDeviceModel(deviceModel,
PioneerAvrBindingConstants.SUPPORTED_DEVICE_MODELS)) {
logger.debug("Device model {} not supported. Odd behaviors may happen.", deviceModel);
thingTypeUID = PioneerAvrBindingConstants.IP_AVR_UNSUPPORTED_THING_TYPE;
}
result = new ThingUID(thingTypeUID, device.getIdentity().getUdn().getIdentifierString());
}
}
}
return result;
}
/**
* Return true only if the given device model is supported.
*
* @param deviceModel
* @return
*/
private boolean isSupportedDeviceModel(String deviceModel, Set<String> supportedDeviceModels) {
return StringUtils.isNotBlank(deviceModel) && supportedDeviceModels.stream()
.anyMatch(input -> StringUtils.startsWithIgnoreCase(deviceModel, input));
}
}

View File

@@ -0,0 +1,384 @@
/**
* 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.pioneeravr.internal.handler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import org.openhab.binding.pioneeravr.internal.PioneerAvrBindingConstants;
import org.openhab.binding.pioneeravr.internal.protocol.RequestResponseFactory;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnectionException;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrResponse;
import org.openhab.binding.pioneeravr.internal.protocol.avr.CommandTypeNotSupportedException;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrDisconnectionEvent;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrDisconnectionListener;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrStatusUpdateEvent;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrUpdateListener;
import org.openhab.binding.pioneeravr.internal.protocol.states.MuteStateValues;
import org.openhab.binding.pioneeravr.internal.protocol.states.PowerStateValues;
import org.openhab.binding.pioneeravr.internal.protocol.utils.DisplayInformationConverter;
import org.openhab.binding.pioneeravr.internal.protocol.utils.VolumeConverter;
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.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractAvrHandler} is responsible for handling commands, which are sent to one of the channels through an
* AVR connection.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public abstract class AbstractAvrHandler extends BaseThingHandler
implements AvrUpdateListener, AvrDisconnectionListener {
private final Logger logger = LoggerFactory.getLogger(AbstractAvrHandler.class);
private AvrConnection connection;
private ScheduledFuture<?> statusCheckerFuture;
public AbstractAvrHandler(Thing thing) {
super(thing);
this.connection = createConnection();
this.connection.addUpdateListener(this);
this.connection.addDisconnectionListener(this);
}
/**
* Create a new connection to the AVR.
*
* @return
*/
protected abstract AvrConnection createConnection();
/**
* Initialize the state of the AVR.
*/
@Override
public void initialize() {
logger.debug("Initializing handler for Pioneer AVR @{}", connection.getConnectionName());
updateStatus(ThingStatus.ONLINE);
// Start the status checker
Runnable statusChecker = () -> {
try {
logger.debug("Checking status of AVR @{}", connection.getConnectionName());
checkStatus();
} catch (LinkageError e) {
logger.warn(
"Failed to check the status for AVR @{}. If a Serial link is used to connect to the AVR, please check that the Bundle org.openhab.io.transport.serial is available. Cause: {}",
connection.getConnectionName(), e.getMessage());
// Stop to check the status of this AVR.
if (statusCheckerFuture != null) {
statusCheckerFuture.cancel(false);
}
}
};
statusCheckerFuture = scheduler.scheduleWithFixedDelay(statusChecker, 1, 10, TimeUnit.SECONDS);
}
/**
* Close the connection and stop the status checker.
*/
@Override
public void dispose() {
super.dispose();
if (statusCheckerFuture != null) {
statusCheckerFuture.cancel(true);
}
if (connection != null) {
connection.close();
}
}
/**
* Called when a Power ON state update is received from the AVR for the given zone.
*/
public void onPowerOn(int zone) {
// When the AVR is Powered ON, query the volume, the mute state and the source input of the zone
connection.sendVolumeQuery(zone);
connection.sendMuteQuery(zone);
connection.sendInputSourceQuery(zone);
connection.sendListeningModeQuery(zone);
}
/**
* Called when a Power OFF state update is received from the AVR.
*/
public void onPowerOff(int zone) {
// When the AVR is Powered OFF, update the status of channels to Undefined
updateState(getChannelUID(PioneerAvrBindingConstants.MUTE_CHANNEL, zone), UnDefType.UNDEF);
updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DB_CHANNEL, zone), UnDefType.UNDEF);
updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DIMMER_CHANNEL, zone), UnDefType.UNDEF);
updateState(getChannelUID(PioneerAvrBindingConstants.SET_INPUT_SOURCE_CHANNEL, zone), UnDefType.UNDEF);
updateState(getChannelUID(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL, zone), UnDefType.UNDEF);
updateState(getChannelUID(PioneerAvrBindingConstants.PLAYING_LISTENING_MODE_CHANNEL, zone), UnDefType.UNDEF);
}
/**
* Check the status of the AVR. Return true if the AVR is online, else return false.
*
* @return
*/
private void checkStatus() {
// If the power query request has not been sent, the connection to the
// AVR has failed. So update its status to OFFLINE.
if (!connection.sendPowerQuery(1)) {
updateStatus(ThingStatus.OFFLINE);
} else {
// If the power query has succeeded, the AVR status is ONLINE.
updateStatus(ThingStatus.ONLINE);
// Then send a power query for zone 2 and 3
connection.sendPowerQuery(2);
connection.sendPowerQuery(3);
}
}
/**
* Send a command to the AVR based on the openHAB command received.
*/
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
try {
boolean commandSent = false;
boolean unknownCommand = false;
if (channelUID.getId().contains(PioneerAvrBindingConstants.POWER_CHANNEL)) {
if (command == RefreshType.REFRESH) {
commandSent = connection.sendPowerQuery(getZoneFromChannelUID(channelUID.getId()));
} else {
commandSent = connection.sendPowerCommand(command, getZoneFromChannelUID(channelUID.getId()));
}
} else if (channelUID.getId().contains(PioneerAvrBindingConstants.VOLUME_DIMMER_CHANNEL)
|| channelUID.getId().contains(PioneerAvrBindingConstants.VOLUME_DB_CHANNEL)) {
if (command == RefreshType.REFRESH) {
commandSent = connection.sendVolumeQuery(getZoneFromChannelUID(channelUID.getId()));
} else {
commandSent = connection.sendVolumeCommand(command, getZoneFromChannelUID(channelUID.getId()));
}
} else if (channelUID.getId().contains(PioneerAvrBindingConstants.SET_INPUT_SOURCE_CHANNEL)) {
if (command == RefreshType.REFRESH) {
commandSent = connection.sendInputSourceQuery(getZoneFromChannelUID(channelUID.getId()));
} else {
commandSent = connection.sendInputSourceCommand(command, getZoneFromChannelUID(channelUID.getId()));
}
} else if (channelUID.getId().contains(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL)) {
if (command == RefreshType.REFRESH) {
commandSent = connection.sendListeningModeQuery(getZoneFromChannelUID(channelUID.getId()));
} else {
commandSent = connection.sendListeningModeCommand(command,
getZoneFromChannelUID(channelUID.getId()));
}
} else if (channelUID.getId().contains(PioneerAvrBindingConstants.MUTE_CHANNEL)) {
if (command == RefreshType.REFRESH) {
commandSent = connection.sendMuteQuery(getZoneFromChannelUID(channelUID.getId()));
} else {
commandSent = connection.sendMuteCommand(command, getZoneFromChannelUID(channelUID.getId()));
}
} else {
unknownCommand = true;
}
// If the command is not unknown and has not been sent, the AVR is Offline
if (!commandSent && !unknownCommand) {
onDisconnection();
}
} catch (CommandTypeNotSupportedException e) {
logger.warn("Unsupported command type received for channel {}.", channelUID.getId());
}
}
/**
* Called when a status update is received from the AVR.
*/
@Override
public void statusUpdateReceived(AvrStatusUpdateEvent event) {
try {
AvrResponse response = RequestResponseFactory.getIpControlResponse(event.getData());
switch (response.getResponseType()) {
case POWER_STATE:
managePowerStateUpdate(response);
break;
case VOLUME_LEVEL:
manageVolumeLevelUpdate(response);
break;
case MUTE_STATE:
manageMuteStateUpdate(response);
break;
case INPUT_SOURCE_CHANNEL:
manageInputSourceChannelUpdate(response);
break;
case LISTENING_MODE:
manageListeningModeUpdate(response);
break;
case PLAYING_LISTENING_MODE:
managePlayingListeningModeUpdate(response);
break;
case DISPLAY_INFORMATION:
manageDisplayedInformationUpdate(response);
break;
default:
logger.debug("Unknown response type from AVR @{}. Response discarded: {}", event.getData(),
event.getConnection());
}
} catch (AvrConnectionException e) {
logger.debug("Unknown response type from AVR @{}. Response discarded: {}", event.getData(),
event.getConnection());
}
}
/**
* Called when the AVR is disconnected
*/
@Override
public void onDisconnection(AvrDisconnectionEvent event) {
onDisconnection();
}
/**
* Process the AVR disconnection.
*/
private void onDisconnection() {
updateStatus(ThingStatus.OFFLINE);
}
/**
* Notify an AVR power state update to openHAB
*
* @param response
*/
private void managePowerStateUpdate(AvrResponse response) {
OnOffType state = PowerStateValues.ON_VALUE.equals(response.getParameterValue()) ? OnOffType.ON : OnOffType.OFF;
// When a Power ON state update is received, call the onPowerOn method.
if (OnOffType.ON == state) {
onPowerOn(response.getZone());
} else {
onPowerOff(response.getZone());
}
updateState(getChannelUID(PioneerAvrBindingConstants.POWER_CHANNEL, response.getZone()), state);
}
/**
* Notify an AVR volume level update to openHAB
*
* @param response
*/
private void manageVolumeLevelUpdate(AvrResponse response) {
updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DB_CHANNEL, response.getZone()), new DecimalType(
VolumeConverter.convertFromIpControlVolumeToDb(response.getParameterValue(), response.getZone())));
updateState(getChannelUID(PioneerAvrBindingConstants.VOLUME_DIMMER_CHANNEL, response.getZone()),
new PercentType((int) VolumeConverter.convertFromIpControlVolumeToPercent(response.getParameterValue(),
response.getZone())));
}
/**
* Notify an AVR mute state update to openHAB
*
* @param response
*/
private void manageMuteStateUpdate(AvrResponse response) {
updateState(getChannelUID(PioneerAvrBindingConstants.MUTE_CHANNEL, response.getZone()),
response.getParameterValue().equals(MuteStateValues.OFF_VALUE) ? OnOffType.OFF : OnOffType.ON);
}
/**
* Notify an AVR input source channel update to openHAB
*
* @param response
*/
private void manageInputSourceChannelUpdate(AvrResponse response) {
updateState(getChannelUID(PioneerAvrBindingConstants.SET_INPUT_SOURCE_CHANNEL, response.getZone()),
new StringType(response.getParameterValue()));
}
/**
* Notify an AVR now-playing, in-effect listening mode (audio output format) update to openHAB
*
* @param response
*/
private void managePlayingListeningModeUpdate(AvrResponse response) {
updateState(getChannelUID(PioneerAvrBindingConstants.PLAYING_LISTENING_MODE_CHANNEL, response.getZone()),
new StringType(response.getParameterValue()));
}
/**
* Notify an AVR set listening mode (user-selected audio mode) update to openHAB
*
* @param response
*/
private void manageListeningModeUpdate(AvrResponse response) {
updateState(getChannelUID(PioneerAvrBindingConstants.LISTENING_MODE_CHANNEL, response.getZone()),
new StringType(response.getParameterValue()));
}
/**
* Notify an AVR displayed information update to openHAB
*
* @param response
*/
private void manageDisplayedInformationUpdate(AvrResponse response) {
updateState(PioneerAvrBindingConstants.DISPLAY_INFORMATION_CHANNEL,
new StringType(DisplayInformationConverter.convertMessageFromIpControl(response.getParameterValue())));
}
/**
* Build the channelUID from the channel name and the zone number.
*
* @param channelName
* @param zone
* @return
*/
protected String getChannelUID(String channelName, int zone) {
return String.format(PioneerAvrBindingConstants.GROUP_CHANNEL_PATTERN, zone, channelName);
}
/**
* Return the zone from the given channelUID.
*
* Return 0 if the zone cannot be extracted from the channelUID.
*
* @param channelUID
* @return
*/
protected int getZoneFromChannelUID(String channelUID) {
int zone = 0;
Matcher matcher = PioneerAvrBindingConstants.GROUP_CHANNEL_ZONE_PATTERN.matcher(channelUID);
if (matcher.find()) {
zone = Integer.valueOf(matcher.group(1));
}
return zone;
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.pioneeravr.internal.handler;
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.pioneeravr.internal.PioneerAvrBindingConstants;
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.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link AvrHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Antoine Besnard - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.pioneeravr")
public class AvrHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
Stream.of(PioneerAvrBindingConstants.IP_AVR_THING_TYPE, PioneerAvrBindingConstants.IP_AVR_THING_TYPE2014,
PioneerAvrBindingConstants.IP_AVR_THING_TYPE2015, PioneerAvrBindingConstants.IP_AVR_THING_TYPE2016,
PioneerAvrBindingConstants.IP_AVR_THING_TYPE2017, PioneerAvrBindingConstants.IP_AVR_THING_TYPE2018,
PioneerAvrBindingConstants.IP_AVR_THING_TYPE2019, PioneerAvrBindingConstants.IP_AVR_THING_TYPE2020,
PioneerAvrBindingConstants.IP_AVR_UNSUPPORTED_THING_TYPE,
PioneerAvrBindingConstants.SERIAL_AVR_THING_TYPE).collect(Collectors.toSet()));
private SerialPortManager serialPortManager;
@Activate
public AvrHandlerFactory(ComponentContext componentContext, final @Reference SerialPortManager serialPortManager) {
super.activate(componentContext);
this.serialPortManager = serialPortManager;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2014)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2015)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2016)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2017)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2018)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2019)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_THING_TYPE2020)
|| thingTypeUID.equals(PioneerAvrBindingConstants.IP_AVR_UNSUPPORTED_THING_TYPE)) {
return new IpAvrHandler(thing);
} else if (thingTypeUID.equals(PioneerAvrBindingConstants.SERIAL_AVR_THING_TYPE)) {
return new SerialAvrHandler(thing, serialPortManager);
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.pioneeravr.internal.handler;
import org.openhab.binding.pioneeravr.internal.PioneerAvrBindingConstants;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
import org.openhab.binding.pioneeravr.internal.protocol.ip.IpAvrConnection;
import org.openhab.core.thing.Thing;
/**
* An handler of an AVR connected through an IP connection.
*
* @author Antoine Besnard - Initial contribution
*/
public class IpAvrHandler extends AbstractAvrHandler {
public IpAvrHandler(Thing thing) {
super(thing);
}
@Override
protected AvrConnection createConnection() {
String host = (String) this.getConfig().get(PioneerAvrBindingConstants.HOST_PARAMETER);
Integer tcpPort = ((Number) this.getConfig().get(PioneerAvrBindingConstants.TCP_PORT_PARAMETER)).intValue();
return new IpAvrConnection(host, tcpPort);
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.pioneeravr.internal.handler;
import org.openhab.binding.pioneeravr.internal.PioneerAvrBindingConstants;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
import org.openhab.binding.pioneeravr.internal.protocol.serial.SerialAvrConnection;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Thing;
/**
* An handler of an AVR connected through a serial port.
*
* @author Antoine Besnard - Initial contribution
*/
public class SerialAvrHandler extends AbstractAvrHandler {
private SerialPortManager serialPortManager;
public SerialAvrHandler(Thing thing, SerialPortManager serialPortManager) {
super(thing);
this.serialPortManager = serialPortManager;
}
@Override
protected AvrConnection createConnection() {
String serialPort = (String) this.getConfig().get(PioneerAvrBindingConstants.SERIAL_PORT_PARAMETER);
return new SerialAvrConnection(serialPort, serialPortManager);
}
}

View File

@@ -0,0 +1,85 @@
/**
* 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.pioneeravr.internal.protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents the display status message send by the Pioneer AV receiver
* (response to "?FL" request)
*
* @author Rainer Ostendorf - Initial contribution
*/
public class DisplayInformation {
private final Logger logger = LoggerFactory.getLogger(DisplayInformation.class);
private Boolean volumeDisplay; // 1-light, 0-OFF
private Boolean guidIcon; // 1-light, 0-OFF
private String infoText = ""; // the actual display text
/**
* parse the display status text send from the receiver
*
* @param responsePayload the responses payload, that is without the leading "FL"
*
* @return
*/
public DisplayInformation(String responsePayload) {
volumeDisplay = false;
guidIcon = false;
infoText = "";
// Example from Pioneer docs: When " [)(]DIGITAL EX " is displayed, response command is:
// FL000005064449474954414C00455800<CR+LF>
// first byte holds the two special flags
byte firstByte = (byte) Integer.parseInt(responsePayload.substring(0, 1), 16);
if ((firstByte & (1 << 0)) == (1 << 0)) {
guidIcon = true;
}
if ((firstByte & (1 << 1)) == (1 << 1)) {
volumeDisplay = true;
}
// convert the ascii values back to string
StringBuilder sb = new StringBuilder();
for (int i = 2; i < responsePayload.length() - 1; i += 2) {
String hexAsciiValue = responsePayload.substring(i, i + 2);
try {
sb.append((char) Integer.parseInt(hexAsciiValue, 16));
} catch (Exception e) {
logger.error("parsing string failed '{}'", responsePayload, e);
}
}
infoText = sb.toString();
}
public String getInfoText() {
return infoText;
}
public void setInfoText(String infoText) {
this.infoText = infoText;
}
public Boolean getVolumeDisplay() {
return volumeDisplay;
}
public Boolean getGuidIcon() {
return guidIcon;
}
}

View File

@@ -0,0 +1,95 @@
/**
* 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.pioneeravr.internal.protocol;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrCommand;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnectionException;
/**
* A command which accept a parameter.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public class ParameterizedCommand extends SimpleCommand {
/**
* List of the commands with a parameter.
*/
public enum ParameterizedCommandType implements AvrCommand.CommandType {
VOLUME_SET("[0-9]{2,3}", "VL", "ZV", "YV", "HZV"),
INPUT_CHANNEL_SET("[0-9]{2}", "FN", "ZS", "ZT", "ZEA"),
LISTENING_MODE_SET("[0-9]{4}", "SR");
private String[] zoneCommands;
private String parameterPattern;
private ParameterizedCommandType(String parameterPattern, String... zoneCommands) {
this.zoneCommands = zoneCommands;
this.parameterPattern = parameterPattern;
}
@Override
public String getCommand(int zone) {
return zoneCommands[zone - 1];
}
public String getParameterPattern() {
return parameterPattern;
}
}
private String parameter;
private String parameterPattern;
protected ParameterizedCommand(ParameterizedCommandType command, int zone) {
super(command, zone);
this.parameterPattern = command.getParameterPattern();
}
/**
* Return the command to send to the AVR with the parameter value configured.
*
* throws {@link AvrConnectionException} if the parameter is null, empty or has a bad format.
*/
@Override
public String getCommand() throws AvrConnectionException {
if (parameter == null) {
throw new AvrConnectionException(
"The parameter of the command " + super.getCommand() + " must not be null.");
}
if (StringUtils.isNotEmpty(parameterPattern) && !parameter.matches(parameterPattern)) {
throw new AvrConnectionException("The parameter value " + parameter + " of the command "
+ super.getCommand() + " does not match the pattern " + parameterPattern);
}
return parameter + super.getCommand();
}
public ParameterizedCommand setParameter(String parameter) {
this.parameter = parameter;
return this;
}
public String getParameter() {
return this.parameter;
}
public String getParameterPattern() {
return parameterPattern;
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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.pioneeravr.internal.protocol;
import org.openhab.binding.pioneeravr.internal.protocol.ParameterizedCommand.ParameterizedCommandType;
import org.openhab.binding.pioneeravr.internal.protocol.SimpleCommand.SimpleCommandType;
import org.openhab.binding.pioneeravr.internal.protocol.ip.IpAvrConnection;
/**
* Factory that allows to build IpControl commands/responses.
*
* @author Antoine Besnard - Initial contribution
*/
public final class RequestResponseFactory {
/**
* Return a connection to the AVR with the given host and port.
*
* @param host
* @param port
* @return
*/
public static IpAvrConnection getConnection(String host, Integer port) {
return new IpAvrConnection(host, port);
}
/**
* Return a ParameterizedCommand of the type given in parameter and for the given zone.
*
* @param command
* @param zone
* @return
*/
public static SimpleCommand getIpControlCommand(SimpleCommandType command, int zone) {
SimpleCommand result = new SimpleCommand(command, zone);
return result;
}
/**
* Return a ParameterizedCommand of the type given in parameter. The
* parameter of the command has to be set before send.
*
* @param command
* @param zone
* @return
*/
public static ParameterizedCommand getIpControlCommand(ParameterizedCommandType command, int zone) {
ParameterizedCommand result = new ParameterizedCommand(command, zone);
return result;
}
/**
* Return a ParameterizedCommand of the type given in parameter. The
* parameter of the command is set with the given paramter value.
*
* @param command
* @param parameter
* @param zone
* @return
*/
public static ParameterizedCommand getIpControlCommand(ParameterizedCommandType command, String parameter,
int zone) {
ParameterizedCommand result = getIpControlCommand(command, zone);
result.setParameter(parameter);
return result;
}
/**
* Return a IpControlResponse object based on the given response data.
*
* @param responseData
* @return
*/
public static Response getIpControlResponse(String responseData) {
return new Response(responseData);
}
}

View File

@@ -0,0 +1,168 @@
/**
* 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.pioneeravr.internal.protocol;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnectionException;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrResponse;
/**
* Represent an AVR response.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public class Response implements AvrResponse {
/**
* List of all supported responses coming from AVR.
*/
public enum ResponseType implements AvrResponse.AvrResponseType {
POWER_STATE("[0-2]", "PWR", "APR", "BPR", "ZEP"),
VOLUME_LEVEL("[0-9]{2,3}", "VOL", "ZV", "YV", "HZV"),
MUTE_STATE("[0-1]", "MUT", "Z2MUT", "Z3MUT", "HZM"),
INPUT_SOURCE_CHANNEL("[0-9]{2}", "FN", "Z2F", "Z3F", "ZEA"),
LISTENING_MODE("[0-9]{4}", "SR"),
PLAYING_LISTENING_MODE("[0-9a-f]{4}", "LM"),
DISPLAY_INFORMATION("[0-9a-fA-F]{30}", "FL");
private String[] responsePrefixZone;
private String parameterPattern;
private Pattern[] matchPatternZone;
private ResponseType(String parameterPattern, String... responsePrefixZone) {
this.responsePrefixZone = responsePrefixZone;
this.parameterPattern = parameterPattern;
matchPatternZone = new Pattern[responsePrefixZone.length];
for (int zoneIndex = 0; zoneIndex < responsePrefixZone.length; zoneIndex++) {
String responsePrefix = responsePrefixZone[zoneIndex];
matchPatternZone[zoneIndex] = Pattern.compile(responsePrefix + "("
+ (StringUtils.isNotEmpty(parameterPattern) ? parameterPattern : "") + ")");
}
}
@Override
public String getResponsePrefix(int zone) {
return responsePrefixZone[zone - 1];
}
@Override
public boolean hasParameter() {
return StringUtils.isNotEmpty(parameterPattern);
}
@Override
public String getParameterPattern() {
return parameterPattern;
}
@Override
public Integer match(String responseData) {
Integer zone = null;
// Check the response data against all zone prefixes.
for (int zoneIndex = 0; zoneIndex < matchPatternZone.length; zoneIndex++) {
if (matchPatternZone[zoneIndex].matcher(responseData).matches()) {
zone = zoneIndex + 1;
break;
}
}
return zone;
}
/**
* Return the parameter value of the given responseData.
*
* @param responseData
* @return
*/
@Override
public String parseParameter(String responseData) {
String result = null;
// Check the response data against all zone prefixes.
for (int zoneIndex = 0; zoneIndex < matchPatternZone.length; zoneIndex++) {
Matcher matcher = matchPatternZone[zoneIndex].matcher(responseData);
if (matcher.find()) {
result = matcher.group(1);
break;
}
}
return result;
}
}
private ResponseType responseType;
private Integer zone;
private String parameter;
public Response(String responseData) throws AvrConnectionException {
if (StringUtils.isEmpty(responseData)) {
throw new AvrConnectionException("responseData is empty. Cannot parse the response.");
}
parseResponseType(responseData);
if (this.responseType == null) {
throw new AvrConnectionException("Cannot find the responseType of the responseData " + responseData);
}
if (this.responseType.hasParameter()) {
this.parameter = this.responseType.parseParameter(responseData);
}
}
/**
* Parse the given response data and fill the
*
* @param responseData
* @return
*/
private void parseResponseType(String responseData) {
for (ResponseType responseType : ResponseType.values()) {
zone = responseType.match(responseData);
if (zone != null) {
this.responseType = responseType;
break;
}
}
}
@Override
public ResponseType getResponseType() {
return this.responseType;
}
@Override
public String getParameterValue() {
return parameter;
}
@Override
public boolean hasParameter() {
return responseType.hasParameter();
}
@Override
public Integer getZone() {
return this.zone;
}
}

View File

@@ -0,0 +1,79 @@
/**
* 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.pioneeravr.internal.protocol;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrCommand;
/**
* A simple command without parameters.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public class SimpleCommand implements AvrCommand {
/**
* List of the simple command types.
*/
public enum SimpleCommandType implements AvrCommand.CommandType {
POWER_ON("PO", "APO", "BPO", "ZEO"),
POWER_OFF("PF", "APF", "BPF", "ZEF"),
POWER_QUERY("?P", "?AP", "?BP", "?ZEP"),
VOLUME_UP("VU", "ZU", "YU", "HZU"),
VOLUME_DOWN("VD", "ZD", "YD", "HZD"),
VOLUME_QUERY("?V", "?ZV", "?YV", "?HZV"),
MUTE_ON("MO", "Z2MO", "Z3MO", "HZMO"),
MUTE_OFF("MF", "Z2MF", "Z3MF", "HZMF"),
MUTE_QUERY("?M", "?Z2M", "?Z3M", "?HZM"),
INPUT_CHANGE_CYCLIC("FU"),
INPUT_CHANGE_REVERSE("FD"),
LISTENING_MODE_CHANGE_CYCLIC("0010SR"),
LISTENING_MODE_QUERY("?S"),
INPUT_QUERY("?F", "?ZS", "?ZT", "?ZEA");
private String zoneCommands[];
private SimpleCommandType(String... command) {
this.zoneCommands = command;
}
@Override
public String getCommand(int zone) {
return zoneCommands[zone - 1];
}
}
private CommandType commandType;
private int zone;
public SimpleCommand(CommandType commandType, int zone) {
this.commandType = commandType;
this.zone = zone;
}
@Override
public String getCommand() {
return commandType.getCommand(zone) + "\r";
}
@Override
public CommandType getCommandType() {
return commandType;
}
@Override
public int getZone() {
return zone;
}
}

View File

@@ -0,0 +1,386 @@
/**
* 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.pioneeravr.internal.protocol;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.pioneeravr.internal.protocol.ParameterizedCommand.ParameterizedCommandType;
import org.openhab.binding.pioneeravr.internal.protocol.SimpleCommand.SimpleCommandType;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrCommand;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
import org.openhab.binding.pioneeravr.internal.protocol.avr.CommandTypeNotSupportedException;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrDisconnectionEvent;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrDisconnectionListener;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrStatusUpdateEvent;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrUpdateListener;
import org.openhab.binding.pioneeravr.internal.protocol.utils.VolumeConverter;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.Command;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* A class that wraps the communication to Pioneer AVR devices by using Input/Ouptut streams.
*
* see {@link http ://www.pioneerelectronics.com/StaticFiles/PUSA/Files/Home%20Custom %20Install/VSX-1120-K-RS232.PDF}
* for the protocol specs
*
* Based on the Onkyo binding by Pauli Anttila and others.
*
* @author Antoine Besnard - Initial contribution
* @author Rainer Ostendorf - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public abstract class StreamAvrConnection implements AvrConnection {
private final Logger logger = LoggerFactory.getLogger(StreamAvrConnection.class);
// The maximum time to wait incoming messages.
private static final Integer READ_TIMEOUT = 1000;
private List<AvrUpdateListener> updateListeners;
private List<AvrDisconnectionListener> disconnectionListeners;
private IpControlInputStreamReader inputStreamReader;
private DataOutputStream outputStream;
public StreamAvrConnection() {
this.updateListeners = new ArrayList<>();
this.disconnectionListeners = new ArrayList<>();
}
@Override
public void addUpdateListener(AvrUpdateListener listener) {
synchronized (updateListeners) {
updateListeners.add(listener);
}
}
@Override
public void addDisconnectionListener(AvrDisconnectionListener listener) {
synchronized (disconnectionListeners) {
disconnectionListeners.add(listener);
}
}
@Override
public boolean connect() {
if (!isConnected()) {
try {
openConnection();
// Start the inputStream reader.
inputStreamReader = new IpControlInputStreamReader(getInputStream());
inputStreamReader.start();
// Get Output stream
outputStream = new DataOutputStream(getOutputStream());
} catch (IOException ioException) {
logger.debug("Can't connect to {}. Cause: {}", getConnectionName(), ioException.getMessage());
}
}
return isConnected();
}
/**
* Open the connection to the AVR.
*
* @throws IOException
*/
protected abstract void openConnection() throws IOException;
/**
* Return the inputStream to read responses.
*
* @return
* @throws IOException
*/
protected abstract InputStream getInputStream() throws IOException;
/**
* Return the outputStream to send commands.
*
* @return
* @throws IOException
*/
protected abstract OutputStream getOutputStream() throws IOException;
@Override
public void close() {
if (inputStreamReader != null) {
// This method block until the reader is really stopped.
inputStreamReader.stopReader();
inputStreamReader = null;
logger.debug("Stream reader stopped!");
}
}
/**
* Sends to command to the receiver. It does not wait for a reply.
*
* @param ipControlCommand
* the command to send.
**/
protected boolean sendCommand(AvrCommand ipControlCommand) {
boolean isSent = false;
if (connect()) {
String command = ipControlCommand.getCommand();
try {
if (logger.isTraceEnabled()) {
logger.trace("Sending {} bytes: {}", command.length(), HexUtils.bytesToHex(command.getBytes()));
}
outputStream.writeBytes(command);
outputStream.flush();
isSent = true;
} catch (IOException ioException) {
logger.error("Error occurred when sending command", ioException);
// If an error occurs, close the connection
close();
}
logger.debug("Command sent to AVR @{}: {}", getConnectionName(), command);
}
return isSent;
}
@Override
public boolean sendPowerQuery(int zone) {
return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.POWER_QUERY, zone));
}
@Override
public boolean sendVolumeQuery(int zone) {
return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.VOLUME_QUERY, zone));
}
@Override
public boolean sendMuteQuery(int zone) {
return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.MUTE_QUERY, zone));
}
@Override
public boolean sendInputSourceQuery(int zone) {
return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.INPUT_QUERY, zone));
}
@Override
public boolean sendListeningModeQuery(int zone) {
return sendCommand(RequestResponseFactory.getIpControlCommand(SimpleCommandType.LISTENING_MODE_QUERY, zone));
}
@Override
public boolean sendPowerCommand(Command command, int zone) throws CommandTypeNotSupportedException {
AvrCommand commandToSend = null;
if (command == OnOffType.ON) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.POWER_ON, zone);
// Send the first Power ON command.
sendCommand(commandToSend);
// According to the Pioneer Specs, the first request only wakeup the
// AVR CPU, the second one Power ON the AVR. Still according to the Pioneer Specs, the second
// request has to be delayed of 100 ms.
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
} else if (command == OnOffType.OFF) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.POWER_OFF, zone);
} else {
throw new CommandTypeNotSupportedException("Command type not supported.");
}
return sendCommand(commandToSend);
}
@Override
public boolean sendVolumeCommand(Command command, int zone) throws CommandTypeNotSupportedException {
boolean commandSent = false;
// The OnOffType for volume is equal to the Mute command
if (command instanceof OnOffType) {
commandSent = sendMuteCommand(command, zone);
} else {
AvrCommand commandToSend = null;
if (command == IncreaseDecreaseType.DECREASE) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.VOLUME_DOWN, zone);
} else if (command == IncreaseDecreaseType.INCREASE) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.VOLUME_UP, zone);
} else if (command instanceof PercentType) {
String ipControlVolume = VolumeConverter
.convertFromPercentToIpControlVolume(((PercentType) command).doubleValue(), zone);
commandToSend = RequestResponseFactory.getIpControlCommand(ParameterizedCommandType.VOLUME_SET, zone)
.setParameter(ipControlVolume);
} else if (command instanceof DecimalType) {
String ipControlVolume = VolumeConverter
.convertFromDbToIpControlVolume(((DecimalType) command).doubleValue(), zone);
commandToSend = RequestResponseFactory.getIpControlCommand(ParameterizedCommandType.VOLUME_SET, zone)
.setParameter(ipControlVolume);
} else {
throw new CommandTypeNotSupportedException("Command type not supported.");
}
commandSent = sendCommand(commandToSend);
}
return commandSent;
}
@Override
public boolean sendInputSourceCommand(Command command, int zone) throws CommandTypeNotSupportedException {
AvrCommand commandToSend = null;
if (command == IncreaseDecreaseType.INCREASE) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.INPUT_CHANGE_CYCLIC, zone);
} else if (command == IncreaseDecreaseType.DECREASE) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.INPUT_CHANGE_REVERSE, zone);
} else if (command instanceof StringType) {
String inputSourceValue = ((StringType) command).toString();
commandToSend = RequestResponseFactory.getIpControlCommand(ParameterizedCommandType.INPUT_CHANNEL_SET, zone)
.setParameter(inputSourceValue);
} else {
throw new CommandTypeNotSupportedException("Command type not supported.");
}
return sendCommand(commandToSend);
}
@Override
public boolean sendListeningModeCommand(Command command, int zone) throws CommandTypeNotSupportedException {
AvrCommand commandToSend = null;
if (command == IncreaseDecreaseType.INCREASE) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.LISTENING_MODE_CHANGE_CYCLIC,
zone);
} else if (command instanceof StringType) {
String listeningModeValue = ((StringType) command).toString();
commandToSend = RequestResponseFactory
.getIpControlCommand(ParameterizedCommandType.LISTENING_MODE_SET, zone)
.setParameter(listeningModeValue);
} else {
throw new CommandTypeNotSupportedException("Command type not supported.");
}
return sendCommand(commandToSend);
}
@Override
public boolean sendMuteCommand(Command command, int zone) throws CommandTypeNotSupportedException {
AvrCommand commandToSend = null;
if (command == OnOffType.ON) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.MUTE_ON, zone);
} else if (command == OnOffType.OFF) {
commandToSend = RequestResponseFactory.getIpControlCommand(SimpleCommandType.MUTE_OFF, zone);
} else {
throw new CommandTypeNotSupportedException("Command type not supported.");
}
return sendCommand(commandToSend);
}
/**
* Read incoming data from the AVR and notify listeners for dataReceived and disconnection.
*
* @author Antoine Besnard
*
*/
private class IpControlInputStreamReader extends Thread {
private BufferedReader bufferedReader;
private volatile boolean stopReader;
// This latch is used to block the stop method until the reader is really stopped.
private CountDownLatch stopLatch;
/**
* Construct a reader that read the given inputStream
*
* @param ipControlSocket
* @throws IOException
*/
public IpControlInputStreamReader(InputStream inputStream) {
this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
this.stopLatch = new CountDownLatch(1);
this.setDaemon(true);
this.setName("IpControlInputStreamReader-" + getConnectionName());
}
@Override
public void run() {
try {
while (!stopReader && !Thread.currentThread().isInterrupted()) {
String receivedData = null;
try {
receivedData = bufferedReader.readLine();
} catch (SocketTimeoutException e) {
// Do nothing. Just happen to allow the thread to check if it has to stop.
}
if (receivedData != null) {
logger.debug("Data received from AVR @{}: {}", getConnectionName(), receivedData);
AvrStatusUpdateEvent event = new AvrStatusUpdateEvent(StreamAvrConnection.this, receivedData);
synchronized (updateListeners) {
for (AvrUpdateListener pioneerAvrEventListener : updateListeners) {
pioneerAvrEventListener.statusUpdateReceived(event);
}
}
}
}
} catch (IOException e) {
logger.warn("The AVR @{} is disconnected.", getConnectionName(), e);
AvrDisconnectionEvent event = new AvrDisconnectionEvent(StreamAvrConnection.this, e);
for (AvrDisconnectionListener pioneerAvrDisconnectionListener : disconnectionListeners) {
pioneerAvrDisconnectionListener.onDisconnection(event);
}
}
// Notify the stopReader method caller that the reader is stopped.
this.stopLatch.countDown();
}
/**
* Stop this reader. Block until the reader is really stopped.
*/
public void stopReader() {
this.stopReader = true;
try {
this.stopLatch.await(READ_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// Do nothing. The timeout is just here for safety and to be sure that the call to this method will not
// block the caller indefinitely.
// This exception should never happen.
}
}
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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.pioneeravr.internal.protocol.avr;
/**
* The base interface for an AVR command.
*
* @author Antoine Besnard - Initial contribution
*/
public interface AvrCommand {
/**
* Represent a CommandType of command requests
*/
public interface CommandType {
/**
* Return the command of this command type for the given zone.
*
* The first zone number is 1.
*
* @return
*/
public String getCommand(int zone);
/**
* Return the name of the command type
*
* @return
*/
public String name();
}
/**
* Return the command to send to the AVR.
*
* @return
*/
public String getCommand();
/**
* Return the number of the zone this command will be sent to.
*
* @return
*/
public int getZone();
/**
* Return the the command type of this command.
*
* @return
*/
public CommandType getCommandType();
}

View File

@@ -0,0 +1,150 @@
/**
* 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.pioneeravr.internal.protocol.avr;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrDisconnectionListener;
import org.openhab.binding.pioneeravr.internal.protocol.event.AvrUpdateListener;
import org.openhab.core.types.Command;
/**
* Represent a connection to a remote Pioneer AVR.
*
* @author Antoine Besnard - Initial contribution
* @author Leroy Foerster - Listening Mode, Playing Listening Mode
*/
public interface AvrConnection {
/**
* Add an update listener. It is notified when an update is received from the AVR.
*
* @param listener
*/
public void addUpdateListener(AvrUpdateListener listener);
/**
* Add a disconnection listener. It is notified when the AVR is disconnected.
*
* @param listener
*/
public void addDisconnectionListener(AvrDisconnectionListener listener);
/**
* Connect to the receiver. Return true if the connection has succeeded or if already connected.
*
**/
public boolean connect();
/**
* Return true if this manager is connected to the AVR.
*
* @return
*/
public boolean isConnected();
/**
* Closes the connection.
**/
public void close();
/**
* Send a power state query to the AVR
*
* @param zone
* @return
*/
public boolean sendPowerQuery(int zone);
/**
* Send a volume level query to the AVR
*
* @param zone
* @return
*/
public boolean sendVolumeQuery(int zone);
/**
* Send a mute state query to the AVR
*
* @param zone
* @return
*/
public boolean sendMuteQuery(int zone);
/**
* Send a source input state query to the AVR
*
* @param zone
* @return
*/
public boolean sendInputSourceQuery(int zone);
/**
* Send a listening mode state query to the AVR
*
* @param zone
* @return
*/
public boolean sendListeningModeQuery(int zone);
/**
* Send a power command ot the AVR based on the openHAB command
*
* @param command
* @param zone
* @return
*/
public boolean sendPowerCommand(Command command, int zone) throws CommandTypeNotSupportedException;
/**
* Send a volume command to the AVR based on the openHAB command
*
* @param command
* @param zone
* @return
*/
public boolean sendVolumeCommand(Command command, int zone) throws CommandTypeNotSupportedException;
/**
* Send a source input selection command to the AVR based on the openHAB command
*
* @param command
* @param zone
* @return
*/
public boolean sendInputSourceCommand(Command command, int zone) throws CommandTypeNotSupportedException;
/**
* Send a listening mode selection command to the AVR based on the openHAB command
*
* @param command
* @param zone
* @return
*/
public boolean sendListeningModeCommand(Command command, int zone) throws CommandTypeNotSupportedException;
/**
* Send a mute command to the AVR based on the openHAB command
*
* @param command
* @param zone
* @return
*/
public boolean sendMuteCommand(Command command, int zone) throws CommandTypeNotSupportedException;
/**
* Return the connection name
*
* @return
*/
public String getConnectionName();
}

View File

@@ -0,0 +1,41 @@
/**
* 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.pioneeravr.internal.protocol.avr;
/**
* Exception for eISCP errors.
*
* Based on the Onkyo binding by Pauli Anttila and others.
*
* @author Rainer Ostendorf - Initial contribution
*/
public class AvrConnectionException extends RuntimeException {
private static final long serialVersionUID = -7970958467980752003L;
public AvrConnectionException() {
super();
}
public AvrConnectionException(String message) {
super(message);
}
public AvrConnectionException(String message, Throwable cause) {
super(message, cause);
}
public AvrConnectionException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,97 @@
/**
* 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.pioneeravr.internal.protocol.avr;
import org.openhab.binding.pioneeravr.internal.protocol.Response.ResponseType;
/**
* Represent a response of the AVR.
*
* @author Antoine Besnard - Initial contribution
*/
public interface AvrResponse {
/**
* Represent the type of a response.
*/
public interface AvrResponseType {
/**
* Return the prefix of the command of this type.
*
* @param zone
* @return
*/
public String getResponsePrefix(int zone);
/**
* Return true if the responses of this type has to have a parameter.
*
* @return
*/
public boolean hasParameter();
/**
* Return the parameter pattern (RegEx) of the response.
*
* @return
*/
public String getParameterPattern();
/**
* Return the zone number if the responseData matches a zone of this responseType.
*
* If any zone matches, return null.
*
* @param responseData
* @return
*/
public Integer match(String responseData);
/**
* Return the parameter value of the given responseData.
*
* @param responseData
* @return
*/
public String parseParameter(String responseData);
}
/**
* Return the response type of this response
*
* @return
*/
public ResponseType getResponseType();
/**
* Return the parameter of this response or null if the resposne has no parameter.
*
* @return
*/
public String getParameterValue();
/**
* Return true if this response has a parameter.
*
* @return
*/
public boolean hasParameter();
/**
* Return the zone number which is concerned by this response.
*
* @return
*/
public Integer getZone();
}

View File

@@ -0,0 +1,39 @@
/**
* 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.pioneeravr.internal.protocol.avr;
/**
* Thrown when a command type is not supported by the channel
*
* @author Antoine Besnard - Initial contribution
*/
public class CommandTypeNotSupportedException extends Exception {
private static final long serialVersionUID = -7970958467980752003L;
public CommandTypeNotSupportedException() {
super();
}
public CommandTypeNotSupportedException(String message) {
super(message);
}
public CommandTypeNotSupportedException(String message, Throwable cause) {
super(message, cause);
}
public CommandTypeNotSupportedException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,39 @@
/**
* 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.pioneeravr.internal.protocol.event;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
/**
* An event fired when an AVR is disconnected.
*
* @author Antoine Besnard - Initial contribution
*/
public class AvrDisconnectionEvent {
private AvrConnection connection;
private Throwable cause;
public AvrDisconnectionEvent(AvrConnection connection, Throwable cause) {
this.connection = connection;
this.cause = cause;
}
public AvrConnection getConnection() {
return connection;
}
public Throwable getCause() {
return cause;
}
}

View File

@@ -0,0 +1,28 @@
/**
* 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.pioneeravr.internal.protocol.event;
/**
* A listener which is notified when an AVR is disconnected.
*
* @author Antoine Besnard - Initial contribution
*/
public interface AvrDisconnectionListener {
/**
* Called when an AVR is disconnected.
*
* @param event
*/
public void onDisconnection(AvrDisconnectionEvent event);
}

View File

@@ -0,0 +1,39 @@
/**
* 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.pioneeravr.internal.protocol.event;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnection;
/**
* The event fired when a status is received from the AVR.
*
* @author Antoine Besnard - Initial contribution
*/
public class AvrStatusUpdateEvent {
private AvrConnection connection;
private String data;
public AvrStatusUpdateEvent(AvrConnection connection, String data) {
this.connection = connection;
this.data = data;
}
public AvrConnection getConnection() {
return connection;
}
public String getData() {
return data;
}
}

View File

@@ -0,0 +1,31 @@
/**
* 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.pioneeravr.internal.protocol.event;
import java.util.EventListener;
/**
* This interface defines interface to receive status updates from pioneerav receiver.
*
* Based on the Onkyo binding by Pauli Anttila and others
*
* @author Antoine Besnard - Initial contribution
* @author Rainer Ostendorf - Initial contribution
*/
public interface AvrUpdateListener extends EventListener {
/**
* Procedure for receive status update from Pioneer receiver.
*/
public void statusUpdateReceived(AvrStatusUpdateEvent event);
}

View File

@@ -0,0 +1,110 @@
/**
* 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.pioneeravr.internal.protocol.ip;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.openhab.binding.pioneeravr.internal.protocol.StreamAvrConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* A class that wraps the communication to a Pioneer AVR devices using an IP connection.
*
* @author Antoine Besnard - Initial contribution
*/
public class IpAvrConnection extends StreamAvrConnection {
private final Logger logger = LoggerFactory.getLogger(IpAvrConnection.class);
/** default port for IP communication **/
public static final int DEFAULT_IPCONTROL_PORT = 8102;
/** Connection timeout in milliseconds **/
private static final int CONNECTION_TIMEOUT = 3000;
/** Socket read timeout in milliseconds **/
private static final int SOCKET_READ_TIMEOUT = 1000;
private int receiverPort;
private String receiverHost;
private Socket ipControlSocket;
public IpAvrConnection(String receiverHost) {
this(receiverHost, null);
}
public IpAvrConnection(String receiverHost, Integer ipControlPort) {
this.receiverHost = receiverHost;
this.receiverPort = ipControlPort != null && ipControlPort >= 1 ? ipControlPort : DEFAULT_IPCONTROL_PORT;
}
@Override
protected void openConnection() throws IOException {
ipControlSocket = new Socket();
// Set this timeout to unblock a blocking read when no data is received. It is useful to check if the
// reading thread has to be stopped (it implies a latency of SOCKET_READ_TIMEOUT at most before the
// thread is really stopped)
ipControlSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
// Enable tcpKeepAlive to detect premature disconnection
// and prevent disconnection because of inactivity
ipControlSocket.setKeepAlive(true);
// Connect to the AVR with a connection timeout.
ipControlSocket.connect(new InetSocketAddress(receiverHost, receiverPort), CONNECTION_TIMEOUT);
logger.debug("Connected to {}:{}", receiverHost, receiverPort);
}
@Override
public boolean isConnected() {
return ipControlSocket != null && ipControlSocket.isConnected() && !ipControlSocket.isClosed();
}
@Override
public void close() {
super.close();
try {
if (ipControlSocket != null) {
ipControlSocket.close();
ipControlSocket = null;
logger.debug("Closed socket!");
}
} catch (IOException ioException) {
logger.error("Closing connection throws an exception!", ioException);
}
}
@Override
public String getConnectionName() {
return receiverHost + ":" + receiverPort;
}
@Override
protected InputStream getInputStream() throws IOException {
return ipControlSocket.getInputStream();
}
@Override
protected OutputStream getOutputStream() throws IOException {
return ipControlSocket.getOutputStream();
}
}

View File

@@ -0,0 +1,109 @@
/**
* 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.pioneeravr.internal.protocol.serial;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.pioneeravr.internal.protocol.StreamAvrConnection;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
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;
/**
* A class that wraps the communication to a Pioneer AVR devices through a serial port
*
* @author Antoine Besnard - Initial contribution
*/
@NonNullByDefault
public class SerialAvrConnection extends StreamAvrConnection {
private final Logger logger = LoggerFactory.getLogger(SerialAvrConnection.class);
private static final Integer LINK_SPEED = 9600;
private final String portName;
private @Nullable SerialPort serialPort;
private final SerialPortManager serialPortManager;
public SerialAvrConnection(String portName, SerialPortManager serialPortManager) {
this.portName = portName;
this.serialPortManager = serialPortManager;
}
@Override
protected void openConnection() throws IOException {
SerialPortIdentifier serialPortIdentifier = serialPortManager.getIdentifier(portName);
if (serialPortIdentifier == null) {
String availablePorts = serialPortManager.getIdentifiers().map(id -> id.getName())
.collect(Collectors.joining(", "));
throw new IOException(
"Serial port with name " + portName + " does not exist. Available port names: " + availablePorts);
}
try {
SerialPort localSerialPort = serialPortIdentifier.open(SerialAvrConnection.class.getSimpleName(), 2000);
localSerialPort.setSerialPortParams(LINK_SPEED, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
serialPort = localSerialPort;
} catch (PortInUseException | UnsupportedCommOperationException e) {
throw new IOException("Failed to connect on port " + portName);
}
logger.debug("Connected to {}", getConnectionName());
}
@Override
public boolean isConnected() {
return serialPort != null;
}
@Override
public void close() {
super.close();
SerialPort localSerialPort = serialPort;
if (localSerialPort != null) {
localSerialPort.close();
serialPort = null;
logger.debug("Closed port {}", portName);
}
}
@Override
public String getConnectionName() {
return portName;
}
@Override
protected @Nullable InputStream getInputStream() throws IOException {
SerialPort localSerialPort = serialPort;
return localSerialPort != null ? localSerialPort.getInputStream() : null;
}
@Override
protected @Nullable OutputStream getOutputStream() throws IOException {
SerialPort localSerialPort = serialPort;
return localSerialPort != null ? localSerialPort.getOutputStream() : null;
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.pioneeravr.internal.protocol.states;
/**
*
* @author Antoine Besnard - Initial contribution
*/
public interface MuteStateValues {
public static final String ON_VALUE = "0";
public static final String OFF_VALUE = "1";
}

View File

@@ -0,0 +1,24 @@
/**
* 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.pioneeravr.internal.protocol.states;
/**
*
* @author Antoine Besnard - Initial contribution
*/
public interface PowerStateValues {
public static final String ON_VALUE = "0";
public static final String OFF_VALUE = "1";
public static final String NETWORK_STANDBY_VALUE = "2";
}

View File

@@ -0,0 +1,50 @@
/**
* 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.pioneeravr.internal.protocol.utils;
import org.openhab.binding.pioneeravr.internal.protocol.avr.AvrConnectionException;
/**
*
* @author Antoine Besnard - Initial contribution
*/
public class DisplayInformationConverter {
/**
* Convert an IpControl information message payload to a readable String.
*
* @param responsePayload
* @return
* @throws AvrConnectionException
*/
public static String convertMessageFromIpControl(String responsePayload) throws AvrConnectionException {
// Example from Pioneer docs: When " [)(]DIGITAL EX " is displayed,
// response command is:
// FL000005064449474954414C00455800<CR+LF>
// First byte holds the two special flags. Do not use it to parse the
// message.
// Convert the ASCII values back to string
StringBuilder sb = new StringBuilder();
for (int i = 2; i < responsePayload.length() - 1; i += 2) {
String hexAsciiValue = responsePayload.substring(i, i + 2);
try {
sb.append((char) Integer.parseInt(hexAsciiValue, 16));
} catch (Exception e) {
throw new AvrConnectionException(
"Failed to parse the reponsePayload as an IpControl information message.", e);
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,99 @@
/**
* 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.pioneeravr.internal.protocol.utils;
import java.text.DecimalFormat;
/**
*
* @author Antoine Besnard - Initial contribution
*/
public final class VolumeConverter {
// Volume Format / MAX_IP / Min_DB for Zones
// Zone1 Command ***VL 000 to 185 (-80.0dB - +12.0dB - 1step = 0.5dB)
// Zone2 Command **ZV 00 to 81 (-80.0dB - + 0.0dB - 1step = 1.0dB)
// Zone3 Command **YV 00 to 81 (-80.0dB - + 0.0dB - 1step = 1.0dB)
// HDZone Command **YV 00 to 81 (-80.0dB - + 0.0dB - 1step = 1.0dB)
private static final String[] IP_CONTROL_VOLUME_FORMAT = { "000", "00", "00", "00" };
private static final String[] IP_CONTROL_VOLUME_DEFAULT_VALUE = { "000", "00", "00", "00" };
private static final double[] MAX_IP_CONTROL_VOLUME = { 184, 80, 80, 80 };
private static final double[] MIN_DB_VOLUME = { 80, 80, 80, 80 };
/**
* Return the double value of the volume from the value received in the IpControl response.
*
* @param ipControlVolume
* @return the volume in Db
*/
public static double convertFromIpControlVolumeToDb(String ipControlVolume, int zone) {
validateZone(zone - 1);
double ipControlVolumeInt = Double.parseDouble(ipControlVolume);
return ((ipControlVolumeInt - 1d) / 2d) - MIN_DB_VOLUME[zone - 1];
}
/**
* Return the string parameter to send to the AVR based on the given volume.
*
* @param volumeDb
* @return the volume for IpControlRequest
*/
public static String convertFromDbToIpControlVolume(double volumeDb, int zone) {
validateZone(zone - 1);
double ipControlVolume = ((MIN_DB_VOLUME[zone - 1] + volumeDb) * 2d) + 1d;
return formatIpControlVolume(ipControlVolume, zone);
}
/**
* Return the String parameter to send to the AVR based on the given persentage of the max volume level.
*
* @param volumePercent
* @return the volume for IpControlRequest
*/
public static String convertFromPercentToIpControlVolume(double volumePercent, int zone) {
validateZone(zone - 1);
double ipControlVolume = 1 + (volumePercent * MAX_IP_CONTROL_VOLUME[zone - 1] / 100);
return formatIpControlVolume(ipControlVolume, zone);
}
/**
* Return the percentage of the max volume levelfrom the value received in the IpControl response.
*
* @param ipControlVolume
* @return the volume percentage
*/
public static double convertFromIpControlVolumeToPercent(String ipControlVolume, int zone) {
validateZone(zone - 1);
double ipControlVolumeInt = Double.parseDouble(ipControlVolume);
return ((ipControlVolumeInt - 1d) * 100d) / MAX_IP_CONTROL_VOLUME[zone - 1];
}
/**
* Format the given double value to an IpControl volume.
*
* @param ipControlVolume
* @return
*/
private static String formatIpControlVolume(double ipControlVolume, int zone) {
validateZone(zone - 1);
DecimalFormat formatter = new DecimalFormat(IP_CONTROL_VOLUME_FORMAT[zone - 1]);
return formatter.format(Math.round(ipControlVolume));
}
private static void validateZone(int zone) {
if (zone < 0 || zone > 3) {
throw new IllegalArgumentException("An unexpected zone was received, the value should be in the range 0-3");
}
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="pioneeravr" 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>PioneerAvr Binding</name>
<description>A binding for Pioneer AVRs.</description>
<author>Antoine Besnard</author>
</binding:binding>