[rotel] Refactor to separate comm handling from protocol handling (#12521)

* [rotel] Refactor to separate comm handling from protocol handling

Each protocol is now handled in a separate class.

Allow to reduce the size of class RotelConnector

Signed-off-by: Laurent Garnier <lg.hc@free.fr>

* buildCommandMessage now throwing RotelException

Comment added when RotelException is catched without any specific
handling

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2022-04-18 19:23:52 +02:00 committed by GitHub
parent 9a7df9fb5b
commit c1073cd89f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2159 additions and 1908 deletions

View File

@ -161,4 +161,82 @@ public class RotelBindingConstants {
// List of all properties // List of all properties
public static final String PROPERTY_PROTOCOL = "protocol"; public static final String PROPERTY_PROTOCOL = "protocol";
// Message types (HEX protocol)
public static final byte PRIMARY_CMD = (byte) 0x10;
public static final byte MAIN_ZONE_CMD = (byte) 0x14;
public static final byte RECORD_SRC_CMD = (byte) 0x15;
public static final byte ZONE2_CMD = (byte) 0x16;
public static final byte ZONE3_CMD = (byte) 0x17;
public static final byte ZONE4_CMD = (byte) 0x18;
public static final byte VOLUME_CMD = (byte) 0x30;
public static final byte ZONE2_VOLUME_CMD = (byte) 0x32;
public static final byte ZONE3_VOLUME_CMD = (byte) 0x33;
public static final byte ZONE4_VOLUME_CMD = (byte) 0x34;
public static final byte TRIGGER_CMD = (byte) 0x40;
public static final byte STANDARD_RESPONSE = (byte) 0x20;
public static final byte TRIGGER_STATUS = (byte) 0x21;
public static final byte SMART_DISPLAY_DATA_1 = (byte) 0x22;
public static final byte SMART_DISPLAY_DATA_2 = (byte) 0x23;
// Common (output) keys used by the HEX and ASCII protocols
public static final String KEY_POWER = "power";
public static final String KEY_VOLUME = "volume";
public static final String KEY_MUTE = "mute";
public static final String KEY_BASS = "bass";
public static final String KEY_TREBLE = "treble";
public static final String KEY_SOURCE = "source";
public static final String KEY_DSP_MODE = "dsp_mode";
public static final String KEY_ERROR = "error";
// Keys only used by the ASCII protocol
public static final String KEY_UPDATE_MODE = "update_mode";
public static final String KEY_DISPLAY_UPDATE = "display_update";
public static final String KEY_VOLUME_MIN = "volume_min";
public static final String KEY_VOLUME_MAX = "volume_max";
public static final String KEY_TONE_MAX = "tone_max";
public static final String KEY1_PLAY_STATUS = "play_status";
public static final String KEY2_PLAY_STATUS = "status";
public static final String KEY_TRACK = "track";
public static final String KEY_DIMMER = "dimmer";
public static final String KEY_FREQ = "freq";
public static final String KEY_TONE = "tone";
public static final String KEY_TCBYPASS = "bypass";
public static final String KEY_BALANCE = "balance";
public static final String KEY_SPEAKER = "speaker";
// Output keys only used by the HEX protocol
public static final String KEY_LINE1 = "line1";
public static final String KEY_LINE2 = "line2";
public static final String KEY_RECORD = "record";
public static final String KEY_RECORD_SEL = "record_sel";
public static final String KEY_ZONE = "zone";
public static final String KEY_POWER_ZONE2 = "power_zone2";
public static final String KEY_POWER_ZONE3 = "power_zone3";
public static final String KEY_POWER_ZONE4 = "power_zone4";
public static final String KEY_SOURCE_ZONE2 = "source_zone2";
public static final String KEY_SOURCE_ZONE3 = "source_zone3";
public static final String KEY_SOURCE_ZONE4 = "source_zone4";
public static final String KEY_VOLUME_ZONE2 = "volume_zone2";
public static final String KEY_VOLUME_ZONE3 = "volume_zone3";
public static final String KEY_VOLUME_ZONE4 = "volume_zone4";
public static final String KEY_MUTE_ZONE2 = "mute_zone2";
public static final String KEY_MUTE_ZONE3 = "mute_zone3";
public static final String KEY_MUTE_ZONE4 = "mute_zone4";
// Specific values for keys
public static final String MSG_VALUE_OFF = "off";
public static final String MSG_VALUE_ON = "on";
public static final String POWER_ON = "on";
public static final String STANDBY = "standby";
public static final String POWER_OFF_DELAYED = "off_delayed";
public static final String MSG_VALUE_SPEAKER_A = "a";
public static final String MSG_VALUE_SPEAKER_B = "b";
public static final String MSG_VALUE_SPEAKER_AB = "a_b";
public static final String MSG_VALUE_MIN = "min";
public static final String MSG_VALUE_MAX = "max";
public static final String MSG_VALUE_FIX = "fix";
public static final String AUTO = "auto";
public static final String MANUAL = "manual";
public static final String PLAY = "play";
public static final String PAUSE = "pause";
public static final String STOP = "stop";
} }

View File

