// List of all properties
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";
}
*/
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.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
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.RotelFlagsMapping;
import org.openhab.binding.rotel.internal.communication.RotelSource;
@NonNullByDefault
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),
+ RSP1068("RSP-1068", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xA1, 42, 5, true,
+ RotelFlagsMapping.MAPPING2),
+ RSP1069("RSP-1069", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xA2, 42, 5, true,
+ RotelFlagsMapping.MAPPING5),
+ RSP1098("RSP-1098", 19200, 1, 1, true, 96, true, 6, false, ZONE_SELECT, 2, (byte) 0xA0, 13, 8, true,
RotelFlagsMapping.MAPPING1),
- RSP1068("RSP-1068", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xA1, 42,
- 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,
+ RSP1570("RSP-1570", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xA3, 42, 5, true,
+ RotelFlagsMapping.MAPPING5),
+ RSP1572("RSP-1572", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4, (byte) 0xA5, 42, 5, true,
+ RotelFlagsMapping.MAPPING5),
+ RSX1055("RSX-1055", 19200, 3, 1, false, 90, false, 12, false, ZONE_SELECT, 1, (byte) 0xC3, 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,
+ RSX1056("RSX-1056", 19200, 1, 1, true, 96, true, 12, false, ZONE_SELECT, 2, (byte) 0xC5, 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,
+ RSX1057("RSX-1057", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC7, 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,
+ RSX1058("RSX-1058", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC8, 13, 8, true,
+ RotelFlagsMapping.MAPPING4),
+ RSX1065("RSX-1065", 19200, 3, 1, false, 96, false, 12, false, ZONE_SELECT, 1, (byte) 0xC1, 42, 5, true,
+ RotelFlagsMapping.MAPPING2),
+ RSX1067("RSX-1067", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2, (byte) 0xC4, 42, 5, true,
RotelFlagsMapping.MAPPING2),
- RSX1067("RSX-1067", 19200, 1, 1, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 2, (byte) 0xC4, 42,
- 5, true, RotelFlagsMapping.MAPPING2),
- RSX1550("RSX-1550", 115200, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 3, (byte) 0xC9, 13,
- 8, true, RotelFlagsMapping.MAPPING3),
- RSX1560("RSX-1560", 115200, 1, 3, true, 96, true, 6, false, RotelCommand.RECORD_FONCTION_SELECT, 3, (byte) 0xCA, 42,
- 5, true, RotelFlagsMapping.MAPPING5),
- RSX1562("RSX-1562", 115200, 2, 3, true, 96, true, null, false, RotelCommand.RECORD_FONCTION_SELECT, 4, (byte) 0xCC,
- 42, 5, true, RotelFlagsMapping.MAPPING5),
- A11("A11", 115200, 4, 96, true, 10, 15, false, -1, false, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- A12("A12", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- A14("A14", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- CD11("CD11", 57600, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- CD14("CD14", 57600, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- RA11("RA-11", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, RotelConnector.SPECIAL_CHARACTERS),
- RA12("RA-12", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, RotelConnector.SPECIAL_CHARACTERS),
- RA1570("RA-1570", 115200, 7, 96, true, 10, 15, true, -1, true, true, false, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RA1572("RA-1572", 115200, 8, 96, true, 10, 15, false, -1, true, true, true, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RA1592("RA-1592", 115200, 9, 96, true, 10, 15, false, -1, true, true, true, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RAP1580("RAP-1580", 115200, 11, 96, true, null, false, 5, false, false, -10, 10,
- RotelConnector.NO_SPECIAL_CHARACTERS),
- RC1570("RC-1570", 115200, 7, 96, true, 10, 15, true, -1, true, false, false, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RC1572("RC-1572", 115200, 8, 96, true, 10, 15, false, -1, true, false, true, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RC1590("RC-1590", 115200, 9, 96, true, 10, 15, false, -1, true, false, true, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS),
- RCD1570("RCD-1570", 115200, 0, null, false, null, true, -1, false, true, 6, 0, RotelConnector.SPECIAL_CHARACTERS),
- RCD1572("RCD-1572", 57600, 0, null, false, null, true, -1, false, true, 6, 0,
- RotelConnector.SPECIAL_CHARACTERS_RCD1572),
- RCX1500("RCX-1500", 115200, 17, 86, true, null, true, -1, false, false, null, null,
- RotelConnector.SPECIAL_CHARACTERS),
- RDD1580("RDD-1580", 115200, 15, null, false, null, true, -1, true, false, null, null,
- RotelConnector.NO_SPECIAL_CHARACTERS),
- RDG1520("RDG-1520", 115200, 16, null, false, null, true, -1, false, false, null, null,
- RotelConnector.SPECIAL_CHARACTERS),
- RSP1576("RSP-1576", 115200, 10, 96, true, null, false, 5, false, false, -10, 10,
- RotelConnector.NO_SPECIAL_CHARACTERS),
- RSP1582("RSP-1582", 115200, 11, 96, true, null, false, 6, false, false, -10, 10,
- RotelConnector.NO_SPECIAL_CHARACTERS),
- RT11("RT-11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- RT1570("RT-1570", 115200, 14, null, false, null, false, -1, false, true, 6, 0,
- RotelConnector.NO_SPECIAL_CHARACTERS),
- T11("T11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- T14("T14", 115200, 13, null, false, null, false, -1, false, true, 6, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- P5("P5", 115200, 20, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- X3("X3", 115200, 18, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS),
- X5("X5", 115200, 19, 96, true, 10, 10, false, -1, true, false, true, 4, 0, RotelConnector.NO_SPECIAL_CHARACTERS);
+ RSX1550("RSX-1550", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xC9, 13, 8, true,
+ RotelFlagsMapping.MAPPING3),
+ RSX1560("RSX-1560", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3, (byte) 0xCA, 42, 5, true,
+ RotelFlagsMapping.MAPPING5),
+ RSX1562("RSX-1562", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4, (byte) 0xCC, 42, 5, true,
+ RotelFlagsMapping.MAPPING5),
+ A11("A11", 115200, 4, 96, true, 10, 15, false, -1, false, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ A12("A12", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ A14("A14", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ CD11("CD11", 57600, 0, null, false, null, true, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ CD14("CD14", 57600, 0, null, false, null, true, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ RA11("RA-11", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
+ RA12("RA-12", 115200, 6, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
+ RA1570("RA-1570", 115200, 7, 96, true, 10, 15, true, -1, true, true, false, 6, 0, SPECIAL_CHARACTERS),
+ RA1572("RA-1572", 115200, 8, 96, true, 10, 15, false, -1, true, true, true, 6, 0, SPECIAL_CHARACTERS),
+ RA1592("RA-1592", 115200, 9, 96, true, 10, 15, false, -1, true, true, true, 6, 0, SPECIAL_CHARACTERS),
+ RAP1580("RAP-1580", 115200, 11, 96, true, null, false, 5, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
+ RC1570("RC-1570", 115200, 7, 96, true, 10, 15, true, -1, true, false, false, 6, 0, SPECIAL_CHARACTERS),
+ RC1572("RC-1572", 115200, 8, 96, true, 10, 15, false, -1, true, false, true, 6, 0, SPECIAL_CHARACTERS),
+ RC1590("RC-1590", 115200, 9, 96, true, 10, 15, false, -1, true, false, true, 6, 0, SPECIAL_CHARACTERS),
+ RCD1570("RCD-1570", 115200, 0, null, false, null, true, -1, false, true, 6, 0, SPECIAL_CHARACTERS),
+ RCD1572("RCD-1572", 57600, 0, null, false, null, true, -1, false, true, 6, 0, SPECIAL_CHARACTERS_RCD1572),
+ RCX1500("RCX-1500", 115200, 17, 86, true, null, true, -1, false, false, null, null, SPECIAL_CHARACTERS),
+ RDD1580("RDD-1580", 115200, 15, null, false, null, true, -1, true, false, null, null, NO_SPECIAL_CHARACTERS),
+ RDG1520("RDG-1520", 115200, 16, null, false, null, true, -1, false, false, null, null, SPECIAL_CHARACTERS),
+ RSP1576("RSP-1576", 115200, 10, 96, true, null, false, 5, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
+ RSP1582("RSP-1582", 115200, 11, 96, true, null, false, 6, false, false, -10, 10, NO_SPECIAL_CHARACTERS),
+ RT11("RT-11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, NO_SPECIAL_CHARACTERS),
+ RT1570("RT-1570", 115200, 14, null, false, null, false, -1, false, true, 6, 0, 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 int baudRate;
@Nullable Integer volumeMax, boolean directVolume, @Nullable Integer toneLevelMax, boolean playControl,
@Nullable RotelCommand zoneSelectCmd, int dspCategory, byte deviceId, int respNbChars, int respNbFlags,
boolean charsBeforeFlags, RotelFlagsMapping flagsMapping) {
- this(name, baudRate, RotelCommand.DISPLAY_REFRESH, sourceCategory, nbAdditionalZones, additionalCommands,
- volumeMax, directVolume, toneLevelMax, null, playControl, zoneSelectCmd, dspCategory, false, false,
- false, null, null, deviceId, respNbChars, respNbFlags, charsBeforeFlags, flagsMapping,
- RotelConnector.NO_SPECIAL_CHARACTERS);
+ this(name, baudRate, DISPLAY_REFRESH, sourceCategory, nbAdditionalZones, additionalCommands, volumeMax,
+ directVolume, toneLevelMax, null, playControl, zoneSelectCmd, dspCategory, false, false, false, null,
+ null, deviceId, respNbChars, respNbFlags, charsBeforeFlags, flagsMapping, NO_SPECIAL_CHARACTERS);
}
/**
@Nullable Integer toneLevelMax, boolean playControl, int dspCategory, boolean getFrequencyAvailable,
boolean getDimmerLevelAvailable, @Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax,
byte[][] specialCharacters) {
- this(name, baudRate, RotelCommand.POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, null,
- playControl, null, dspCategory, getFrequencyAvailable, false, getDimmerLevelAvailable, diummerLevelMin,
+ this(name, baudRate, POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, null, playControl,
+ null, dspCategory, getFrequencyAvailable, false, getDimmerLevelAvailable, diummerLevelMin,
diummerLevelMax, (byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, specialCharacters);
}
@Nullable Integer toneLevelMax, @Nullable Integer balanceLevelMax, boolean playControl, int dspCategory,
boolean getFrequencyAvailable, boolean getSpeakerGroupsAvailable, boolean getDimmerLevelAvailable,
@Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax, byte[][] specialCharacters) {
- this(name, baudRate, RotelCommand.POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax,
- balanceLevelMax, playControl, null, dspCategory, getFrequencyAvailable, getSpeakerGroupsAvailable,
+ this(name, baudRate, POWER, sourceCategory, 0, false, volumeMax, directVolume, toneLevelMax, balanceLevelMax,
+ playControl, null, dspCategory, getFrequencyAvailable, getSpeakerGroupsAvailable,
getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, (byte) 0, 0, 0, false,
RotelFlagsMapping.NO_MAPPING, specialCharacters);
}
*/
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.Nullable;
import org.openhab.binding.rotel.internal.RotelException;
@NonNullByDefault
public enum RotelCommand {
- POWER_TOGGLE("Power Toggle", RotelConnector.PRIMARY_CMD, (byte) 0x0A, "power_toggle", "power_toggle"),
- POWER_OFF("Power Off", RotelConnector.PRIMARY_CMD, (byte) 0x4A, "power_off", "power_off"),
- POWER_ON("Power On", RotelConnector.PRIMARY_CMD, (byte) 0x4B, "power_on", "power_on"),
+ POWER_TOGGLE("Power Toggle", PRIMARY_CMD, (byte) 0x0A, "power_toggle", "power_toggle"),
+ POWER_OFF("Power Off", PRIMARY_CMD, (byte) 0x4A, "power_off", "power_off"),
+ POWER_ON("Power On", PRIMARY_CMD, (byte) 0x4B, "power_on", "power_on"),
POWER("Request current power status", "get_current_power", "power?"),
- ZONE_SELECT("Zone Select", RotelConnector.PRIMARY_CMD, (byte) 0x23),
- MAIN_ZONE_POWER_TOGGLE("Main Zone Power Toggle", RotelConnector.MAIN_ZONE_CMD, (byte) 0x0A),
- MAIN_ZONE_POWER_OFF("Main Zone Power Off", RotelConnector.MAIN_ZONE_CMD, (byte) 0x4A),
- MAIN_ZONE_POWER_ON("Main Zone Power On", RotelConnector.MAIN_ZONE_CMD, (byte) 0x4B),
- ZONE2_POWER_TOGGLE("Zone 2 Power Toggle", RotelConnector.ZONE2_CMD, (byte) 0x0A),
- ZONE2_POWER_OFF("Zone 2 Power Off", RotelConnector.ZONE2_CMD, (byte) 0x4A),
- ZONE2_POWER_ON("Zone 2 Power On", RotelConnector.ZONE2_CMD, (byte) 0x4B),
- ZONE3_POWER_TOGGLE("Zone 3 Power Toggle", RotelConnector.ZONE3_CMD, (byte) 0x0A),
- ZONE3_POWER_OFF("Zone 3 Power Off", RotelConnector.ZONE3_CMD, (byte) 0x4A),
- ZONE3_POWER_ON("Zone 3 Power On", RotelConnector.ZONE3_CMD, (byte) 0x4B),
- ZONE4_POWER_TOGGLE("Zone 4 Power Toggle", RotelConnector.ZONE4_CMD, (byte) 0x0A),
- ZONE4_POWER_OFF("Zone 4 Power Off", RotelConnector.ZONE4_CMD, (byte) 0x4A),
- ZONE4_POWER_ON("Zone 4 Power On", RotelConnector.ZONE4_CMD, (byte) 0x4B),
- VOLUME_UP("Volume Up", RotelConnector.PRIMARY_CMD, (byte) 0x0B, "volume_up", "vol_up"),
- VOLUME_DOWN("Volume Down", RotelConnector.PRIMARY_CMD, (byte) 0x0C, "volume_down", "vol_dwn"),
- VOLUME_SET("Set Volume to level", RotelConnector.VOLUME_CMD, (byte) 0, "volume_", "vol_"),
+ ZONE_SELECT("Zone Select", PRIMARY_CMD, (byte) 0x23),
+ MAIN_ZONE_POWER_TOGGLE("Main Zone Power Toggle", MAIN_ZONE_CMD, (byte) 0x0A),
+ MAIN_ZONE_POWER_OFF("Main Zone Power Off", MAIN_ZONE_CMD, (byte) 0x4A),
+ MAIN_ZONE_POWER_ON("Main Zone Power On", MAIN_ZONE_CMD, (byte) 0x4B),
+ ZONE2_POWER_TOGGLE("Zone 2 Power Toggle", ZONE2_CMD, (byte) 0x0A),
+ ZONE2_POWER_OFF("Zone 2 Power Off", ZONE2_CMD, (byte) 0x4A),
+ ZONE2_POWER_ON("Zone 2 Power On", ZONE2_CMD, (byte) 0x4B),
+ ZONE3_POWER_TOGGLE("Zone 3 Power Toggle", ZONE3_CMD, (byte) 0x0A),
+ ZONE3_POWER_OFF("Zone 3 Power Off", ZONE3_CMD, (byte) 0x4A),
+ ZONE3_POWER_ON("Zone 3 Power On", ZONE3_CMD, (byte) 0x4B),
+ ZONE4_POWER_TOGGLE("Zone 4 Power Toggle", ZONE4_CMD, (byte) 0x0A),
+ ZONE4_POWER_OFF("Zone 4 Power Off", ZONE4_CMD, (byte) 0x4A),
+ ZONE4_POWER_ON("Zone 4 Power On", ZONE4_CMD, (byte) 0x4B),
+ VOLUME_UP("Volume Up", PRIMARY_CMD, (byte) 0x0B, "volume_up", "vol_up"),
+ VOLUME_DOWN("Volume Down", PRIMARY_CMD, (byte) 0x0C, "volume_down", "vol_dwn"),
+ VOLUME_SET("Set Volume to level", VOLUME_CMD, (byte) 0, "volume_", "vol_"),
VOLUME_GET("Request current volume level", "get_volume", "volume?"),
VOLUME_GET_MIN("Request Min volume level", "get_volume_min", 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_OFF("Mute Off", "mute_off", "mute_off"),
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_DOWN("Main Zone Volume Down", RotelConnector.MAIN_ZONE_CMD, (byte) 1),
- MAIN_ZONE_MUTE_TOGGLE("Main Zone Mute Toggle", RotelConnector.MAIN_ZONE_CMD, (byte) 0x1E),
- MAIN_ZONE_MUTE_ON("Main Zone Mute On", RotelConnector.MAIN_ZONE_CMD, (byte) 0x6C),
- MAIN_ZONE_MUTE_OFF("Main Zone Mute Off", RotelConnector.MAIN_ZONE_CMD, (byte) 0x6D),
- ZONE2_VOLUME_UP("Zone 2 Volume Up", RotelConnector.ZONE2_CMD, (byte) 0),
- ZONE2_VOLUME_DOWN("Zone 2 Volume Down", RotelConnector.ZONE2_CMD, (byte) 1),
- ZONE2_VOLUME_SET("Set Zone 2 Volume to level", RotelConnector.ZONE2_VOLUME_CMD, (byte) 0),
- ZONE2_MUTE_TOGGLE("Zone 2 Mute Toggle", RotelConnector.ZONE2_CMD, (byte) 0x1E),
- ZONE2_MUTE_ON("Zone 2 Mute On", RotelConnector.ZONE2_CMD, (byte) 0x6C),
- ZONE2_MUTE_OFF("Zone 2 Mute Off", RotelConnector.ZONE2_CMD, (byte) 0x6D),
- ZONE3_VOLUME_UP("Zone 3 Volume Up", RotelConnector.ZONE3_CMD, (byte) 0),
- ZONE3_VOLUME_DOWN("Zone 3 Volume Down", RotelConnector.ZONE3_CMD, (byte) 1),
- ZONE3_VOLUME_SET("Set Zone 3 Volume to level", RotelConnector.ZONE3_VOLUME_CMD, (byte) 0),
- ZONE3_MUTE_TOGGLE("Zone 3 Mute Toggle", RotelConnector.ZONE3_CMD, (byte) 0x1E),
- ZONE3_MUTE_ON("Zone 3 Mute On", RotelConnector.ZONE3_CMD, (byte) 0x6C),
- ZONE3_MUTE_OFF("Zone 3 Mute Off", RotelConnector.ZONE3_CMD, (byte) 0x6D),
- ZONE4_VOLUME_UP("Zone 4 Volume Up", RotelConnector.ZONE4_CMD, (byte) 0),
- ZONE4_VOLUME_DOWN("Zone 4 Volume Down", RotelConnector.ZONE4_CMD, (byte) 1),
- ZONE4_VOLUME_SET("Set Zone 4 Volume to level", RotelConnector.ZONE4_VOLUME_CMD, (byte) 0),
- ZONE4_MUTE_TOGGLE("Zone 4 Mute Toggle", RotelConnector.ZONE4_CMD, (byte) 0x1E),
- ZONE4_MUTE_ON("Zone 4 Mute On", RotelConnector.ZONE4_CMD, (byte) 0x6C),
- ZONE4_MUTE_OFF("Zone 4 Mute Off", RotelConnector.ZONE4_CMD, (byte) 0x6D),
- SOURCE_CD("Source CD", RotelConnector.PRIMARY_CMD, (byte) 0x02, "cd", "cd"),
- SOURCE_TUNER("Source Tuner", RotelConnector.PRIMARY_CMD, (byte) 0x03, "tuner", "tuner"),
- SOURCE_TAPE("Source Tape", RotelConnector.PRIMARY_CMD, (byte) 0x04, "tape", "tape"),
- SOURCE_VIDEO1("Source Video 1", RotelConnector.PRIMARY_CMD, (byte) 0x05, "video1", "video1"),
- SOURCE_VIDEO2("Source Video 2", RotelConnector.PRIMARY_CMD, (byte) 0x06, "video2", "video2"),
- SOURCE_VIDEO3("Source Video 3", RotelConnector.PRIMARY_CMD, (byte) 0x07, "video3", "video3"),
- SOURCE_VIDEO4("Source Video 4", RotelConnector.PRIMARY_CMD, (byte) 0x08, "video4", "video4"),
- SOURCE_VIDEO5("Source Video 5", RotelConnector.PRIMARY_CMD, (byte) 0x09, "video5", "video5"),
- SOURCE_VIDEO6("Source Video 6", RotelConnector.PRIMARY_CMD, (byte) 0x94, "video6", "video6"),
+ MAIN_ZONE_VOLUME_UP("Main Zone Volume Up", MAIN_ZONE_CMD, (byte) 0),
+ MAIN_ZONE_VOLUME_DOWN("Main Zone Volume Down", MAIN_ZONE_CMD, (byte) 1),
+ MAIN_ZONE_MUTE_TOGGLE("Main Zone Mute Toggle", MAIN_ZONE_CMD, (byte) 0x1E),
+ MAIN_ZONE_MUTE_ON("Main Zone Mute On", MAIN_ZONE_CMD, (byte) 0x6C),
+ MAIN_ZONE_MUTE_OFF("Main Zone Mute Off", MAIN_ZONE_CMD, (byte) 0x6D),
+ ZONE2_VOLUME_UP("Zone 2 Volume Up", ZONE2_CMD, (byte) 0),
+ ZONE2_VOLUME_DOWN("Zone 2 Volume Down", ZONE2_CMD, (byte) 1),
+ ZONE2_VOLUME_SET("Set Zone 2 Volume to level", ZONE2_VOLUME_CMD, (byte) 0),
+ ZONE2_MUTE_TOGGLE("Zone 2 Mute Toggle", ZONE2_CMD, (byte) 0x1E),
+ ZONE2_MUTE_ON("Zone 2 Mute On", ZONE2_CMD, (byte) 0x6C),
+ ZONE2_MUTE_OFF("Zone 2 Mute Off", ZONE2_CMD, (byte) 0x6D),
+ ZONE3_VOLUME_UP("Zone 3 Volume Up", ZONE3_CMD, (byte) 0),
+ ZONE3_VOLUME_DOWN("Zone 3 Volume Down", ZONE3_CMD, (byte) 1),
+ ZONE3_VOLUME_SET("Set Zone 3 Volume to level", ZONE3_VOLUME_CMD, (byte) 0),
+ ZONE3_MUTE_TOGGLE("Zone 3 Mute Toggle", ZONE3_CMD, (byte) 0x1E),
+ ZONE3_MUTE_ON("Zone 3 Mute On", ZONE3_CMD, (byte) 0x6C),
+ ZONE3_MUTE_OFF("Zone 3 Mute Off", ZONE3_CMD, (byte) 0x6D),
+ ZONE4_VOLUME_UP("Zone 4 Volume Up", ZONE4_CMD, (byte) 0),
+ ZONE4_VOLUME_DOWN("Zone 4 Volume Down", ZONE4_CMD, (byte) 1),
+ ZONE4_VOLUME_SET("Set Zone 4 Volume to level", ZONE4_VOLUME_CMD, (byte) 0),
+ ZONE4_MUTE_TOGGLE("Zone 4 Mute Toggle", ZONE4_CMD, (byte) 0x1E),
+ ZONE4_MUTE_ON("Zone 4 Mute On", ZONE4_CMD, (byte) 0x6C),
+ ZONE4_MUTE_OFF("Zone 4 Mute Off", ZONE4_CMD, (byte) 0x6D),
+ SOURCE_CD("Source CD", PRIMARY_CMD, (byte) 0x02, "cd", "cd"),
+ SOURCE_TUNER("Source Tuner", PRIMARY_CMD, (byte) 0x03, "tuner", "tuner"),
+ SOURCE_TAPE("Source Tape", PRIMARY_CMD, (byte) 0x04, "tape", "tape"),
+ SOURCE_VIDEO1("Source Video 1", PRIMARY_CMD, (byte) 0x05, "video1", "video1"),
+ SOURCE_VIDEO2("Source Video 2", PRIMARY_CMD, (byte) 0x06, "video2", "video2"),
+ SOURCE_VIDEO3("Source Video 3", PRIMARY_CMD, (byte) 0x07, "video3", "video3"),
+ SOURCE_VIDEO4("Source Video 4", PRIMARY_CMD, (byte) 0x08, "video4", "video4"),
+ SOURCE_VIDEO5("Source Video 5", PRIMARY_CMD, (byte) 0x09, "video5", "video5"),
+ SOURCE_VIDEO6("Source Video 6", PRIMARY_CMD, (byte) 0x94, "video6", "video6"),
SOURCE_VIDEO7("Source Video 7", "video7", "video7"),
SOURCE_VIDEO8("Source Video 8", "video8", "video8"),
- SOURCE_PHONO("Source Phono", RotelConnector.PRIMARY_CMD, (byte) 0x35, "phono", "phono"),
- SOURCE_USB("Source Front USB", RotelConnector.PRIMARY_CMD, (byte) 0x8E, "usb", "usb"),
+ SOURCE_PHONO("Source Phono", PRIMARY_CMD, (byte) 0x35, "phono", "phono"),
+ SOURCE_USB("Source Front USB", PRIMARY_CMD, (byte) 0x8E, "usb", "usb"),
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_AUX1("Source Aux 1", "aux1", "aux1"),
SOURCE_AUX2("Source Aux 2", "aux2", "aux2"),
SOURCE_IRADIO("Source iRadio", "iradio", "iradio"),
SOURCE_NETWORK("Source Network", "network", "network"),
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_cd"),
- MAIN_ZONE_SOURCE_TUNER("Main Zone Source Tuner", RotelConnector.MAIN_ZONE_CMD, (byte) 0x03, "main_zone_tuner",
- "main_zone_tuner"),
- 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_SOURCE_CD("Main Zone Source CD", MAIN_ZONE_CMD, (byte) 0x02, "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_TAPE("Main Zone Source Tape", MAIN_ZONE_CMD, (byte) 0x04, "main_zone_tape", "main_zone_tape"),
+ MAIN_ZONE_SOURCE_VIDEO1("Main Zone Source Video 1", MAIN_ZONE_CMD, (byte) 0x05, "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_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_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_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_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_SOURCE_USB("Main Zone Source Front USB", RotelConnector.MAIN_ZONE_CMD, (byte) 0x8E, "main_zone_usb",
- "main_zone_usb"),
- MAIN_ZONE_SOURCE_MULTI_INPUT("Main Zone Source Multi Input", RotelConnector.MAIN_ZONE_CMD, (byte) 0x15,
- "main_zone_multi_input", "main_zone_multi_input"),
- RECORD_SOURCE_CD("Record Source CD", RotelConnector.RECORD_SRC_CMD, (byte) 0x02, "record_cd", "record_cd"),
- RECORD_SOURCE_TUNER("Record Source Tuner", RotelConnector.RECORD_SRC_CMD, (byte) 0x03, "record_tuner",
- "record_tuner"),
- RECORD_SOURCE_TAPE("Record Source Tape", RotelConnector.RECORD_SRC_CMD, (byte) 0x04, "record_tape", "record_tape"),
- RECORD_SOURCE_VIDEO1("Record Source Video 1", RotelConnector.RECORD_SRC_CMD, (byte) 0x05, "record_video1",
- "record_video1"),
- RECORD_SOURCE_VIDEO2("Record Source Video 2", RotelConnector.RECORD_SRC_CMD, (byte) 0x06, "record_video2",
- "record_video2"),
- RECORD_SOURCE_VIDEO3("Record Source Video 3", RotelConnector.RECORD_SRC_CMD, (byte) 0x07, "record_video3",
- "record_video3"),
- RECORD_SOURCE_VIDEO4("Record Source Video 4", RotelConnector.RECORD_SRC_CMD, (byte) 0x08, "record_video4",
- "record_video4"),
- RECORD_SOURCE_VIDEO5("Record Source Video 5", RotelConnector.RECORD_SRC_CMD, (byte) 0x09, "record_video5",
- "record_video5"),
- RECORD_SOURCE_VIDEO6("Record Source Video 6", RotelConnector.RECORD_SRC_CMD, (byte) 0x94, "record_video6",
- "record_video6"),
- RECORD_SOURCE_USB("Record Source Front USB", RotelConnector.RECORD_SRC_CMD, (byte) 0x8E, "record_usb",
- "record_usb"),
- RECORD_SOURCE_MAIN("Record Follow Main Zone Source", RotelConnector.RECORD_SRC_CMD, (byte) 0x6B,
- "record_follow_main", "record_follow_main"),
- ZONE2_SOURCE_CD("Zone 2 Source CD", RotelConnector.ZONE2_CMD, (byte) 0x02, "zone2_cd", "zone2_cd"),
- ZONE2_SOURCE_TUNER("Zone 2 Source Tuner", RotelConnector.ZONE2_CMD, (byte) 0x03, "zone2_tuner", "zone2_tuner"),
- 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",
+ MAIN_ZONE_SOURCE_USB("Main Zone Source Front USB", MAIN_ZONE_CMD, (byte) 0x8E, "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_multi_input"),
+ RECORD_SOURCE_CD("Record Source CD", 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_TAPE("Record Source Tape", RECORD_SRC_CMD, (byte) 0x04, "record_tape", "record_tape"),
+ RECORD_SOURCE_VIDEO1("Record Source Video 1", RECORD_SRC_CMD, (byte) 0x05, "record_video1", "record_video1"),
+ RECORD_SOURCE_VIDEO2("Record Source Video 2", RECORD_SRC_CMD, (byte) 0x06, "record_video2", "record_video2"),
+ RECORD_SOURCE_VIDEO3("Record Source Video 3", RECORD_SRC_CMD, (byte) 0x07, "record_video3", "record_video3"),
+ RECORD_SOURCE_VIDEO4("Record Source Video 4", RECORD_SRC_CMD, (byte) 0x08, "record_video4", "record_video4"),
+ RECORD_SOURCE_VIDEO5("Record Source Video 5", RECORD_SRC_CMD, (byte) 0x09, "record_video5", "record_video5"),
+ RECORD_SOURCE_VIDEO6("Record Source Video 6", RECORD_SRC_CMD, (byte) 0x94, "record_video6", "record_video6"),
+ RECORD_SOURCE_USB("Record Source Front USB", RECORD_SRC_CMD, (byte) 0x8E, "record_usb", "record_usb"),
+ RECORD_SOURCE_MAIN("Record Follow Main Zone Source", RECORD_SRC_CMD, (byte) 0x6B, "record_follow_main",
+ "record_follow_main"),
+ ZONE2_SOURCE_CD("Zone 2 Source CD", ZONE2_CMD, (byte) 0x02, "zone2_cd", "zone2_cd"),
+ ZONE2_SOURCE_TUNER("Zone 2 Source Tuner", ZONE2_CMD, (byte) 0x03, "zone2_tuner", "zone2_tuner"),
+ ZONE2_SOURCE_TAPE("Zone 2 Source Tape", ZONE2_CMD, (byte) 0x04, "zone2_tape", "zone2_tape"),
+ ZONE2_SOURCE_VIDEO1("Zone 2 Source Video 1", ZONE2_CMD, (byte) 0x05, "zone2_video1", "zone2_video1"),
+ ZONE2_SOURCE_VIDEO2("Zone 2 Source Video 2", ZONE2_CMD, (byte) 0x06, "zone2_video2", "zone2_video2"),
+ ZONE2_SOURCE_VIDEO3("Zone 2 Source Video 3", ZONE2_CMD, (byte) 0x07, "zone2_video3", "zone2_video3"),
+ ZONE2_SOURCE_VIDEO4("Zone 2 Source Video 4", ZONE2_CMD, (byte) 0x08, "zone2_video4", "zone2_video4"),
+ ZONE2_SOURCE_VIDEO5("Zone 2 Source Video 5", ZONE2_CMD, (byte) 0x09, "zone2_video5", "zone2_video5"),
+ ZONE2_SOURCE_VIDEO6("Zone 2 Source Video 6", ZONE2_CMD, (byte) 0x94, "zone2_video6", "zone2_video6"),
+ ZONE2_SOURCE_USB("Zone 2 Source Front USB", ZONE2_CMD, (byte) 0x8E, "zone2_usb", "zone2_usb"),
+ ZONE2_SOURCE_MAIN("Zone 2 Follow Main Zone Source", ZONE2_CMD, (byte) 0x6B, "zone2_follow_main",
"zone2_follow_main"),
- ZONE3_SOURCE_CD("Zone 3 Source CD", RotelConnector.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_TAPE("Zone 3 Source Tape", RotelConnector.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_VIDEO2("Zone 3 Source Video 2", RotelConnector.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_VIDEO4("Zone 3 Source Video 4", RotelConnector.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_VIDEO6("Zone 3 Source Video 6", RotelConnector.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_MAIN("Zone 3 Follow Main Zone Source", RotelConnector.ZONE3_CMD, (byte) 0x6B, "zone3_follow_main",
+ ZONE3_SOURCE_CD("Zone 3 Source CD", ZONE3_CMD, (byte) 0x02, "zone3_cd", "zone3_cd"),
+ ZONE3_SOURCE_TUNER("Zone 3 Source Tuner", ZONE3_CMD, (byte) 0x03, "zone3_tuner", "zone3_tuner"),
+ ZONE3_SOURCE_TAPE("Zone 3 Source Tape", ZONE3_CMD, (byte) 0x04, "zone3_tape", "zone3_tape"),
+ ZONE3_SOURCE_VIDEO1("Zone 3 Source Video 1", ZONE3_CMD, (byte) 0x05, "zone3_video1", "zone3_video1"),
+ ZONE3_SOURCE_VIDEO2("Zone 3 Source Video 2", ZONE3_CMD, (byte) 0x06, "zone3_video2", "zone3_video2"),
+ ZONE3_SOURCE_VIDEO3("Zone 3 Source Video 3", ZONE3_CMD, (byte) 0x07, "zone3_video3", "zone3_video3"),
+ ZONE3_SOURCE_VIDEO4("Zone 3 Source Video 4", ZONE3_CMD, (byte) 0x08, "zone3_video4", "zone3_video4"),
+ ZONE3_SOURCE_VIDEO5("Zone 3 Source Video 5", ZONE3_CMD, (byte) 0x09, "zone3_video5", "zone3_video5"),
+ ZONE3_SOURCE_VIDEO6("Zone 3 Source Video 6", ZONE3_CMD, (byte) 0x94, "zone3_video6", "zone3_video6"),
+ 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", ZONE3_CMD, (byte) 0x6B, "zone3_follow_main",
"zone3_follow_main"),
- ZONE4_SOURCE_CD("Zone 4 Source CD", RotelConnector.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_TAPE("Zone 4 Source Tape", RotelConnector.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_VIDEO2("Zone 4 Source Video 2", RotelConnector.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_VIDEO4("Zone 4 Source Video 4", RotelConnector.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_VIDEO6("Zone 4 Source Video 6", RotelConnector.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_MAIN("Zone 4 Follow Main Zone Source", RotelConnector.ZONE4_CMD, (byte) 0x6B, "zone4_follow_main",
+ ZONE4_SOURCE_CD("Zone 4 Source CD", ZONE4_CMD, (byte) 0x02, "zone4_cd", "zone4_cd"),
+ ZONE4_SOURCE_TUNER("Zone 4 Source Tuner", ZONE4_CMD, (byte) 0x03, "zone4_tuner", "zone4_tuner"),
+ ZONE4_SOURCE_TAPE("Zone 4 Source Tape", ZONE4_CMD, (byte) 0x04, "zone4_tape", "zone4_tape"),
+ ZONE4_SOURCE_VIDEO1("Zone 4 Source Video 1", ZONE4_CMD, (byte) 0x05, "zone4_video1", "zone4_video1"),
+ ZONE4_SOURCE_VIDEO2("Zone 4 Source Video 2", ZONE4_CMD, (byte) 0x06, "zone4_video2", "zone4_video2"),
+ ZONE4_SOURCE_VIDEO3("Zone 4 Source Video 3", ZONE4_CMD, (byte) 0x07, "zone4_video3", "zone4_video3"),
+ ZONE4_SOURCE_VIDEO4("Zone 4 Source Video 4", ZONE4_CMD, (byte) 0x08, "zone4_video4", "zone4_video4"),
+ ZONE4_SOURCE_VIDEO5("Zone 4 Source Video 5", ZONE4_CMD, (byte) 0x09, "zone4_video5", "zone4_video5"),
+ ZONE4_SOURCE_VIDEO6("Zone 4 Source Video 6", ZONE4_CMD, (byte) 0x94, "zone4_video6", "zone4_video6"),
+ 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", ZONE4_CMD, (byte) 0x6B, "zone4_follow_main",
"zone4_follow_main"),
- STEREO("Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x11, "2channel", "2channel"),
- STEREO3("Dolby 3 Stereo ", RotelConnector.PRIMARY_CMD, (byte) 0x12, "3channel", "3channel"),
- STEREO5("5 Channel Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x5B, "5channel", "5channel"),
- STEREO7("7 Channel Stereo", RotelConnector.PRIMARY_CMD, (byte) 0x5C, "7channel", "7channel"),
+ STEREO("Stereo", PRIMARY_CMD, (byte) 0x11, "2channel", "2channel"),
+ STEREO3("Dolby 3 Stereo ", PRIMARY_CMD, (byte) 0x12, "3channel", "3channel"),
+ STEREO5("5 Channel Stereo", PRIMARY_CMD, (byte) 0x5B, "5channel", "5channel"),
+ STEREO7("7 Channel Stereo", PRIMARY_CMD, (byte) 0x5C, "7channel", "7channel"),
STEREO9("9 Channel Stereo", "9channel", "9channel"),
STEREO11("11 Channel Stereo", "11channel", "11channel"),
- DSP1("DSP 1", RotelConnector.PRIMARY_CMD, (byte) 0x57),
- DSP2("DSP 2", RotelConnector.PRIMARY_CMD, (byte) 0x58),
- DSP3("DSP 3", RotelConnector.PRIMARY_CMD, (byte) 0x59),
- DSP4("DSP 4", RotelConnector.PRIMARY_CMD, (byte) 0x5A),
- PROLOGIC("Dolby Pro Logic", RotelConnector.PRIMARY_CMD, (byte) 0x5F),
- PLII_CINEMA("Dolby PLII Cinema", RotelConnector.PRIMARY_CMD, (byte) 0x5D, "prologic_movie", "prologic_movie"),
- PLII_MUSIC("Dolby PLII Music", RotelConnector.PRIMARY_CMD, (byte) 0x5E, "prologic_music", "prologic_music"),
- PLII_GAME("Dolby PLII Game", RotelConnector.PRIMARY_CMD, (byte) 0x74, "prologic_game", "prologic_game"),
- PLIIZ("Dolby PLIIz", RotelConnector.PRIMARY_CMD, (byte) 0x92, "prologic_iiz", "prologic_iiz"),
- NEO6_MUSIC("dts Neo:6 Music", RotelConnector.PRIMARY_CMD, (byte) 0x60, "neo6_music", "neo6_music"),
- NEO6_CINEMA("dts Neo:6 Cinema", RotelConnector.PRIMARY_CMD, (byte) 0x61, "neo6_cinema", "neo6_cinema"),
+ DSP1("DSP 1", PRIMARY_CMD, (byte) 0x57),
+ DSP2("DSP 2", PRIMARY_CMD, (byte) 0x58),
+ DSP3("DSP 3", PRIMARY_CMD, (byte) 0x59),
+ DSP4("DSP 4", PRIMARY_CMD, (byte) 0x5A),
+ PROLOGIC("Dolby Pro Logic", PRIMARY_CMD, (byte) 0x5F),
+ PLII_CINEMA("Dolby PLII Cinema", PRIMARY_CMD, (byte) 0x5D, "prologic_movie", "prologic_movie"),
+ PLII_MUSIC("Dolby PLII Music", PRIMARY_CMD, (byte) 0x5E, "prologic_music", "prologic_music"),
+ PLII_GAME("Dolby PLII Game", PRIMARY_CMD, (byte) 0x74, "prologic_game", "prologic_game"),
+ PLIIZ("Dolby PLIIz", PRIMARY_CMD, (byte) 0x92, "prologic_iiz", "prologic_iiz"),
+ NEO6_MUSIC("dts Neo:6 Music", PRIMARY_CMD, (byte) 0x60, "neo6_music", "neo6_music"),
+ NEO6_CINEMA("dts Neo:6 Cinema", PRIMARY_CMD, (byte) 0x61, "neo6_cinema", "neo6_cinema"),
ATMOS("Dolby Atmos", "dolby_atmos", "dolby_atmos"),
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"),
TONE_MAX("Request Max tone level", "get_tone_max", null),
- TONE_CONTROL_SELECT("Tone Control Select", RotelConnector.PRIMARY_CMD, (byte) 0x67),
- TREBLE_UP("Treble Up", RotelConnector.PRIMARY_CMD, (byte) 0x0D, "treble_up", "treble_up"),
- TREBLE_DOWN("Treble Down", RotelConnector.PRIMARY_CMD, (byte) 0x0E, "treble_down", "treble_down"),
+ TONE_CONTROL_SELECT("Tone Control Select", PRIMARY_CMD, (byte) 0x67),
+ TREBLE_UP("Treble Up", PRIMARY_CMD, (byte) 0x0D, "treble_up", "treble_up"),
+ TREBLE_DOWN("Treble Down", PRIMARY_CMD, (byte) 0x0E, "treble_down", "treble_down"),
TREBLE_SET("Set Treble to level", "treble_", "treble_"),
TREBLE("Request current treble level", "get_treble", "treble?"),
- BASS_UP("Bass Up", RotelConnector.PRIMARY_CMD, (byte) 0x0F, "bass_up", "bass_up"),
- BASS_DOWN("Bass Down", RotelConnector.PRIMARY_CMD, (byte) 0x10, "bass_down", "bass_down"),
+ BASS_UP("Bass Up", PRIMARY_CMD, (byte) 0x0F, "bass_up", "bass_up"),
+ BASS_DOWN("Bass Down", PRIMARY_CMD, (byte) 0x10, "bass_down", "bass_down"),
BASS_SET("Set Bass to level", "bass_", "bass_"),
BASS("Request current bass level", "get_bass", "bass?"),
- RECORD_FONCTION_SELECT("Record Function Select", RotelConnector.PRIMARY_CMD, (byte) 0x17),
- PLAY("Play Source", RotelConnector.PRIMARY_CMD, (byte) 0x04, "play", "play"),
- STOP("Stop Source", RotelConnector.PRIMARY_CMD, (byte) 0x06, "stop", "stop"),
- PAUSE("Pause Source", RotelConnector.PRIMARY_CMD, (byte) 0x05, "pause", "pause"),
+ RECORD_FONCTION_SELECT("Record Function Select", PRIMARY_CMD, (byte) 0x17),
+ PLAY("Play Source", PRIMARY_CMD, (byte) 0x04, "play", "play"),
+ STOP("Stop Source", PRIMARY_CMD, (byte) 0x06, "stop", "stop"),
+ PAUSE("Pause Source", PRIMARY_CMD, (byte) 0x05, "pause", "pause"),
CD_PLAY_STATUS("Request CD play status", "get_cd_play_status", null),
PLAY_STATUS("Request source play status", "get_play_status", "status"),
- TRACK_FORWARD("Track Forward", RotelConnector.PRIMARY_CMD, (byte) 0x09, "track_fwd", "trkf"),
- TRACK_BACKWORD("Track Backward", RotelConnector.PRIMARY_CMD, (byte) 0x08, "track_back", "trkb"),
+ TRACK_FORWARD("Track Forward", PRIMARY_CMD, (byte) 0x09, "track_fwd", "trkf"),
+ TRACK_BACKWORD("Track Backward", PRIMARY_CMD, (byte) 0x08, "track_back", "trkb"),
TRACK("Request current CD track number", null, "track"),
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_SET("Set front display dimmer to level", "dimmer_", "dimmer_"),
UPDATE_AUTO("Set Update to Auto", "display_update_auto", "rs232_update_on"),
BALANCE_LEFT("Balance Left", "balance_left", "balance_l"),
BALANCE_SET("Set Balance to level", "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_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_OFF("Unset Speaker B Output", "speaker_b_off", "speaker_b_off"),
SPEAKER("Request current active speaker outputs", "get_current_speaker", "speaker?");
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.PatternSyntaxException;
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.core.util.HexUtils;
+import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final Logger logger = LoggerFactory.getLogger(RotelConnector.class);
- public static final byte[] READ_ERROR = "read_error".getBytes(StandardCharsets.US_ASCII);
-
- protected static final byte START = (byte) 0xFE;
-
- // Message types
- 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;
- private static final byte TRIGGER_CMD = (byte) 0x40;
- protected static final byte STANDARD_RESPONSE = (byte) 0x20;
- private static final byte TRIGGER_STATUS = (byte) 0x21;
- private static final byte SMART_DISPLAY_DATA_1 = (byte) 0x22;
- private static final byte SMART_DISPLAY_DATA_2 = (byte) 0x23;
-
- // Keys used by the HEX protocol
- 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 ";
-
- // Keys 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_POWER = "power";
- public static final String KEY_VOLUME_MIN = "volume_min";
- public static final String KEY_VOLUME_MAX = "volume_max";
- public static final String KEY_VOLUME = "volume";
- public static final String KEY_MUTE = "mute";
- public static final String KEY_TONE_MAX = "tone_max";
- 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 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_DSP_MODE = "dsp_mode";
- 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";
-
- // Special keys used by the binding
- 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";
- public static final String KEY_ERROR = "error";
-
- 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";
- protected static final String AUTO = "auto";
- protected static final String MANUAL = "manual";
- 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 PLAY = "play";
- public static final String PAUSE = "pause";
- public static final String STOP = "stop";
- private static final String SOURCE = "source";
- 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";
-
- private RotelModel model;
- private RotelProtocol protocol;
- protected Map<RotelSource, String> sourcesLabels;
- private boolean simu;
+ private final boolean simu;
+ protected final Thread readerThread;
/** The output stream */
protected @Nullable OutputStream dataOut;
/** true if the connection is established, false if not */
private boolean connected;
- protected String readerThreadName;
- private @Nullable Thread readerThread;
-
- private List<RotelMessageEventListener> listeners = new ArrayList<>();
-
- /** 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 = {};
-
/**
* Constructor
*
- * @param model the Rotel model in use
- * @param protocol the protocol to be used
+ * @param protocolHandler the protocol handler
* @param simu whether the communication is simulated or real
* @param readerThreadName the name of thread to be created
*/
- public RotelConnector(RotelModel model, RotelProtocol protocol, Map<RotelSource, String> sourcesLabels,
- boolean simu, String readerThreadName) {
- this.model = model;
- this.protocol = protocol;
- this.sourcesLabels = sourcesLabels;
+ public RotelConnector(RotelAbstractProtocolHandler protocolHandler, boolean simu, String readerThreadName) {
this.simu = simu;
- this.readerThreadName = readerThreadName;
- }
-
- /**
- * Get the Rotel model
- *
- * @return the model
- */
- public RotelModel getModel() {
- return model;
- }
-
- /**
- * Get the protocol to be used
- *
- * @return the protocol
- */
- public RotelProtocol getProtocol() {
- return protocol;
+ this.readerThread = new RotelReaderThread(this, protocolHandler, readerThreadName);
}
/**
this.connected = connected;
}
- /**
- * Set the thread that handles the feedback messages
- *
- * @param readerThread the thread
- */
- protected void setReaderThread(Thread readerThread) {
- this.readerThread = readerThread;
- }
-
/**
* Open the connection with the Rotel device
*
* Stop the thread that handles the feedback messages and close the opened input and output streams
*/
protected void cleanup() {
- Thread readerThread = this.readerThread;
- if (readerThread != null) {
- readerThread.interrupt();
- try {
- readerThread.join();
- } catch (InterruptedException e) {
- }
- this.readerThread = null;
+ readerThread.interrupt();
+ try {
+ readerThread.join();
+ } catch (InterruptedException e) {
}
OutputStream dataOut = this.dataOut;
if (dataOut != null) {
/**
* Request the Rotel device to execute a command
*
- * @param cmd the command to execute
- *
- * @throws RotelException - In case of any problem
- */
- public void sendCommand(RotelCommand cmd) throws RotelException {
- sendCommand(cmd, null);
- }
-
- /**
- * Request the Rotel device to execute a command
- *
- * @param cmd the command to execute
- * @param value the integer value to consider for volume, bass or treble adjustment
+ * @param cmdName the command name
+ * @param dataBuffer the data buffer containing the encoded command
*
* @throws RotelException - In case of any problem
*/
- public void sendCommand(RotelCommand cmd, @Nullable Integer value) throws RotelException {
- String messageStr;
- byte[] message = new byte[0];
- switch (protocol) {
- case HEX:
- if (cmd.getHexType() == 0) {
- logger.debug("Send comman \"{}\" ignored: not available for HEX protocol", cmd.getName());
- return;
- } else {
- final int size = 6;
- 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("Send command \"{}\" => {}", cmd.getName(), HexUtils.bytesToHex(message));
- }
- break;
- case ASCII_V1:
- messageStr = cmd.getAsciiCommandV1();
- if (messageStr == null) {
- logger.debug("Send comman \"{}\" ignored: not available for ASCII V1 protocol", cmd.getName());
- return;
- } else {
- 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 += "!";
- }
- message = messageStr.getBytes(StandardCharsets.US_ASCII);
- logger.debug("Send command \"{}\" => {}", cmd.getName(), messageStr);
- }
- break;
- case ASCII_V2:
- messageStr = cmd.getAsciiCommandV2();
- if (messageStr == null) {
- logger.debug("Send comman \"{}\" ignored: not available for ASCII V2 protocol", cmd.getName());
- return;
- } else {
- 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 += "!";
- }
- message = messageStr.getBytes(StandardCharsets.US_ASCII);
- logger.debug("Send command \"{}\" => {}", cmd.getName(), messageStr);
- }
- break;
- }
+ public void writeOutput(String cmdName, byte[] dataBuffer) throws RotelException {
if (simu) {
return;
}
OutputStream dataOut = this.dataOut;
if (dataOut == null) {
- throw new RotelException("Send command \"" + cmd.getName() + "\" failed: output stream is null");
+ throw new RotelException("Send command \"" + cmdName + "\" failed: output stream is null");
}
try {
- dataOut.write(message);
+ dataOut.write(dataBuffer);
dataOut.flush();
} catch (IOException e) {
- logger.debug("Send command \"{}\" failed: {}", cmd.getName(), e.getMessage());
- throw new RotelException("Send command \"" + cmd.getName() + "\" failed", e);
- }
- logger.debug("Send command \"{}\" succeeded", cmd.getName());
- }
-
- /**
- * Validate the content of a feedback message
- *
- * @param responseMessage the buffer containing the feedback message
- *
- * @throws RotelException - If the message has unexpected content
- */
- private void validateResponse(byte[] responseMessage) throws RotelException {
- if (protocol == RotelProtocol.HEX) {
- // 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");
- }
- } else {
- // 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");
- }
- }
- }
-
- /**
- * 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
- */
- protected byte computeCheckSum(byte[] message, int maxIdx) {
- int result = 0;
- for (int i = 1; i <= maxIdx; i++) {
- result += (message[i] & 0x000000FF);
- }
- return (byte) (result & 0x000000FF);
- }
-
- /**
- * 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);
- }
-
- /**
- * Analyze an incoming message and dispatch corresponding (key, value) to the event listeners
- *
- * @param incomingMessage the received message
- */
- public void handleIncomingMessage(byte[] incomingMessage) {
- logger.debug("handleIncomingMessage: bytes {}", HexUtils.bytesToHex(incomingMessage));
-
- if (READ_ERROR.equals(incomingMessage)) {
- dispatchKeyValue(KEY_ERROR, MSG_VALUE_ON);
- return;
- }
-
- try {
- validateResponse(incomingMessage);
- } catch (RotelException e) {
- return;
- }
-
- if (protocol == RotelProtocol.HEX) {
- handleValidHexMessage(incomingMessage);
- } else {
- handleValidAsciiMessage(incomingMessage);
- }
- }
-
- /**
- * Analyze a valid HEX message and dispatch corresponding (key, value) to the event listeners
- *
- * @param incomingMessage the received message
- */
- private void handleValidHexMessage(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) {
- }
- try {
- dispatchKeyValue(KEY_POWER_ZONE3, model.isZone3On(flags) ? POWER_ON : STANDBY);
- } catch (RotelException e1) {
- }
- try {
- dispatchKeyValue(KEY_POWER_ZONE4, model.isZone4On(flags) ? POWER_ON : STANDBY);
- } catch (RotelException e1) {
- }
- 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) {
- }
- }
- } catch (RotelException e1) {
- checkMultiIn = true;
- }
- boolean checkStereo = true;
- try {
- checkStereo = !model.isMoreThan2Channels(flags);
- } catch (RotelException e1) {
- }
-
- 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);
- }
- }
-
- /**
- * Analyze a valid ASCII message and dispatch corresponding (key, value) to the event listeners
- *
- * @param incomingMessage the received message
- */
- public void handleValidAsciiMessage(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);
- }
- }
-
- /**
- * 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) {
- }
- } 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) {
- }
- } 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;
- }
-
- /**
- * 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);
- }
-
- /**
- * Dispatch an event (key, value) to the event listeners
- *
- * @param key the key
- * @param value the value
- */
- private 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);
+ logger.debug("Send command \"{}\" failed: {}", cmdName, e.getMessage());
+ throw new RotelException("Send command \"" + cmdName + "\" failed", e);
}
+ logger.debug("Send command \"{}\" succeeded", cmdName);
}
}
import java.io.InterruptedIOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
-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.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*
* @param address the IP address of the projector
* @param port the TCP port to be used
- * @param model the projector model in use
- * @param protocol the protocol to be used
+ * @param protocolHandler the protocol handler
* @param readerThreadName the name of thread to be created
*/
- public RotelIpConnector(String address, Integer port, RotelModel model, RotelProtocol protocol,
- Map<RotelSource, String> sourcesLabels, String readerThreadName) {
- super(model, protocol, sourcesLabels, false, readerThreadName);
+ public RotelIpConnector(String address, Integer port, RotelAbstractProtocolHandler protocolHandler,
+ String readerThreadName) {
+ super(protocolHandler, false, readerThreadName);
this.address = address;
this.port = port;
dataOut = new DataOutputStream(clientSocket.getOutputStream());
dataIn = new DataInputStream(clientSocket.getInputStream());
- Thread thread = new RotelReaderThread(this, readerThreadName);
- setReaderThread(thread);
- thread.start();
+ readerThread.start();
this.clientSocket = clientSocket;
+++ /dev/null
-/**
- * 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.communication;
-
-import java.util.EventObject;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * Rotel event used to notify changes coming from messages received from the Rotel device
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public class RotelMessageEvent extends EventObject {
-
- private static final long serialVersionUID = 1L;
- private String key;
- private String value;
-
- public RotelMessageEvent(Object source, String key, String value) {
- super(source);
- this.key = key;
- this.value = value;
- }
-
- public String getKey() {
- return key;
- }
-
- public String getValue() {
- return value;
- }
-}
+++ /dev/null
-/**
- * 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.communication;
-
-import java.util.EventListener;
-import java.util.EventObject;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * Rotel Event Listener interface. Handles incoming Rotel message events
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public interface RotelMessageEventListener extends EventListener {
-
- /**
- * Event handler method for incoming Rotel message events
- *
- * @param event the event object
- */
- public void onNewMessageEvent(EventObject event);
-}
+++ /dev/null
-/**
- * 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.communication;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.rotel.internal.RotelException;
-
-/**
- * Represents the different kinds of protocols
- *
- * @author Laurent Garnier - Initial contribution
- */
-@NonNullByDefault
-public enum RotelProtocol {
-
- HEX("HEX"),
- ASCII_V1("ASCII_V1"),
- ASCII_V2("ASCII_V2");
-
- private String name;
-
- /**
- * Constructor
- *
- * @param name the protocol name
- */
- private RotelProtocol(String name) {
- this.name = name;
- }
-
- /**
- * Get the protocol name
- *
- * @return the protocol name
- */
- public String getName() {
- return name;
- }
-
- /**
- * Get the protocol associated to a name
- *
- * @param name the name used to identify the protocol
- *
- * @return the protocol associated to the searched name
- *
- * @throws RotelException - If no protocol is associated to the searched name
- */
- public static RotelProtocol getFromName(String name) throws RotelException {
- for (RotelProtocol value : RotelProtocol.values()) {
- if (value.getName().equals(name)) {
- return value;
- }
- }
- throw new RotelException("Invalid protocol name: " + name);
- }
-}
package org.openhab.binding.rotel.internal.communication;
import java.io.InterruptedIOException;
-import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.rotel.internal.RotelException;
+import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@NonNullByDefault
public class RotelReaderThread extends Thread {
- private final Logger logger = LoggerFactory.getLogger(RotelReaderThread.class);
-
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
*
- * @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
*/
- public RotelReaderThread(RotelConnector connector, String threadName) {
+ public RotelReaderThread(RotelConnector connector, RotelAbstractProtocolHandler protocolHandler,
+ String threadName) {
super(threadName);
this.connector = connector;
+ this.protocolHandler = protocolHandler;
}
@Override
public void run() {
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[] dataBuffer = new byte[size];
- boolean startCodeReached = false;
- int count = 0;
- int index = 0;
- final char terminatingChar = (protocol == RotelProtocol.ASCII_V1) ? '!' : '$';
try {
while (!Thread.interrupted()) {
int len = connector.readInput(readDataBuffer);
if (len > 0) {
- for (int i = 0; i < len; i++) {
- 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;
- }
- }
- }
+ protocolHandler.handleIncomingData(readDataBuffer, len);
}
}
} catch (InterruptedIOException e) {
logger.debug("Interrupted via InterruptedIOException");
} catch (RotelException e) {
logger.debug("Reading failed: {}", e.getMessage(), e);
- connector.handleIncomingMessage(RotelConnector.READ_ERROR);
+ protocolHandler.handleInIncomingError();
}
logger.debug("Data listener stopped");
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-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.protocol.RotelAbstractProtocolHandler;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
private String serialPortName;
private SerialPortManager serialPortManager;
+ private int baudRate;
+
private @Nullable SerialPort serialPort;
/**
*
* @param serialPortManager the serial port manager
* @param serialPortName the serial port name to be used
- * @param model the projector model in use
- * @param protocol the protocol to be used
+ * @param baudRate the baud rate to be used
+ * @param protocolHandler the protocol handler
* @param readerThreadName the name of thread to be created
*/
- public RotelSerialConnector(SerialPortManager serialPortManager, String serialPortName, RotelModel model,
- RotelProtocol protocol, Map<RotelSource, String> sourcesLabels, String readerThreadName) {
- super(model, protocol, sourcesLabels, false, readerThreadName);
+ public RotelSerialConnector(SerialPortManager serialPortManager, String serialPortName, int baudRate,
+ RotelAbstractProtocolHandler protocolHandler, String readerThreadName) {
+ super(protocolHandler, false, readerThreadName);
+ this.baudRate = baudRate;
this.serialPortManager = serialPortManager;
this.serialPortName = serialPortName;
}
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);
commPort.enableReceiveThreshold(1);
commPort.enableReceiveTimeout(100);
}
}
- Thread thread = new RotelReaderThread(this, readerThreadName);
- setReaderThread(thread);
- thread.start();
+ readerThread.start();
this.serialPort = commPort;
this.dataIn = dataIn;
*/
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.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.openhab.binding.rotel.internal.RotelException;
import org.openhab.binding.rotel.internal.RotelModel;
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.LoggerFactory;
@NonNullByDefault
public class RotelSimuConnector extends RotelConnector {
+ private static final int STEP_TONE_LEVEL = 1;
+
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();
* Constructor
*
* @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
*/
- public RotelSimuConnector(RotelModel model, RotelProtocol protocol, Map<RotelSource, String> sourcesLabels,
- String readerThreadName) {
- super(model, protocol, sourcesLabels, true, readerThreadName);
+ public RotelSimuConnector(RotelModel model, RotelAbstractProtocolHandler protocolHandler,
+ Map<RotelSource, String> sourcesLabels, String readerThreadName) {
+ super(protocolHandler, true, readerThreadName);
+ this.model = model;
+ this.protocol = protocolHandler.getProtocol();
+ this.sourcesLabels = sourcesLabels;
this.minVolume = 0;
this.maxVolume = model.hasVolumeControl() ? model.getVolumeMax() : 0;
this.maxToneLevel = model.hasToneControl() ? model.getToneLevelMax() : 0;
@Override
public synchronized void open() throws RotelException {
logger.debug("Opening simulated connection");
- Thread thread = new RotelReaderThread(this, readerThreadName);
- setReaderThread(thread);
- thread.start();
+ readerThread.start();
setConnected(true);
logger.debug("Simulated connection opened");
}
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
*
* @param cmd the sent command
* @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 textLine1Left = buildSourceLine1LeftResponse();
String textLine1Right = buildVolumeLine1RightResponse();
break;
case ZONE2_POWER_OFF:
powerZone2 = false;
- text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2);
showZone = 2;
resetZone = false;
break;
case ZONE2_POWER_ON:
powerZone2 = true;
- text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2);
showZone = 2;
resetZone = false;
resetZone = false;
break;
case RECORD_FONCTION_SELECT:
- if (getModel().getNbAdditionalZones() >= 1 && getModel().getZoneSelectCmd() == cmd) {
+ if (model.getNbAdditionalZones() >= 1 && model.getZoneSelectCmd() == cmd) {
showZone++;
- if (showZone > getModel().getNbAdditionalZones()) {
+ if (showZone > model.getNbAdditionalZones()) {
showZone = 1;
if (!power) {
showZone++;
textLine2 = buildRecordResponse();
} else if (showZone == 2) {
selectingRecord = false;
- text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2);
} else if (showZone == 3) {
selectingRecord = false;
resetZone = false;
break;
case ZONE_SELECT:
- if (getModel().getNbAdditionalZones() == 0
- || (getModel().getNbAdditionalZones() > 1 && getModel().getZoneSelectCmd() == cmd)
- || (showZone == 1 && getModel().getZoneSelectCmd() != cmd)) {
+ if (model.getNbAdditionalZones() == 0
+ || (model.getNbAdditionalZones() > 1 && model.getZoneSelectCmd() == cmd)
+ || (showZone == 1 && model.getZoneSelectCmd() != cmd)) {
accepted = false;
} else {
- if (getModel().getZoneSelectCmd() == cmd) {
+ if (model.getZoneSelectCmd() == cmd) {
if (!power && !powerZone2) {
showZone = 2;
powerZone2 = true;
}
}
if (showZone == 2) {
- text = textLine2 = buildZonePowerResponse(
- getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", powerZone2, sourceZone2);
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ powerZone2, sourceZone2);
} else if (showZone == 3) {
text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3);
} else if (showZone == 4) {
if (volumeZone2 < maxVolume) {
volumeZone2++;
}
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
case ZONE2_VOLUME_DOWN:
if (volumeZone2 > minVolume) {
volumeZone2--;
}
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
case ZONE2_VOLUME_SET:
if (value != null) {
volumeZone2 = value;
}
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
case VOLUME_UP:
- if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) {
+ if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (volumeZone2 < maxVolume) {
volumeZone2++;
}
- text = textLine2 = buildZoneVolumeResponse(
- getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2);
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ muteZone2, volumeZone2);
resetZone = false;
} else {
accepted = false;
}
break;
case VOLUME_DOWN:
- if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) {
+ if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (volumeZone2 > minVolume) {
volumeZone2--;
}
- text = textLine2 = buildZoneVolumeResponse(
- getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2);
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ muteZone2, volumeZone2);
resetZone = false;
} else {
accepted = false;
}
break;
case VOLUME_SET:
- if (!getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1 && showZone == 2) {
+ if (!model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
if (value != null) {
volumeZone2 = value;
}
- text = textLine2 = buildZoneVolumeResponse(
- getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE", muteZone2, volumeZone2);
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ muteZone2, volumeZone2);
resetZone = false;
} else {
accepted = false;
break;
case ZONE2_MUTE_TOGGLE:
muteZone2 = !muteZone2;
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
case ZONE2_MUTE_ON:
muteZone2 = true;
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
case ZONE2_MUTE_OFF:
muteZone2 = false;
- text = textLine2 = buildZoneVolumeResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZoneVolumeResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
muteZone2, volumeZone2);
break;
default:
}
if (!accepted) {
try {
- sourceZone2 = getModel().getZone2SourceFromCommand(cmd);
+ sourceZone2 = model.getZone2SourceFromCommand(cmd);
powerZone2 = true;
- text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2);
muteZone2 = false;
accepted = true;
} catch (RotelException e) {
}
}
- if (!accepted && !getModel().hasZone2Commands() && getModel().getNbAdditionalZones() >= 1
- && showZone == 2) {
+ if (!accepted && !model.hasZone2Commands() && model.getNbAdditionalZones() >= 1 && showZone == 2) {
try {
- sourceZone2 = getModel().getSourceFromCommand(cmd);
+ sourceZone2 = model.getSourceFromCommand(cmd);
powerZone2 = true;
- text = textLine2 = buildZonePowerResponse(getModel().getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
+ text = textLine2 = buildZonePowerResponse(model.getNbAdditionalZones() > 1 ? "ZONE2" : "ZONE",
powerZone2, sourceZone2);
muteZone2 = false;
accepted = true;
}
if (!accepted) {
try {
- sourceZone3 = getModel().getZone3SourceFromCommand(cmd);
+ sourceZone3 = model.getZone3SourceFromCommand(cmd);
powerZone3 = true;
text = textLine2 = buildZonePowerResponse("ZONE3", powerZone3, sourceZone3);
muteZone3 = false;
}
if (!accepted) {
try {
- sourceZone4 = getModel().getZone4SourceFromCommand(cmd);
+ sourceZone4 = model.getZone4SourceFromCommand(cmd);
powerZone4 = true;
text = textLine2 = buildZonePowerResponse("ZONE4", powerZone4, sourceZone4);
muteZone4 = false;
switch (cmd) {
case UPDATE_AUTO:
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;
case UPDATE_MANUAL:
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;
case VOLUME_GET_MIN:
textAscii = buildAsciiResponse(KEY_VOLUME_MIN, minVolume);
multiinput = !multiinput;
text = "MULTI IN " + (multiinput ? "ON" : "OFF");
try {
- source = getModel().getSourceFromCommand(cmd);
+ source = model.getSourceFromCommand(cmd);
textLine1Left = buildSourceLine1LeftResponse();
textAscii = buildSourceAsciiResponse();
mute = false;
}
if (!accepted) {
try {
- source = getModel().getMainZoneSourceFromCommand(cmd);
+ source = model.getMainZoneSourceFromCommand(cmd);
text = buildSourceLine1Response();
textLine1Left = buildSourceLine1LeftResponse();
textAscii = buildSourceAsciiResponse();
}
if (!accepted) {
try {
- if (selectingRecord && !getModel().hasOtherThanPrimaryCommands()) {
- recordSource = getModel().getSourceFromCommand(cmd);
+ if (selectingRecord && !model.hasOtherThanPrimaryCommands()) {
+ recordSource = model.getSourceFromCommand(cmd);
} else {
- source = getModel().getSourceFromCommand(cmd);
+ source = model.getSourceFromCommand(cmd);
}
text = buildSourceLine1Response();
textLine1Left = buildSourceLine1LeftResponse();
}
if (!accepted) {
try {
- recordSource = getModel().getRecordSourceFromCommand(cmd);
+ recordSource = model.getRecordSourceFromCommand(cmd);
text = buildSourceLine1Response();
textLine2 = buildRecordResponse();
accepted = true;
showZone = 0;
}
- if (getModel().getRespNbChars() == 42) {
+ if (model.getRespNbChars() == 42) {
while (textLine1Left.length() < 14) {
textLine1Left += " ";
}
text = textLine1Left + textLine1Right + textLine2;
}
- if (getProtocol() == RotelProtocol.HEX) {
- byte[] chars = Arrays.copyOf(text.getBytes(StandardCharsets.US_ASCII), getModel().getRespNbChars());
- byte[] flags = new byte[getModel().getRespNbFlags()];
+ if (protocol == RotelProtocol.HEX) {
+ byte[] chars = Arrays.copyOf(text.getBytes(StandardCharsets.US_ASCII), model.getRespNbChars());
+ byte[] flags = new byte[model.getRespNbFlags()];
try {
- getModel().setMultiInput(flags, multiinput);
+ model.setMultiInput(flags, multiinput);
} catch (RotelException e) {
}
try {
- getModel().setZone2(flags, powerZone2);
+ model.setZone2(flags, powerZone2);
} catch (RotelException e) {
}
try {
- getModel().setZone3(flags, powerZone3);
+ model.setZone3(flags, powerZone3);
} catch (RotelException e) {
}
try {
- getModel().setZone4(flags, powerZone4);
+ model.setZone4(flags, powerZone4);
} catch (RotelException e) {
}
- int size = 6 + getModel().getRespNbChars() + getModel().getRespNbFlags();
+ int size = 6 + model.getRespNbChars() + model.getRespNbFlags();
byte[] dataBuffer = new byte[size];
int idx = 0;
dataBuffer[idx++] = START;
dataBuffer[idx++] = (byte) (size - 4);
- dataBuffer[idx++] = getModel().getDeviceId();
+ dataBuffer[idx++] = model.getDeviceId();
dataBuffer[idx++] = STANDARD_RESPONSE;
- if (getModel().isCharsBeforeFlags()) {
- System.arraycopy(chars, 0, dataBuffer, idx, getModel().getRespNbChars());
- idx += getModel().getRespNbChars();
- System.arraycopy(flags, 0, dataBuffer, idx, getModel().getRespNbFlags());
- idx += getModel().getRespNbFlags();
+ if (model.isCharsBeforeFlags()) {
+ System.arraycopy(chars, 0, dataBuffer, idx, model.getRespNbChars());
+ idx += model.getRespNbChars();
+ System.arraycopy(flags, 0, dataBuffer, idx, model.getRespNbFlags());
+ idx += model.getRespNbFlags();
} else {
- System.arraycopy(flags, 0, dataBuffer, idx, getModel().getRespNbFlags());
- idx += getModel().getRespNbFlags();
- System.arraycopy(chars, 0, dataBuffer, idx, getModel().getRespNbChars());
- idx += getModel().getRespNbChars();
+ System.arraycopy(flags, 0, dataBuffer, idx, model.getRespNbFlags());
+ idx += model.getRespNbFlags();
+ System.arraycopy(chars, 0, dataBuffer, idx, model.getRespNbChars());
+ idx += model.getRespNbChars();
}
- byte checksum = computeCheckSum(dataBuffer, idx - 1);
+ byte checksum = RotelHexProtocolHandler.computeCheckSum(dataBuffer, idx - 1);
if ((checksum & 0x000000FF) == 0x000000FD) {
dataBuffer[idx++] = (byte) 0xFD;
dataBuffer[idx++] = 0;
idxInFeedbackMsg = 0;
}
} else {
- String command = textAscii + (getProtocol() == RotelProtocol.ASCII_V1 ? "!" : "$");
+ String command = textAscii + (protocol == RotelProtocol.ASCII_V1 ? "!" : "$");
synchronized (lock) {
feedbackMsg = command.getBytes(StandardCharsets.US_ASCII);
idxInFeedbackMsg = 0;
status = STOP;
break;
}
- return buildAsciiResponse(getProtocol() == RotelProtocol.ASCII_V1 ? KEY1_PLAY_STATUS : KEY2_PLAY_STATUS,
- status);
+ return buildAsciiResponse(protocol == RotelProtocol.ASCII_V1 ? KEY1_PLAY_STATUS : KEY2_PLAY_STATUS, status);
}
private String buildTrackAsciiResponse() {
import org.openhab.binding.rotel.internal.communication.RotelConnector;
import org.openhab.binding.rotel.internal.communication.RotelDsp;
import org.openhab.binding.rotel.internal.communication.RotelIpConnector;
-import org.openhab.binding.rotel.internal.communication.RotelMessageEvent;
-import org.openhab.binding.rotel.internal.communication.RotelMessageEventListener;
-import org.openhab.binding.rotel.internal.communication.RotelProtocol;
import org.openhab.binding.rotel.internal.communication.RotelSerialConnector;
import org.openhab.binding.rotel.internal.communication.RotelSimuConnector;
import org.openhab.binding.rotel.internal.communication.RotelSource;
import org.openhab.binding.rotel.internal.configuration.RotelThingConfiguration;
+import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
+import org.openhab.binding.rotel.internal.protocol.RotelMessageEvent;
+import org.openhab.binding.rotel.internal.protocol.RotelMessageEventListener;
+import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
+import org.openhab.binding.rotel.internal.protocol.ascii.RotelAsciiV1ProtocolHandler;
+import org.openhab.binding.rotel.internal.protocol.ascii.RotelAsciiV2ProtocolHandler;
+import org.openhab.binding.rotel.internal.protocol.hex.RotelHexProtocolHandler;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
private RotelStateDescriptionOptionProvider stateDescriptionProvider;
private SerialPortManager serialPortManager;
- private RotelConnector connector = new RotelSimuConnector(DEFAULT_MODEL, RotelProtocol.HEX, new HashMap<>(),
- "OH-binding-rotel");
+ private RotelModel model;
+ private RotelProtocol protocol;
+ private RotelAbstractProtocolHandler protocolHandler;
+ private RotelConnector connector;
private int minVolume;
private int maxVolume;
super(thing);
this.stateDescriptionProvider = stateDescriptionProvider;
this.serialPortManager = serialPortManager;
+ this.model = DEFAULT_MODEL;
+ this.protocolHandler = new RotelHexProtocolHandler(model, Map.of());
+ this.protocol = protocolHandler.getProtocol();
+ this.connector = new RotelSimuConnector(model, protocolHandler, new HashMap<>(), "OH-binding-rotel");
}
@Override
public void initialize() {
logger.debug("Start initializing handler for thing {}", getThing().getUID());
- RotelModel rotelModel;
switch (getThing().getThingTypeUID().getId()) {
case THING_TYPE_ID_RSP1066:
- rotelModel = RotelModel.RSP1066;
+ model = RotelModel.RSP1066;
break;
case THING_TYPE_ID_RSP1068:
- rotelModel = RotelModel.RSP1068;
+ model = RotelModel.RSP1068;
break;
case THING_TYPE_ID_RSP1069:
- rotelModel = RotelModel.RSP1069;
+ model = RotelModel.RSP1069;
break;
case THING_TYPE_ID_RSP1098:
- rotelModel = RotelModel.RSP1098;
+ model = RotelModel.RSP1098;
break;
case THING_TYPE_ID_RSP1570:
- rotelModel = RotelModel.RSP1570;
+ model = RotelModel.RSP1570;
break;
case THING_TYPE_ID_RSP1572:
- rotelModel = RotelModel.RSP1572;
+ model = RotelModel.RSP1572;
break;
case THING_TYPE_ID_RSX1055:
- rotelModel = RotelModel.RSX1055;
+ model = RotelModel.RSX1055;
break;
case THING_TYPE_ID_RSX1056:
- rotelModel = RotelModel.RSX1056;
+ model = RotelModel.RSX1056;
break;
case THING_TYPE_ID_RSX1057:
- rotelModel = RotelModel.RSX1057;
+ model = RotelModel.RSX1057;
break;
case THING_TYPE_ID_RSX1058:
- rotelModel = RotelModel.RSX1058;
+ model = RotelModel.RSX1058;
break;
case THING_TYPE_ID_RSX1065:
- rotelModel = RotelModel.RSX1065;
+ model = RotelModel.RSX1065;
break;
case THING_TYPE_ID_RSX1067:
- rotelModel = RotelModel.RSX1067;
+ model = RotelModel.RSX1067;
break;
case THING_TYPE_ID_RSX1550:
- rotelModel = RotelModel.RSX1550;
+ model = RotelModel.RSX1550;
break;
case THING_TYPE_ID_RSX1560:
- rotelModel = RotelModel.RSX1560;
+ model = RotelModel.RSX1560;
break;
case THING_TYPE_ID_RSX1562:
- rotelModel = RotelModel.RSX1562;
+ model = RotelModel.RSX1562;
break;
case THING_TYPE_ID_A11:
- rotelModel = RotelModel.A11;
+ model = RotelModel.A11;
break;
case THING_TYPE_ID_A12:
- rotelModel = RotelModel.A12;
+ model = RotelModel.A12;
break;
case THING_TYPE_ID_A14:
- rotelModel = RotelModel.A14;
+ model = RotelModel.A14;
break;
case THING_TYPE_ID_CD11:
- rotelModel = RotelModel.CD11;
+ model = RotelModel.CD11;
break;
case THING_TYPE_ID_CD14:
- rotelModel = RotelModel.CD14;
+ model = RotelModel.CD14;
break;
case THING_TYPE_ID_RA11:
- rotelModel = RotelModel.RA11;
+ model = RotelModel.RA11;
break;
case THING_TYPE_ID_RA12:
- rotelModel = RotelModel.RA12;
+ model = RotelModel.RA12;
break;
case THING_TYPE_ID_RA1570:
- rotelModel = RotelModel.RA1570;
+ model = RotelModel.RA1570;
break;
case THING_TYPE_ID_RA1572:
- rotelModel = RotelModel.RA1572;
+ model = RotelModel.RA1572;
break;
case THING_TYPE_ID_RA1592:
- rotelModel = RotelModel.RA1592;
+ model = RotelModel.RA1592;
break;
case THING_TYPE_ID_RAP1580:
- rotelModel = RotelModel.RAP1580;
+ model = RotelModel.RAP1580;
break;
case THING_TYPE_ID_RC1570:
- rotelModel = RotelModel.RC1570;
+ model = RotelModel.RC1570;
break;
case THING_TYPE_ID_RC1572:
- rotelModel = RotelModel.RC1572;
+ model = RotelModel.RC1572;
break;
case THING_TYPE_ID_RC1590:
- rotelModel = RotelModel.RC1590;
+ model = RotelModel.RC1590;
break;
case THING_TYPE_ID_RCD1570:
- rotelModel = RotelModel.RCD1570;
+ model = RotelModel.RCD1570;
break;
case THING_TYPE_ID_RCD1572:
- rotelModel = RotelModel.RCD1572;
+ model = RotelModel.RCD1572;
break;
case THING_TYPE_ID_RCX1500:
- rotelModel = RotelModel.RCX1500;
+ model = RotelModel.RCX1500;
break;
case THING_TYPE_ID_RDD1580:
- rotelModel = RotelModel.RDD1580;
+ model = RotelModel.RDD1580;
break;
case THING_TYPE_ID_RDG1520:
case THING_TYPE_ID_RT09:
- rotelModel = RotelModel.RDG1520;
+ model = RotelModel.RDG1520;
break;
case THING_TYPE_ID_RSP1576:
- rotelModel = RotelModel.RSP1576;
+ model = RotelModel.RSP1576;
break;
case THING_TYPE_ID_RSP1582:
- rotelModel = RotelModel.RSP1582;
+ model = RotelModel.RSP1582;
break;
case THING_TYPE_ID_RT11:
- rotelModel = RotelModel.RT11;
+ model = RotelModel.RT11;
break;
case THING_TYPE_ID_RT1570:
- rotelModel = RotelModel.RT1570;
+ model = RotelModel.RT1570;
break;
case THING_TYPE_ID_T11:
- rotelModel = RotelModel.T11;
+ model = RotelModel.T11;
break;
case THING_TYPE_ID_T14:
- rotelModel = RotelModel.T14;
+ model = RotelModel.T14;
break;
case THING_TYPE_ID_P5:
- rotelModel = RotelModel.P5;
+ model = RotelModel.P5;
break;
case THING_TYPE_ID_X3:
- rotelModel = RotelModel.X3;
+ model = RotelModel.X3;
break;
case THING_TYPE_ID_X5:
- rotelModel = RotelModel.X5;
+ model = RotelModel.X5;
break;
default:
- rotelModel = DEFAULT_MODEL;
+ model = DEFAULT_MODEL;
break;
}
RotelThingConfiguration config = getConfigAs(RotelThingConfiguration.class);
- RotelProtocol rotelProtocol = RotelProtocol.HEX;
+ protocol = RotelProtocol.HEX;
if (config.protocol != null && !config.protocol.isEmpty()) {
try {
- rotelProtocol = RotelProtocol.getFromName(config.protocol);
+ protocol = RotelProtocol.getFromName(config.protocol);
} catch (RotelException e) {
+ // Invalid protocol name in configuration, HEX will be considered by default
}
} else {
Map<String, String> properties = editProperties();
String property = properties.get(RotelBindingConstants.PROPERTY_PROTOCOL);
if (property != null && !property.isEmpty()) {
try {
- rotelProtocol = RotelProtocol.getFromName(property);
+ protocol = RotelProtocol.getFromName(property);
} catch (RotelException e) {
+ // Invalid protocol name in thing property, HEX will be considered by default
}
}
}
- logger.debug("rotelProtocol {}", rotelProtocol.getName());
+ logger.debug("rotelProtocol {}", protocol.getName());
Map<RotelSource, String> sourcesCustomLabels = new HashMap<>();
Map<RotelSource, String> sourcesLabels = new HashMap<>();
String readerThreadName = "OH-binding-" + getThing().getUID().getAsString();
- connector = new RotelSimuConnector(rotelModel, rotelProtocol, sourcesLabels, readerThreadName);
-
- if (rotelModel.hasVolumeControl()) {
- maxVolume = rotelModel.getVolumeMax();
- if (!rotelModel.hasDirectVolumeControl()) {
+ if (model.hasVolumeControl()) {
+ maxVolume = model.getVolumeMax();
+ if (!model.hasDirectVolumeControl()) {
logger.info(
"Set minValue to {} and maxValue to {} for your sitemap widget attached to your volume item.",
minVolume, maxVolume);
}
}
- if (rotelModel.hasToneControl()) {
- maxToneLevel = rotelModel.getToneLevelMax();
+ if (model.hasToneControl()) {
+ maxToneLevel = model.getToneLevelMax();
minToneLevel = -maxToneLevel;
logger.info(
"Set minValue to {} and maxValue to {} for your sitemap widget attached to your bass or treble item.",
minToneLevel, maxToneLevel);
}
- if (rotelModel.hasBalanceControl()) {
- maxBalanceLevel = rotelModel.getBalanceLevelMax();
+ if (model.hasBalanceControl()) {
+ maxBalanceLevel = model.getBalanceLevelMax();
minBalanceLevel = -maxBalanceLevel;
logger.info("Set minValue to {} and maxValue to {} for your sitemap widget attached to your balance item.",
minBalanceLevel, maxBalanceLevel);
if (configError != null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configError);
} else {
- for (RotelSource src : rotelModel.getSources()) {
+ for (RotelSource src : model.getSources()) {
// Consider custom input labels
String label = null;
switch (src.getName()) {
sourcesLabels.put(src, (label == null || label.isEmpty()) ? src.getLabel() : label);
}
+ if (protocol == RotelProtocol.HEX) {
+ protocolHandler = new RotelHexProtocolHandler(model, sourcesLabels);
+ } else if (protocol == RotelProtocol.ASCII_V1) {
+ protocolHandler = new RotelAsciiV1ProtocolHandler(model);
+ } else {
+ protocolHandler = new RotelAsciiV2ProtocolHandler(model);
+ }
+
if (USE_SIMULATED_DEVICE) {
- connector = new RotelSimuConnector(rotelModel, rotelProtocol, sourcesLabels, readerThreadName);
+ connector = new RotelSimuConnector(model, protocolHandler, sourcesLabels, readerThreadName);
} else if (config.serialPort != null) {
- connector = new RotelSerialConnector(serialPortManager, config.serialPort, rotelModel, rotelProtocol,
- sourcesLabels, readerThreadName);
+ connector = new RotelSerialConnector(serialPortManager, config.serialPort, model.getBaudRate(),
+ protocolHandler, readerThreadName);
} else {
- connector = new RotelIpConnector(config.host, config.port, rotelModel, rotelProtocol, sourcesLabels,
- readerThreadName);
+ connector = new RotelIpConnector(config.host, config.port, protocolHandler, readerThreadName);
}
- if (rotelModel.hasSourceControl()) {
+ if (model.hasSourceControl()) {
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_SOURCE),
- getStateOptions(rotelModel.getSources(), sourcesCustomLabels));
+ getStateOptions(model.getSources(), sourcesCustomLabels));
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_MAIN_SOURCE),
- getStateOptions(rotelModel.getSources(), sourcesCustomLabels));
+ getStateOptions(model.getSources(), sourcesCustomLabels));
stateDescriptionProvider.setStateOptions(
new ChannelUID(getThing().getUID(), CHANNEL_MAIN_RECORD_SOURCE),
- getStateOptions(rotelModel.getRecordSources(), sourcesCustomLabels));
+ getStateOptions(model.getRecordSources(), sourcesCustomLabels));
}
- if (rotelModel.hasZone2SourceControl()) {
+ if (model.hasZone2SourceControl()) {
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_ZONE2_SOURCE),
- getStateOptions(rotelModel.getZone2Sources(), sourcesCustomLabels));
+ getStateOptions(model.getZone2Sources(), sourcesCustomLabels));
}
- if (rotelModel.hasZone3SourceControl()) {
+ if (model.hasZone3SourceControl()) {
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_ZONE3_SOURCE),
- getStateOptions(rotelModel.getZone3Sources(), sourcesCustomLabels));
+ getStateOptions(model.getZone3Sources(), sourcesCustomLabels));
}
- if (rotelModel.hasZone4SourceControl()) {
+ if (model.hasZone4SourceControl()) {
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_ZONE4_SOURCE),
- getStateOptions(rotelModel.getZone4Sources(), sourcesCustomLabels));
+ getStateOptions(model.getZone4Sources(), sourcesCustomLabels));
}
- if (rotelModel.hasDspControl()) {
+ if (model.hasDspControl()) {
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_DSP),
- rotelModel.getDspStateOptions());
+ model.getDspStateOptions());
stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_MAIN_DSP),
- rotelModel.getDspStateOptions());
+ model.getDspStateOptions());
}
updateStatus(ThingStatus.UNKNOWN);
handlePowerCmd(channel, command, getPowerOnCommand(), getPowerOffCommand());
break;
case CHANNEL_ZONE2_POWER:
- if (connector.getModel().hasZone2Commands()) {
+ if (model.hasZone2Commands()) {
handlePowerCmd(channel, command, RotelCommand.ZONE2_POWER_ON, RotelCommand.ZONE2_POWER_OFF);
- } else if (connector.getModel().getNbAdditionalZones() == 1) {
+ } else if (model.getNbAdditionalZones() == 1) {
if (isPowerOn() || powerZone2) {
- selectZone(2, connector.getModel().getZoneSelectCmd());
+ selectZone(2, model.getZoneSelectCmd());
}
- connector.sendCommand(RotelCommand.ZONE_SELECT);
+ sendCommand(RotelCommand.ZONE_SELECT);
} else {
success = false;
logger.debug("Command {} from channel {} failed: unavailable feature", command, channel);
}
break;
case CHANNEL_ZONE3_POWER:
- if (connector.getModel().hasZone3Commands()) {
+ if (model.hasZone3Commands()) {
handlePowerCmd(channel, command, RotelCommand.ZONE3_POWER_ON, RotelCommand.ZONE3_POWER_OFF);
} else {
success = false;
}
break;
case CHANNEL_ZONE4_POWER:
- if (connector.getModel().hasZone4Commands()) {
+ if (model.hasZone4Commands()) {
handlePowerCmd(channel, command, RotelCommand.ZONE4_POWER_ON, RotelCommand.ZONE4_POWER_OFF);
} else {
success = false;
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
} else {
- src = connector.getModel().getSourceFromName(command.toString());
- cmd = connector.getModel().hasOtherThanPrimaryCommands() ? src.getMainZoneCommand()
- : src.getCommand();
+ src = model.getSourceFromName(command.toString());
+ cmd = model.hasOtherThanPrimaryCommands() ? src.getMainZoneCommand() : src.getCommand();
if (cmd != null) {
- connector.sendCommand(cmd);
- if (connector.getModel().canGetFrequency()) {
+ sendCommand(cmd);
+ if (model.canGetFrequency()) {
// send <new-source> returns
// 1.) the selected <new-source>
// 2.) the used frequency
// at response-time the frequency has the value of <old-source>
// so we must wait a short moment to get the frequency of <new-source>
Thread.sleep(1000);
- connector.sendCommand(RotelCommand.FREQUENCY);
+ sendCommand(RotelCommand.FREQUENCY);
Thread.sleep(100);
updateChannelState(CHANNEL_FREQUENCY);
}
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (connector.getModel().hasOtherThanPrimaryCommands()) {
- src = connector.getModel().getSourceFromName(command.toString());
+ } else if (model.hasOtherThanPrimaryCommands()) {
+ src = model.getSourceFromName(command.toString());
cmd = src.getRecordCommand();
if (cmd != null) {
- connector.sendCommand(cmd);
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined record source command",
command, channel);
}
} else {
- src = connector.getModel().getSourceFromName(command.toString());
+ src = model.getSourceFromName(command.toString());
cmd = src.getCommand();
if (cmd != null) {
- connector.sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
+ sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
Thread.sleep(100);
- connector.sendCommand(cmd);
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined source command", command,
if (!powerZone2) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 2 in standby", command, channel);
- } else if (connector.getModel().hasZone2Commands()) {
- src = connector.getModel().getSourceFromName(command.toString());
+ } else if (model.hasZone2Commands()) {
+ src = model.getSourceFromName(command.toString());
cmd = src.getZone2Command();
if (cmd != null) {
- connector.sendCommand(cmd);
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined zone 2 source command",
command, channel);
}
- } else if (connector.getModel().getNbAdditionalZones() >= 1) {
- src = connector.getModel().getSourceFromName(command.toString());
+ } else if (model.getNbAdditionalZones() >= 1) {
+ src = model.getSourceFromName(command.toString());
cmd = src.getCommand();
if (cmd != null) {
- selectZone(2, connector.getModel().getZoneSelectCmd());
- connector.sendCommand(cmd);
+ selectZone(2, model.getZoneSelectCmd());
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined source command", command,
if (!powerZone3) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 3 in standby", command, channel);
- } else if (connector.getModel().hasZone3Commands()) {
- src = connector.getModel().getSourceFromName(command.toString());
+ } else if (model.hasZone3Commands()) {
+ src = model.getSourceFromName(command.toString());
cmd = src.getZone3Command();
if (cmd != null) {
- connector.sendCommand(cmd);
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined zone 3 source command",
if (!powerZone4) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 4 in standby", command, channel);
- } else if (connector.getModel().hasZone4Commands()) {
- src = connector.getModel().getSourceFromName(command.toString());
+ } else if (model.hasZone4Commands()) {
+ src = model.getSourceFromName(command.toString());
cmd = src.getZone4Command();
if (cmd != null) {
- connector.sendCommand(cmd);
+ sendCommand(cmd);
} else {
success = false;
logger.debug("Command {} from channel {} failed: undefined zone 4 source command",
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
} else {
- connector.sendCommand(connector.getModel().getCommandFromDspName(command.toString()));
+ sendCommand(model.getCommandFromDspName(command.toString()));
}
break;
case CHANNEL_VOLUME:
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl()) {
+ } else if (model.hasVolumeControl()) {
handleVolumeCmd(volume, channel, command, getVolumeUpCommand(), getVolumeDownCommand(),
RotelCommand.VOLUME_SET);
} else {
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl()) {
+ } else if (model.hasVolumeControl()) {
handleVolumeCmd(volume, channel, command, getVolumeUpCommand(), getVolumeDownCommand(),
null);
} else {
success = false;
logger.debug("Command {} from channel {} ignored: fixed volume in zone 2", command,
channel);
- } else if (connector.getModel().hasVolumeControl()
- && connector.getModel().getNbAdditionalZones() >= 1) {
- if (connector.getModel().hasZone2Commands()) {
+ } else if (model.hasVolumeControl() && model.getNbAdditionalZones() >= 1) {
+ if (model.hasZone2Commands()) {
handleVolumeCmd(volumeZone2, channel, command, RotelCommand.ZONE2_VOLUME_UP,
RotelCommand.ZONE2_VOLUME_DOWN, RotelCommand.ZONE2_VOLUME_SET);
} else {
- selectZone(2, connector.getModel().getZoneSelectCmd());
+ selectZone(2, model.getZoneSelectCmd());
handleVolumeCmd(volumeZone2, channel, command, RotelCommand.VOLUME_UP,
RotelCommand.VOLUME_DOWN, RotelCommand.VOLUME_SET);
}
success = false;
logger.debug("Command {} from channel {} ignored: fixed volume in zone 2", command,
channel);
- } else if (connector.getModel().hasVolumeControl()
- && connector.getModel().getNbAdditionalZones() >= 1) {
- if (connector.getModel().hasZone2Commands()) {
+ } else if (model.hasVolumeControl() && model.getNbAdditionalZones() >= 1) {
+ if (model.hasZone2Commands()) {
handleVolumeCmd(volumeZone2, channel, command, RotelCommand.ZONE2_VOLUME_UP,
RotelCommand.ZONE2_VOLUME_DOWN, null);
} else {
- selectZone(2, connector.getModel().getZoneSelectCmd());
+ selectZone(2, model.getZoneSelectCmd());
handleVolumeCmd(volumeZone2, channel, command, RotelCommand.VOLUME_UP,
RotelCommand.VOLUME_DOWN, null);
}
success = false;
logger.debug("Command {} from channel {} ignored: fixed volume in zone 3", command,
channel);
- } else if (connector.getModel().hasVolumeControl() && connector.getModel().hasZone3Commands()) {
+ } else if (model.hasVolumeControl() && model.hasZone3Commands()) {
handleVolumeCmd(volumeZone3, channel, command, RotelCommand.ZONE3_VOLUME_UP,
RotelCommand.ZONE3_VOLUME_DOWN, RotelCommand.ZONE3_VOLUME_SET);
} else {
success = false;
logger.debug("Command {} from channel {} ignored: fixed volume in zone 4", command,
channel);
- } else if (connector.getModel().hasVolumeControl() && connector.getModel().hasZone4Commands()) {
+ } else if (model.hasVolumeControl() && model.hasZone4Commands()) {
handleVolumeCmd(volumeZone4, channel, command, RotelCommand.ZONE4_VOLUME_UP,
RotelCommand.ZONE4_VOLUME_DOWN, RotelCommand.ZONE4_VOLUME_SET);
} else {
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl()) {
- handleMuteCmd(connector.getProtocol() == RotelProtocol.HEX, channel, command,
- getMuteOnCommand(), getMuteOffCommand(), getMuteToggleCommand());
+ } else if (model.hasVolumeControl()) {
+ handleMuteCmd(protocol == RotelProtocol.HEX, channel, command, getMuteOnCommand(),
+ getMuteOffCommand(), getMuteToggleCommand());
} else {
success = false;
logger.debug("Command {} from channel {} failed: unavailable feature", command, channel);
if (!powerZone2) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 2 in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl() && connector.getModel().hasZone2Commands()) {
+ } else if (model.hasVolumeControl() && model.hasZone2Commands()) {
handleMuteCmd(false, channel, command, RotelCommand.ZONE2_MUTE_ON,
RotelCommand.ZONE2_MUTE_OFF, RotelCommand.ZONE2_MUTE_TOGGLE);
} else {
if (!powerZone3) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 3 in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl() && connector.getModel().hasZone3Commands()) {
+ } else if (model.hasVolumeControl() && model.hasZone3Commands()) {
handleMuteCmd(false, channel, command, RotelCommand.ZONE3_MUTE_ON,
RotelCommand.ZONE3_MUTE_OFF, RotelCommand.ZONE3_MUTE_TOGGLE);
} else {
if (!powerZone4) {
success = false;
logger.debug("Command {} from channel {} ignored: zone 4 in standby", command, channel);
- } else if (connector.getModel().hasVolumeControl() && connector.getModel().hasZone4Commands()) {
+ } else if (model.hasVolumeControl() && model.hasZone4Commands()) {
handleMuteCmd(false, channel, command, RotelCommand.ZONE4_MUTE_ON,
RotelCommand.ZONE4_MUTE_OFF, RotelCommand.ZONE4_MUTE_TOGGLE);
} else {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
} else if (command instanceof PlayPauseType && command == PlayPauseType.PLAY) {
- connector.sendCommand(RotelCommand.PLAY);
+ sendCommand(RotelCommand.PLAY);
} else if (command instanceof PlayPauseType && command == PlayPauseType.PAUSE) {
- connector.sendCommand(RotelCommand.PAUSE);
- if (connector.getProtocol() == RotelProtocol.ASCII_V1
- && connector.getModel() != RotelModel.RCD1570
- && connector.getModel() != RotelModel.RCD1572
- && connector.getModel() != RotelModel.RCX1500) {
+ sendCommand(RotelCommand.PAUSE);
+ if (protocol == RotelProtocol.ASCII_V1 && model != RotelModel.RCD1570
+ && model != RotelModel.RCD1572 && model != RotelModel.RCX1500) {
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.PLAY_STATUS);
+ sendCommand(RotelCommand.PLAY_STATUS);
}
} else if (command instanceof NextPreviousType && command == NextPreviousType.NEXT) {
- connector.sendCommand(RotelCommand.TRACK_FORWARD);
+ sendCommand(RotelCommand.TRACK_FORWARD);
} else if (command instanceof NextPreviousType && command == NextPreviousType.PREVIOUS) {
- connector.sendCommand(RotelCommand.TRACK_BACKWORD);
+ sendCommand(RotelCommand.TRACK_BACKWORD);
} else {
success = false;
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (!connector.getModel().hasDimmerControl()) {
+ } else if (!model.hasDimmerControl()) {
success = false;
logger.debug("Command {} from channel {} failed: unavailable feature", command, channel);
} else if (command instanceof PercentType) {
int dimmer = (int) Math.round(((PercentType) command).doubleValue() / 100.0
- * (connector.getModel().getDimmerLevelMax()
- - connector.getModel().getDimmerLevelMin()))
- + connector.getModel().getDimmerLevelMin();
- connector.sendCommand(RotelCommand.DIMMER_LEVEL_SET, dimmer);
+ * (model.getDimmerLevelMax() - model.getDimmerLevelMin()))
+ + model.getDimmerLevelMin();
+ sendCommand(RotelCommand.DIMMER_LEVEL_SET, dimmer);
} else {
success = false;
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (!connector.getModel().hasToneControl()
- || connector.getProtocol() == RotelProtocol.HEX) {
+ } else if (!model.hasToneControl() || protocol == RotelProtocol.HEX) {
success = false;
logger.debug("Command {} from channel {} failed: unavailable feature", command, channel);
} else {
handleTcbypassCmd(channel, command,
- connector.getProtocol() == RotelProtocol.ASCII_V1 ? RotelCommand.TONE_CONTROLS_OFF
+ protocol == RotelProtocol.ASCII_V1 ? RotelCommand.TONE_CONTROLS_OFF
: RotelCommand.TCBYPASS_ON,
- connector.getProtocol() == RotelProtocol.ASCII_V1 ? RotelCommand.TONE_CONTROLS_ON
+ protocol == RotelProtocol.ASCII_V1 ? RotelCommand.TONE_CONTROLS_ON
: RotelCommand.TCBYPASS_OFF);
}
break;
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
- } else if (!connector.getModel().hasBalanceControl()
- || connector.getProtocol() == RotelProtocol.HEX) {
+ } else if (!model.hasBalanceControl() || protocol == RotelProtocol.HEX) {
success = false;
logger.debug("Command {} from channel {} failed: unavailable feature", command, channel);
} else {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
} else {
- handleSpeakerCmd(connector.getProtocol() == RotelProtocol.HEX, channel, command,
- RotelCommand.SPEAKER_A_ON, RotelCommand.SPEAKER_A_OFF,
- RotelCommand.SPEAKER_A_TOGGLE);
+ handleSpeakerCmd(protocol == RotelProtocol.HEX, channel, command, RotelCommand.SPEAKER_A_ON,
+ RotelCommand.SPEAKER_A_OFF, RotelCommand.SPEAKER_A_TOGGLE);
}
break;
case CHANNEL_SPEAKER_B:
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
} else {
- handleSpeakerCmd(connector.getProtocol() == RotelProtocol.HEX, channel, command,
- RotelCommand.SPEAKER_B_ON, RotelCommand.SPEAKER_B_OFF,
- RotelCommand.SPEAKER_B_TOGGLE);
+ handleSpeakerCmd(protocol == RotelProtocol.HEX, channel, command, RotelCommand.SPEAKER_B_ON,
+ RotelCommand.SPEAKER_B_OFF, RotelCommand.SPEAKER_B_TOGGLE);
}
break;
default:
private void handlePowerCmd(String channel, Command command, RotelCommand onCmd, RotelCommand offCmd)
throws RotelException {
if (command instanceof OnOffType && command == OnOffType.ON) {
- connector.sendCommand(onCmd);
+ sendCommand(onCmd);
} else if (command instanceof OnOffType && command == OnOffType.OFF) {
- connector.sendCommand(offCmd);
+ sendCommand(offCmd);
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
}
private void handleVolumeCmd(int current, String channel, Command command, RotelCommand upCmd, RotelCommand downCmd,
@Nullable RotelCommand setCmd) throws RotelException {
if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.INCREASE) {
- connector.sendCommand(upCmd);
+ sendCommand(upCmd);
} else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.DECREASE) {
- connector.sendCommand(downCmd);
+ sendCommand(downCmd);
} else if (command instanceof DecimalType && setCmd == null) {
int value = ((DecimalType) command).intValue();
if (value >= minVolume && value <= maxVolume) {
if (value > current) {
- connector.sendCommand(upCmd);
+ sendCommand(upCmd);
} else if (value < current) {
- connector.sendCommand(downCmd);
+ sendCommand(downCmd);
}
}
} else if (command instanceof PercentType && setCmd != null) {
int value = (int) Math.round(((PercentType) command).doubleValue() / 100.0 * (maxVolume - minVolume))
+ minVolume;
- connector.sendCommand(setCmd, value);
+ sendCommand(setCmd, value);
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
}
RotelCommand offCmd, RotelCommand toggleCmd) throws RotelException {
if (command instanceof OnOffType) {
if (onlyToggle) {
- connector.sendCommand(toggleCmd);
+ sendCommand(toggleCmd);
} else if (command == OnOffType.ON) {
- connector.sendCommand(onCmd);
+ sendCommand(onCmd);
} else if (command == OnOffType.OFF) {
- connector.sendCommand(offCmd);
+ sendCommand(offCmd);
}
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
RotelCommand downCmd, RotelCommand setCmd) throws RotelException, InterruptedException {
if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.INCREASE) {
selectToneControl(nbSelect);
- connector.sendCommand(upCmd);
+ sendCommand(upCmd);
} else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.DECREASE) {
selectToneControl(nbSelect);
- connector.sendCommand(downCmd);
+ sendCommand(downCmd);
} else if (command instanceof DecimalType) {
int value = ((DecimalType) command).intValue();
if (value >= minToneLevel && value <= maxToneLevel) {
- if (connector.getProtocol() != RotelProtocol.HEX) {
- connector.sendCommand(setCmd, value);
+ if (protocol != RotelProtocol.HEX) {
+ sendCommand(setCmd, value);
} else if (value > current) {
selectToneControl(nbSelect);
- connector.sendCommand(upCmd);
+ sendCommand(upCmd);
} else if (value < current) {
selectToneControl(nbSelect);
- connector.sendCommand(downCmd);
+ sendCommand(downCmd);
}
}
} else {
throws RotelException, InterruptedException {
if (command instanceof OnOffType) {
if (command == OnOffType.ON) {
- connector.sendCommand(onCmd);
+ sendCommand(onCmd);
bass = 0;
treble = 0;
updateChannelState(CHANNEL_BASS);
updateChannelState(CHANNEL_TREBLE);
} else if (command == OnOffType.OFF) {
- connector.sendCommand(offCmd);
+ sendCommand(offCmd);
Thread.sleep(200);
- connector.sendCommand(RotelCommand.BASS);
+ sendCommand(RotelCommand.BASS);
Thread.sleep(200);
- connector.sendCommand(RotelCommand.TREBLE);
+ sendCommand(RotelCommand.TREBLE);
}
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
RotelCommand offCmd, RotelCommand toggleCmd) throws RotelException {
if (command instanceof OnOffType) {
if (onlyToggle) {
- connector.sendCommand(toggleCmd);
+ sendCommand(toggleCmd);
} else if (command == OnOffType.ON) {
- connector.sendCommand(onCmd);
+ sendCommand(onCmd);
} else if (command == OnOffType.OFF) {
- connector.sendCommand(offCmd);
+ sendCommand(offCmd);
}
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
private void handleBalanceCmd(String channel, Command command, RotelCommand leftCmd, RotelCommand rightCmd,
RotelCommand setCmd) throws RotelException, InterruptedException {
if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.INCREASE) {
- connector.sendCommand(rightCmd);
+ sendCommand(rightCmd);
} else if (command instanceof IncreaseDecreaseType && command == IncreaseDecreaseType.DECREASE) {
- connector.sendCommand(leftCmd);
+ sendCommand(leftCmd);
} else if (command instanceof DecimalType) {
int value = ((DecimalType) command).intValue();
if (value >= minBalanceLevel && value <= maxBalanceLevel) {
- connector.sendCommand(setCmd, value);
+ sendCommand(setCmd, value);
}
} else {
logger.debug("Command {} from channel {} failed: invalid command value", command, channel);
*/
private void selectToneControl(int nbSelect) throws RotelException, InterruptedException {
// No tone control select command for RSX-1065
- if (connector.getProtocol() == RotelProtocol.HEX && connector.getModel() != RotelModel.RSX1065) {
+ if (protocol == RotelProtocol.HEX && model != RotelModel.RSX1065) {
selectFeature(nbSelect, RotelCommand.RECORD_FONCTION_SELECT, RotelCommand.TONE_CONTROL_SELECT);
}
}
*/
private void selectZone(int zone, @Nullable RotelCommand selectCommand)
throws RotelException, InterruptedException {
- if (connector.getProtocol() == RotelProtocol.HEX && connector.getModel().getNbAdditionalZones() >= 1
- && zone >= 1 && zone != currentZone && selectCommand != null) {
+ if (protocol == RotelProtocol.HEX && model.getNbAdditionalZones() >= 1 && zone >= 1 && zone != currentZone
+ && selectCommand != null) {
int nbSelect;
if (zone < currentZone) {
- nbSelect = zone + connector.getModel().getNbAdditionalZones() - currentZone;
+ nbSelect = zone + model.getNbAdditionalZones() - currentZone;
if (isPowerOn() && selectCommand == RotelCommand.RECORD_FONCTION_SELECT) {
nbSelect++;
}
*/
private void selectFeature(int nbSelect, @Nullable RotelCommand preCmd, RotelCommand selectCmd)
throws RotelException, InterruptedException {
- if (connector.getProtocol() == RotelProtocol.HEX) {
+ if (protocol == RotelProtocol.HEX) {
if (preCmd != null) {
- connector.sendCommand(preCmd);
+ sendCommand(preCmd);
Thread.sleep(100);
}
for (int i = 1; i <= nbSelect; i++) {
- connector.sendCommand(selectCmd);
+ sendCommand(selectCmd);
Thread.sleep(200);
}
}
* @return true if the connection is opened successfully or flase if not
*/
private synchronized boolean openConnection() {
- connector.addEventListener(this);
+ protocolHandler.addEventListener(this);
try {
connector.open();
} catch (RotelException e) {
*/
private synchronized void closeConnection() {
connector.close();
- connector.removeEventListener(this);
+ protocolHandler.removeEventListener(this);
logger.debug("closeConnection(): disconnected");
}
String key = evt.getKey();
String value = evt.getValue().trim();
- if (!RotelConnector.KEY_ERROR.equals(key)) {
+ if (!KEY_ERROR.equals(key)) {
updateStatus(ThingStatus.ONLINE);
}
try {
switch (key) {
- case RotelConnector.KEY_ERROR:
+ case KEY_ERROR:
logger.debug("Reading feedback message failed");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-reading-thread");
closeConnection();
break;
- case RotelConnector.KEY_LINE1:
+ case KEY_LINE1:
frontPanelLine1 = value;
updateChannelState(CHANNEL_LINE1);
break;
- case RotelConnector.KEY_LINE2:
+ case KEY_LINE2:
frontPanelLine2 = value;
updateChannelState(CHANNEL_LINE2);
break;
- case RotelConnector.KEY_ZONE:
+ case KEY_ZONE:
currentZone = Integer.parseInt(value);
break;
- case RotelConnector.KEY_RECORD_SEL:
- selectingRecord = RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value);
+ case KEY_RECORD_SEL:
+ selectingRecord = MSG_VALUE_ON.equalsIgnoreCase(value);
break;
- case RotelConnector.KEY_POWER:
- if (RotelConnector.POWER_ON.equalsIgnoreCase(value)) {
+ case KEY_POWER:
+ if (POWER_ON.equalsIgnoreCase(value)) {
handlePowerOn();
- } else if (RotelConnector.STANDBY.equalsIgnoreCase(value)) {
+ } else if (STANDBY.equalsIgnoreCase(value)) {
handlePowerOff();
- } else if (RotelConnector.POWER_OFF_DELAYED.equalsIgnoreCase(value)) {
+ } else if (POWER_OFF_DELAYED.equalsIgnoreCase(value)) {
schedulePowerOffJob(false);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_POWER_ZONE2:
- if (RotelConnector.POWER_ON.equalsIgnoreCase(value)) {
+ case KEY_POWER_ZONE2:
+ if (POWER_ON.equalsIgnoreCase(value)) {
handlePowerOnZone2();
- } else if (RotelConnector.STANDBY.equalsIgnoreCase(value)) {
+ } else if (STANDBY.equalsIgnoreCase(value)) {
handlePowerOffZone2();
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_POWER_ZONE3:
- if (RotelConnector.POWER_ON.equalsIgnoreCase(value)) {
+ case KEY_POWER_ZONE3:
+ if (POWER_ON.equalsIgnoreCase(value)) {
handlePowerOnZone3();
- } else if (RotelConnector.STANDBY.equalsIgnoreCase(value)) {
+ } else if (STANDBY.equalsIgnoreCase(value)) {
handlePowerOffZone3();
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_POWER_ZONE4:
- if (RotelConnector.POWER_ON.equalsIgnoreCase(value)) {
+ case KEY_POWER_ZONE4:
+ if (POWER_ON.equalsIgnoreCase(value)) {
handlePowerOnZone4();
- } else if (RotelConnector.STANDBY.equalsIgnoreCase(value)) {
+ } else if (STANDBY.equalsIgnoreCase(value)) {
handlePowerOffZone4();
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_VOLUME_MIN:
+ case KEY_VOLUME_MIN:
minVolume = Integer.parseInt(value);
- if (!connector.getModel().hasDirectVolumeControl()) {
+ if (!model.hasDirectVolumeControl()) {
logger.info("Set minValue to {} for your sitemap widget attached to your volume item.",
minVolume);
}
break;
- case RotelConnector.KEY_VOLUME_MAX:
+ case KEY_VOLUME_MAX:
maxVolume = Integer.parseInt(value);
- if (!connector.getModel().hasDirectVolumeControl()) {
+ if (!model.hasDirectVolumeControl()) {
logger.info("Set maxValue to {} for your sitemap widget attached to your volume item.",
maxVolume);
}
break;
- case RotelConnector.KEY_VOLUME:
- if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ case KEY_VOLUME:
+ if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
volume = minVolume;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
volume = maxVolume;
} else {
volume = Integer.parseInt(value);
updateChannelState(CHANNEL_MAIN_VOLUME);
updateChannelState(CHANNEL_MAIN_VOLUME_UP_DOWN);
break;
- case RotelConnector.KEY_MUTE:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_MUTE:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
mute = true;
updateChannelState(CHANNEL_MUTE);
updateChannelState(CHANNEL_MAIN_MUTE);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
mute = false;
updateChannelState(CHANNEL_MUTE);
updateChannelState(CHANNEL_MAIN_MUTE);
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_VOLUME_ZONE2:
+ case KEY_VOLUME_ZONE2:
fixedVolumeZone2 = false;
- if (RotelConnector.MSG_VALUE_FIX.equalsIgnoreCase(value)) {
+ if (MSG_VALUE_FIX.equalsIgnoreCase(value)) {
fixedVolumeZone2 = true;
- } else if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
volumeZone2 = minVolume;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
volumeZone2 = maxVolume;
} else {
volumeZone2 = Integer.parseInt(value);
updateChannelState(CHANNEL_ZONE2_VOLUME);
updateChannelState(CHANNEL_ZONE2_VOLUME_UP_DOWN);
break;
- case RotelConnector.KEY_VOLUME_ZONE3:
+ case KEY_VOLUME_ZONE3:
fixedVolumeZone3 = false;
- if (RotelConnector.MSG_VALUE_FIX.equalsIgnoreCase(value)) {
+ if (MSG_VALUE_FIX.equalsIgnoreCase(value)) {
fixedVolumeZone3 = true;
- } else if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
volumeZone3 = minVolume;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
volumeZone3 = maxVolume;
} else {
volumeZone3 = Integer.parseInt(value);
}
updateChannelState(CHANNEL_ZONE3_VOLUME);
break;
- case RotelConnector.KEY_VOLUME_ZONE4:
+ case KEY_VOLUME_ZONE4:
fixedVolumeZone4 = false;
- if (RotelConnector.MSG_VALUE_FIX.equalsIgnoreCase(value)) {
+ if (MSG_VALUE_FIX.equalsIgnoreCase(value)) {
fixedVolumeZone4 = true;
- } else if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
volumeZone4 = minVolume;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
volumeZone4 = maxVolume;
} else {
volumeZone4 = Integer.parseInt(value);
}
updateChannelState(CHANNEL_ZONE4_VOLUME);
break;
- case RotelConnector.KEY_MUTE_ZONE2:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_MUTE_ZONE2:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
muteZone2 = true;
updateChannelState(CHANNEL_ZONE2_MUTE);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
muteZone2 = false;
updateChannelState(CHANNEL_ZONE2_MUTE);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_MUTE_ZONE3:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_MUTE_ZONE3:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
muteZone3 = true;
updateChannelState(CHANNEL_ZONE3_MUTE);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
muteZone3 = false;
updateChannelState(CHANNEL_ZONE3_MUTE);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_MUTE_ZONE4:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_MUTE_ZONE4:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
muteZone4 = true;
updateChannelState(CHANNEL_ZONE4_MUTE);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
muteZone4 = false;
updateChannelState(CHANNEL_ZONE4_MUTE);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_TONE_MAX:
+ case KEY_TONE_MAX:
maxToneLevel = Integer.parseInt(value);
minToneLevel = -maxToneLevel;
logger.info(
"Set minValue to {} and maxValue to {} for your sitemap widget attached to your bass or treble item.",
minToneLevel, maxToneLevel);
break;
- case RotelConnector.KEY_BASS:
- if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ case KEY_BASS:
+ if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
bass = minToneLevel;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
bass = maxToneLevel;
} else {
bass = Integer.parseInt(value);
updateChannelState(CHANNEL_BASS);
updateChannelState(CHANNEL_MAIN_BASS);
break;
- case RotelConnector.KEY_TREBLE:
- if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ case KEY_TREBLE:
+ if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
treble = minToneLevel;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
treble = maxToneLevel;
} else {
treble = Integer.parseInt(value);
updateChannelState(CHANNEL_TREBLE);
updateChannelState(CHANNEL_MAIN_TREBLE);
break;
- case RotelConnector.KEY_SOURCE:
- source = connector.getModel().getSourceFromCommand(RotelCommand.getFromAsciiCommand(value));
+ case KEY_SOURCE:
+ source = model.getSourceFromCommand(RotelCommand.getFromAsciiCommand(value));
updateChannelState(CHANNEL_SOURCE);
updateChannelState(CHANNEL_MAIN_SOURCE);
break;
- case RotelConnector.KEY_RECORD:
- recordSource = connector.getModel()
- .getRecordSourceFromCommand(RotelCommand.getFromAsciiCommand(value));
+ case KEY_RECORD:
+ recordSource = model.getRecordSourceFromCommand(RotelCommand.getFromAsciiCommand(value));
updateChannelState(CHANNEL_MAIN_RECORD_SOURCE);
break;
- case RotelConnector.KEY_SOURCE_ZONE2:
- sourceZone2 = connector.getModel()
- .getZone2SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
+ case KEY_SOURCE_ZONE2:
+ sourceZone2 = model.getZone2SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
updateChannelState(CHANNEL_ZONE2_SOURCE);
break;
- case RotelConnector.KEY_SOURCE_ZONE3:
- sourceZone3 = connector.getModel()
- .getZone3SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
+ case KEY_SOURCE_ZONE3:
+ sourceZone3 = model.getZone3SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
updateChannelState(CHANNEL_ZONE3_SOURCE);
break;
- case RotelConnector.KEY_SOURCE_ZONE4:
- sourceZone4 = connector.getModel()
- .getZone4SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
+ case KEY_SOURCE_ZONE4:
+ sourceZone4 = model.getZone4SourceFromCommand(RotelCommand.getFromAsciiCommand(value));
updateChannelState(CHANNEL_ZONE4_SOURCE);
break;
- case RotelConnector.KEY_DSP_MODE:
+ case KEY_DSP_MODE:
if ("dolby_pliix_movie".equals(value)) {
value = "dolby_plii_movie";
} else if ("dolby_pliix_music".equals(value)) {
} else if ("dolby_pliix_game".equals(value)) {
value = "dolby_plii_game";
}
- dsp = connector.getModel().getDspFromFeedback(value);
+ dsp = model.getDspFromFeedback(value);
logger.debug("DSP {}", dsp.getName());
updateChannelState(CHANNEL_DSP);
updateChannelState(CHANNEL_MAIN_DSP);
break;
- case RotelConnector.KEY1_PLAY_STATUS:
- case RotelConnector.KEY2_PLAY_STATUS:
- if (RotelConnector.PLAY.equalsIgnoreCase(value)) {
+ case KEY1_PLAY_STATUS:
+ case KEY2_PLAY_STATUS:
+ if (PLAY.equalsIgnoreCase(value)) {
playStatus = RotelPlayStatus.PLAYING;
updateChannelState(CHANNEL_PLAY_CONTROL);
- } else if (RotelConnector.PAUSE.equalsIgnoreCase(value)) {
+ } else if (PAUSE.equalsIgnoreCase(value)) {
playStatus = RotelPlayStatus.PAUSED;
updateChannelState(CHANNEL_PLAY_CONTROL);
- } else if (RotelConnector.STOP.equalsIgnoreCase(value)) {
+ } else if (STOP.equalsIgnoreCase(value)) {
playStatus = RotelPlayStatus.STOPPED;
updateChannelState(CHANNEL_PLAY_CONTROL);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_TRACK:
- if (source.getName().equals("CD") && !connector.getModel().hasSourceControl()) {
+ case KEY_TRACK:
+ if (source.getName().equals("CD") && !model.hasSourceControl()) {
track = Integer.parseInt(value);
updateChannelState(CHANNEL_TRACK);
}
break;
- case RotelConnector.KEY_FREQ:
- if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ case KEY_FREQ:
+ if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
frequency = 0.0;
} else {
// Suppress a potential ending "k" or "K"
}
updateChannelState(CHANNEL_FREQUENCY);
break;
- case RotelConnector.KEY_DIMMER:
+ case KEY_DIMMER:
brightness = Integer.parseInt(value);
updateChannelState(CHANNEL_BRIGHTNESS);
break;
- case RotelConnector.KEY_UPDATE_MODE:
- case RotelConnector.KEY_DISPLAY_UPDATE:
+ case KEY_UPDATE_MODE:
+ case KEY_DISPLAY_UPDATE:
break;
- case RotelConnector.KEY_TONE:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_TONE:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
tcbypass = false;
updateChannelState(CHANNEL_TCBYPASS);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
tcbypass = true;
updateChannelState(CHANNEL_TCBYPASS);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_TCBYPASS:
- if (RotelConnector.MSG_VALUE_ON.equalsIgnoreCase(value)) {
+ case KEY_TCBYPASS:
+ if (MSG_VALUE_ON.equalsIgnoreCase(value)) {
tcbypass = true;
updateChannelState(CHANNEL_TCBYPASS);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
tcbypass = false;
updateChannelState(CHANNEL_TCBYPASS);
} else {
throw new RotelException("Invalid value");
}
break;
- case RotelConnector.KEY_BALANCE:
- if (RotelConnector.MSG_VALUE_MIN.equalsIgnoreCase(value)) {
+ case KEY_BALANCE:
+ if (MSG_VALUE_MIN.equalsIgnoreCase(value)) {
balance = minBalanceLevel;
- } else if (RotelConnector.MSG_VALUE_MAX.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_MAX.equalsIgnoreCase(value)) {
balance = maxBalanceLevel;
} else if (value.toUpperCase().startsWith("L")) {
balance = -Integer.parseInt(value.substring(1));
}
updateChannelState(CHANNEL_BALANCE);
break;
- case RotelConnector.KEY_SPEAKER:
- if (RotelConnector.MSG_VALUE_SPEAKER_A.equalsIgnoreCase(value)) {
+ case KEY_SPEAKER:
+ if (MSG_VALUE_SPEAKER_A.equalsIgnoreCase(value)) {
speakera = true;
speakerb = false;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
- } else if (RotelConnector.MSG_VALUE_SPEAKER_B.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_SPEAKER_B.equalsIgnoreCase(value)) {
speakera = false;
speakerb = true;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
- } else if (RotelConnector.MSG_VALUE_SPEAKER_AB.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_SPEAKER_AB.equalsIgnoreCase(value)) {
speakera = true;
speakerb = true;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
- } else if (RotelConnector.MSG_VALUE_OFF.equalsIgnoreCase(value)) {
+ } else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
speakera = false;
speakerb = false;
updateChannelState(CHANNEL_SPEAKER_A);
synchronized (sequenceLock) {
logger.debug("Power ON job");
try {
- switch (connector.getProtocol()) {
+ switch (protocol) {
case HEX:
- if (connector.getModel().getRespNbChars() <= 13
- && connector.getModel().hasVolumeControl()) {
- connector.sendCommand(getVolumeDownCommand());
+ if (model.getRespNbChars() <= 13 && model.hasVolumeControl()) {
+ sendCommand(getVolumeDownCommand());
Thread.sleep(100);
- connector.sendCommand(getVolumeUpCommand());
+ sendCommand(getVolumeUpCommand());
Thread.sleep(100);
}
- if (connector.getModel().getNbAdditionalZones() >= 1) {
- if (currentZone != 1 && connector.getModel()
- .getZoneSelectCmd() == RotelCommand.RECORD_FONCTION_SELECT) {
- selectZone(1, connector.getModel().getZoneSelectCmd());
+ if (model.getNbAdditionalZones() >= 1) {
+ if (currentZone != 1
+ && model.getZoneSelectCmd() == RotelCommand.RECORD_FONCTION_SELECT) {
+ selectZone(1, model.getZoneSelectCmd());
} else if (!selectingRecord) {
- connector.sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
+ sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
Thread.sleep(100);
}
} else {
- connector.sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
+ sendCommand(RotelCommand.RECORD_FONCTION_SELECT);
Thread.sleep(100);
}
- if (connector.getModel().hasToneControl()) {
- if (connector.getModel() == RotelModel.RSX1065) {
+ if (model.hasToneControl()) {
+ if (model == RotelModel.RSX1065) {
// No tone control select command
- connector.sendCommand(RotelCommand.TREBLE_DOWN);
+ sendCommand(RotelCommand.TREBLE_DOWN);
Thread.sleep(100);
- connector.sendCommand(RotelCommand.TREBLE_UP);
+ sendCommand(RotelCommand.TREBLE_UP);
Thread.sleep(100);
- connector.sendCommand(RotelCommand.BASS_DOWN);
+ sendCommand(RotelCommand.BASS_DOWN);
Thread.sleep(100);
- connector.sendCommand(RotelCommand.BASS_UP);
+ sendCommand(RotelCommand.BASS_UP);
Thread.sleep(100);
} else {
selectFeature(2, null, RotelCommand.TONE_CONTROL_SELECT);
}
break;
case ASCII_V1:
- if (connector.getModel() != RotelModel.RAP1580 && connector.getModel() != RotelModel.RDD1580
- && connector.getModel() != RotelModel.RSP1576
- && connector.getModel() != RotelModel.RSP1582) {
- connector.sendCommand(RotelCommand.UPDATE_AUTO);
+ if (model != RotelModel.RAP1580 && model != RotelModel.RDD1580
+ && model != RotelModel.RSP1576 && model != RotelModel.RSP1582) {
+ sendCommand(RotelCommand.UPDATE_AUTO);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasSourceControl()) {
- connector.sendCommand(RotelCommand.SOURCE);
+ if (model.hasSourceControl()) {
+ sendCommand(RotelCommand.SOURCE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasVolumeControl() || connector.getModel().hasToneControl()) {
- if (connector.getModel().hasVolumeControl()
- && connector.getModel() != RotelModel.RAP1580
- && connector.getModel() != RotelModel.RSP1576
- && connector.getModel() != RotelModel.RSP1582) {
- connector.sendCommand(RotelCommand.VOLUME_GET_MIN);
+ if (model.hasVolumeControl() || model.hasToneControl()) {
+ if (model.hasVolumeControl() && model != RotelModel.RAP1580
+ && model != RotelModel.RSP1576 && model != RotelModel.RSP1582) {
+ sendCommand(RotelCommand.VOLUME_GET_MIN);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.VOLUME_GET_MAX);
+ sendCommand(RotelCommand.VOLUME_GET_MAX);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasToneControl()) {
- connector.sendCommand(RotelCommand.TONE_MAX);
+ if (model.hasToneControl()) {
+ sendCommand(RotelCommand.TONE_MAX);
Thread.sleep(SLEEP_INTV);
}
// Wait enough to be sure to get the min/max values requested just before
Thread.sleep(250);
- if (connector.getModel().hasVolumeControl()) {
- connector.sendCommand(RotelCommand.VOLUME_GET);
+ if (model.hasVolumeControl()) {
+ sendCommand(RotelCommand.VOLUME_GET);
Thread.sleep(SLEEP_INTV);
- if (connector.getModel() != RotelModel.RA11
- && connector.getModel() != RotelModel.RA12
- && connector.getModel() != RotelModel.RCX1500) {
- connector.sendCommand(RotelCommand.MUTE);
+ if (model != RotelModel.RA11 && model != RotelModel.RA12
+ && model != RotelModel.RCX1500) {
+ sendCommand(RotelCommand.MUTE);
Thread.sleep(SLEEP_INTV);
}
}
- if (connector.getModel().hasToneControl()) {
- connector.sendCommand(RotelCommand.BASS);
+ if (model.hasToneControl()) {
+ sendCommand(RotelCommand.BASS);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.TREBLE);
+ sendCommand(RotelCommand.TREBLE);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.TONE_CONTROLS);
+ sendCommand(RotelCommand.TONE_CONTROLS);
Thread.sleep(SLEEP_INTV);
}
}
- if (connector.getModel().hasBalanceControl()) {
- connector.sendCommand(RotelCommand.BALANCE);
+ if (model.hasBalanceControl()) {
+ sendCommand(RotelCommand.BALANCE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasPlayControl()) {
- if (connector.getModel() != RotelModel.RCD1570
- && connector.getModel() != RotelModel.RCD1572
- && (connector.getModel() != RotelModel.RCX1500
- || !source.getName().equals("CD"))) {
- connector.sendCommand(RotelCommand.PLAY_STATUS);
+ if (model.hasPlayControl()) {
+ if (model != RotelModel.RCD1570 && model != RotelModel.RCD1572
+ && (model != RotelModel.RCX1500 || !source.getName().equals("CD"))) {
+ sendCommand(RotelCommand.PLAY_STATUS);
Thread.sleep(SLEEP_INTV);
} else {
- connector.sendCommand(RotelCommand.CD_PLAY_STATUS);
+ sendCommand(RotelCommand.CD_PLAY_STATUS);
Thread.sleep(SLEEP_INTV);
}
}
- if (connector.getModel().hasDspControl()) {
- connector.sendCommand(RotelCommand.DSP_MODE);
+ if (model.hasDspControl()) {
+ sendCommand(RotelCommand.DSP_MODE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().canGetFrequency()) {
- connector.sendCommand(RotelCommand.FREQUENCY);
+ if (model.canGetFrequency()) {
+ sendCommand(RotelCommand.FREQUENCY);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasDimmerControl() && connector.getModel().canGetDimmerLevel()) {
- connector.sendCommand(RotelCommand.DIMMER_LEVEL_GET);
+ if (model.hasDimmerControl() && model.canGetDimmerLevel()) {
+ sendCommand(RotelCommand.DIMMER_LEVEL_GET);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasSpeakerGroups()) {
- connector.sendCommand(RotelCommand.SPEAKER);
+ if (model.hasSpeakerGroups()) {
+ sendCommand(RotelCommand.SPEAKER);
Thread.sleep(SLEEP_INTV);
}
break;
case ASCII_V2:
- connector.sendCommand(RotelCommand.UPDATE_AUTO);
+ sendCommand(RotelCommand.UPDATE_AUTO);
Thread.sleep(SLEEP_INTV);
- if (connector.getModel().hasSourceControl()) {
- connector.sendCommand(RotelCommand.SOURCE);
+ if (model.hasSourceControl()) {
+ sendCommand(RotelCommand.SOURCE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasVolumeControl()) {
- connector.sendCommand(RotelCommand.VOLUME_GET);
+ if (model.hasVolumeControl()) {
+ sendCommand(RotelCommand.VOLUME_GET);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.MUTE);
+ sendCommand(RotelCommand.MUTE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasToneControl()) {
- connector.sendCommand(RotelCommand.BASS);
+ if (model.hasToneControl()) {
+ sendCommand(RotelCommand.BASS);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.TREBLE);
+ sendCommand(RotelCommand.TREBLE);
Thread.sleep(SLEEP_INTV);
- connector.sendCommand(RotelCommand.TCBYPASS);
+ sendCommand(RotelCommand.TCBYPASS);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasBalanceControl()) {
- connector.sendCommand(RotelCommand.BALANCE);
+ if (model.hasBalanceControl()) {
+ sendCommand(RotelCommand.BALANCE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasPlayControl()) {
- connector.sendCommand(RotelCommand.PLAY_STATUS);
+ if (model.hasPlayControl()) {
+ sendCommand(RotelCommand.PLAY_STATUS);
Thread.sleep(SLEEP_INTV);
- if (source.getName().equals("CD") && !connector.getModel().hasSourceControl()) {
- connector.sendCommand(RotelCommand.TRACK);
+ if (source.getName().equals("CD") && !model.hasSourceControl()) {
+ sendCommand(RotelCommand.TRACK);
Thread.sleep(SLEEP_INTV);
}
}
- if (connector.getModel().hasDspControl()) {
- connector.sendCommand(RotelCommand.DSP_MODE);
+ if (model.hasDspControl()) {
+ sendCommand(RotelCommand.DSP_MODE);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().canGetFrequency()) {
- connector.sendCommand(RotelCommand.FREQUENCY);
+ if (model.canGetFrequency()) {
+ sendCommand(RotelCommand.FREQUENCY);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasDimmerControl() && connector.getModel().canGetDimmerLevel()) {
- connector.sendCommand(RotelCommand.DIMMER_LEVEL_GET);
+ if (model.hasDimmerControl() && model.canGetDimmerLevel()) {
+ sendCommand(RotelCommand.DIMMER_LEVEL_GET);
Thread.sleep(SLEEP_INTV);
}
- if (connector.getModel().hasSpeakerGroups()) {
- connector.sendCommand(RotelCommand.SPEAKER);
+ if (model.hasSpeakerGroups()) {
+ sendCommand(RotelCommand.SPEAKER);
Thread.sleep(SLEEP_INTV);
}
break;
synchronized (sequenceLock) {
logger.debug("Power ON zone 2 job");
try {
- if (connector.getProtocol() == RotelProtocol.HEX
- && connector.getModel().getNbAdditionalZones() >= 1) {
- selectZone(2, connector.getModel().getZoneSelectCmd());
- connector.sendCommand(connector.getModel().hasZone2Commands() ? RotelCommand.ZONE2_VOLUME_DOWN
- : RotelCommand.VOLUME_DOWN);
+ if (protocol == RotelProtocol.HEX && model.getNbAdditionalZones() >= 1) {
+ selectZone(2, model.getZoneSelectCmd());
+ sendCommand(
+ model.hasZone2Commands() ? RotelCommand.ZONE2_VOLUME_DOWN : RotelCommand.VOLUME_DOWN);
Thread.sleep(100);
- connector.sendCommand(connector.getModel().hasZone2Commands() ? RotelCommand.ZONE2_VOLUME_UP
- : RotelCommand.VOLUME_UP);
+ sendCommand(model.hasZone2Commands() ? RotelCommand.ZONE2_VOLUME_UP : RotelCommand.VOLUME_UP);
Thread.sleep(100);
}
} catch (RotelException e) {
synchronized (sequenceLock) {
logger.debug("Power ON zone 3 job");
try {
- if (connector.getProtocol() == RotelProtocol.HEX
- && connector.getModel().getNbAdditionalZones() >= 2) {
- selectZone(3, connector.getModel().getZoneSelectCmd());
- connector.sendCommand(connector.getModel().hasZone3Commands() ? RotelCommand.ZONE3_VOLUME_DOWN
- : RotelCommand.VOLUME_DOWN);
+ if (protocol == RotelProtocol.HEX && model.getNbAdditionalZones() >= 2) {
+ selectZone(3, model.getZoneSelectCmd());
+ sendCommand(
+ model.hasZone3Commands() ? RotelCommand.ZONE3_VOLUME_DOWN : RotelCommand.VOLUME_DOWN);
Thread.sleep(100);
- connector.sendCommand(connector.getModel().hasZone3Commands() ? RotelCommand.ZONE3_VOLUME_UP
- : RotelCommand.VOLUME_UP);
+ sendCommand(model.hasZone3Commands() ? RotelCommand.ZONE3_VOLUME_UP : RotelCommand.VOLUME_UP);
Thread.sleep(100);
}
} catch (RotelException e) {
synchronized (sequenceLock) {
logger.debug("Power ON zone 4 job");
try {
- if (connector.getProtocol() == RotelProtocol.HEX
- && connector.getModel().getNbAdditionalZones() >= 3) {
- selectZone(4, connector.getModel().getZoneSelectCmd());
- connector.sendCommand(connector.getModel().hasZone4Commands() ? RotelCommand.ZONE4_VOLUME_DOWN
- : RotelCommand.VOLUME_DOWN);
+ if (protocol == RotelProtocol.HEX && model.getNbAdditionalZones() >= 3) {
+ selectZone(4, model.getZoneSelectCmd());
+ sendCommand(
+ model.hasZone4Commands() ? RotelCommand.ZONE4_VOLUME_DOWN : RotelCommand.VOLUME_DOWN);
Thread.sleep(100);
- connector.sendCommand(connector.getModel().hasZone4Commands() ? RotelCommand.ZONE4_VOLUME_UP
- : RotelCommand.VOLUME_UP);
+ sendCommand(model.hasZone4Commands() ? RotelCommand.ZONE4_VOLUME_UP : RotelCommand.VOLUME_UP);
Thread.sleep(100);
}
} catch (RotelException e) {
synchronized (sequenceLock) {
schedulePowerOffJob(true);
try {
- connector.sendCommand(connector.getModel().getPowerStateCmd());
+ sendCommand(model.getPowerStateCmd());
} catch (RotelException e) {
error = "@text/offline.comm-error-first-command-after-reconnection";
logger.debug("First command after connection failed", e);
state = new StringType(frontPanelLine2);
break;
case CHANNEL_BRIGHTNESS:
- if (isPowerOn() && connector.getModel().hasDimmerControl()) {
- long dimmerPct = Math.round((double) (brightness - connector.getModel().getDimmerLevelMin())
- / (double) (connector.getModel().getDimmerLevelMax()
- - connector.getModel().getDimmerLevelMin())
- * 100.0);
+ if (isPowerOn() && model.hasDimmerControl()) {
+ long dimmerPct = Math.round((double) (brightness - model.getDimmerLevelMin())
+ / (double) (model.getDimmerLevelMax() - model.getDimmerLevelMin()) * 100.0);
state = new PercentType(BigDecimal.valueOf(dimmerPct));
}
break;
* @return the command
*/
private RotelCommand getPowerOnCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_POWER_ON
- : RotelCommand.POWER_ON;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_POWER_ON : RotelCommand.POWER_ON;
}
/**
* @return the command
*/
private RotelCommand getPowerOffCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_POWER_OFF
- : RotelCommand.POWER_OFF;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_POWER_OFF : RotelCommand.POWER_OFF;
}
/**
* @return the command
*/
private RotelCommand getVolumeUpCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_UP
- : RotelCommand.VOLUME_UP;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_UP : RotelCommand.VOLUME_UP;
}
/**
* @return the command
*/
private RotelCommand getVolumeDownCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_DOWN
- : RotelCommand.VOLUME_DOWN;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_DOWN : RotelCommand.VOLUME_DOWN;
}
/**
* @return the command
*/
private RotelCommand getMuteOnCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_ON
- : RotelCommand.MUTE_ON;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_ON : RotelCommand.MUTE_ON;
}
/**
* @return the command
*/
private RotelCommand getMuteOffCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_OFF
- : RotelCommand.MUTE_OFF;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_OFF : RotelCommand.MUTE_OFF;
}
/**
* @return the command
*/
private RotelCommand getMuteToggleCommand() {
- return connector.getModel().hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_TOGGLE
- : RotelCommand.MUTE_TOGGLE;
+ return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_MUTE_TOGGLE : RotelCommand.MUTE_TOGGLE;
+ }
+
+ private void sendCommand(RotelCommand cmd) throws RotelException {
+ sendCommand(cmd, null);
+ }
+
+ /**
+ * Request the Rotel device to execute a command
+ *
+ * @param cmd the command to execute
+ * @param value the integer value to consider for volume, bass or treble adjustment
+ *
+ * @throws RotelException - In case of any problem
+ */
+ private void sendCommand(RotelCommand cmd, @Nullable Integer value) throws RotelException {
+ byte[] message;
+ try {
+ message = protocolHandler.buildCommandMessage(cmd, value);
+ } catch (RotelException e) {
+ // Command not supported
+ logger.debug("sendCommand: {}", e.getMessage());
+ return;
+ }
+ connector.writeOutput(cmd.getName(), message);
+
+ if (connector instanceof RotelSimuConnector) {
+ if ((protocol == RotelProtocol.HEX && cmd.getHexType() != 0)
+ || (protocol == RotelProtocol.ASCII_V1 && cmd.getAsciiCommandV1() != null)
+ || (protocol == RotelProtocol.ASCII_V2 && cmd.getAsciiCommandV2() != null)) {
+ ((RotelSimuConnector) connector).buildFeedbackMessage(cmd, value);
+ }
+ }
}
}
--- /dev/null
+/**
+ * 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);
+ }
+ }
+}
--- /dev/null
+/**
+ * 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 java.util.EventObject;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Rotel event used to notify changes coming from messages received from the Rotel device
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public class RotelMessageEvent extends EventObject {
+
+ private static final long serialVersionUID = 1L;
+ private String key;
+ private String value;
+
+ public RotelMessageEvent(Object source, String key, String value) {
+ super(source);
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
--- /dev/null
+/**
+ * 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 java.util.EventListener;
+import java.util.EventObject;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Rotel Event Listener interface. Handles incoming Rotel message events
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public interface RotelMessageEventListener extends EventListener {
+
+ /**
+ * Event handler method for incoming Rotel message events
+ *
+ * @param event the event object
+ */
+ public void onNewMessageEvent(EventObject event);
+}
--- /dev/null
+/**
+ * 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 org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.rotel.internal.RotelException;
+
+/**
+ * Represents the different kinds of protocols
+ *
+ * @author Laurent Garnier - Initial contribution
+ */
+@NonNullByDefault
+public enum RotelProtocol {
+
+ HEX("HEX"),
+ ASCII_V1("ASCII_V1"),
+ ASCII_V2("ASCII_V2");
+
+ private String name;
+
+ /**
+ * Constructor
+ *
+ * @param name the protocol name
+ */
+ private RotelProtocol(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the protocol name
+ *
+ * @return the protocol name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the protocol associated to a name
+ *
+ * @param name the name used to identify the protocol
+ *
+ * @return the protocol associated to the searched name
+ *
+ * @throws RotelException - If no protocol is associated to the searched name
+ */
+ public static RotelProtocol getFromName(String name) throws RotelException {
+ for (RotelProtocol value : RotelProtocol.values()) {
+ if (value.getName().equals(name)) {
+ return value;
+ }
+ }
+ throw new RotelException("Invalid protocol name: " + name);
+ }
+}
--- /dev/null
+/**
+ * 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);
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}
--- /dev/null
+/**
+ * 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;
+ }
+}