@ -12,13 +12,15 @@
*/ */
package org.openhab.binding.rotel.internal; package org.openhab.binding.rotel.internal;
import static org.openhab.binding.rotel.internal.communication.RotelCommand.*;
import static org.openhab.binding.rotel.internal.protocol.ascii.RotelAbstractAsciiProtocolHandler.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.communication.RotelCommand; import org.openhab.binding.rotel.internal.communication.RotelCommand;
import org.openhab.binding.rotel.internal.communication.RotelConnector;
import org.openhab.binding.rotel.internal.communication.RotelDsp; import org.openhab.binding.rotel.internal.communication.RotelDsp;
import org.openhab.binding.rotel.internal.communication.RotelFlagsMapping; import org.openhab.binding.rotel.internal.communication.RotelFlagsMapping;
import org.openhab.binding.rotel.internal.communication.RotelSource; import org.openhab.binding.rotel.internal.communication.RotelSource;
@ -32,78 +34,64 @@ import org.openhab.core.types.StateOption;
@NonNullByDefault @NonNullByDefault
public enum RotelModel { public enum RotelModel {
RSP1066("RSP-1066", 19200, 3, 1, false, 90, false, 12, false, RotelCommand.ZONE_SELECT, 1, (byte) 0xC2, 13, 8, true, RSP1066("RSP-1066", 19200, 3, 1, false, 90, false, 12, false, ZONE_SELECT, 1, (byte) 0xC2, 13, 8, true,
RotelFlagsMapping.MAPPING1), RotelFlagsMapping.MAPPING1),
RSP1068("RSP-1068", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xA1, 42, RSP1068("RSP-1068", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xA1, 42, 5, true,
5, true, RotelFlagsMapping.MAPPING2),
RSP1069("RSP-1069", 38400, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xA2, 42,
5, true, RotelFlagsMapping.MAPPING5),
RSP1098("RSP-1098", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.ZONE_SELECT, 2, (byte) 0xA0, 13, 8, true,
RotelFlagsMapping.MAPPING1),
RSP1570("RSP-1570", 115200, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 3, (byte) 0xA3, 42,
5, true, RotelFlagsMapping.MAPPING5),
RSP1572("RSP-1572", 115200, 2, 3, true, 96, true, null, false, RotelCommand.RECORD_FONCTION_SELECT, 4, (byte) 0xA5,
42, 5, true, RotelFlagsMapping.MAPPING5),
RSX1055("RSX-1055", 19200, 3, 1, false, 90, false, 12, false, RotelCommand.ZONE_SELECT, 1, (byte) 0xC3, 13, 8, true,
RotelFlagsMapping.MAPPING1),
RSX1056("RSX-1056", 19200, 1, 1, true, 96, true, 12, false, RotelCommand.ZONE_SELECT, 2, (byte) 0xC5, 13, 8, true,
RotelFlagsMapping.MAPPING1),
RSX1057("RSX-1057", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xC7, 13,
8, true, RotelFlagsMapping.MAPPING1),
RSX1058("RSX-1058", 38400, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xC8, 13,
8, true, RotelFlagsMapping.MAPPING4),
RSX1065("RSX-1065", 19200, 3, 1, false, 96, false, 12, false, RotelCommand.ZONE_SELECT, 1, (byte) 0xC1, 42, 5, true,
RotelFlagsMapping.MAPPING2), RotelFlagsMapping.MAPPING2),
RSX1067("RSX-1067", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xC4, 42, RSP1069("RSP-1069", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xA2, 42, 5, true,
5, true, RotelFlagsMapping.MAPPING2), RotelFlagsMapping.MAPPING5),
RSX1550("RSX-1550", 115200, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 3, (byte) 0xC9, 13, RSP1098("RSP-1098", 19200, 1, 1, true, 96, true, 6, false, ZONE_SELECT, 2, (byte) 0xA0, 13, 8, true,
8, true, RotelFlagsMapping.MAPPING3), RotelFlagsMapping.MAPPING1),
RSX1560("RSX-1560", 115200, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 3, (byte) 0xCA, 42, RSP1570("RSP-1570", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xA3, 42, 5, true,
5, true, RotelFlagsMapping.MAPPING5), RotelFlagsMapping.MAPPING5),
RSX1562("RSX-1562", 115200, 2, 3, true, 96, true, null, false, RotelCommand.RECORD_FONCTION_SELECT, 4, (byte) 0xCC, RSP1572("RSP-1572", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4, (byte) 0xA5, 42, 5, true,
42, 5, true, RotelFlagsMapping.MAPPING5), RotelFlagsMapping.MAPPING5),
A11("A11", 115200, 4, 96, true, 10, 15, false, -1, false, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RSX1055("RSX-1055", 19200, 3, 1, false, 90, false, 12, false, ZONE_SELECT, 1, (byte) 0xC3, 13, 8, true,
A12("A12", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RotelFlagsMapping.MAPPING1),
A14("A14", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RSX1056("RSX-1056", 19200, 1, 1, true, 96, true, 12, false, ZONE_SELECT, 2, (byte) 0xC5, 13, 8, true,
CD11("CD11", 57600, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RotelFlagsMapping.MAPPING1),
CD14("CD14", 57600, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RSX1057("RSX-1057", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC7, 13, 8, true,
RA11("RA-11", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, RotelConnector.SPECIAL_CHARACTERS), RotelFlagsMapping.MAPPING1),
RA12("RA-12", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, RotelConnector.SPECIAL_CHARACTERS), RSX1058("RSX-1058", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC8, 13, 8, true,
RA1570("RA-1570", 115200, 7, 96, true, 10, 15, true, -1, true, true, false, 6, 0, RotelFlagsMapping.MAPPING4),
RotelConnector.SPECIAL_CHARACTERS), RSX1065("RSX-1065", 19200, 3, 1, false, 96, false, 12, false, ZONE_SELECT, 1, (byte) 0xC1, 42, 5, true,
RA1572("RA-1572", 115200, 8, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelFlagsMapping.MAPPING2),
RotelConnector.SPECIAL_CHARACTERS), RSX1067("RSX-1067", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC4, 42, 5, true,
RA1592("RA-1592", 115200, 9, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelFlagsMapping.MAPPING2),
RotelConnector.SPECIAL_CHARACTERS), RSX1550("RSX-1550", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xC9, 13, 8, true,
RAP1580("RAP-1580", 115200, 11, 96, true, null, false, 5, false, false, -10, 10, RotelFlagsMapping.MAPPING3),
RotelConnector.NO_SPECIAL_CHARACTERS), RSX1560("RSX-1560", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xCA, 42, 5, true,
RC1570("RC-1570", 115200, 7, 96, true, 10, 15, true, -1, true, false, false, 6, 0, RotelFlagsMapping.MAPPING5),
RotelConnector.SPECIAL_CHARACTERS), RSX1562("RSX-1562", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4, (byte) 0xCC, 42, 5, true,
RC1572("RC-1572", 115200, 8, 96, true, 10, 15, false, -1, true, false, true, 6, 0, RotelFlagsMapping.MAPPING5),
RotelConnector.SPECIAL_CHARACTERS), A11("A11", 115200, 4, 96, true, 10, 15, false, -1, false, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
RC1590("RC-1590", 115200, 9, 96, true, 10, 15, false, -1, true, false, true, 6, 0, A12("A12", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
RotelConnector.SPECIAL_CHARACTERS), A14("A14", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
RCD1570("RCD-1570", 115200, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.SPECIAL_CHARACTERS), CD11("CD11", 57600, 0, null, false, null, true, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
RCD1572("RCD-1572", 57600, 0, null, false, null, true, -1, false, true, 6, 0, CD14("CD14", 57600, 0, null, false, null, true, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
RotelConnector.SPECIAL_CHARACTERS_RCD1572), RA11("RA-11", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
RCX1500("RCX-1500", 115200, 17, 86, true, null, true, -1, false, false, null, null, RA12("RA-12", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
RotelConnector.SPECIAL_CHARACTERS), RA1570("RA-1570", 115200, 7, 96, true, 10, 15, true, -1, true, true, false, 6, 0, SPECIAL_CHARACTERS),
RDD1580("RDD-1580", 115200, 15, null, false, null, true, -1, true, false, null, null, RA1572("RA-1572", 115200, 8, 96, true, 10, 15, false, -1, true, true, true, 6, 0, SPECIAL_CHARACTERS),
RotelConnector.NO_SPECIAL_CHARACTERS), RA1592("RA-1592", 115200, 9, 96, true, 10, 15, false, -1, true, true, true, 6, 0, SPECIAL_CHARACTERS),
RDG1520("RDG-1520", 115200, 16, null, false, null, true, -1, false, false, null, null, RAP1580("RAP-1580", 115200, 11, 96, true, null, false, 5, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
RotelConnector.SPECIAL_CHARACTERS), RC1570("RC-1570", 115200, 7, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
RSP1576("RSP-1576", 115200, 10, 96, true, null, false, 5, false, false, -10, 10, RC1572("RC-1572", 115200, 8, 96, true, 10, 15, false, -1, true, false, true, 6, 0, SPECIAL_CHARACTERS),
RotelConnector.NO_SPECIAL_CHARACTERS), RC1590("RC-1590", 115200, 9, 96, true, 10, 15, false, -1, true, false, true, 6, 0, SPECIAL_CHARACTERS),
RSP1582("RSP-1582", 115200, 11, 96, true, null, false, 6, false, false, -10, 10, RCD1570("RCD-1570", 115200, 0, null, false, null, true, -1, false, true, 6, 0, SPECIAL_CHARACTERS),
RotelConnector.NO_SPECIAL_CHARACTERS), RCD1572("RCD-1572", 57600, 0, null, false, null, true, -1, false, true, 6, 0, SPECIAL_CHARACTERS_RCD1572),
RT11("RT-11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RCX1500("RCX-1500", 115200, 17, 86, true, null, true, -1, false, false, null, null, SPECIAL_CHARACTERS),
RT1570("RT-1570", 115200, 14, null, false, null, false, -1, false, true, 6, 0, RDD1580("RDD-1580", 115200, 15, null, false, null, true, -1, true, false, null, null, NO_SPECIAL_CHARACTERS),
RotelConnector.NO_SPECIAL_CHARACTERS), RDG1520("RDG-1520", 115200, 16, null, false, null, true, -1, false, false, null, null, SPECIAL_CHARACTERS),
T11("T11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RSP1576("RSP-1576", 115200, 10, 96, true, null, false, 5, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
T14("T14", 115200, 13, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RSP1582("RSP-1582", 115200, 11, 96, true, null, false, 6, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
P5("P5", 115200, 20, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RT11("RT-11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
X3("X3", 115200, 18, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS), RT1570("RT-1570", 115200, 14, null, false, null, false, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
X5("X5", 115200, 19, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS); T11("T11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
T14("T14", 115200, 13, null, false, null, false, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
P5("P5", 115200, 20, 96, true, 10, 10, false, -1, true, false, true, 4, 0, NO_SPECIAL_CHARACTERS),
X3("X3", 115200, 18, 96, true, 10, 10, false, -1, true, false, true, 4, 0, NO_SPECIAL_CHARACTERS),
X5("X5", 115200, 19, 96, true, 10, 10, false, -1, true, false, true, 4, 0, NO_SPECIAL_CHARACTERS);
private String name; private String name;
private int baudRate; private int baudRate;
@ -154,10 +142,9 @@ public enum RotelModel {
@Nullable Integer volumeMax, boolean directVolume, @Nullable Integer toneLevelMax, boolean playControl, @Nullable Integer volumeMax, boolean directVolume, @Nullable Integer toneLevelMax, boolean playControl,
@Nullable RotelCommand zoneSelectCmd, int dspCategory, byte deviceId, int respNbChars, int respNbFlags, @Nullable RotelCommand zoneSelectCmd, int dspCategory, byte deviceId, int respNbChars, int respNbFlags,
boolean charsBeforeFlags, RotelFlagsMapping flagsMapping) { boolean charsBeforeFlags, RotelFlagsMapping flagsMapping) {
this(name, baudRate, RotelCommand.DISPLAY_REFRESH, sourceCategory, nbAdditionalZones, additionalCommands, this(name, baudRate, DISPLAY_REFRESH, sourceCategory, nbAdditionalZones, additionalCommands, volumeMax,
volumeMax, directVolume, toneLevelMax, null, playControl, zoneSelectCmd, dspCategory, false, false, directVolume, toneLevelMax, null, playControl, zoneSelectCmd, dspCategory, false, false, false, null,
false, null, null, deviceId, respNbChars, respNbFlags, charsBeforeFlags, flagsMapping, null, deviceId, respNbChars, respNbFlags, charsBeforeFlags, flagsMapping, NO_SPECIAL_CHARACTERS);
RotelConnector.NO_SPECIAL_CHARACTERS);
} }
/** /**
@ -181,8 +168,8 @@ public enum RotelModel {
@Nullable Integer toneLevelMax, boolean playControl, int dspCategory, boolean getFrequencyAvailable, @Nullable Integer toneLevelMax, boolean playControl, int dspCategory, boolean getFrequencyAvailable,
boolean getDimmerLevelAvailable, @Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax, boolean getDimmerLevelAvailable, @Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax,
byte[][] specialCharacters) { byte[][] specialCharacters) {
this(name, baudRate, RotelCommand.POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, null, this(name, baudRate, POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, null, playControl,
playControl, null, dspCategory, getFrequencyAvailable, false, getDimmerLevelAvailable, diummerLevelMin, null, dspCategory, getFrequencyAvailable, false, getDimmerLevelAvailable, diummerLevelMin,
diummerLevelMax, (byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, specialCharacters); diummerLevelMax, (byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, specialCharacters);
} }
@ -209,8 +196,8 @@ public enum RotelModel {
@Nullable Integer toneLevelMax, @Nullable Integer balanceLevelMax, boolean playControl, int dspCategory, @Nullable Integer toneLevelMax, @Nullable Integer balanceLevelMax, boolean playControl, int dspCategory,
boolean getFrequencyAvailable, boolean getSpeakerGroupsAvailable, boolean getDimmerLevelAvailable, boolean getFrequencyAvailable, boolean getSpeakerGroupsAvailable, boolean getDimmerLevelAvailable,
@Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax, byte[][] specialCharacters) { @Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax, byte[][] specialCharacters) {
this(name, baudRate, RotelCommand.POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, this(name, baudRate, POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, balanceLevelMax,
balanceLevelMax, playControl, null, dspCategory, getFrequencyAvailable, getSpeakerGroupsAvailable, playControl, null, dspCategory, getFrequencyAvailable, getSpeakerGroupsAvailable,
getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, (byte) 0, 0, 0, false, getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, (byte) 0, 0, 0, false,
RotelFlagsMapping.NO_MAPPING, specialCharacters); RotelFlagsMapping.NO_MAPPING, specialCharacters);
} }

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.communication;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;
@ -24,71 +26,71 @@ import org.openhab.binding.rotel.internal.RotelException;
@NonNullByDefault @NonNullByDefault
public enum RotelCommand { public enum RotelCommand {
POWER_TOGGLE("Power Toggle", RotelConnector.PRIMARY_CMD, (byte) 0x0A, "power_toggle", "power_toggle"), POWER_TOGGLE("Power Toggle", PRIMARY_CMD, (byte) 0x0A, "power_toggle", "power_toggle"),
POWER_OFF("Power Off", RotelConnector.PRIMARY_CMD, (byte) 0x4A, "power_off", "power_off"), POWER_OFF("Power Off", PRIMARY_CMD, (byte) 0x4A, "power_off", "power_off"),
POWER_ON("Power On", RotelConnector.PRIMARY_CMD, (byte) 0x4B, "power_on", "power_on"), POWER_ON("Power On", PRIMARY_CMD, (byte) 0x4B, "power_on", "power_on"),
POWER("Request current power status", "get_current_power", "power?"), POWER("Request current power status", "get_current_power", "power?"),
ZONE_SELECT("Zone Select", RotelConnector.PRIMARY_CMD, (byte) 0x23), ZONE_SELECT("Zone Select", PRIMARY_CMD, (byte) 0x23),
MAIN_ZONE_POWER_TOGGLE("Main Zone Power Toggle", RotelConnector.MAIN_ZONE_CMD, (byte) 0x0A), MAIN_ZONE_POWER_TOGGLE("Main Zone Power Toggle", MAIN_ZONE_CMD, (byte) 0x0A),
MAIN_ZONE_POWER_OFF("Main Zone Power Off", RotelConnector.MAIN_ZONE_CMD, (byte) 0x4A), MAIN_ZONE_POWER_OFF("Main Zone Power Off", MAIN_ZONE_CMD, (byte) 0x4A),
MAIN_ZONE_POWER_ON("Main Zone Power On", RotelConnector.MAIN_ZONE_CMD, (byte) 0x4B), MAIN_ZONE_POWER_ON("Main Zone Power On", MAIN_ZONE_CMD, (byte) 0x4B),
ZONE2_POWER_TOGGLE("Zone 2 Power Toggle", RotelConnector.ZONE2_CMD, (byte) 0x0A), ZONE2_POWER_TOGGLE("Zone 2 Power Toggle", ZONE2_CMD, (byte) 0x0A),
ZONE2_POWER_OFF("Zone 2 Power Off", RotelConnector.ZONE2_CMD, (byte) 0x4A), ZONE2_POWER_OFF("Zone 2 Power Off", ZONE2_CMD, (byte) 0x4A),
ZONE2_POWER_ON("Zone 2 Power On", RotelConnector.ZONE2_CMD, (byte) 0x4B), ZONE2_POWER_ON("Zone 2 Power On", ZONE2_CMD, (byte) 0x4B),
ZONE3_POWER_TOGGLE("Zone 3 Power Toggle", RotelConnector.ZONE3_CMD, (byte) 0x0A), ZONE3_POWER_TOGGLE("Zone 3 Power Toggle", ZONE3_CMD, (byte) 0x0A),
ZONE3_POWER_OFF("Zone 3 Power Off", RotelConnector.ZONE3_CMD, (byte) 0x4A), ZONE3_POWER_OFF("Zone 3 Power Off", ZONE3_CMD, (byte) 0x4A),
ZONE3_POWER_ON("Zone 3 Power On", RotelConnector.ZONE3_CMD, (byte) 0x4B), ZONE3_POWER_ON("Zone 3 Power On", ZONE3_CMD, (byte) 0x4B),
ZONE4_POWER_TOGGLE("Zone 4 Power Toggle", RotelConnector.ZONE4_CMD, (byte) 0x0A), ZONE4_POWER_TOGGLE("Zone 4 Power Toggle", ZONE4_CMD, (byte) 0x0A),
ZONE4_POWER_OFF("Zone 4 Power Off", RotelConnector.ZONE4_CMD, (byte) 0x4A), ZONE4_POWER_OFF("Zone 4 Power Off", ZONE4_CMD, (byte) 0x4A),
ZONE4_POWER_ON("Zone 4 Power On", RotelConnector.ZONE4_CMD, (byte) 0x4B), ZONE4_POWER_ON("Zone 4 Power On", ZONE4_CMD, (byte) 0x4B),
VOLUME_UP("Volume Up", RotelConnector.PRIMARY_CMD, (byte) 0x0B, "volume_up", "vol_up"), VOLUME_UP("Volume Up", PRIMARY_CMD, (byte) 0x0B, "volume_up", "vol_up"),
VOLUME_DOWN("Volume Down", RotelConnector.PRIMARY_CMD, (byte) 0x0C, "volume_down", "vol_dwn"), VOLUME_DOWN("Volume Down", PRIMARY_CMD, (byte) 0x0C, "volume_down", "vol_dwn"),
VOLUME_SET("Set Volume to level", RotelConnector.VOLUME_CMD, (byte) 0, "volume_", "vol_"), VOLUME_SET("Set Volume to level", VOLUME_CMD, (byte) 0, "volume_", "vol_"),
VOLUME_GET("Request current volume level", "get_volume", "volume?"), VOLUME_GET("Request current volume level", "get_volume", "volume?"),
VOLUME_GET_MIN("Request Min volume level", "get_volume_min", null), VOLUME_GET_MIN("Request Min volume level", "get_volume_min", null),
VOLUME_GET_MAX("Request Max volume level", "get_volume_max", null), VOLUME_GET_MAX("Request Max volume level", "get_volume_max", null),
MUTE_TOGGLE("Mute Toggle", RotelConnector.PRIMARY_CMD, (byte) 0x1E, "mute", "mute"), MUTE_TOGGLE("Mute Toggle", PRIMARY_CMD, (byte) 0x1E, "mute", "mute"),
MUTE_ON("Mute On", "mute_on", "mute_on"), MUTE_ON("Mute On", "mute_on", "mute_on"),
MUTE_OFF("Mute Off", "mute_off", "mute_off"), MUTE_OFF("Mute Off", "mute_off", "mute_off"),
MUTE("Request current mute status", "get_mute_status", "mute?"), MUTE("Request current mute status", "get_mute_status", "mute?"),
MAIN_ZONE_VOLUME_UP("Main Zone Volume Up", RotelConnector.MAIN_ZONE_CMD, (byte) 0), MAIN_ZONE_VOLUME_UP("Main Zone Volume Up", MAIN_ZONE_CMD, (byte) 0),
MAIN_ZONE_VOLUME_DOWN("Main Zone Volume Down", RotelConnector.MAIN_ZONE_CMD, (byte) 1), MAIN_ZONE_VOLUME_DOWN("Main Zone Volume Down", MAIN_ZONE_CMD, (byte) 1),
MAIN_ZONE_MUTE_TOGGLE("Main Zone Mute Toggle", RotelConnector.MAIN_ZONE_CMD, (byte) 0x1E), MAIN_ZONE_MUTE_TOGGLE("Main Zone Mute Toggle", MAIN_ZONE_CMD, (byte) 0x1E),
MAIN_ZONE_MUTE_ON("Main Zone Mute On", RotelConnector.MAIN_ZONE_CMD, (byte) 0x6C), MAIN_ZONE_MUTE_ON("Main Zone Mute On", MAIN_ZONE_CMD, (byte) 0x6C),
MAIN_ZONE_MUTE_OFF("Main Zone Mute Off", RotelConnector.MAIN_ZONE_CMD, (byte) 0x6D), MAIN_ZONE_MUTE_OFF("Main Zone Mute Off", MAIN_ZONE_CMD, (byte) 0x6D),
ZONE2_VOLUME_UP("Zone 2 Volume Up", RotelConnector.ZONE2_CMD, (byte) 0), ZONE2_VOLUME_UP("Zone 2 Volume Up", ZONE2_CMD, (byte) 0),
ZONE2_VOLUME_DOWN("Zone 2 Volume Down", RotelConnector.ZONE2_CMD, (byte) 1), ZONE2_VOLUME_DOWN("Zone 2 Volume Down", ZONE2_CMD, (byte) 1),
ZONE2_VOLUME_SET("Set Zone 2 Volume to level", RotelConnector.ZONE2_VOLUME_CMD, (byte) 0), ZONE2_VOLUME_SET("Set Zone 2 Volume to level", ZONE2_VOLUME_CMD, (byte) 0),
ZONE2_MUTE_TOGGLE("Zone 2 Mute Toggle", RotelConnector.ZONE2_CMD, (byte) 0x1E), ZONE2_MUTE_TOGGLE("Zone 2 Mute Toggle", ZONE2_CMD, (byte) 0x1E),
ZONE2_MUTE_ON("Zone 2 Mute On", RotelConnector.ZONE2_CMD, (byte) 0x6C), ZONE2_MUTE_ON("Zone 2 Mute On", ZONE2_CMD, (byte) 0x6C),
ZONE2_MUTE_OFF("Zone 2 Mute Off", RotelConnector.ZONE2_CMD, (byte) 0x6D), ZONE2_MUTE_OFF("Zone 2 Mute Off", ZONE2_CMD, (byte) 0x6D),
ZONE3_VOLUME_UP("Zone 3 Volume Up", RotelConnector.ZONE3_CMD, (byte) 0), ZONE3_VOLUME_UP("Zone 3 Volume Up", ZONE3_CMD, (byte) 0),
ZONE3_VOLUME_DOWN("Zone 3 Volume Down", RotelConnector.ZONE3_CMD, (byte) 1), ZONE3_VOLUME_DOWN("Zone 3 Volume Down", ZONE3_CMD, (byte) 1),
ZONE3_VOLUME_SET("Set Zone 3 Volume to level", RotelConnector.ZONE3_VOLUME_CMD, (byte) 0), ZONE3_VOLUME_SET("Set Zone 3 Volume to level", ZONE3_VOLUME_CMD, (byte) 0),
ZONE3_MUTE_TOGGLE("Zone 3 Mute Toggle", RotelConnector.ZONE3_CMD, (byte) 0x1E), ZONE3_MUTE_TOGGLE("Zone 3 Mute Toggle", ZONE3_CMD, (byte) 0x1E),
ZONE3_MUTE_ON("Zone 3 Mute On", RotelConnector.ZONE3_CMD, (byte) 0x6C), ZONE3_MUTE_ON("Zone 3 Mute On", ZONE3_CMD, (byte) 0x6C),
ZONE3_MUTE_OFF("Zone 3 Mute Off", RotelConnector.ZONE3_CMD, (byte) 0x6D), ZONE3_MUTE_OFF("Zone 3 Mute Off", ZONE3_CMD, (byte) 0x6D),
ZONE4_VOLUME_UP("Zone 4 Volume Up", RotelConnector.ZONE4_CMD, (byte) 0), ZONE4_VOLUME_UP("Zone 4 Volume Up", ZONE4_CMD, (byte) 0),
ZONE4_VOLUME_DOWN("Zone 4 Volume Down", RotelConnector.ZONE4_CMD, (byte) 1), ZONE4_VOLUME_DOWN("Zone 4 Volume Down", ZONE4_CMD, (byte) 1),
ZONE4_VOLUME_SET("Set Zone 4 Volume to level", RotelConnector.ZONE4_VOLUME_CMD, (byte) 0), ZONE4_VOLUME_SET("Set Zone 4 Volume to level", ZONE4_VOLUME_CMD, (byte) 0),
ZONE4_MUTE_TOGGLE("Zone 4 Mute Toggle", RotelConnector.ZONE4_CMD, (byte) 0x1E), ZONE4_MUTE_TOGGLE("Zone 4 Mute Toggle", ZONE4_CMD, (byte) 0x1E),
ZONE4_MUTE_ON("Zone 4 Mute On", RotelConnector.ZONE4_CMD, (byte) 0x6C), ZONE4_MUTE_ON("Zone 4 Mute On", ZONE4_CMD, (byte) 0x6C),
ZONE4_MUTE_OFF("Zone 4 Mute Off", RotelConnector.ZONE4_CMD, (byte) 0x6D), ZONE4_MUTE_OFF("Zone 4 Mute Off", ZONE4_CMD, (byte) 0x6D),
SOURCE_CD("Source CD", RotelConnector.PRIMARY_CMD, (byte) 0x02, "cd", "cd"), SOURCE_CD("Source CD", PRIMARY_CMD, (byte) 0x02, "cd", "cd"),
SOURCE_TUNER("Source Tuner", RotelConnector.PRIMARY_CMD, (byte) 0x03, "tuner", "tuner"), SOURCE_TUNER("Source Tuner", PRIMARY_CMD, (byte) 0x03, "tuner", "tuner"),
SOURCE_TAPE("Source Tape", RotelConnector.PRIMARY_CMD, (byte) 0x04, "tape", "tape"), SOURCE_TAPE("Source Tape", PRIMARY_CMD, (byte) 0x04, "tape", "tape"),
SOURCE_VIDEO1("Source Video 1", RotelConnector.PRIMARY_CMD, (byte) 0x05, "video1", "video1"), SOURCE_VIDEO1("Source Video 1", PRIMARY_CMD, (byte) 0x05, "video1", "video1"),
SOURCE_VIDEO2("Source Video 2", RotelConnector.PRIMARY_CMD, (byte) 0x06, "video2", "video2"), SOURCE_VIDEO2("Source Video 2", PRIMARY_CMD, (byte) 0x06, "video2", "video2"),
SOURCE_VIDEO3("Source Video 3", RotelConnector.PRIMARY_CMD, (byte) 0x07, "video3", "video3"), SOURCE_VIDEO3("Source Video 3", PRIMARY_CMD, (byte) 0x07, "video3", "video3"),
SOURCE_VIDEO4("Source Video 4", RotelConnector.PRIMARY_CMD, (byte) 0x08, "video4", "video4"), SOURCE_VIDEO4("Source Video 4", PRIMARY_CMD, (byte) 0x08, "video4", "video4"),
SOURCE_VIDEO5("Source Video 5", RotelConnector.PRIMARY_CMD, (byte) 0x09, "video5", "video5"), SOURCE_VIDEO5("Source Video 5", PRIMARY_CMD, (byte) 0x09, "video5", "video5"),
SOURCE_VIDEO6("Source Video 6", RotelConnector.PRIMARY_CMD, (byte) 0x94, "video6", "video6"), SOURCE_VIDEO6("Source Video 6", PRIMARY_CMD, (byte) 0x94, "video6", "video6"),
SOURCE_VIDEO7("Source Video 7", "video7", "video7"), SOURCE_VIDEO7("Source Video 7", "video7", "video7"),
SOURCE_VIDEO8("Source Video 8", "video8", "video8"), SOURCE_VIDEO8("Source Video 8", "video8", "video8"),
SOURCE_PHONO("Source Phono", RotelConnector.PRIMARY_CMD, (byte) 0x35, "phono", "phono"), SOURCE_PHONO("Source Phono", PRIMARY_CMD, (byte) 0x35, "phono", "phono"),
SOURCE_USB("Source Front USB", RotelConnector.PRIMARY_CMD, (byte) 0x8E, "usb", "usb"), SOURCE_USB("Source Front USB", PRIMARY_CMD, (byte) 0x8E, "usb", "usb"),
SOURCE_PCUSB("Source PC USB", "pc_usb", "pcusb"), SOURCE_PCUSB("Source PC USB", "pc_usb", "pcusb"),
SOURCE_MULTI_INPUT("Source Multi Input", RotelConnector.PRIMARY_CMD, (byte) 0x15, "multi_input", "multi_input"), SOURCE_MULTI_INPUT("Source Multi Input", PRIMARY_CMD, (byte) 0x15, "multi_input", "multi_input"),
SOURCE_AUX("Source Aux", "aux", "aux"), SOURCE_AUX("Source Aux", "aux", "aux"),
SOURCE_AUX1("Source Aux 1", "aux1", "aux1"), SOURCE_AUX1("Source Aux 1", "aux1", "aux1"),
SOURCE_AUX2("Source Aux 2", "aux2", "aux2"), SOURCE_AUX2("Source Aux 2", "aux2", "aux2"),
@ -111,126 +113,114 @@ public enum RotelCommand {
SOURCE_IRADIO("Source iRadio", "iradio", "iradio"), SOURCE_IRADIO("Source iRadio", "iradio", "iradio"),
SOURCE_NETWORK("Source Network", "network", "network"), SOURCE_NETWORK("Source Network", "network", "network"),
SOURCE("Request current source", "get_current_source", "source?"), SOURCE("Request current source", "get_current_source", "source?"),
MAIN_ZONE_SOURCE_CD("Main Zone Source CD", RotelConnector.MAIN_ZONE_CMD, (byte) 0x02, "main_zone_cd", MAIN_ZONE_SOURCE_CD("Main Zone Source CD", MAIN_ZONE_CMD, (byte) 0x02, "main_zone_cd", "main_zone_cd"),
"main_zone_cd"), MAIN_ZONE_SOURCE_TUNER("Main Zone Source Tuner", MAIN_ZONE_CMD, (byte) 0x03, "main_zone_tuner", "main_zone_tuner"),
MAIN_ZONE_SOURCE_TUNER("Main Zone Source Tuner", RotelConnector.MAIN_ZONE_CMD, (byte) 0x03, "main_zone_tuner", MAIN_ZONE_SOURCE_TAPE("Main Zone Source Tape", MAIN_ZONE_CMD, (byte) 0x04, "main_zone_tape", "main_zone_tape"),
"main_zone_tuner"), MAIN_ZONE_SOURCE_VIDEO1("Main Zone Source Video 1", MAIN_ZONE_CMD, (byte) 0x05, "main_zone_video1",
MAIN_ZONE_SOURCE_TAPE("Main Zone Source Tape", RotelConnector.MAIN_ZONE_CMD, (byte) 0x04, "main_zone_tape",
"main_zone_tape"),
MAIN_ZONE_SOURCE_VIDEO1("Main Zone Source Video 1", RotelConnector.MAIN_ZONE_CMD, (byte) 0x05, "main_zone_video1",
"main_zone_video1"), "main_zone_video1"),
MAIN_ZONE_SOURCE_VIDEO2("Main Zone Source Video 2", RotelConnector.MAIN_ZONE_CMD, (byte) 0x06, "main_zone_video2", MAIN_ZONE_SOURCE_VIDEO2("Main Zone Source Video 2", MAIN_ZONE_CMD, (byte) 0x06, "main_zone_video2",
"main_zone_video2"), "main_zone_video2"),
MAIN_ZONE_SOURCE_VIDEO3("Main Zone Source Video 3", RotelConnector.MAIN_ZONE_CMD, (byte) 0x07, "main_zone_video3", MAIN_ZONE_SOURCE_VIDEO3("Main Zone Source Video 3", MAIN_ZONE_CMD, (byte) 0x07, "main_zone_video3",
"main_zone_video3"), "main_zone_video3"),
MAIN_ZONE_SOURCE_VIDEO4("Main Zone Source Video 4", RotelConnector.MAIN_ZONE_CMD, (byte) 0x08, "main_zone_video4", MAIN_ZONE_SOURCE_VIDEO4("Main Zone Source Video 4", MAIN_ZONE_CMD, (byte) 0x08, "main_zone_video4",
"main_zone_video4"), "main_zone_video4"),
MAIN_ZONE_SOURCE_VIDEO5("Main Zone Source Video 5", RotelConnector.MAIN_ZONE_CMD, (byte) 0x09, "main_zone_video5", MAIN_ZONE_SOURCE_VIDEO5("Main Zone Source Video 5", MAIN_ZONE_CMD, (byte) 0x09, "main_zone_video5",
"main_zone_video5"), "main_zone_video5"),
MAIN_ZONE_SOURCE_VIDEO6("Main Zone Source Video 6", RotelConnector.MAIN_ZONE_CMD, (byte) 0x94, "main_zone_video6", MAIN_ZONE_SOURCE_VIDEO6("Main Zone Source Video 6", MAIN_ZONE_CMD, (byte) 0x94, "main_zone_video6",
"main_zone_video6"), "main_zone_video6"),
MAIN_ZONE_SOURCE_USB("Main Zone Source Front USB", RotelConnector.MAIN_ZONE_CMD, (byte) 0x8E, "main_zone_usb", MAIN_ZONE_SOURCE_USB("Main Zone Source Front USB", MAIN_ZONE_CMD, (byte) 0x8E, "main_zone_usb", "main_zone_usb"),
"main_zone_usb"), MAIN_ZONE_SOURCE_MULTI_INPUT("Main Zone Source Multi Input", MAIN_ZONE_CMD, (byte) 0x15, "main_zone_multi_input",
MAIN_ZONE_SOURCE_MULTI_INPUT("Main Zone Source Multi Input", RotelConnector.MAIN_ZONE_CMD, (byte) 0x15, "main_zone_multi_input"),
"main_zone_multi_input", "main_zone_multi_input"), RECORD_SOURCE_CD("Record Source CD", RECORD_SRC_CMD, (byte) 0x02, "record_cd", "record_cd"),
RECORD_SOURCE_CD("Record Source CD", RotelConnector.RECORD_SRC_CMD, (byte) 0x02, "record_cd", "record_cd"), RECORD_SOURCE_TUNER("Record Source Tuner", RECORD_SRC_CMD, (byte) 0x03, "record_tuner", "record_tuner"),
RECORD_SOURCE_TUNER("Record Source Tuner", RotelConnector.RECORD_SRC_CMD, (byte) 0x03, "record_tuner", RECORD_SOURCE_TAPE("Record Source Tape", RECORD_SRC_CMD, (byte) 0x04, "record_tape", "record_tape"),
"record_tuner"), RECORD_SOURCE_VIDEO1("Record Source Video 1", RECORD_SRC_CMD, (byte) 0x05, "record_video1", "record_video1"),
RECORD_SOURCE_TAPE("Record Source Tape", RotelConnector.RECORD_SRC_CMD, (byte) 0x04, "record_tape", "record_tape"), RECORD_SOURCE_VIDEO2("Record Source Video 2", RECORD_SRC_CMD, (byte) 0x06, "record_video2", "record_video2"),
RECORD_SOURCE_VIDEO1("Record Source Video 1", RotelConnector.RECORD_SRC_CMD, (byte) 0x05, "record_video1", RECORD_SOURCE_VIDEO3("Record Source Video 3", RECORD_SRC_CMD, (byte) 0x07, "record_video3", "record_video3"),
"record_video1"), RECORD_SOURCE_VIDEO4("Record Source Video 4", RECORD_SRC_CMD, (byte) 0x08, "record_video4", "record_video4"),
RECORD_SOURCE_VIDEO2("Record Source Video 2", RotelConnector.RECORD_SRC_CMD, (byte) 0x06, "record_video2", RECORD_SOURCE_VIDEO5("Record Source Video 5", RECORD_SRC_CMD, (byte) 0x09, "record_video5", "record_video5"),
"record_video2"), RECORD_SOURCE_VIDEO6("Record Source Video 6", RECORD_SRC_CMD, (byte) 0x94, "record_video6", "record_video6"),
RECORD_SOURCE_VIDEO3("Record Source Video 3", RotelConnector.RECORD_SRC_CMD, (byte) 0x07, "record_video3", RECORD_SOURCE_USB("Record Source Front USB", RECORD_SRC_CMD, (byte) 0x8E, "record_usb", "record_usb"),
"record_video3"), RECORD_SOURCE_MAIN("Record Follow Main Zone Source", RECORD_SRC_CMD, (byte) 0x6B, "record_follow_main",
RECORD_SOURCE_VIDEO4("Record Source Video 4", RotelConnector.RECORD_SRC_CMD, (byte) 0x08, "record_video4", "record_follow_main"),
"record_video4"), ZONE2_SOURCE_CD("Zone 2 Source CD", ZONE2_CMD, (byte) 0x02, "zone2_cd", "zone2_cd"),
RECORD_SOURCE_VIDEO5("Record Source Video 5", RotelConnector.RECORD_SRC_CMD, (byte) 0x09, "record_video5", ZONE2_SOURCE_TUNER("Zone 2 Source Tuner", ZONE2_CMD, (byte) 0x03, "zone2_tuner", "zone2_tuner"),
"record_video5"), ZONE2_SOURCE_TAPE("Zone 2 Source Tape", ZONE2_CMD, (byte) 0x04, "zone2_tape", "zone2_tape"),
RECORD_SOURCE_VIDEO6("Record Source Video 6", RotelConnector.RECORD_SRC_CMD, (byte) 0x94, "record_video6", ZONE2_SOURCE_VIDEO1("Zone 2 Source Video 1", ZONE2_CMD, (byte) 0x05, "zone2_video1", "zone2_video1"),
"record_video6"), ZONE2_SOURCE_VIDEO2("Zone 2 Source Video 2", ZONE2_CMD, (byte) 0x06, "zone2_video2", "zone2_video2"),
RECORD_SOURCE_USB("Record Source Front USB", RotelConnector.RECORD_SRC_CMD, (byte) 0x8E, "record_usb", ZONE2_SOURCE_VIDEO3("Zone 2 Source Video 3", ZONE2_CMD, (byte) 0x07, "zone2_video3", "zone2_video3"),
"record_usb"), ZONE2_SOURCE_VIDEO4("Zone 2 Source Video 4", ZONE2_CMD, (byte) 0x08, "zone2_video4", "zone2_video4"),
RECORD_SOURCE_MAIN("Record Follow Main Zone Source", RotelConnector.RECORD_SRC_CMD, (byte) 0x6B, ZONE2_SOURCE_VIDEO5("Zone 2 Source Video 5", ZONE2_CMD, (byte) 0x09, "zone2_video5", "zone2_video5"),
"record_follow_main", "record_follow_main"), ZONE2_SOURCE_VIDEO6("Zone 2 Source Video 6", ZONE2_CMD, (byte) 0x94, "zone2_video6", "zone2_video6"),
ZONE2_SOURCE_CD("Zone 2 Source CD", RotelConnector.ZONE2_CMD, (byte) 0x02, "zone2_cd", "zone2_cd"), ZONE2_SOURCE_USB("Zone 2 Source Front USB", ZONE2_CMD, (byte) 0x8E, "zone2_usb", "zone2_usb"),
ZONE2_SOURCE_TUNER("Zone 2 Source Tuner", RotelConnector.ZONE2_CMD, (byte) 0x03, "zone2_tuner", "zone2_tuner"), ZONE2_SOURCE_MAIN("Zone 2 Follow Main Zone Source", ZONE2_CMD, (byte) 0x6B, "zone2_follow_main",
ZONE2_SOURCE_TAPE("Zone 2 Source Tape", RotelConnector.ZONE2_CMD, (byte) 0x04, "zone2_tape", "zone2_tape"),
ZONE2_SOURCE_VIDEO1("Zone 2 Source Video 1", RotelConnector.ZONE2_CMD, (byte) 0x05, "zone2_video1", "zone2_video1"),
ZONE2_SOURCE_VIDEO2("Zone 2 Source Video 2", RotelConnector.ZONE2_CMD, (byte) 0x06, "zone2_video2", "zone2_video2"),
ZONE2_SOURCE_VIDEO3("Zone 2 Source Video 3", RotelConnector.ZONE2_CMD, (byte) 0x07, "zone2_video3", "zone2_video3"),
ZONE2_SOURCE_VIDEO4("Zone 2 Source Video 4", RotelConnector.ZONE2_CMD, (byte) 0x08, "zone2_video4", "zone2_video4"),
ZONE2_SOURCE_VIDEO5("Zone 2 Source Video 5", RotelConnector.ZONE2_CMD, (byte) 0x09, "zone2_video5", "zone2_video5"),
ZONE2_SOURCE_VIDEO6("Zone 2 Source Video 6", RotelConnector.ZONE2_CMD, (byte) 0x94, "zone2_video6", "zone2_video6"),
ZONE2_SOURCE_USB("Zone 2 Source Front USB", RotelConnector.ZONE2_CMD, (byte) 0x8E, "zone2_usb", "zone2_usb"),
ZONE2_SOURCE_MAIN("Zone 2 Follow Main Zone Source", RotelConnector.ZONE2_CMD, (byte) 0x6B, "zone2_follow_main",
"zone2_follow_main"), "zone2_follow_main"),
ZONE3_SOURCE_CD("Zone 3 Source CD", RotelConnector.ZONE3_CMD, (byte) 0x02, "zone3_cd", "zone3_cd"), ZONE3_SOURCE_CD("Zone 3 Source CD", ZONE3_CMD, (byte) 0x02, "zone3_cd", "zone3_cd"),
ZONE3_SOURCE_TUNER("Zone 3 Source Tuner", RotelConnector.ZONE3_CMD, (byte) 0x03, "zone3_tuner", "zone3_tuner"), ZONE3_SOURCE_TUNER("Zone 3 Source Tuner", ZONE3_CMD, (byte) 0x03, "zone3_tuner", "zone3_tuner"),
ZONE3_SOURCE_TAPE("Zone 3 Source Tape", RotelConnector.ZONE3_CMD, (byte) 0x04, "zone3_tape", "zone3_tape"), ZONE3_SOURCE_TAPE("Zone 3 Source Tape", ZONE3_CMD, (byte) 0x04, "zone3_tape", "zone3_tape"),
ZONE3_SOURCE_VIDEO1("Zone 3 Source Video 1", RotelConnector.ZONE3_CMD, (byte) 0x05, "zone3_video1", "zone3_video1"), ZONE3_SOURCE_VIDEO1("Zone 3 Source Video 1", ZONE3_CMD, (byte) 0x05, "zone3_video1", "zone3_video1"),
ZONE3_SOURCE_VIDEO2("Zone 3 Source Video 2", RotelConnector.ZONE3_CMD, (byte) 0x06, "zone3_video2", "zone3_video2"), ZONE3_SOURCE_VIDEO2("Zone 3 Source Video 2", ZONE3_CMD, (byte) 0x06, "zone3_video2", "zone3_video2"),
ZONE3_SOURCE_VIDEO3("Zone 3 Source Video 3", RotelConnector.ZONE3_CMD, (byte) 0x07, "zone3_video3", "zone3_video3"), ZONE3_SOURCE_VIDEO3("Zone 3 Source Video 3", ZONE3_CMD, (byte) 0x07, "zone3_video3", "zone3_video3"),
ZONE3_SOURCE_VIDEO4("Zone 3 Source Video 4", RotelConnector.ZONE3_CMD, (byte) 0x08, "zone3_video4", "zone3_video4"), ZONE3_SOURCE_VIDEO4("Zone 3 Source Video 4", ZONE3_CMD, (byte) 0x08, "zone3_video4", "zone3_video4"),
ZONE3_SOURCE_VIDEO5("Zone 3 Source Video 5", RotelConnector.ZONE3_CMD, (byte) 0x09, "zone3_video5", "zone3_video5"), ZONE3_SOURCE_VIDEO5("Zone 3 Source Video 5", ZONE3_CMD, (byte) 0x09, "zone3_video5", "zone3_video5"),
ZONE3_SOURCE_VIDEO6("Zone 3 Source Video 6", RotelConnector.ZONE3_CMD, (byte) 0x94, "zone3_video6", "zone3_video6"), ZONE3_SOURCE_VIDEO6("Zone 3 Source Video 6", ZONE3_CMD, (byte) 0x94, "zone3_video6", "zone3_video6"),
ZONE3_SOURCE_USB("Zone 3 Source Front USB", RotelConnector.ZONE3_CMD, (byte) 0x8E, "zone3_usb", "zone3_usb"), ZONE3_SOURCE_USB("Zone 3 Source Front USB", ZONE3_CMD, (byte) 0x8E, "zone3_usb", "zone3_usb"),
ZONE3_SOURCE_MAIN("Zone 3 Follow Main Zone Source", RotelConnector.ZONE3_CMD, (byte) 0x6B, "zone3_follow_main", ZONE3_SOURCE_MAIN("Zone 3 Follow Main Zone Source", ZONE3_CMD, (byte) 0x6B, "zone3_follow_main",
"zone3_follow_main"), "zone3_follow_main"),
ZONE4_SOURCE_CD("Zone 4 Source CD", RotelConnector.ZONE4_CMD, (byte) 0x02, "zone4_cd", "zone4_cd"), ZONE4_SOURCE_CD("Zone 4 Source CD", ZONE4_CMD, (byte) 0x02, "zone4_cd", "zone4_cd"),
ZONE4_SOURCE_TUNER("Zone 4 Source Tuner", RotelConnector.ZONE4_CMD, (byte) 0x03, "zone4_tuner", "zone4_tuner"), ZONE4_SOURCE_TUNER("Zone 4 Source Tuner", ZONE4_CMD, (byte) 0x03, "zone4_tuner", "zone4_tuner"),
ZONE4_SOURCE_TAPE("Zone 4 Source Tape", RotelConnector.ZONE4_CMD, (byte) 0x04, "zone4_tape", "zone4_tape"), ZONE4_SOURCE_TAPE("Zone 4 Source Tape", ZONE4_CMD, (byte) 0x04, "zone4_tape", "zone4_tape"),
ZONE4_SOURCE_VIDEO1("Zone 4 Source Video 1", RotelConnector.ZONE4_CMD, (byte) 0x05, "zone4_video1", "zone4_video1"), ZONE4_SOURCE_VIDEO1("Zone 4 Source Video 1", ZONE4_CMD, (byte) 0x05, "zone4_video1", "zone4_video1"),
ZONE4_SOURCE_VIDEO2("Zone 4 Source Video 2", RotelConnector.ZONE4_CMD, (byte) 0x06, "zone4_video2", "zone4_video2"), ZONE4_SOURCE_VIDEO2("Zone 4 Source Video 2", ZONE4_CMD, (byte) 0x06, "zone4_video2", "zone4_video2"),
ZONE4_SOURCE_VIDEO3("Zone 4 Source Video 3", RotelConnector.ZONE4_CMD, (byte) 0x07, "zone4_video3", "zone4_video3"), ZONE4_SOURCE_VIDEO3("Zone 4 Source Video 3", ZONE4_CMD, (byte) 0x07, "zone4_video3", "zone4_video3"),
ZONE4_SOURCE_VIDEO4("Zone 4 Source Video 4", RotelConnector.ZONE4_CMD, (byte) 0x08, "zone4_video4", "zone4_video4"), ZONE4_SOURCE_VIDEO4("Zone 4 Source Video 4", ZONE4_CMD, (byte) 0x08, "zone4_video4", "zone4_video4"),
ZONE4_SOURCE_VIDEO5("Zone 4 Source Video 5", RotelConnector.ZONE4_CMD, (byte) 0x09, "zone4_video5", "zone4_video5"), ZONE4_SOURCE_VIDEO5("Zone 4 Source Video 5", ZONE4_CMD, (byte) 0x09, "zone4_video5", "zone4_video5"),
ZONE4_SOURCE_VIDEO6("Zone 4 Source Video 6", RotelConnector.ZONE4_CMD, (byte) 0x94, "zone4_video6", "zone4_video6"), ZONE4_SOURCE_VIDEO6("Zone 4 Source Video 6", ZONE4_CMD, (byte) 0x94, "zone4_video6", "zone4_video6"),
ZONE4_SOURCE_USB("Zone 4 Source Front USB", RotelConnector.ZONE4_CMD, (byte) 0x8E, "zone4_usb", "zone4_usb"), ZONE4_SOURCE_USB("Zone 4 Source Front USB", ZONE4_CMD, (byte) 0x8E, "zone4_usb", "zone4_usb"),
ZONE4_SOURCE_MAIN("Zone 4 Follow Main Zone Source", RotelConnector.ZONE4_CMD, (byte) 0x6B, "zone4_follow_main", ZONE4_SOURCE_MAIN("Zone 4 Follow Main Zone Source", ZONE4_CMD, (byte) 0x6B, "zone4_follow_main",
"zone4_follow_main"), "zone4_follow_main"),
STEREO("Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x11, "2channel", "2channel"), STEREO("Stereo", PRIMARY_CMD, (byte) 0x11, "2channel", "2channel"),
STEREO3("Dolby 3 Stereo ", RotelConnector.PRIMARY_CMD, (byte) 0x12, "3channel", "3channel"), STEREO3("Dolby 3 Stereo ", PRIMARY_CMD, (byte) 0x12, "3channel", "3channel"),
STEREO5("5 Channel Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x5B, "5channel", "5channel"), STEREO5("5 Channel Stereo", PRIMARY_CMD, (byte) 0x5B, "5channel", "5channel"),
STEREO7("7 Channel Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x5C, "7channel", "7channel"), STEREO7("7 Channel Stereo", PRIMARY_CMD, (byte) 0x5C, "7channel", "7channel"),
STEREO9("9 Channel Stereo", "9channel", "9channel"), STEREO9("9 Channel Stereo", "9channel", "9channel"),
STEREO11("11 Channel Stereo", "11channel", "11channel"), STEREO11("11 Channel Stereo", "11channel", "11channel"),
DSP1("DSP 1", RotelConnector.PRIMARY_CMD, (byte) 0x57), DSP1("DSP 1", PRIMARY_CMD, (byte) 0x57),
DSP2("DSP 2", RotelConnector.PRIMARY_CMD, (byte) 0x58), DSP2("DSP 2", PRIMARY_CMD, (byte) 0x58),
DSP3("DSP 3", RotelConnector.PRIMARY_CMD, (byte) 0x59), DSP3("DSP 3", PRIMARY_CMD, (byte) 0x59),
DSP4("DSP 4", RotelConnector.PRIMARY_CMD, (byte) 0x5A), DSP4("DSP 4", PRIMARY_CMD, (byte) 0x5A),
PROLOGIC("Dolby Pro Logic", RotelConnector.PRIMARY_CMD, (byte) 0x5F), PROLOGIC("Dolby Pro Logic", PRIMARY_CMD, (byte) 0x5F),
PLII_CINEMA("Dolby PLII Cinema", RotelConnector.PRIMARY_CMD, (byte) 0x5D, "prologic_movie", "prologic_movie"), PLII_CINEMA("Dolby PLII Cinema", PRIMARY_CMD, (byte) 0x5D, "prologic_movie", "prologic_movie"),
PLII_MUSIC("Dolby PLII Music", RotelConnector.PRIMARY_CMD, (byte) 0x5E, "prologic_music", "prologic_music"), PLII_MUSIC("Dolby PLII Music", PRIMARY_CMD, (byte) 0x5E, "prologic_music", "prologic_music"),
PLII_GAME("Dolby PLII Game", RotelConnector.PRIMARY_CMD, (byte) 0x74, "prologic_game", "prologic_game"), PLII_GAME("Dolby PLII Game", PRIMARY_CMD, (byte) 0x74, "prologic_game", "prologic_game"),
PLIIZ("Dolby PLIIz", RotelConnector.PRIMARY_CMD, (byte) 0x92, "prologic_iiz", "prologic_iiz"), PLIIZ("Dolby PLIIz", PRIMARY_CMD, (byte) 0x92, "prologic_iiz", "prologic_iiz"),
NEO6_MUSIC("dts Neo:6 Music", RotelConnector.PRIMARY_CMD, (byte) 0x60, "neo6_music", "neo6_music"), NEO6_MUSIC("dts Neo:6 Music", PRIMARY_CMD, (byte) 0x60, "neo6_music", "neo6_music"),
NEO6_CINEMA("dts Neo:6 Cinema", RotelConnector.PRIMARY_CMD, (byte) 0x61, "neo6_cinema", "neo6_cinema"), NEO6_CINEMA("dts Neo:6 Cinema", PRIMARY_CMD, (byte) 0x61, "neo6_cinema", "neo6_cinema"),
ATMOS("Dolby Atmos", "dolby_atmos", "dolby_atmos"), ATMOS("Dolby Atmos", "dolby_atmos", "dolby_atmos"),
NEURAL_X("dts Neural:X", "dts_neural", "dts_neural"), NEURAL_X("dts Neural:X", "dts_neural", "dts_neural"),
BYPASS("Analog Bypass", RotelConnector.PRIMARY_CMD, (byte) 0x11, "bypass", "bypass"), BYPASS("Analog Bypass", PRIMARY_CMD, (byte) 0x11, "bypass", "bypass"),
DSP_MODE("Request current DSP mode", "get_dsp_mode", "dsp_mode"), DSP_MODE("Request current DSP mode", "get_dsp_mode", "dsp_mode"),
TONE_MAX("Request Max tone level", "get_tone_max", null), TONE_MAX("Request Max tone level", "get_tone_max", null),
TONE_CONTROL_SELECT("Tone Control Select", RotelConnector.PRIMARY_CMD, (byte) 0x67), TONE_CONTROL_SELECT("Tone Control Select", PRIMARY_CMD, (byte) 0x67),
TREBLE_UP("Treble Up", RotelConnector.PRIMARY_CMD, (byte) 0x0D, "treble_up", "treble_up"), TREBLE_UP("Treble Up", PRIMARY_CMD, (byte) 0x0D, "treble_up", "treble_up"),
TREBLE_DOWN("Treble Down", RotelConnector.PRIMARY_CMD, (byte) 0x0E, "treble_down", "treble_down"), TREBLE_DOWN("Treble Down", PRIMARY_CMD, (byte) 0x0E, "treble_down", "treble_down"),
TREBLE_SET("Set Treble to level", "treble_", "treble_"), TREBLE_SET("Set Treble to level", "treble_", "treble_"),
TREBLE("Request current treble level", "get_treble", "treble?"), TREBLE("Request current treble level", "get_treble", "treble?"),
BASS_UP("Bass Up", RotelConnector.PRIMARY_CMD, (byte) 0x0F, "bass_up", "bass_up"), BASS_UP("Bass Up", PRIMARY_CMD, (byte) 0x0F, "bass_up", "bass_up"),
BASS_DOWN("Bass Down", RotelConnector.PRIMARY_CMD, (byte) 0x10, "bass_down", "bass_down"), BASS_DOWN("Bass Down", PRIMARY_CMD, (byte) 0x10, "bass_down", "bass_down"),
BASS_SET("Set Bass to level", "bass_", "bass_"), BASS_SET("Set Bass to level", "bass_", "bass_"),
BASS("Request current bass level", "get_bass", "bass?"), BASS("Request current bass level", "get_bass", "bass?"),
RECORD_FONCTION_SELECT("Record Function Select", RotelConnector.PRIMARY_CMD, (byte) 0x17), RECORD_FONCTION_SELECT("Record Function Select", PRIMARY_CMD, (byte) 0x17),
PLAY("Play Source", RotelConnector.PRIMARY_CMD, (byte) 0x04, "play", "play"), PLAY("Play Source", PRIMARY_CMD, (byte) 0x04, "play", "play"),
STOP("Stop Source", RotelConnector.PRIMARY_CMD, (byte) 0x06, "stop", "stop"), STOP("Stop Source", PRIMARY_CMD, (byte) 0x06, "stop", "stop"),
PAUSE("Pause Source", RotelConnector.PRIMARY_CMD, (byte) 0x05, "pause", "pause"), PAUSE("Pause Source", PRIMARY_CMD, (byte) 0x05, "pause", "pause"),
CD_PLAY_STATUS("Request CD play status", "get_cd_play_status", null), CD_PLAY_STATUS("Request CD play status", "get_cd_play_status", null),
PLAY_STATUS("Request source play status", "get_play_status", "status"), PLAY_STATUS("Request source play status", "get_play_status", "status"),
TRACK_FORWARD("Track Forward", RotelConnector.PRIMARY_CMD, (byte) 0x09, "track_fwd", "trkf"), TRACK_FORWARD("Track Forward", PRIMARY_CMD, (byte) 0x09, "track_fwd", "trkf"),
TRACK_BACKWORD("Track Backward", RotelConnector.PRIMARY_CMD, (byte) 0x08, "track_back", "trkb"), TRACK_BACKWORD("Track Backward", PRIMARY_CMD, (byte) 0x08, "track_back", "trkb"),
TRACK("Request current CD track number", null, "track"), TRACK("Request current CD track number", null, "track"),
FREQUENCY("Request current frequency for digital source input", "get_current_freq", "freq?"), FREQUENCY("Request current frequency for digital source input", "get_current_freq", "freq?"),
DISPLAY_REFRESH("Display Refresh", RotelConnector.PRIMARY_CMD, (byte) 0xFF), DISPLAY_REFRESH("Display Refresh", PRIMARY_CMD, (byte) 0xFF),
DIMMER_LEVEL_GET("Request current front display dimmer level", "get_current_dimmer", "dimmer?"), DIMMER_LEVEL_GET("Request current front display dimmer level", "get_current_dimmer", "dimmer?"),
DIMMER_LEVEL_SET("Set front display dimmer to level", "dimmer_", "dimmer_"), DIMMER_LEVEL_SET("Set front display dimmer to level", "dimmer_", "dimmer_"),
UPDATE_AUTO("Set Update to Auto", "display_update_auto", "rs232_update_on"), UPDATE_AUTO("Set Update to Auto", "display_update_auto", "rs232_update_on"),
@ -245,10 +235,10 @@ public enum RotelCommand {
BALANCE_LEFT("Balance Left", "balance_left", "balance_l"), BALANCE_LEFT("Balance Left", "balance_left", "balance_l"),
BALANCE_SET("Set Balance to level", "balance_", "balance_"), BALANCE_SET("Set Balance to level", "balance_", "balance_"),
BALANCE("Request current balance setting", "get_balance", "balance?"), BALANCE("Request current balance setting", "get_balance", "balance?"),
SPEAKER_A_TOGGLE("Toggle Speaker A Output", RotelConnector.PRIMARY_CMD, (byte) 0x50, "speaker_a", "speaker_a"), SPEAKER_A_TOGGLE("Toggle Speaker A Output", PRIMARY_CMD, (byte) 0x50, "speaker_a", "speaker_a"),
SPEAKER_A_ON("Set Speaker A Output", "speaker_a_on", "speaker_a_on"), SPEAKER_A_ON("Set Speaker A Output", "speaker_a_on", "speaker_a_on"),
SPEAKER_A_OFF("Unset Speaker A Output", "speaker_a_off", "speaker_a_off"), SPEAKER_A_OFF("Unset Speaker A Output", "speaker_a_off", "speaker_a_off"),
SPEAKER_B_TOGGLE("Toggle Speaker B Output", RotelConnector.PRIMARY_CMD, (byte) 0x51, "speaker_b", "speaker_b"), SPEAKER_B_TOGGLE("Toggle Speaker B Output", PRIMARY_CMD, (byte) 0x51, "speaker_b", "speaker_b"),
SPEAKER_B_ON("Set Speaker B Output", "speaker_b_on", "speaker_b_on"), SPEAKER_B_ON("Set Speaker B Output", "speaker_b_on", "speaker_b_on"),
SPEAKER_B_OFF("Unset Speaker B Output", "speaker_b_off", "speaker_b_off"), SPEAKER_B_OFF("Unset Speaker B Output", "speaker_b_off", "speaker_b_off"),
SPEAKER("Request current active speaker outputs", "get_current_speaker", "speaker?"); SPEAKER("Request current active speaker outputs", "get_current_speaker", "speaker?");

View File

@ -19,12 +19,11 @@ import java.io.InputStream;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.net.Socket; import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel; import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -48,13 +47,12 @@ public class RotelIpConnector extends RotelConnector {
* *
* @param address the IP address of the projector * @param address the IP address of the projector
* @param port the TCP port to be used * @param port the TCP port to be used
* @param model the projector model in use * @param protocolHandler the protocol handler
* @param protocol the protocol to be used
* @param readerThreadName the name of thread to be created * @param readerThreadName the name of thread to be created
*/ */
public RotelIpConnector(String address, Integer port, RotelModel model, RotelProtocol protocol, public RotelIpConnector(String address, Integer port, RotelAbstractProtocolHandler protocolHandler,
Map<RotelSource, String> sourcesLabels, String readerThreadName) { String readerThreadName) {
super(model, protocol, sourcesLabels, false, readerThreadName); super(protocolHandler, false, readerThreadName);
this.address = address; this.address = address;
this.port = port; this.port = port;
@ -70,9 +68,7 @@ public class RotelIpConnector extends RotelConnector {
dataOut = new DataOutputStream(clientSocket.getOutputStream()); dataOut = new DataOutputStream(clientSocket.getOutputStream());
dataIn = new DataInputStream(clientSocket.getInputStream()); dataIn = new DataInputStream(clientSocket.getInputStream());
Thread thread = new RotelReaderThread(this, readerThreadName); readerThread.start();
setReaderThread(thread);
thread.start();
this.clientSocket = clientSocket; this.clientSocket = clientSocket;

View File

@ -13,10 +13,10 @@
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.communication;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -28,79 +28,38 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault @NonNullByDefault
public class RotelReaderThread extends Thread { public class RotelReaderThread extends Thread {
private final Logger logger = LoggerFactory.getLogger(RotelReaderThread.class);
private static final int READ_BUFFER_SIZE = 16; private static final int READ_BUFFER_SIZE = 16;
private RotelConnector connector; private final Logger logger = LoggerFactory.getLogger(RotelReaderThread.class);
private final RotelConnector connector;
private final RotelAbstractProtocolHandler protocolHandler;
/** /**
* Constructor * Constructor
* *
* @param connector the object that should handle the received message * @param connector the connector to read input data
* @param protocolHandler the protocol handler
* @param threadName the name of the thread * @param threadName the name of the thread
*/ */
public RotelReaderThread(RotelConnector connector, String threadName) { public RotelReaderThread(RotelConnector connector, RotelAbstractProtocolHandler protocolHandler,
String threadName) {
super(threadName); super(threadName);
this.connector = connector; this.connector = connector;
this.protocolHandler = protocolHandler;
} }
@Override @Override
public void run() { public void run() {
logger.debug("Data listener started"); logger.debug("Data listener started");
RotelProtocol protocol = connector.getProtocol();
final int size = (protocol == RotelProtocol.HEX)
? (6 + connector.getModel().getRespNbChars() + connector.getModel().getRespNbFlags())
: 64;
byte[] readDataBuffer = new byte[READ_BUFFER_SIZE]; byte[] readDataBuffer = new byte[READ_BUFFER_SIZE];
byte[] dataBuffer = new byte[size];
boolean startCodeReached = false;
int count = 0;
int index = 0;
final char terminatingChar = (protocol == RotelProtocol.ASCII_V1) ? '!' : '$';
try { try {
while (!Thread.interrupted()) { while (!Thread.interrupted()) {
int len = connector.readInput(readDataBuffer); int len = connector.readInput(readDataBuffer);
if (len > 0) { if (len > 0) {
for (int i = 0; i < len; i++) { protocolHandler.handleIncomingData(readDataBuffer, len);
if (protocol == RotelProtocol.HEX) {
if (readDataBuffer[i] == RotelConnector.START) {
startCodeReached = true;
count = 0;
index = 0;
}
if (startCodeReached) {
if (index < size) {
dataBuffer[index++] = readDataBuffer[i];
}
if (index == 2) {
count = readDataBuffer[i];
} else if ((count > 0) && (index == (count + 3))) {
if ((readDataBuffer[i] & 0x000000FF) == 0x000000FD) {
count++;
} else {
byte[] msg = Arrays.copyOf(dataBuffer, index);
connector.handleIncomingMessage(msg);
startCodeReached = false;
}
}
}
} else {
if (index < size) {
dataBuffer[index++] = readDataBuffer[i];
}
if (readDataBuffer[i] == terminatingChar) {
if (index >= size) {
dataBuffer[index - 1] = (byte) terminatingChar;
}
byte[] msg = Arrays.copyOf(dataBuffer, index);
connector.handleIncomingMessage(msg);
index = 0;
}
}
}
} }
} }
} catch (InterruptedIOException e) { } catch (InterruptedIOException e) {
@ -108,7 +67,7 @@ public class RotelReaderThread extends Thread {
logger.debug("Interrupted via InterruptedIOException"); logger.debug("Interrupted via InterruptedIOException");
} catch (RotelException e) { } catch (RotelException e) {
logger.debug("Reading failed: {}", e.getMessage(), e); logger.debug("Reading failed: {}", e.getMessage(), e);
connector.handleIncomingMessage(RotelConnector.READ_ERROR); protocolHandler.handleInIncomingError();
} }
logger.debug("Data listener stopped"); logger.debug("Data listener stopped");

View File

@ -15,12 +15,11 @@ package org.openhab.binding.rotel.internal.communication;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel; import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.openhab.core.io.transport.serial.PortInUseException; import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort; import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortIdentifier; import org.openhab.core.io.transport.serial.SerialPortIdentifier;
@ -42,6 +41,8 @@ public class RotelSerialConnector extends RotelConnector {
private String serialPortName; private String serialPortName;
private SerialPortManager serialPortManager; private SerialPortManager serialPortManager;
private int baudRate;
private @Nullable SerialPort serialPort; private @Nullable SerialPort serialPort;
/** /**
@ -49,14 +50,15 @@ public class RotelSerialConnector extends RotelConnector {
* *
* @param serialPortManager the serial port manager * @param serialPortManager the serial port manager
* @param serialPortName the serial port name to be used * @param serialPortName the serial port name to be used
* @param model the projector model in use * @param baudRate the baud rate to be used
* @param protocol the protocol to be used * @param protocolHandler the protocol handler
* @param readerThreadName the name of thread to be created * @param readerThreadName the name of thread to be created
*/ */
public RotelSerialConnector(SerialPortManager serialPortManager, String serialPortName, RotelModel model, public RotelSerialConnector(SerialPortManager serialPortManager, String serialPortName, int baudRate,
RotelProtocol protocol, Map<RotelSource, String> sourcesLabels, String readerThreadName) { RotelAbstractProtocolHandler protocolHandler, String readerThreadName) {
super(model, protocol, sourcesLabels, false, readerThreadName); super(protocolHandler, false, readerThreadName);
this.baudRate = baudRate;
this.serialPortManager = serialPortManager; this.serialPortManager = serialPortManager;
this.serialPortName = serialPortName; this.serialPortName = serialPortName;
} }
@ -73,7 +75,7 @@ public class RotelSerialConnector extends RotelConnector {
SerialPort commPort = portIdentifier.open(this.getClass().getName(), 2000); SerialPort commPort = portIdentifier.open(this.getClass().getName(), 2000);
commPort.setSerialPortParams(getModel().getBaudRate(), SerialPort.DATABITS_8, SerialPort.STOPBITS_1, commPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE); SerialPort.PARITY_NONE);
commPort.enableReceiveThreshold(1); commPort.enableReceiveThreshold(1);
commPort.enableReceiveTimeout(100); commPort.enableReceiveTimeout(100);
@ -92,9 +94,7 @@ public class RotelSerialConnector extends RotelConnector {
} }
} }
Thread thread = new RotelReaderThread(this, readerThreadName); readerThread.start();
setReaderThread(thread);
thread.start();
this.serialPort = commPort; this.serialPort = commPort;
this.dataIn = dataIn; this.dataIn = dataIn;

View File

@ -12,6 +12,9 @@
*/ */
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.communication;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import static org.openhab.binding.rotel.internal.protocol.hex.RotelHexProtocolHandler.START;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
@ -23,6 +26,9 @@ import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel; import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.RotelPlayStatus; import org.openhab.binding.rotel.internal.RotelPlayStatus;
import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
import org.openhab.binding.rotel.internal.protocol.hex.RotelHexProtocolHandler;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -34,9 +40,13 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault @NonNullByDefault
public class RotelSimuConnector extends RotelConnector { public class RotelSimuConnector extends RotelConnector {
private static final int STEP_TONE_LEVEL = 1;
private final Logger logger = LoggerFactory.getLogger(RotelSimuConnector.class); private final Logger logger = LoggerFactory.getLogger(RotelSimuConnector.class);
private static final int STEP_TONE_LEVEL = 1; private final RotelModel model;
private final RotelProtocol protocol;
private final Map<RotelSource, String> sourcesLabels;
private Object lock = new Object(); private Object lock = new Object();
@ -80,12 +90,16 @@ public class RotelSimuConnector extends RotelConnector {
* Constructor * Constructor
* *
* @param model the projector model in use * @param model the projector model in use
* @param protocol the protocol to be used * @param protocolHandler the protocol handler
* @param sourcesLabels the custom labels for sources
* @param readerThreadName the name of thread to be created * @param readerThreadName the name of thread to be created
*/ */
public RotelSimuConnector(RotelModel model, RotelProtocol protocol, Map<RotelSource, String> sourcesLabels, public RotelSimuConnector(RotelModel model, RotelAbstractProtocolHandler protocolHandler,
String readerThreadName) { Map<RotelSource, String> sourcesLabels, String readerThreadName) {
super(model, protocol, sourcesLabels, true, readerThreadName); super(protocolHandler, true, readerThreadName);
this.model = model;
this.protocol = protocolHandler.getProtocol();
this.sourcesLabels = sourcesLabels;
this.minVolume = 0; this.minVolume = 0;
this.maxVolume = model.hasVolumeControl() ? model.getVolumeMax() : 0; this.maxVolume = model.hasVolumeControl() ? model.getVolumeMax() : 0;
this.maxToneLevel = model.hasToneControl() ? model.getToneLevelMax() : 0; this.maxToneLevel = model.hasToneControl() ? model.getToneLevelMax() : 0;
@ -95,9 +109,7 @@ public class RotelSimuConnector extends RotelConnector {
@Override @Override
public synchronized void open() throws RotelException { public synchronized void open() throws RotelException {
logger.debug("Opening simulated connection"); logger.debug("Opening simulated connection");
Thread thread = new RotelReaderThread(this, readerThreadName); readerThread.start();
setReaderThread(thread);
thread.start();
setConnected(true); setConnected(true);
logger.debug("Simulated connection opened"); logger.debug("Simulated connection opened");
} }
@ -132,23 +144,13 @@ public class RotelSimuConnector extends RotelConnector {
return 0; return 0;
} }
@Override
public void sendCommand(RotelCommand cmd, @Nullable Integer value) throws RotelException {
super.sendCommand(cmd, value);
if ((getProtocol() == RotelProtocol.HEX && cmd.getHexType() != 0)
|| (getProtocol() == RotelProtocol.ASCII_V1 && cmd.getAsciiCommandV1() != null)
|| (getProtocol() == RotelProtocol.ASCII_V2 && cmd.getAsciiCommandV2() != null)) {
buildFeedbackMessage(cmd, value);
}
}
/** /**
* Built the simulated feedback message for a sent command * Built the simulated feedback message for a sent command
* *
* @param cmd the sent command * @param cmd the sent command
* @param value the integer value considered in the sent command for volume, bass or treble adjustment * @param value the integer value considered in the sent command for volume, bass or treble adjustment
*/ */
private void buildFeedbackMessage(RotelCommand cmd, @Nullable Integer value) { public void buildFeedbackMessage(RotelCommand cmd, @Nullable Integer value) {
String text = buildSourceLine1Response(); String text = buildSourceLine1Response();
String textLine1Left = buildSourceLine1LeftResponse(); String textLine1Left = buildSourceLine1LeftResponse();
String textLine1Right = buildVolumeLine1RightResponse(); String textLine1Right = buildVolumeLine1RightResponse();
@ -180,14 +182,14 @@ public class RotelSimuConnector extends RotelConnector {
break; break;
case ZONE2_POWER_OFF: case ZONE2_POWER_OFF:
powerZone2 = false; powerZone2 = false;
text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2); powerZone2, sourceZone2);
showZone = 2; showZone = 2;
resetZone = false; resetZone = false;
break; break;
case ZONE2_POWER_ON: case ZONE2_POWER_ON:
powerZone2 = true; powerZone2 = true;
text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2); powerZone2, sourceZone2);
showZone = 2; showZone = 2;
resetZone = false; resetZone = false;
@ -217,9 +219,9 @@ public class RotelSimuConnector extends RotelConnector {
resetZone = false; resetZone = false;
break; break;
case RECORD_FONCTION_SELECT: case RECORD_FONCTION_SELECT:
if (getModel().getNbAdditionalZones() >= 1 && getModel().getZoneSelectCmd() == cmd) { if (model.getNbAdditionalZones() >= 1 && model.getZoneSelectCmd() == cmd) {
showZone++; showZone++;
if (showZone > getModel().getNbAdditionalZones()) { if (showZone > model.getNbAdditionalZones()) {
showZone = 1; showZone = 1;
if (!power) { if (!power) {
showZone++; showZone++;
@ -234,7 +236,7 @@ public class RotelSimuConnector extends RotelConnector {
textLine2 = buildRecordResponse(); textLine2 = buildRecordResponse();
} else if (showZone == 2) { } else if (showZone == 2) {
selectingRecord = false; selectingRecord = false;
text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2); powerZone2, sourceZone2);
} else if (showZone == 3) { } else if (showZone == 3) {
selectingRecord = false; selectingRecord = false;
@ -246,12 +248,12 @@ public class RotelSimuConnector extends RotelConnector {
resetZone = false; resetZone = false;
break; break;
case ZONE_SELECT: case ZONE_SELECT:
if (getModel().getNbAdditionalZones() == 0 if (model.getNbAdditionalZones() == 0
|| (getModel().getNbAdditionalZones() > 1 && getModel().getZoneSelectCmd() == cmd) || (model.getNbAdditionalZones() > 1 && model.getZoneSelectCmd() == cmd)
|| (showZone == 1 && getModel().getZoneSelectCmd() != cmd)) { || (showZone == 1 && model.getZoneSelectCmd() != cmd)) {
accepted = false; accepted = false;
} else { } else {
if (getModel().getZoneSelectCmd() == cmd) { if (model.getZoneSelectCmd() == cmd) {
if (!power && !powerZone2) { if (!power && !powerZone2) {
showZone = 2; showZone = 2;
powerZone2 = true; powerZone2 = true;
@ -270,8 +272,8 @@ public class RotelSimuConnector extends RotelConnector {
} }
} }
if (showZone == 2) { if (showZone == 2) {
text = textLine2 = buildZonePowerResponse( text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", powerZone2, sourceZone2); powerZone2, sourceZone2);
} else if (showZone == 3) { } else if (showZone == 3) {
text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3); text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3);
} else if (showZone == 4) { } else if (showZone == 4) {
@ -291,54 +293,54 @@ public class RotelSimuConnector extends RotelConnector {
if (volumeZone2 < maxVolume) { if (volumeZone2 < maxVolume) {
volumeZone2++; volumeZone2++;
} }
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
case ZONE2_VOLUME_DOWN: case ZONE2_VOLUME_DOWN:
if (volumeZone2 > minVolume) { if (volumeZone2 > minVolume) {
volumeZone2--; volumeZone2--;
} }
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
case ZONE2_VOLUME_SET: case ZONE2_VOLUME_SET:
if (value != null) { if (value != null) {
volumeZone2 = value; volumeZone2 = value;
} }
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
case VOLUME_UP: case VOLUME_UP:
if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) { if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (volumeZone2 < maxVolume) { if (volumeZone2 < maxVolume) {
volumeZone2++; volumeZone2++;
} }
text = textLine2 = buildZoneVolumeResponse( text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2); muteZone2, volumeZone2);
resetZone = false; resetZone = false;
} else { } else {
accepted = false; accepted = false;
} }
break; break;
case VOLUME_DOWN: case VOLUME_DOWN:
if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) { if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (volumeZone2 > minVolume) { if (volumeZone2 > minVolume) {
volumeZone2--; volumeZone2--;
} }
text = textLine2 = buildZoneVolumeResponse( text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2); muteZone2, volumeZone2);
resetZone = false; resetZone = false;
} else { } else {
accepted = false; accepted = false;
} }
break; break;
case VOLUME_SET: case VOLUME_SET:
if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) { if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (value != null) { if (value != null) {
volumeZone2 = value; volumeZone2 = value;
} }
text = textLine2 = buildZoneVolumeResponse( text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2); muteZone2, volumeZone2);
resetZone = false; resetZone = false;
} else { } else {
accepted = false; accepted = false;
@ -346,17 +348,17 @@ public class RotelSimuConnector extends RotelConnector {
break; break;
case ZONE2_MUTE_TOGGLE: case ZONE2_MUTE_TOGGLE:
muteZone2 = !muteZone2; muteZone2 = !muteZone2;
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
case ZONE2_MUTE_ON: case ZONE2_MUTE_ON:
muteZone2 = true; muteZone2 = true;
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
case ZONE2_MUTE_OFF: case ZONE2_MUTE_OFF:
muteZone2 = false; muteZone2 = false;
text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2); muteZone2, volumeZone2);
break; break;
default: default:
@ -365,9 +367,9 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
sourceZone2 = getModel().getZone2SourceFromCommand(cmd); sourceZone2 = model.getZone2SourceFromCommand(cmd);
powerZone2 = true; powerZone2 = true;
text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2); powerZone2, sourceZone2);
muteZone2 = false; muteZone2 = false;
accepted = true; accepted = true;
@ -376,12 +378,11 @@ public class RotelSimuConnector extends RotelConnector {
} catch (RotelException e) { } catch (RotelException e) {
} }
} }
if (!accepted && !getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 if (!accepted && !model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
&& showZone == 2) {
try { try {
sourceZone2 = getModel().getSourceFromCommand(cmd); sourceZone2 = model.getSourceFromCommand(cmd);
powerZone2 = true; powerZone2 = true;
text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2); powerZone2, sourceZone2);
muteZone2 = false; muteZone2 = false;
accepted = true; accepted = true;
@ -429,7 +430,7 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
sourceZone3 = getModel().getZone3SourceFromCommand(cmd); sourceZone3 = model.getZone3SourceFromCommand(cmd);
powerZone3 = true; powerZone3 = true;
text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3); text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3);
muteZone3 = false; muteZone3 = false;
@ -479,7 +480,7 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
sourceZone4 = getModel().getZone4SourceFromCommand(cmd); sourceZone4 = model.getZone4SourceFromCommand(cmd);
powerZone4 = true; powerZone4 = true;
text = textLine2 = buildZonePowerResponse("ZONE4", powerZone4, sourceZone4); text = textLine2 = buildZonePowerResponse("ZONE4", powerZone4, sourceZone4);
muteZone4 = false; muteZone4 = false;
@ -495,11 +496,11 @@ public class RotelSimuConnector extends RotelConnector {
switch (cmd) { switch (cmd) {
case UPDATE_AUTO: case UPDATE_AUTO:
textAscii = buildAsciiResponse( textAscii = buildAsciiResponse(
getProtocol() == RotelProtocol.ASCII_V1 ? KEY_DISPLAY_UPDATE : KEY_UPDATE_MODE, "AUTO"); protocol == RotelProtocol.ASCII_V1 ? KEY_DISPLAY_UPDATE : KEY_UPDATE_MODE, AUTO);
break; break;
case UPDATE_MANUAL: case UPDATE_MANUAL:
textAscii = buildAsciiResponse( textAscii = buildAsciiResponse(
getProtocol() == RotelProtocol.ASCII_V1 ? KEY_DISPLAY_UPDATE : KEY_UPDATE_MODE, "MANUAL"); protocol == RotelProtocol.ASCII_V1 ? KEY_DISPLAY_UPDATE : KEY_UPDATE_MODE, MANUAL);
break; break;
case VOLUME_GET_MIN: case VOLUME_GET_MIN:
textAscii = buildAsciiResponse(KEY_VOLUME_MIN, minVolume); textAscii = buildAsciiResponse(KEY_VOLUME_MIN, minVolume);
@ -668,7 +669,7 @@ public class RotelSimuConnector extends RotelConnector {
multiinput = !multiinput; multiinput = !multiinput;
text = "MULTI IN " + (multiinput ? "ON" : "OFF"); text = "MULTI IN " + (multiinput ? "ON" : "OFF");
try { try {
source = getModel().getSourceFromCommand(cmd); source = model.getSourceFromCommand(cmd);
textLine1Left = buildSourceLine1LeftResponse(); textLine1Left = buildSourceLine1LeftResponse();
textAscii = buildSourceAsciiResponse(); textAscii = buildSourceAsciiResponse();
mute = false; mute = false;
@ -795,7 +796,7 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
source = getModel().getMainZoneSourceFromCommand(cmd); source = model.getMainZoneSourceFromCommand(cmd);
text = buildSourceLine1Response(); text = buildSourceLine1Response();
textLine1Left = buildSourceLine1LeftResponse(); textLine1Left = buildSourceLine1LeftResponse();
textAscii = buildSourceAsciiResponse(); textAscii = buildSourceAsciiResponse();
@ -805,10 +806,10 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
if (selectingRecord && !getModel().hasOtherThanPrimaryCommands()) { if (selectingRecord && !model.hasOtherThanPrimaryCommands()) {
recordSource = getModel().getSourceFromCommand(cmd); recordSource = model.getSourceFromCommand(cmd);
} else { } else {
source = getModel().getSourceFromCommand(cmd); source = model.getSourceFromCommand(cmd);
} }
text = buildSourceLine1Response(); text = buildSourceLine1Response();
textLine1Left = buildSourceLine1LeftResponse(); textLine1Left = buildSourceLine1LeftResponse();
@ -820,7 +821,7 @@ public class RotelSimuConnector extends RotelConnector {
} }
if (!accepted) { if (!accepted) {
try { try {
recordSource = getModel().getRecordSourceFromCommand(cmd); recordSource = model.getRecordSourceFromCommand(cmd);
text = buildSourceLine1Response(); text = buildSourceLine1Response();
textLine2 = buildRecordResponse(); textLine2 = buildRecordResponse();
accepted = true; accepted = true;
@ -840,7 +841,7 @@ public class RotelSimuConnector extends RotelConnector {
showZone = 0; showZone = 0;
} }
if (getModel().getRespNbChars() == 42) { if (model.getRespNbChars() == 42) {
while (textLine1Left.length() < 14) { while (textLine1Left.length() < 14) {
textLine1Left += " "; textLine1Left += " ";
} }
@ -853,44 +854,44 @@ public class RotelSimuConnector extends RotelConnector {
text = textLine1Left + textLine1Right + textLine2; text = textLine1Left + textLine1Right + textLine2;
} }
if (getProtocol() == RotelProtocol.HEX) { if (protocol == RotelProtocol.HEX) {
byte[] chars = Arrays.copyOf(text.getBytes(StandardCharsets.US_ASCII), getModel().getRespNbChars()); byte[] chars = Arrays.copyOf(text.getBytes(StandardCharsets.US_ASCII), model.getRespNbChars());
byte[] flags = new byte[getModel().getRespNbFlags()]; byte[] flags = new byte[model.getRespNbFlags()];
try { try {
getModel().setMultiInput(flags, multiinput); model.setMultiInput(flags, multiinput);
} catch (RotelException e) { } catch (RotelException e) {
} }
try { try {
getModel().setZone2(flags, powerZone2); model.setZone2(flags, powerZone2);
} catch (RotelException e) { } catch (RotelException e) {
} }
try { try {
getModel().setZone3(flags, powerZone3); model.setZone3(flags, powerZone3);
} catch (RotelException e) { } catch (RotelException e) {
} }
try { try {
getModel().setZone4(flags, powerZone4); model.setZone4(flags, powerZone4);
} catch (RotelException e) { } catch (RotelException e) {
} }
int size = 6 + getModel().getRespNbChars() + getModel().getRespNbFlags(); int size = 6 + model.getRespNbChars() + model.getRespNbFlags();
byte[] dataBuffer = new byte[size]; byte[] dataBuffer = new byte[size];
int idx = 0; int idx = 0;
dataBuffer[idx++] = START; dataBuffer[idx++] = START;
dataBuffer[idx++] = (byte) (size - 4); dataBuffer[idx++] = (byte) (size - 4);
dataBuffer[idx++] = getModel().getDeviceId(); dataBuffer[idx++] = model.getDeviceId();
dataBuffer[idx++] = STANDARD_RESPONSE; dataBuffer[idx++] = STANDARD_RESPONSE;
if (getModel().isCharsBeforeFlags()) { if (model.isCharsBeforeFlags()) {
System.arraycopy(chars, 0, dataBuffer, idx, getModel().getRespNbChars()); System.arraycopy(chars, 0, dataBuffer, idx, model.getRespNbChars());
idx += getModel().getRespNbChars(); idx += model.getRespNbChars();
System.arraycopy(flags, 0, dataBuffer, idx, getModel().getRespNbFlags()); System.arraycopy(flags, 0, dataBuffer, idx, model.getRespNbFlags());
idx += getModel().getRespNbFlags(); idx += model.getRespNbFlags();
} else { } else {
System.arraycopy(flags, 0, dataBuffer, idx, getModel().getRespNbFlags()); System.arraycopy(flags, 0, dataBuffer, idx, model.getRespNbFlags());
idx += getModel().getRespNbFlags(); idx += model.getRespNbFlags();
System.arraycopy(chars, 0, dataBuffer, idx, getModel().getRespNbChars()); System.arraycopy(chars, 0, dataBuffer, idx, model.getRespNbChars());
idx += getModel().getRespNbChars(); idx += model.getRespNbChars();
} }
byte checksum = computeCheckSum(dataBuffer, idx - 1); byte checksum = RotelHexProtocolHandler.computeCheckSum(dataBuffer, idx - 1);
if ((checksum & 0x000000FF) == 0x000000FD) { if ((checksum & 0x000000FF) == 0x000000FD) {
dataBuffer[idx++] = (byte) 0xFD; dataBuffer[idx++] = (byte) 0xFD;
dataBuffer[idx++] = 0; dataBuffer[idx++] = 0;
@ -905,7 +906,7 @@ public class RotelSimuConnector extends RotelConnector {
idxInFeedbackMsg = 0; idxInFeedbackMsg = 0;
} }
} else { } else {
String command = textAscii + (getProtocol() == RotelProtocol.ASCII_V1 ? "!" : "$"); String command = textAscii + (protocol == RotelProtocol.ASCII_V1 ? "!" : "$");
synchronized (lock) { synchronized (lock) {
feedbackMsg = command.getBytes(StandardCharsets.US_ASCII); feedbackMsg = command.getBytes(StandardCharsets.US_ASCII);
idxInFeedbackMsg = 0; idxInFeedbackMsg = 0;
@ -978,8 +979,7 @@ public class RotelSimuConnector extends RotelConnector {
status = STOP; status = STOP;
break; break;
} }
return buildAsciiResponse(getProtocol() == RotelProtocol.ASCII_V1 ? KEY1_PLAY_STATUS : KEY2_PLAY_STATUS, return buildAsciiResponse(protocol == RotelProtocol.ASCII_V1 ? KEY1_PLAY_STATUS : KEY2_PLAY_STATUS, status);
status);
} }
private String buildTrackAsciiResponse() { private String buildTrackAsciiResponse() {

View File

@ -0,0 +1,133 @@
/**
* Copyright (c) 2010-2022 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.rotel.internal.protocol;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.communication.RotelCommand;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class for handling a Rotel protocol (build of command messages, decoding of incoming data)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public abstract class RotelAbstractProtocolHandler {
private final Logger logger = LoggerFactory.getLogger(RotelAbstractProtocolHandler.class);
protected final RotelModel model;
private final List<RotelMessageEventListener> listeners = new ArrayList<>();
/**
* Constructor
*
* @param model the Rotel model in use
*/
public RotelAbstractProtocolHandler(RotelModel model) {
this.model = model;
}
public abstract RotelProtocol getProtocol();
/**
* Build the message associated to a Rotel command
*
* @param cmd the command to execute
* @param value the integer value to consider for volume, bass or treble adjustment
*
* @throws RotelException - In case the command is not supported by the protocol
*/
public abstract byte[] buildCommandMessage(RotelCommand cmd, @Nullable Integer value) throws RotelException;
public abstract void handleIncomingData(byte[] inDataBuffer, int length);
public void handleInIncomingError() {
dispatchKeyValue(KEY_ERROR, MSG_VALUE_ON);
}
/**
* Analyze an incoming message and dispatch corresponding (key, value) to the event listeners
*
* @param incomingMessage the received message
*/
protected void handleIncomingMessage(byte[] incomingMessage) {
logger.debug("handleIncomingMessage: bytes {}", HexUtils.bytesToHex(incomingMessage));
try {
validateResponse(incomingMessage);
} catch (RotelException e) {
return;
}
handleValidMessage(incomingMessage);
}
/**
* Validate the content of a feedback message
*
* @param responseMessage the buffer containing the feedback message
*
* @throws RotelException - If the message has unexpected content
*/
protected abstract void validateResponse(byte[] responseMessage) throws RotelException;
/**
* Analyze a valid HEX message and dispatch corresponding (key, value) to the event listeners
*
* @param incomingMessage the received message
*/
protected abstract void handleValidMessage(byte[] incomingMessage);
/**
* Add a listener to the list of listeners to be notified with events
*
* @param listener the listener
*/
public void addEventListener(RotelMessageEventListener listener) {
listeners.add(listener);
}
/**
* Remove a listener from the list of listeners to be notified with events
*
* @param listener the listener
*/
public void removeEventListener(RotelMessageEventListener listener) {
listeners.remove(listener);
}
/**
* Dispatch an event (key, value) to the event listeners
*
* @param key the key
* @param value the value
*/
protected void dispatchKeyValue(String key, String value) {
RotelMessageEvent event = new RotelMessageEvent(this, key, value);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onNewMessageEvent(event);
}
}
}

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.protocol;
import java.util.EventObject; import java.util.EventObject;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.protocol;
import java.util.EventListener; import java.util.EventListener;
import java.util.EventObject; import java.util.EventObject;

View File

@ -10,7 +10,7 @@
* *
* SPDX-License-Identifier: EPL-2.0 * SPDX-License-Identifier: EPL-2.0
*/ */
package org.openhab.binding.rotel.internal.communication; package org.openhab.binding.rotel.internal.protocol;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.rotel.internal.RotelException; import org.openhab.binding.rotel.internal.RotelException;

View File

@ -0,0 +1,195 @@
/**
* Copyright (c) 2010-2022 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.rotel.internal.protocol.ascii;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.regex.PatternSyntaxException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class for handling a Rotel ASCII protocol (build of command messages, decoding of incoming data)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public abstract class RotelAbstractAsciiProtocolHandler extends RotelAbstractProtocolHandler {
/** Special characters that can be found in the feedback messages for several devices using the ASCII protocol */
public static final byte[][] SPECIAL_CHARACTERS = { { (byte) 0xEE, (byte) 0x82, (byte) 0x85 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x84 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x92 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x87 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x8E },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x89 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x93 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x8C }, { (byte) 0xEE, (byte) 0x82, (byte) 0x8F },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x8A }, { (byte) 0xEE, (byte) 0x82, (byte) 0x8B },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x81 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x82 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x83 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x94 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x97 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x98 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x80 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x99 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x9A }, { (byte) 0xEE, (byte) 0x82, (byte) 0x88 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x95 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x96 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x90 }, { (byte) 0xEE, (byte) 0x82, (byte) 0x91 },
{ (byte) 0xEE, (byte) 0x82, (byte) 0x8D }, { (byte) 0xEE, (byte) 0x80, (byte) 0x80, (byte) 0xEE,
(byte) 0x80, (byte) 0x81, (byte) 0xEE, (byte) 0x80, (byte) 0x82 } };
/** Special characters that can be found in the feedback messages for the RCD-1572 */
public static final byte[][] SPECIAL_CHARACTERS_RCD1572 = { { (byte) 0xC2, (byte) 0x8C },
{ (byte) 0xC2, (byte) 0x54 }, { (byte) 0xC2, (byte) 0x81 }, { (byte) 0xC2, (byte) 0x82 },
{ (byte) 0xC2, (byte) 0x83 } };
/** Empty table of special characters */
public static final byte[][] NO_SPECIAL_CHARACTERS = {};
private final Logger logger = LoggerFactory.getLogger(RotelAbstractAsciiProtocolHandler.class);
private final char terminatingChar;
private final int size;
private final byte[] dataBuffer;
private int index;
/**
* Constructor
*
* @param model the Rotel model in use
* @param protocol the protocol to be used
*/
public RotelAbstractAsciiProtocolHandler(RotelModel model, char terminatingChar) {
super(model);
this.terminatingChar = terminatingChar;
this.size = 64;
this.dataBuffer = new byte[size];
this.index = 0;
}
@Override
public void handleIncomingData(byte[] inDataBuffer, int length) {
for (int i = 0; i < length; i++) {
if (index < size) {
dataBuffer[index++] = inDataBuffer[i];
}
if (inDataBuffer[i] == terminatingChar) {
if (index >= size) {
dataBuffer[index - 1] = (byte) terminatingChar;
}
byte[] msg = Arrays.copyOf(dataBuffer, index);
handleIncomingMessage(msg);
index = 0;
}
}
}
/**
* Validate the content of a feedback message
*
* @param responseMessage the buffer containing the feedback message
*
* @throws RotelException - If the message has unexpected content
*/
@Override
protected void validateResponse(byte[] responseMessage) throws RotelException {
// Check minimum message length
if (responseMessage.length < 1) {
logger.debug("Unexpected message length: {}", responseMessage.length);
throw new RotelException("Unexpected message length");
}
if (responseMessage[responseMessage.length - 1] != '!' && responseMessage[responseMessage.length - 1] != '$') {
logger.debug("Unexpected ending character in response: {}",
Integer.toHexString(responseMessage[responseMessage.length - 1] & 0x000000FF));
throw new RotelException("Unexpected ending character in response");
}
}
/**
* Analyze a valid ASCII message and dispatch corresponding (key, value) to the event listeners
*
* @param incomingMessage the received message
*/
@Override
protected void handleValidMessage(byte[] incomingMessage) {
byte[] message = filterMessage(incomingMessage, model.getSpecialCharacters());
// Replace characters with code < 32 by a space before converting to a string
for (int i = 0; i < message.length; i++) {
if (message[i] < 0x20) {
message[i] = 0x20;
}
}
String value = new String(message, 0, message.length - 1, StandardCharsets.US_ASCII);
logger.debug("handleValidAsciiMessage: chars *{}*", value);
value = value.trim();
if (value.isEmpty()) {
return;
}
try {
String[] splittedValue = value.split("=");
if (splittedValue.length != 2) {
logger.debug("handleValidAsciiMessage: ignored message {}", value);
} else {
dispatchKeyValue(splittedValue[0].trim().toLowerCase(), splittedValue[1]);
}
} catch (PatternSyntaxException e) {
logger.debug("handleValidAsciiMessage: ignored message {}", value);
}
}
/**
* Suppress certain sequences of bytes from a message
*
* @param message the message as a table of bytes
* @param bytesSequences the table containing the sequence of bytes to be ignored
*
* @return the message without the unexpected sequence of bytes
*/
private byte[] filterMessage(byte[] message, byte[][] bytesSequences) {
if (bytesSequences.length == 0) {
return message;
}
byte[] filteredMsg = new byte[message.length];
int srcIdx = 0;
int dstIdx = 0;
while (srcIdx < message.length) {
int ignoredLength = 0;
for (int i = 0; i < bytesSequences.length; i++) {
int size = bytesSequences[i].length;
if ((message.length - srcIdx) >= size) {
boolean match = true;
for (int j = 0; j < size; j++) {
if (message[srcIdx + j] != bytesSequences[i][j]) {
match = false;
break;
}
}
if (match) {
ignoredLength = size;
break;
}
}
}
if (ignoredLength > 0) {
srcIdx += ignoredLength;
} else {
filteredMsg[dstIdx++] = message[srcIdx++];
}
}
return Arrays.copyOf(filteredMsg, dstIdx);
}
}

View File

@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2022 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.rotel.internal.protocol.ascii;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.communication.RotelCommand;
import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for handling the Rotel ASCII V1 protocol (build of command messages, decoding of incoming data)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public class RotelAsciiV1ProtocolHandler extends RotelAbstractAsciiProtocolHandler {
private static final char CHAR_END_RESPONSE = '!';
private final Logger logger = LoggerFactory.getLogger(RotelAsciiV1ProtocolHandler.class);
/**
* Constructor
*
* @param model the Rotel model in use
*/
public RotelAsciiV1ProtocolHandler(RotelModel model) {
super(model, CHAR_END_RESPONSE);
}
@Override
public RotelProtocol getProtocol() {
return RotelProtocol.ASCII_V1;
}
@Override
public byte[] buildCommandMessage(RotelCommand cmd, @Nullable Integer value) throws RotelException {
String messageStr = cmd.getAsciiCommandV1();
if (messageStr == null) {
throw new RotelException("Command \"" + cmd.getName() + "\" ignored: not available for ASCII V1 protocol");
}
if (value != null) {
switch (cmd) {
case VOLUME_SET:
messageStr += String.format("%d", value);
break;
case BASS_SET:
case TREBLE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
messageStr += String.format("+%02d", value);
} else {
messageStr += String.format("-%02d", -value);
}
break;
case BALANCE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
messageStr += String.format("R%02d", value);
} else {
messageStr += String.format("L%02d", -value);
}
break;
case DIMMER_LEVEL_SET:
if (value > 0 && model.getDimmerLevelMin() < 0) {
messageStr += String.format("+%d", value);
} else {
messageStr += String.format("%d", value);
}
break;
default:
break;
}
}
if (!messageStr.endsWith("?")) {
messageStr += "!";
}
byte[] message = messageStr.getBytes(StandardCharsets.US_ASCII);
logger.debug("Command \"{}\" => {}", cmd.getName(), messageStr);
return message;
}
}

View File

@ -0,0 +1,100 @@
/**
* Copyright (c) 2010-2022 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.rotel.internal.protocol.ascii;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.communication.RotelCommand;
import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for handling the Rotel ASCII V2 protocol (build of command messages, decoding of incoming data)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public class RotelAsciiV2ProtocolHandler extends RotelAbstractAsciiProtocolHandler {
private static final char CHAR_END_RESPONSE = '$';
private final Logger logger = LoggerFactory.getLogger(RotelAsciiV2ProtocolHandler.class);
/**
* Constructor
*
* @param model the Rotel model in use
*/
public RotelAsciiV2ProtocolHandler(RotelModel model) {
super(model, CHAR_END_RESPONSE);
}
@Override
public RotelProtocol getProtocol() {
return RotelProtocol.ASCII_V2;
}
@Override
public byte[] buildCommandMessage(RotelCommand cmd, @Nullable Integer value) throws RotelException {
String messageStr = cmd.getAsciiCommandV2();
if (messageStr == null) {
throw new RotelException("Command \"" + cmd.getName() + "\" ignored: not available for ASCII V2 protocol");
}
if (value != null) {
switch (cmd) {
case VOLUME_SET:
messageStr += String.format("%02d", value);
break;
case BASS_SET:
case TREBLE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
messageStr += String.format("+%02d", value);
} else {
messageStr += String.format("-%02d", -value);
}
break;
case BALANCE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
messageStr += String.format("r%02d", value);
} else {
messageStr += String.format("l%02d", -value);
}
break;
case DIMMER_LEVEL_SET:
if (value > 0 && model.getDimmerLevelMin() < 0) {
messageStr += String.format("+%d", value);
} else {
messageStr += String.format("%d", value);
}
break;
default:
break;
}
}
if (!messageStr.endsWith("?")) {
messageStr += "!";
}
byte[] message = messageStr.getBytes(StandardCharsets.US_ASCII);
logger.debug("Command \"{}\" => {}", cmd.getName(), messageStr);
return message;
}
}

View File

@ -0,0 +1,778 @@
/**
* Copyright (c) 2010-2022 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.rotel.internal.protocol.hex;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
import org.openhab.binding.rotel.internal.communication.RotelCommand;
import org.openhab.binding.rotel.internal.communication.RotelDsp;
import org.openhab.binding.rotel.internal.communication.RotelFlagsMapping;
import org.openhab.binding.rotel.internal.communication.RotelSource;
import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class for handling the Rotel HEX protocol (build of command messages, decoding of incoming data)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
public static final byte START = (byte) 0xFE;
private static final String KEY1_HEX_VOLUME = "volume ";
private static final String KEY2_HEX_VOLUME = "vol ";
private static final String KEY_HEX_MUTE = "mute ";
private static final String KEY1_HEX_BASS = "bass ";
private static final String KEY2_HEX_BASS = "lf ";
private static final String KEY1_HEX_TREBLE = "treble ";
private static final String KEY2_HEX_TREBLE = "hf ";
private static final String KEY_HEX_MULTI_IN = "multi in ";
private static final String KEY_HEX_STEREO = "stereo";
private static final String KEY1_HEX_3CH = "3 stereo";
private static final String KEY2_HEX_3CH = "dolby 3 stereo";
private static final String KEY_HEX_5CH = "5ch stereo";
private static final String KEY_HEX_7CH = "7ch stereo";
private static final String KEY_HEX_MUSIC1 = "music 1";
private static final String KEY_HEX_MUSIC2 = "music 2";
private static final String KEY_HEX_MUSIC3 = "music 3";
private static final String KEY_HEX_MUSIC4 = "music 4";
private static final String KEY_HEX_DSP1 = "dsp 1";
private static final String KEY_HEX_DSP2 = "dsp 2";
private static final String KEY_HEX_DSP3 = "dsp 3";
private static final String KEY_HEX_DSP4 = "dsp 4";
private static final String KEY1_HEX_PROLOGIC = "prologic emu";
private static final String KEY2_HEX_PROLOGIC = "dolby pro logic";
private static final String KEY1_HEX_PLII_CINEMA = "prologic cin";
private static final String KEY2_HEX_PLII_CINEMA = "dolby pl c";
private static final String KEY1_HEX_PLII_MUSIC = "prologic mus";
private static final String KEY2_HEX_PLII_MUSIC = "dolby pl m";
private static final String KEY1_HEX_PLII_GAME = "prologic gam";
private static final String KEY2_HEX_PLII_GAME = "dolby pl g";
private static final String KEY1_HEX_PLIIX_CINEMA = "pl x cinema";
private static final String KEY2_HEX_PLIIX_CINEMA = "dolby pl x c";
private static final String KEY1_HEX_PLIIX_MUSIC = "pl x music";
private static final String KEY2_HEX_PLIIX_MUSIC = "dolby pl x m";
private static final String KEY1_HEX_PLIIX_GAME = "pl x game";
private static final String KEY2_HEX_PLIIX_GAME = "dolby pl x g";
private static final String KEY_HEX_PLIIZ = "dolby pl z";
private static final String KEY1_HEX_DTS_NEO6_CINEMA = "neo 6 cinema";
private static final String KEY2_HEX_DTS_NEO6_CINEMA = "dts neo:6 c";
private static final String KEY1_HEX_DTS_NEO6_MUSIC = "neo 6 music";
private static final String KEY2_HEX_DTS_NEO6_MUSIC = "dts neo:6 m";
private static final String KEY_HEX_DTS = "dts";
private static final String KEY_HEX_DTS_ES = "dts-es";
private static final String KEY_HEX_DTS_96 = "dts 96";
private static final String KEY_HEX_DD = "dolby digital";
private static final String KEY_HEX_DD_EX = "dolby d ex";
private static final String KEY_HEX_PCM = "pcm";
private static final String KEY_HEX_LPCM = "lpcm";
private static final String KEY_HEX_MPEG = "mpeg";
private static final String KEY_HEX_BYPASS = "bypass";
private static final String KEY1_HEX_ZONE2 = "zone ";
private static final String KEY2_HEX_ZONE2 = "zone2 ";
private static final String KEY_HEX_ZONE3 = "zone3 ";
private static final String KEY_HEX_ZONE4 = "zone4 ";
private static final String KEY_HEX_RECORD = "rec ";
private static final String SOURCE = "source";
private final Logger logger = LoggerFactory.getLogger(RotelHexProtocolHandler.class);
private final Map<RotelSource, String> sourcesLabels;
private final int size;
private final byte[] dataBuffer;
private boolean startCodeReached;
private int count;
private int index;
/**
* Constructor
*
* @param model the Rotel model in use
* @param sourcesLabels the custom labels for sources
*/
public RotelHexProtocolHandler(RotelModel model, Map<RotelSource, String> sourcesLabels) {
super(model);
this.sourcesLabels = sourcesLabels;
this.size = (6 + model.getRespNbChars() + model.getRespNbFlags());
this.dataBuffer = new byte[size];
this.startCodeReached = false;
this.count = 0;
this.index = 0;
}
@Override
public RotelProtocol getProtocol() {
return RotelProtocol.HEX;
}
@Override
public byte[] buildCommandMessage(RotelCommand cmd, @Nullable Integer value) throws RotelException {
if (cmd.getHexType() == 0) {
throw new RotelException("Command \"" + cmd.getName() + "\" ignored: not available for HEX protocol");
}
final int size = 6;
byte[] message = new byte[size];
int idx = 0;
message[idx++] = START;
message[idx++] = 3;
message[idx++] = model.getDeviceId();
message[idx++] = cmd.getHexType();
message[idx++] = (value == null) ? cmd.getHexKey() : (byte) (value & 0x000000FF);
final byte checksum = computeCheckSum(message, idx - 1);
if ((checksum & 0x000000FF) == 0x000000FD || (checksum & 0x000000FF) == 0x000000FE) {
message = Arrays.copyOf(message, size + 1);
message[idx++] = (byte) 0xFD;
message[idx++] = ((checksum & 0x000000FF) == 0x000000FD) ? (byte) 0 : (byte) 1;
} else {
message[idx++] = checksum;
}
logger.debug("Command \"{}\" => {}", cmd.getName(), HexUtils.bytesToHex(message));
return message;
}
@Override
public void handleIncomingData(byte[] inDataBuffer, int length) {
for (int i = 0; i < length; i++) {
if (inDataBuffer[i] == RotelHexProtocolHandler.START) {
startCodeReached = true;
count = 0;
index = 0;
}
if (startCodeReached) {
if (index < size) {
dataBuffer[index++] = inDataBuffer[i];
}
if (index == 2) {
count = inDataBuffer[i];
} else if ((count > 0) && (index == (count + 3))) {
if ((inDataBuffer[i] & 0x000000FF) == 0x000000FD) {
count++;
} else {
byte[] msg = Arrays.copyOf(dataBuffer, index);
handleIncomingMessage(msg);
startCodeReached = false;
}
}
}
}
}
/**
* Validate the content of a feedback message
*
* @param responseMessage the buffer containing the feedback message
*
* @throws RotelException - If the message has unexpected content
*/
@Override
protected void validateResponse(byte[] responseMessage) throws RotelException {
// Check minimum message length
if (responseMessage.length < 6) {
logger.debug("Unexpected message length: {}", responseMessage.length);
throw new RotelException("Unexpected message length");
}
// Check START
if (responseMessage[0] != START) {
logger.debug("Unexpected START in response: {} rather than {}",
Integer.toHexString(responseMessage[0] & 0x000000FF), Integer.toHexString(START & 0x000000FF));
throw new RotelException("Unexpected START in response");
}
// Check ID
if (responseMessage[2] != model.getDeviceId()) {
logger.debug("Unexpected ID in response: {} rather than {}",
Integer.toHexString(responseMessage[2] & 0x000000FF),
Integer.toHexString(model.getDeviceId() & 0x000000FF));
throw new RotelException("Unexpected ID in response");
}
// Check TYPE
if (responseMessage[3] != STANDARD_RESPONSE && responseMessage[3] != TRIGGER_STATUS
&& responseMessage[3] != SMART_DISPLAY_DATA_1 && responseMessage[3] != SMART_DISPLAY_DATA_2
&& responseMessage[3] != PRIMARY_CMD && responseMessage[3] != MAIN_ZONE_CMD
&& responseMessage[3] != RECORD_SRC_CMD && responseMessage[3] != ZONE2_CMD
&& responseMessage[3] != ZONE3_CMD && responseMessage[3] != ZONE4_CMD
&& responseMessage[3] != VOLUME_CMD && responseMessage[3] != ZONE2_VOLUME_CMD
&& responseMessage[3] != ZONE3_VOLUME_CMD && responseMessage[3] != ZONE4_VOLUME_CMD
&& responseMessage[3] != TRIGGER_CMD) {
logger.debug("Unexpected TYPE in response: {}", Integer.toHexString(responseMessage[3] & 0x000000FF));
throw new RotelException("Unexpected TYPE in response");
}
int expectedLen = (responseMessage[3] == STANDARD_RESPONSE)
? (5 + model.getRespNbChars() + model.getRespNbFlags())
: responseMessage.length;
// Check COUNT
if (responseMessage[1] != (expectedLen - 3)) {
logger.debug("Unexpected COUNT in response: {} rather than {}",
Integer.toHexString(responseMessage[1] & 0x000000FF),
Integer.toHexString((expectedLen - 3) & 0x000000FF));
throw new RotelException("Unexpected COUNT in response");
}
final byte checksum = computeCheckSum(responseMessage, expectedLen - 2);
if ((checksum & 0x000000FF) == 0x000000FD || (checksum & 0x000000FF) == 0x000000FE) {
expectedLen++;
}
// Check message length
if (responseMessage.length != expectedLen) {
logger.debug("Unexpected message length: {} rather than {}", responseMessage.length, expectedLen);
throw new RotelException("Unexpected message length");
}
// Check sum
if ((checksum & 0x000000FF) == 0x000000FD) {
if ((responseMessage[responseMessage.length - 2] & 0x000000FF) != 0x000000FD
|| (responseMessage[responseMessage.length - 1] & 0x000000FF) != 0) {
logger.debug("Invalid check sum in response: {} rather than FD00", HexUtils.bytesToHex(
Arrays.copyOfRange(responseMessage, responseMessage.length - 2, responseMessage.length)));
throw new RotelException("Invalid check sum in response");
}
} else if ((checksum & 0x000000FF) == 0x000000FE) {
if ((responseMessage[responseMessage.length - 2] & 0x000000FF) != 0x000000FD
|| (responseMessage[responseMessage.length - 1] & 0x000000FF) != 1) {
logger.debug("Invalid check sum in response: {} rather than FD01", HexUtils.bytesToHex(
Arrays.copyOfRange(responseMessage, responseMessage.length - 2, responseMessage.length)));
throw new RotelException("Invalid check sum in response");
}
} else if ((checksum & 0x000000FF) != (responseMessage[responseMessage.length - 1] & 0x000000FF)) {
logger.debug("Invalid check sum in response: {} rather than {}",
Integer.toHexString(responseMessage[responseMessage.length - 1] & 0x000000FF),
Integer.toHexString(checksum & 0x000000FF));
throw new RotelException("Invalid check sum in response");
}
}
/**
* Compute the checksum of a message
*
* @param message the buffer containing the message
* @param maxIdx the position in the buffer at which the sum has to be stopped
*
* @return the checksum as a byte
*/
public static byte computeCheckSum(byte[] message, int maxIdx) {
int result = 0;
for (int i = 1; i <= maxIdx; i++) {
result += (message[i] & 0x000000FF);
}
return (byte) (result & 0x000000FF);
}
/**
* Analyze a valid HEX message and dispatch corresponding (key, value) to the event listeners
*
* @param incomingMessage the received message
*/
@Override
protected void handleValidMessage(byte[] incomingMessage) {
if (incomingMessage[3] != STANDARD_RESPONSE) {
return;
}
final int idxChars = model.isCharsBeforeFlags() ? 4 : (4 + model.getRespNbFlags());
// Replace characters with code < 32 by a space before converting to a string
for (int i = idxChars; i < (idxChars + model.getRespNbChars()); i++) {
if (incomingMessage[i] < 0x20) {
incomingMessage[i] = 0x20;
}
}
String value = new String(incomingMessage, idxChars, model.getRespNbChars(), StandardCharsets.US_ASCII);
logger.debug("handleValidHexMessage: chars *{}*", value);
final int idxFlags = model.isCharsBeforeFlags() ? (4 + model.getRespNbChars()) : 4;
final byte[] flags = Arrays.copyOfRange(incomingMessage, idxFlags, idxFlags + model.getRespNbFlags());
if (logger.isTraceEnabled()) {
for (int i = 1; i <= flags.length; i++) {
try {
logger.trace("handleValidHexMessage: Flag {} = {} bits 7-0 = {} {} {} {} {} {} {} {}", i,
Integer.toHexString(flags[i - 1] & 0x000000FF), RotelFlagsMapping.isBitFlagOn(flags, i, 7),
RotelFlagsMapping.isBitFlagOn(flags, i, 6), RotelFlagsMapping.isBitFlagOn(flags, i, 5),
RotelFlagsMapping.isBitFlagOn(flags, i, 4), RotelFlagsMapping.isBitFlagOn(flags, i, 3),
RotelFlagsMapping.isBitFlagOn(flags, i, 2), RotelFlagsMapping.isBitFlagOn(flags, i, 1),
RotelFlagsMapping.isBitFlagOn(flags, i, 0));
} catch (RotelException e1) {
}
}
}
try {
dispatchKeyValue(KEY_POWER_ZONE2, model.isZone2On(flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Can't get zone power information from flags data, so we just do not notify of this information that way
}
try {
dispatchKeyValue(KEY_POWER_ZONE3, model.isZone3On(flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Can't get zone power information from flags data, so we just do not notify of this information that way
}
try {
dispatchKeyValue(KEY_POWER_ZONE4, model.isZone4On(flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Can't get zone power information from flags data, so we just do not notify of this information that way
}
boolean checkMultiIn = false;
boolean checkSource = true;
try {
if (model.isMultiInputOn(flags)) {
checkSource = false;
try {
RotelSource source = model.getSourceFromName(RotelSource.CAT1_MULTI.getName());
RotelCommand cmd = source.getCommand();
if (cmd != null) {
String value2 = cmd.getAsciiCommandV2();
if (value2 != null) {
dispatchKeyValue(KEY_SOURCE, value2);
}
}
} catch (RotelException e1) {
// MULTI source not declared for the model (should not happen), we do not notify of this source
}
}
} catch (RotelException e1) {
// Can't get status of multiple input source from flags data, checkMultiIn is set to true to get this
// information in another way
checkMultiIn = true;
}
boolean checkStereo = true;
try {
checkStereo = !model.isMoreThan2Channels(flags);
} catch (RotelException e1) {
// Can't get stereo information from flags data, checkStereo is set to true to get this information in
// another way
}
String valueLowerCase = value.trim().toLowerCase();
if (!valueLowerCase.isEmpty() && !valueLowerCase.startsWith(KEY1_HEX_ZONE2)
&& !valueLowerCase.startsWith(KEY2_HEX_ZONE2) && !valueLowerCase.startsWith(KEY_HEX_ZONE3)
&& !valueLowerCase.startsWith(KEY_HEX_ZONE4)) {
dispatchKeyValue(KEY_POWER, POWER_ON);
}
if (model.getRespNbChars() == 42) {
// 2 lines of 21 characters with a left part and a right part
// Line 1 left
value = new String(incomingMessage, idxChars, 14, StandardCharsets.US_ASCII);
logger.debug("handleValidHexMessage: line 1 left *{}*", value);
parseText(value, checkSource, checkMultiIn, false, false, false, false, false, true);
// Line 1 right
value = new String(incomingMessage, idxChars + 14, 7, StandardCharsets.US_ASCII);
logger.debug("handleValidHexMessage: line 1 right *{}*", value);
parseText(value, false, false, false, false, false, false, false, true);
// Full line 1
value = new String(incomingMessage, idxChars, 21, StandardCharsets.US_ASCII);
dispatchKeyValue(KEY_LINE1, value);
// Line 2 right
value = new String(incomingMessage, idxChars + 35, 7, StandardCharsets.US_ASCII);
logger.debug("handleValidHexMessage: line 2 right *{}*", value);
parseText(value, false, false, false, false, false, false, false, true);
// Full line 2
value = new String(incomingMessage, idxChars + 21, 21, StandardCharsets.US_ASCII);
logger.debug("handleValidHexMessage: line 2 *{}*", value);
parseText(value, false, false, true, true, false, true, true, true);
dispatchKeyValue(KEY_LINE2, value);
} else {
value = new String(incomingMessage, idxChars, model.getRespNbChars(), StandardCharsets.US_ASCII);
parseText(value, checkSource, checkMultiIn, true, false, true, true, checkStereo, false);
dispatchKeyValue(KEY_LINE1, value);
}
if (valueLowerCase.isEmpty()) {
dispatchKeyValue(KEY_POWER, POWER_OFF_DELAYED);
}
}
/**
* Parse a text and dispatch appropriate (key, value) to the event listeners for found information
*
* @param text the text to be parsed
* @param searchSource true if a source has to be searched in the text
* @param searchMultiIn true if MULTI IN indication has to be searched in the text
* @param searchZone true if a zone information has to be searched in the text
* @param searchRecord true if a record source has to be searched in the text
* @param searchRecordAfterSource true if a record source has to be searched in the text after the a found source
* @param searchDsp true if a DSP mode has to be searched in the text
* @param searchStereo true if a STEREO has to be considered in the search
* @param multipleInfo true if source and volume/mute are provided separately
*/
private void parseText(String text, boolean searchSource, boolean searchMultiIn, boolean searchZone,
boolean searchRecord, boolean searchRecordAfterSource, boolean searchDsp, boolean searchStereo,
boolean multipleInfo) {
String value = text.trim();
String valueLowerCase = value.toLowerCase();
if (searchRecord) {
dispatchKeyValue(KEY_RECORD_SEL, valueLowerCase.startsWith(KEY_HEX_RECORD) ? MSG_VALUE_ON : MSG_VALUE_OFF);
}
if (searchZone) {
if (valueLowerCase.startsWith(KEY1_HEX_ZONE2) || valueLowerCase.startsWith(KEY2_HEX_ZONE2)) {
dispatchKeyValue(KEY_ZONE, "2");
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE3)) {
dispatchKeyValue(KEY_ZONE, "3");
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE4)) {
dispatchKeyValue(KEY_ZONE, "4");
} else {
dispatchKeyValue(KEY_ZONE, "1");
}
}
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_VOLUME) ? KEY1_HEX_VOLUME.length() : KEY2_HEX_VOLUME.length());
dispatchKeyValue(KEY_VOLUME, value);
dispatchKeyValue(KEY_MUTE, MSG_VALUE_OFF);
} else if (valueLowerCase.startsWith(KEY_HEX_MUTE)) {
value = value.substring(KEY_HEX_MUTE.length()).trim();
if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
dispatchKeyValue(KEY_MUTE, MSG_VALUE_ON);
} else {
logger.debug("Invalid value {} for zone mute", value);
}
} else if (valueLowerCase.startsWith(KEY1_HEX_BASS) || valueLowerCase.startsWith(KEY2_HEX_BASS)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_BASS) ? KEY1_HEX_BASS.length() : KEY2_HEX_BASS.length());
dispatchKeyValue(KEY_BASS, value);
} else if (valueLowerCase.startsWith(KEY1_HEX_TREBLE) || valueLowerCase.startsWith(KEY2_HEX_TREBLE)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_TREBLE) ? KEY1_HEX_TREBLE.length() : KEY2_HEX_TREBLE.length());
dispatchKeyValue(KEY_TREBLE, value);
} else if (searchMultiIn && valueLowerCase.startsWith(KEY_HEX_MULTI_IN)) {
value = value.substring(KEY_HEX_MULTI_IN.length()).trim();
if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
try {
RotelSource source = model.getSourceFromName(RotelSource.CAT1_MULTI.getName());
RotelCommand cmd = source.getCommand();
if (cmd != null) {
String value2 = cmd.getAsciiCommandV2();
if (value2 != null) {
dispatchKeyValue(KEY_SOURCE, value2);
}
}
} catch (RotelException e1) {
// MULTI source not declared for the model (should not happen), we do not notify of this source
}
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
logger.debug("Invalid value {} for MULTI IN", value);
}
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_BYPASS)) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_BYPASS.getFeedback());
} else if (searchDsp && searchStereo && valueLowerCase.startsWith(KEY_HEX_STEREO)) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_3CH) || valueLowerCase.startsWith(KEY2_HEX_3CH))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_STEREO3.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_5CH)) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_STEREO5.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_7CH)) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_STEREO7.getFeedback());
} else if (searchDsp
&& (valueLowerCase.startsWith(KEY_HEX_MUSIC1) || valueLowerCase.startsWith(KEY_HEX_DSP1))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_DSP1.getFeedback());
} else if (searchDsp
&& (valueLowerCase.startsWith(KEY_HEX_MUSIC2) || valueLowerCase.startsWith(KEY_HEX_DSP2))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_DSP2.getFeedback());
} else if (searchDsp
&& (valueLowerCase.startsWith(KEY_HEX_MUSIC3) || valueLowerCase.startsWith(KEY_HEX_DSP3))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_DSP3.getFeedback());
} else if (searchDsp
&& (valueLowerCase.startsWith(KEY_HEX_MUSIC4) || valueLowerCase.startsWith(KEY_HEX_DSP4))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_DSP4.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_PLII_CINEMA)
|| valueLowerCase.startsWith(KEY2_HEX_PLII_CINEMA) || valueLowerCase.startsWith(KEY1_HEX_PLIIX_CINEMA)
|| searchDsp && valueLowerCase.startsWith(KEY2_HEX_PLIIX_CINEMA))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT2_PLII_CINEMA.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_PLII_MUSIC)
|| valueLowerCase.startsWith(KEY2_HEX_PLII_MUSIC) || valueLowerCase.startsWith(KEY1_HEX_PLIIX_MUSIC)
|| valueLowerCase.startsWith(KEY2_HEX_PLIIX_MUSIC))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT2_PLII_MUSIC.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_PLII_GAME)
|| valueLowerCase.startsWith(KEY2_HEX_PLII_GAME) || valueLowerCase.startsWith(KEY1_HEX_PLIIX_GAME)
|| valueLowerCase.startsWith(KEY2_HEX_PLIIX_GAME))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT2_PLII_GAME.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_PLIIZ)) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_PLIIZ.getFeedback());
} else if (searchDsp
&& (valueLowerCase.startsWith(KEY1_HEX_PROLOGIC) || valueLowerCase.startsWith(KEY2_HEX_PROLOGIC))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_PROLOGIC.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_DTS_NEO6_CINEMA)
|| valueLowerCase.startsWith(KEY2_HEX_DTS_NEO6_CINEMA))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NEO6_CINEMA.getFeedback());
} else if (searchDsp && (valueLowerCase.startsWith(KEY1_HEX_DTS_NEO6_MUSIC)
|| valueLowerCase.startsWith(KEY2_HEX_DTS_NEO6_MUSIC))) {
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NEO6_MUSIC.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_DTS_ES)) {
logger.debug("DTS-ES");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_DTS_96)) {
logger.debug("DTS 96");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_DTS)) {
logger.debug("DTS");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_DD_EX)) {
logger.debug("DD-EX");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_DD)) {
logger.debug("DD");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_LPCM)) {
logger.debug("LPCM");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_PCM)) {
logger.debug("PCM");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchDsp && valueLowerCase.startsWith(KEY_HEX_MPEG)) {
logger.debug("MPEG");
dispatchKeyValue(KEY_DSP_MODE, RotelDsp.CAT4_NONE.getFeedback());
} else if (searchZone
&& (valueLowerCase.startsWith(KEY1_HEX_ZONE2) || valueLowerCase.startsWith(KEY2_HEX_ZONE2))) {
value = value.substring(
valueLowerCase.startsWith(KEY1_HEX_ZONE2) ? KEY1_HEX_ZONE2.length() : KEY2_HEX_ZONE2.length());
parseZone2(value, multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY_HEX_ZONE3)) {
parseZone3(value.substring(KEY_HEX_ZONE3.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY_HEX_ZONE4)) {
parseZone4(value.substring(KEY_HEX_ZONE4.length()), multipleInfo);
} else if (searchRecord && valueLowerCase.startsWith(KEY_HEX_RECORD)) {
parseRecord(value.substring(KEY_HEX_RECORD.length()));
} else if (searchSource || searchRecordAfterSource) {
parseSourceAndRecord(value, searchSource, searchRecordAfterSource, multipleInfo);
}
}
/**
* Parse a text to identify a source
*
* @param text the text to be parsed
* @param acceptFollowMain true if follow main has to be considered in the search
*
* @return the identified source or null if no source is identified in the text
*/
private @Nullable RotelSource parseSource(String text, boolean acceptFollowMain) {
String value = text.trim();
RotelSource source = null;
if (!value.isEmpty()) {
if (acceptFollowMain && SOURCE.equalsIgnoreCase(value)) {
try {
source = model.getSourceFromName(RotelSource.CAT1_FOLLOW_MAIN.getName());
} catch (RotelException e) {
// MAIN (follow main zone source) source not declared for the model, we return null
}
} else {
for (RotelSource src : sourcesLabels.keySet()) {
String label = sourcesLabels.get(src);
if (label != null && value.startsWith(label)) {
if (source == null || sourcesLabels.get(source).length() < label.length()) {
source = src;
}
}
}
}
}
return source;
}
private void parseSourceAndRecord(String text, boolean searchSource, boolean searchRecordAfterSource,
boolean multipleInfo) {
RotelSource source = parseSource(text, false);
if (source != null) {
if (searchSource) {
RotelCommand cmd = source.getCommand();
if (cmd != null) {
String value2 = cmd.getAsciiCommandV2();
if (value2 != null) {
dispatchKeyValue(KEY_SOURCE, value2);
if (!multipleInfo) {
dispatchKeyValue(KEY_MUTE, MSG_VALUE_OFF);
}
}
}
}
if (searchRecordAfterSource) {
String value = text.substring(getSourceLabel(source).length()).trim();
source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getRecordCommand();
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
dispatchKeyValue(KEY_RECORD, value);
}
}
}
}
}
}
private String getSourceLabel(RotelSource source) {
String label = sourcesLabels.get(source);
return (label == null) ? source.getLabel() : label;
}
private void parseRecord(String text) {
String value = text.trim();
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getRecordCommand();
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
dispatchKeyValue(KEY_RECORD, value);
}
}
} else {
logger.debug("Invalid value {} for record source", value);
}
}
private void parseZone2(String text, boolean multipleInfo) {
String value = text.trim();
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_VOLUME) ? KEY1_HEX_VOLUME.length() : KEY2_HEX_VOLUME.length());
dispatchKeyValue(KEY_VOLUME_ZONE2, value);
dispatchKeyValue(KEY_MUTE_ZONE2, MSG_VALUE_OFF);
} else if (valueLowerCase.startsWith(KEY_HEX_MUTE)) {
value = value.substring(KEY_HEX_MUTE.length()).trim();
if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
dispatchKeyValue(KEY_MUTE_ZONE2, MSG_VALUE_ON);
} else {
logger.debug("Invalid value {} for zone mute", value);
}
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone2Command();
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
dispatchKeyValue(KEY_SOURCE_ZONE2, value);
if (!multipleInfo) {
dispatchKeyValue(KEY_MUTE_ZONE2, MSG_VALUE_OFF);
}
}
}
} else {
logger.debug("Invalid value {} for zone 2 source", value);
}
}
}
private void parseZone3(String text, boolean multipleInfo) {
String value = text.trim();
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_VOLUME) ? KEY1_HEX_VOLUME.length() : KEY2_HEX_VOLUME.length());
dispatchKeyValue(KEY_VOLUME_ZONE3, value);
dispatchKeyValue(KEY_MUTE_ZONE3, MSG_VALUE_OFF);
} else if (valueLowerCase.startsWith(KEY_HEX_MUTE)) {
value = value.substring(KEY_HEX_MUTE.length()).trim();
if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
dispatchKeyValue(KEY_MUTE_ZONE3, MSG_VALUE_ON);
} else {
logger.debug("Invalid value {} for zone mute", value);
}
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone3Command();
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
dispatchKeyValue(KEY_SOURCE_ZONE3, value);
if (!multipleInfo) {
dispatchKeyValue(KEY_MUTE_ZONE3, MSG_VALUE_OFF);
}
}
}
} else {
logger.debug("Invalid value {} for zone 3 source", value);
}
}
}
private void parseZone4(String text, boolean multipleInfo) {
String value = text.trim();
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
valueLowerCase.startsWith(KEY1_HEX_VOLUME) ? KEY1_HEX_VOLUME.length() : KEY2_HEX_VOLUME.length());
dispatchKeyValue(KEY_VOLUME_ZONE4, value);
dispatchKeyValue(KEY_MUTE_ZONE4, MSG_VALUE_OFF);
} else if (valueLowerCase.startsWith(KEY_HEX_MUTE)) {
value = value.substring(KEY_HEX_MUTE.length()).trim();
if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
dispatchKeyValue(KEY_MUTE_ZONE4, MSG_VALUE_ON);
} else {
logger.debug("Invalid value {} for zone mute", value);
}
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone4Command();
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
dispatchKeyValue(KEY_SOURCE_ZONE4, value);
if (!multipleInfo) {
dispatchKeyValue(KEY_MUTE_ZONE4, MSG_VALUE_OFF);
}
}
}
} else {
logger.debug("Invalid value {} for zone 4 source", value);
}
}
}
/**
* Extract from a string a number
*
* @param value the string
* @param startIndex the index in the string at which the integer has to be extracted
*
* @return the number as a string with its sign and no blank between the sign and the digits
*/
private String extractNumber(String value, int startIndex) {
String result = value.substring(startIndex).trim();
// Delete possible blank(s) between the sign and the number
if (result.startsWith("+") || result.startsWith("-")) {
result = result.substring(0, 1) + result.substring(1, result.length()).trim();
}
return result;
}
}