[rotel] Add support of multiple zones (ASCII protocol) (#13136)

* [rotel] Add support of multiple zones (ASCII protocol)

Models C8 and C8+ (distribution amplifiers)

Code factorization.
Simulator updated.

* Set model/firmware thing properties (ASCII V2 protocol)
* Review comment: NumberOf rather than Nb
* Review comment: getZoneCommand: IllegalArgumentException if numZone is
outside 1-4
* Review comment: constant for volumeUpDown
* Simplification: method isPowerOn with numZone parameter (range 0-4)
* Review comment: fix for getPowerOffCommand
* Review comment: new method isZoneAvailable to factorize code
* Review comment: use MAX_NUMBER_OF_ZONES to check range validity

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2022-07-23 13:41:38 +02:00 committed by GitHub
parent 96e78c12e4
commit 41f4ca7a51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1913 additions and 1312 deletions

View File

@ -68,6 +68,7 @@ public class RotelBindingConstants {
public static final String THING_TYPE_ID_RT1570 = "rt1570";
public static final String THING_TYPE_ID_T11 = "t11";
public static final String THING_TYPE_ID_T14 = "t14";
public static final String THING_TYPE_ID_C8 = "c8";
public static final String THING_TYPE_ID_M8 = "m8";
public static final String THING_TYPE_ID_P5 = "p5";
public static final String THING_TYPE_ID_S5 = "s5";
@ -116,6 +117,7 @@ public class RotelBindingConstants {
public static final ThingTypeUID THING_TYPE_RT1570 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_RT1570);
public static final ThingTypeUID THING_TYPE_T11 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_T11);
public static final ThingTypeUID THING_TYPE_T14 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_T14);
public static final ThingTypeUID THING_TYPE_C8 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_C8);
public static final ThingTypeUID THING_TYPE_M8 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_M8);
public static final ThingTypeUID THING_TYPE_P5 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_P5);
public static final ThingTypeUID THING_TYPE_S5 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_S5);
@ -124,45 +126,80 @@ public class RotelBindingConstants {
// List of all Channel ids
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_MAIN_POWER = "mainZone#power";
public static final String CHANNEL_SOURCE = "source";
public static final String CHANNEL_MAIN_SOURCE = "mainZone#source";
public static final String CHANNEL_MAIN_RECORD_SOURCE = "mainZone#recordSource";
public static final String CHANNEL_RECORD_SOURCE = "recordSource";
public static final String CHANNEL_DSP = "dsp";
public static final String CHANNEL_MAIN_DSP = "mainZone#dsp";
public static final String CHANNEL_VOLUME = "volume";
public static final String CHANNEL_MAIN_VOLUME = "mainZone#volume";
public static final String CHANNEL_MAIN_VOLUME_UP_DOWN = "mainZone#volumeUpDown";
public static final String CHANNEL_VOLUME_UP_DOWN = "volumeUpDown";
public static final String CHANNEL_MUTE = "mute";
public static final String CHANNEL_MAIN_MUTE = "mainZone#mute";
public static final String CHANNEL_BASS = "bass";
public static final String CHANNEL_MAIN_BASS = "mainZone#bass";
public static final String CHANNEL_TREBLE = "treble";
public static final String CHANNEL_MAIN_TREBLE = "mainZone#treble";
public static final String CHANNEL_PLAY_CONTROL = "playControl";
public static final String CHANNEL_TRACK = "track";
public static final String CHANNEL_FREQUENCY = "frequency";
public static final String CHANNEL_LINE1 = "mainZone#line1";
public static final String CHANNEL_LINE2 = "mainZone#line2";
public static final String CHANNEL_BRIGHTNESS = "brightness";
public static final String CHANNEL_ZONE2_POWER = "zone2#power";
public static final String CHANNEL_ZONE2_SOURCE = "zone2#source";
public static final String CHANNEL_ZONE2_VOLUME = "zone2#volume";
public static final String CHANNEL_ZONE2_VOLUME_UP_DOWN = "zone2#volumeUpDown";
public static final String CHANNEL_ZONE2_MUTE = "zone2#mute";
public static final String CHANNEL_ZONE3_POWER = "zone3#power";
public static final String CHANNEL_ZONE3_SOURCE = "zone3#source";
public static final String CHANNEL_ZONE3_VOLUME = "zone3#volume";
public static final String CHANNEL_ZONE3_MUTE = "zone3#mute";
public static final String CHANNEL_ZONE4_POWER = "zone4#power";
public static final String CHANNEL_ZONE4_SOURCE = "zone4#source";
public static final String CHANNEL_ZONE4_VOLUME = "zone4#volume";
public static final String CHANNEL_ZONE4_MUTE = "zone4#mute";
public static final String CHANNEL_TCBYPASS = "tcbypass";
public static final String CHANNEL_BALANCE = "balance";
public static final String CHANNEL_SPEAKER_A = "speakera";
public static final String CHANNEL_SPEAKER_B = "speakerb";
public static final String CHANNEL_GROUP_ALL_ZONES = "allZones";
public static final String CHANNEL_ALL_POWER = CHANNEL_GROUP_ALL_ZONES + "#" + CHANNEL_POWER;
public static final String CHANNEL_ALL_BRIGHTNESS = CHANNEL_GROUP_ALL_ZONES + "#" + CHANNEL_BRIGHTNESS;
public static final String CHANNEL_GROUP_MAIN_ZONE = "mainZone";
public static final String CHANNEL_MAIN_POWER = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_POWER;
public static final String CHANNEL_MAIN_SOURCE = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_SOURCE;
public static final String CHANNEL_MAIN_RECORD_SOURCE = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_RECORD_SOURCE;
public static final String CHANNEL_MAIN_DSP = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_DSP;
public static final String CHANNEL_MAIN_VOLUME = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_VOLUME;
public static final String CHANNEL_MAIN_VOLUME_UP_DOWN = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_VOLUME_UP_DOWN;
public static final String CHANNEL_MAIN_MUTE = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_MUTE;
public static final String CHANNEL_MAIN_BASS = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_BASS;
public static final String CHANNEL_MAIN_TREBLE = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_TREBLE;
public static final String CHANNEL_GROUP_ZONE1 = "zone1";
public static final String CHANNEL_ZONE1_SOURCE = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_SOURCE;
public static final String CHANNEL_ZONE1_VOLUME = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_VOLUME;
public static final String CHANNEL_ZONE1_MUTE = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_MUTE;
public static final String CHANNEL_ZONE1_BASS = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_BASS;
public static final String CHANNEL_ZONE1_TREBLE = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_TREBLE;
public static final String CHANNEL_ZONE1_BALANCE = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_BALANCE;
public static final String CHANNEL_ZONE1_FREQUENCY = CHANNEL_GROUP_ZONE1 + "#" + CHANNEL_FREQUENCY;
public static final String CHANNEL_GROUP_ZONE2 = "zone2";
public static final String CHANNEL_ZONE2_POWER = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_POWER;
public static final String CHANNEL_ZONE2_SOURCE = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_SOURCE;
public static final String CHANNEL_ZONE2_VOLUME = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_VOLUME;
public static final String CHANNEL_ZONE2_VOLUME_UP_DOWN = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_VOLUME_UP_DOWN;
public static final String CHANNEL_ZONE2_MUTE = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_MUTE;
public static final String CHANNEL_ZONE2_BASS = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_BASS;
public static final String CHANNEL_ZONE2_TREBLE = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_TREBLE;
public static final String CHANNEL_ZONE2_BALANCE = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_BALANCE;
public static final String CHANNEL_ZONE2_FREQUENCY = CHANNEL_GROUP_ZONE2 + "#" + CHANNEL_FREQUENCY;
public static final String CHANNEL_GROUP_ZONE3 = "zone3";
public static final String CHANNEL_ZONE3_POWER = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_POWER;
public static final String CHANNEL_ZONE3_SOURCE = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_SOURCE;
public static final String CHANNEL_ZONE3_VOLUME = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_VOLUME;
public static final String CHANNEL_ZONE3_MUTE = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_MUTE;
public static final String CHANNEL_ZONE3_BASS = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_BASS;
public static final String CHANNEL_ZONE3_TREBLE = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_TREBLE;
public static final String CHANNEL_ZONE3_BALANCE = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_BALANCE;
public static final String CHANNEL_ZONE3_FREQUENCY = CHANNEL_GROUP_ZONE3 + "#" + CHANNEL_FREQUENCY;
public static final String CHANNEL_GROUP_ZONE4 = "zone4";
public static final String CHANNEL_ZONE4_POWER = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_POWER;
public static final String CHANNEL_ZONE4_SOURCE = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_SOURCE;
public static final String CHANNEL_ZONE4_VOLUME = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_VOLUME;
public static final String CHANNEL_ZONE4_MUTE = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_MUTE;
public static final String CHANNEL_ZONE4_BASS = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_BASS;
public static final String CHANNEL_ZONE4_TREBLE = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_TREBLE;
public static final String CHANNEL_ZONE4_BALANCE = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_BALANCE;
public static final String CHANNEL_ZONE4_FREQUENCY = CHANNEL_GROUP_ZONE4 + "#" + CHANNEL_FREQUENCY;
// List of all properties
public static final String PROPERTY_PROTOCOL = "protocol";
@ -186,13 +223,34 @@ public class RotelBindingConstants {
// 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_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 = "mute";
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_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_INPUT = "input";
public static final String KEY_INPUT_ZONE1 = "input_zone1";
public static final String KEY_INPUT_ZONE2 = "input_zone2";
public static final String KEY_INPUT_ZONE3 = "input_zone3";
public static final String KEY_INPUT_ZONE4 = "input_zone4";
public static final String KEY_VOLUME_ZONE1 = "volume_zone1";
public static final String KEY_MUTE_ZONE1 = "mute_zone1";
public static final String KEY_BASS_ZONE1 = "bass_zone1";
public static final String KEY_BASS_ZONE2 = "bass_zone2";
public static final String KEY_BASS_ZONE3 = "bass_zone3";
public static final String KEY_BASS_ZONE4 = "bass_zone4";
public static final String KEY_TREBLE_ZONE1 = "treble_zone1";
public static final String KEY_TREBLE_ZONE2 = "treble_zone2";
public static final String KEY_TREBLE_ZONE3 = "treble_zone3";
public static final String KEY_TREBLE_ZONE4 = "treble_zone4";
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";
@ -203,10 +261,20 @@ public class RotelBindingConstants {
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_FREQ_ZONE1 = "freq_zone1";
public static final String KEY_FREQ_ZONE2 = "freq_zone2";
public static final String KEY_FREQ_ZONE3 = "freq_zone3";
public static final String KEY_FREQ_ZONE4 = "freq_zone4";
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_BALANCE_ZONE1 = "balance_zone1";
public static final String KEY_BALANCE_ZONE2 = "balance_zone2";
public static final String KEY_BALANCE_ZONE3 = "balance_zone3";
public static final String KEY_BALANCE_ZONE4 = "balance_zone4";
public static final String KEY_SPEAKER = "speaker";
public static final String KEY_MODEL = "model";
public static final String KEY_VERSION = "version";
// Output keys only used by the HEX protocol
public static final String KEY_LINE1 = "line1";
public static final String KEY_LINE2 = "line2";
@ -219,16 +287,11 @@ public class RotelBindingConstants {
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 MSG_VALUE_NONE = "none";
public static final String POWER_ON = "on";
public static final String STANDBY = "standby";
public static final String POWER_OFF_DELAYED = "off_delayed";
@ -243,4 +306,6 @@ public class RotelBindingConstants {
public static final String PLAY = "play";
public static final String PAUSE = "pause";
public static final String STOP = "stop";
public static final int MAX_NUMBER_OF_ZONES = 4;
}

View File

@ -50,8 +50,8 @@ public class RotelHandlerFactory extends BaseThingHandlerFactory {
THING_TYPE_RA12, THING_TYPE_RA1570, THING_TYPE_RA1572, THING_TYPE_RA1592, THING_TYPE_RAP1580,
THING_TYPE_RC1570, THING_TYPE_RC1572, THING_TYPE_RC1590, THING_TYPE_RCD1570, THING_TYPE_RCD1572,
THING_TYPE_RCX1500, THING_TYPE_RDD1580, THING_TYPE_RDG1520, THING_TYPE_RSP1576, THING_TYPE_RSP1582,
THING_TYPE_RT09, THING_TYPE_RT11, THING_TYPE_RT1570, THING_TYPE_T11, THING_TYPE_T14, THING_TYPE_M8,
THING_TYPE_P5, THING_TYPE_S5, THING_TYPE_X3, THING_TYPE_X5)
THING_TYPE_RT09, THING_TYPE_RT11, THING_TYPE_RT1570, THING_TYPE_T11, THING_TYPE_T14, THING_TYPE_C8,
THING_TYPE_M8, THING_TYPE_P5, THING_TYPE_S5, THING_TYPE_X3, THING_TYPE_X5)
.collect(Collectors.toSet()));
private final SerialPortManager serialPortManager;

View File

@ -12,6 +12,7 @@
*/
package org.openhab.binding.rotel.internal;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.MAX_NUMBER_OF_ZONES;
import static org.openhab.binding.rotel.internal.communication.RotelCommand.*;
import static org.openhab.binding.rotel.internal.protocol.ascii.RotelAbstractAsciiProtocolHandler.*;
@ -89,6 +90,8 @@ public enum RotelModel {
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),
C8("C8", 115200, POWER, 21, 3, true, false, 96, true, 10, false, 10, false, null, -1, true, false, true, 4, 0,
(byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, NO_SPECIAL_CHARACTERS),
M8("M8", 115200, 0, null, false, null, false, -1, false, true, 4, 0, NO_SPECIAL_CHARACTERS),
P5("P5", 115200, 20, 96, true, 10, 10, false, -1, true, false, true, 4, 0, NO_SPECIAL_CHARACTERS),
S5("S5", 115200, 0, null, false, null, false, -1, false, true, 4, 0, NO_SPECIAL_CHARACTERS),
@ -101,9 +104,11 @@ public enum RotelModel {
private int sourceCategory;
private int nbAdditionalZones;
private boolean additionalCommands;
private boolean powerControlPerZone;
private @Nullable Integer volumeMax;
private boolean directVolume;
private @Nullable Integer toneLevelMax;
private boolean getBypassStatusAvailable;
private boolean playControl;
private @Nullable RotelCommand zoneSelectCmd;
private int dspCategory;
@ -144,9 +149,9 @@ public enum RotelModel {
@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, 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);
this(name, baudRate, DISPLAY_REFRESH, sourceCategory, nbAdditionalZones, additionalCommands, true, volumeMax,
directVolume, toneLevelMax, false, null, playControl, zoneSelectCmd, dspCategory, false, false, false,
null, null, deviceId, respNbChars, respNbFlags, charsBeforeFlags, flagsMapping, NO_SPECIAL_CHARACTERS);
}
/**
@ -170,9 +175,10 @@ public enum RotelModel {
@Nullable Integer toneLevelMax, boolean playControl, int dspCategory, boolean getFrequencyAvailable,
boolean getDimmerLevelAvailable, @Nullable Integer diummerLevelMin, @Nullable Integer diummerLevelMax,
byte[][] specialCharacters) {
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);
this(name, baudRate, POWER, sourceCategory, 0, false, false, volumeMax, directVolume, toneLevelMax,
toneLevelMax != null, null, playControl, null, dspCategory, getFrequencyAvailable, false,
getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, (byte) 0, 0, 0, false,
RotelFlagsMapping.NO_MAPPING, specialCharacters);
}
/**
@ -198,10 +204,10 @@ public enum RotelModel {
@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, 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);
this(name, baudRate, POWER, sourceCategory, 0, false, false, volumeMax, directVolume, toneLevelMax,
toneLevelMax != null, balanceLevelMax, playControl, null, dspCategory, getFrequencyAvailable,
getSpeakerGroupsAvailable, getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, (byte) 0, 0, 0,
false, RotelFlagsMapping.NO_MAPPING, specialCharacters);
}
/**
@ -213,9 +219,11 @@ public enum RotelModel {
* @param sourceCategory the category from {@link RotelSource}
* @param nbAdditionalZones the number of additional zones
* @param additionalCommands true if other than primary commands are available
* @param powerControlPerZone true if device supports power control per zone
* @param volumeMax the maximum volume or null if no volume management is available
* @param directVolume true if a command to set the volume with a value is available
* @param toneLevelMax the maximum tone level or null if no bass/treble management is available
* @param getBypassStatusAvailable true if the command to get the bypass status for tone control is available
* @param balanceLevelMax the maximum balance level or null if no balance management is available
* @param playControl true if control of source playback is available
* @param zoneSelectCmd the command to be used to select a zone
@ -233,9 +241,9 @@ public enum RotelModel {
* @param specialCharacters the table of special characters that can be found in the standard response message
*/
private RotelModel(String name, int baudRate, RotelCommand powerStateCmd, int sourceCategory, int nbAdditionalZones,
boolean additionalCommands, @Nullable Integer volumeMax, boolean directVolume,
@Nullable Integer toneLevelMax, @Nullable Integer balanceLevelMax, boolean playControl,
@Nullable RotelCommand zoneSelectCmd, int dspCategory, boolean getFrequencyAvailable,
boolean additionalCommands, boolean powerControlPerZone, @Nullable Integer volumeMax, boolean directVolume,
@Nullable Integer toneLevelMax, boolean getBypassStatusAvailable, @Nullable Integer balanceLevelMax,
boolean playControl, @Nullable RotelCommand zoneSelectCmd, int dspCategory, boolean getFrequencyAvailable,
boolean getSpeakerGroupsAvailable, boolean getDimmerLevelAvailable, @Nullable Integer diummerLevelMin,
@Nullable Integer diummerLevelMax, byte deviceId, int respNbChars, int respNbFlags,
boolean charsBeforeFlags, RotelFlagsMapping flagsMapping, byte[][] specialCharacters) {
@ -245,9 +253,11 @@ public enum RotelModel {
this.sourceCategory = sourceCategory;
this.nbAdditionalZones = nbAdditionalZones;
this.additionalCommands = additionalCommands;
this.powerControlPerZone = powerControlPerZone;
this.volumeMax = volumeMax;
this.directVolume = directVolume;
this.toneLevelMax = toneLevelMax;
this.getBypassStatusAvailable = getBypassStatusAvailable;
this.balanceLevelMax = balanceLevelMax;
this.playControl = playControl;
this.zoneSelectCmd = zoneSelectCmd;
@ -302,12 +312,16 @@ public enum RotelModel {
}
/**
* Get the number of additional zones
* Get the number of zones
*
* @return the number of additional zones
* @return the number of zones
*/
public int getNbAdditionalZones() {
return nbAdditionalZones;
public int getNumberOfZones() {
return nbAdditionalZones + 1;
}
private boolean isZoneAvailable(int numZone) {
return numZone >= 1 && numZone <= getNumberOfZones();
}
/**
@ -320,57 +334,40 @@ public enum RotelModel {
}
/**
* Inform whether zone 2 commands are available
* Inform whether zone N commands are available
*
* @return true if zone 2 commands are available
* @param numZone the zone number, 1 for for zone 1 until 4 for zone 4
*
* @return true if zone N commands are available
*/
public boolean hasZone2Commands() {
return nbAdditionalZones >= 1 && additionalCommands;
public boolean hasZoneCommands(int numZone) {
if (numZone < 1 || numZone > MAX_NUMBER_OF_ZONES) {
throw new IllegalArgumentException("numZone must be in range 1-" + MAX_NUMBER_OF_ZONES);
}
return additionalCommands && isZoneAvailable(numZone);
}
/**
* Inform whether zone 3 commands are available
* Inform whether source control is available in a zone
*
* @return true if zone 3 commands are available
*/
public boolean hasZone3Commands() {
return nbAdditionalZones >= 2 && additionalCommands;
}
/**
* Inform whether zone 4 commands are available
*
* @return true if zone 4 commands are available
*/
public boolean hasZone4Commands() {
return nbAdditionalZones >= 3 && additionalCommands;
}
/**
* Inform whether source control is available in the zone 2
* @param numZone the zone number, 1 for zone 1 until 4 for zone 4
*
* @return true if source control is available
*/
public boolean hasZone2SourceControl() {
return sourceCategory >= 1 && nbAdditionalZones >= 1;
public boolean hasZoneSourceControl(int numZone) {
if (numZone < 1 || numZone > MAX_NUMBER_OF_ZONES) {
throw new IllegalArgumentException("numZone must be in range 1-" + MAX_NUMBER_OF_ZONES);
}
return hasSourceControl() && isZoneAvailable(numZone);
}
/**
* Inform whether source control is available in the zone 3
* Inform whether device supports power control per zone
*
* @return true if source control is available
* @return true if device supports power control per zone
*/
public boolean hasZone3SourceControl() {
return sourceCategory >= 1 && nbAdditionalZones >= 2;
}
/**
* Inform whether source control is available in the zone 4
*
* @return true if source control is available
*/
public boolean hasZone4SourceControl() {
return sourceCategory >= 1 && nbAdditionalZones >= 3;
public boolean hasPowerControlPerZone() {
return powerControlPerZone;
}
/**
@ -410,6 +407,15 @@ public enum RotelModel {
return toneLevelMax != null;
}
/**
* Inform whether the command to get the current bypass status for tone control is available
*
* @return true if the command is available
*/
public boolean canGetBypassStatus() {
return getBypassStatusAvailable;
}
/**
* Get the maximum tone level
*
@ -577,40 +583,17 @@ public enum RotelModel {
}
/**
* Get the list of available {@link RotelSource} in the main zone
* Get the list of available {@link RotelSource} in a zone
*
* @return the list of available {@link RotelSource} in the main zone
*/
public List<RotelSource> getMainZoneSources() {
return (hasSourceControl() && hasOtherThanPrimaryCommands()) ? RotelSource.getSources(sourceCategory, 1)
: new ArrayList<>();
}
/**
* Get the list of available {@link RotelSource} in the zone 2
* @param numZone the zone number, 1 for zone 1 until 4 for zone 4
*
* @return the list of available {@link RotelSource} in the zone 2
*/
public List<RotelSource> getZone2Sources() {
return hasZone2SourceControl() ? RotelSource.getSources(sourceCategory, 2) : new ArrayList<>();
}
/**
* Get the list of available {@link RotelSource} in the zone 3
*
* @return the list of available {@link RotelSource} in the zone 3
*/
public List<RotelSource> getZone3Sources() {
return hasZone3SourceControl() ? RotelSource.getSources(sourceCategory, 3) : new ArrayList<>();
}
/**
* Get the list of available {@link RotelSource} in the zone 4
*
* @return the list of available {@link RotelSource} in the zone 4
*/
public List<RotelSource> getZone4Sources() {
return hasZone4SourceControl() ? RotelSource.getSources(sourceCategory, 4) : new ArrayList<>();
public List<RotelSource> getZoneSources(int numZone) {
if (numZone < 1 || numZone > MAX_NUMBER_OF_ZONES) {
throw new IllegalArgumentException("numZone must be in range 1-" + MAX_NUMBER_OF_ZONES);
}
return hasZoneSourceControl(numZone) ? RotelSource.getSources(sourceCategory, numZone) : new ArrayList<>();
}
/**
@ -649,55 +632,20 @@ public enum RotelModel {
}
/**
* Get the main zone source associated to a command
* Get the zone N source associated to a command
*
* @param command the command used to identify the main zone source
* @param command the command used to identify the zone N source
* @param numZone the zone number, 1 for zone 1 until 4 for zone 4
*
* @return the main zone source associated to the searched command
* @return the zone N source associated to the searched command
*
* @throws RotelException - If no main zone source is associated to the searched command
* @throws RotelException - If no zone N source is associated to the searched command
*/
public RotelSource getMainZoneSourceFromCommand(RotelCommand command) throws RotelException {
return RotelSource.getFromCommand(sourceCategory, command, 1);
}
/**
* Get the zone 2 source associated to a command
*
* @param command the command used to identify the zone 2 source
*
* @return the zone 2 source associated to the searched command
*
* @throws RotelException - If no zone 2 source is associated to the searched command
*/
public RotelSource getZone2SourceFromCommand(RotelCommand command) throws RotelException {
return RotelSource.getFromCommand(sourceCategory, command, 2);
}
/**
* Get the zone 3 source associated to a command
*
* @param command the command used to identify the zone 3 source
*
* @return the zone 3 source associated to the searched command
*
* @throws RotelException - If no zone 3 source is associated to the searched command
*/
public RotelSource getZone3SourceFromCommand(RotelCommand command) throws RotelException {
return RotelSource.getFromCommand(sourceCategory, command, 3);
}
/**
* Get the zone 4 source associated to a command
*
* @param command the command used to identify the zone 4 source
*
* @return the zone 4 source associated to the searched command
*
* @throws RotelException - If no zone 4 source is associated to the searched command
*/
public RotelSource getZone4SourceFromCommand(RotelCommand command) throws RotelException {
return RotelSource.getFromCommand(sourceCategory, command, 4);
public RotelSource getZoneSourceFromCommand(RotelCommand command, int numZone) throws RotelException {
if (numZone < 1 || numZone > MAX_NUMBER_OF_ZONES) {
throw new IllegalArgumentException("numZone must be in range 1-" + MAX_NUMBER_OF_ZONES);
}
return RotelSource.getFromCommand(sourceCategory, command, numZone);
}
/**

View File

@ -58,24 +58,30 @@ public enum RotelCommand {
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),
ZONE1_VOLUME_UP("Zone 1 Volume Up", null, "z1:vol_up"),
ZONE1_VOLUME_DOWN("Zone 1 Volume Down", null, "z1:vol_dwn"),
ZONE1_VOLUME_SET("Set Zone 1 Volume to level", null, "z1:vol_"),
ZONE1_MUTE_TOGGLE("Zone 1 Mute Toggle", null, "z1:mute"),
ZONE1_MUTE_ON("Zone 1 Mute On", null, "z1:mute_on"),
ZONE1_MUTE_OFF("Zone 1 Mute Off", null, "z1:mute_off"),
ZONE2_VOLUME_UP("Zone 2 Volume Up", ZONE2_CMD, (byte) 0, null, "z2:vol_up"),
ZONE2_VOLUME_DOWN("Zone 2 Volume Down", ZONE2_CMD, (byte) 1, null, "z2:vol_dwn"),
ZONE2_VOLUME_SET("Set Zone 2 Volume to level", ZONE2_VOLUME_CMD, (byte) 0, null, "z2:vol_"),
ZONE2_MUTE_TOGGLE("Zone 2 Mute Toggle", ZONE2_CMD, (byte) 0x1E, null, "z2:mute"),
ZONE2_MUTE_ON("Zone 2 Mute On", ZONE2_CMD, (byte) 0x6C, null, "z2:mute_on"),
ZONE2_MUTE_OFF("Zone 2 Mute Off", ZONE2_CMD, (byte) 0x6D, null, "z2:mute_off"),
ZONE3_VOLUME_UP("Zone 3 Volume Up", ZONE3_CMD, (byte) 0, null, "z3:vol_up"),
ZONE3_VOLUME_DOWN("Zone 3 Volume Down", ZONE3_CMD, (byte) 1, null, "z3:vol_dwn"),
ZONE3_VOLUME_SET("Set Zone 3 Volume to level", ZONE3_VOLUME_CMD, (byte) 0, null, "z3:vol_"),
ZONE3_MUTE_TOGGLE("Zone 3 Mute Toggle", ZONE3_CMD, (byte) 0x1E, null, "z3:mute"),
ZONE3_MUTE_ON("Zone 3 Mute On", ZONE3_CMD, (byte) 0x6C, null, "z3:mute_on"),
ZONE3_MUTE_OFF("Zone 3 Mute Off", ZONE3_CMD, (byte) 0x6D, null, "z3:mute_off"),
ZONE4_VOLUME_UP("Zone 4 Volume Up", ZONE4_CMD, (byte) 0, null, "z4:vol_up"),
ZONE4_VOLUME_DOWN("Zone 4 Volume Down", ZONE4_CMD, (byte) 1, null, "z4:vol_dwn"),
ZONE4_VOLUME_SET("Set Zone 4 Volume to level", ZONE4_VOLUME_CMD, (byte) 0, null, "z4:vol_"),
ZONE4_MUTE_TOGGLE("Zone 4 Mute Toggle", ZONE4_CMD, (byte) 0x1E, null, "z4:mute"),
ZONE4_MUTE_ON("Zone 4 Mute On", ZONE4_CMD, (byte) 0x6C, null, "z4:mute_on"),
ZONE4_MUTE_OFF("Zone 4 Mute Off", ZONE4_CMD, (byte) 0x6D, null, "z4:mute_off"),
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"),
@ -112,7 +118,12 @@ public enum RotelCommand {
SOURCE_PLAYFI("Source PlayFi", "playfi", "playfi"),
SOURCE_IRADIO("Source iRadio", "iradio", "iradio"),
SOURCE_NETWORK("Source Network", "network", "network"),
SOURCE_INPUT_A("Source Input A", null, "input_a"),
SOURCE_INPUT_B("Source Input B", null, "input_b"),
SOURCE_INPUT_C("Source Input C", null, "input_c"),
SOURCE_INPUT_D("Source Input D", null, "input_d"),
SOURCE("Request current source", "get_current_source", "source?"),
INPUT("Request current source", null, "input?"),
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"),
@ -131,6 +142,10 @@ public enum RotelCommand {
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"),
ZONE1_SOURCE_INPUT_A("Zone 1 Source Input A", null, "z1:input_a"),
ZONE1_SOURCE_INPUT_B("Zone 1 Source Input B", null, "z1:input_b"),
ZONE1_SOURCE_INPUT_C("Zone 1 Source Input C", null, "z1:input_c"),
ZONE1_SOURCE_INPUT_D("Zone 1 Source Input D", null, "z1:input_d"),
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"),
@ -155,6 +170,10 @@ public enum RotelCommand {
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"),
ZONE2_SOURCE_INPUT_A("Zone 2 Source Input A", null, "z2:input_a"),
ZONE2_SOURCE_INPUT_B("Zone 2 Source Input B", null, "z2:input_b"),
ZONE2_SOURCE_INPUT_C("Zone 2 Source Input C", null, "z2:input_c"),
ZONE2_SOURCE_INPUT_D("Zone 2 Source Input D", null, "z2:input_d"),
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"),
@ -167,6 +186,10 @@ public enum RotelCommand {
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"),
ZONE3_SOURCE_INPUT_A("Zone 3 Source Input A", null, "z3:input_a"),
ZONE3_SOURCE_INPUT_B("Zone 3 Source Input B", null, "z3:input_b"),
ZONE3_SOURCE_INPUT_C("Zone 3 Source Input C", null, "z3:input_c"),
ZONE3_SOURCE_INPUT_D("Zone 3 Source Input D", null, "z3:input_d"),
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"),
@ -179,6 +202,10 @@ public enum RotelCommand {
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"),
ZONE4_SOURCE_INPUT_A("Zone 4 Source Input A", null, "z4:input_a"),
ZONE4_SOURCE_INPUT_B("Zone 4 Source Input B", null, "z4:input_b"),
ZONE4_SOURCE_INPUT_C("Zone 4 Source Input C", null, "z4:input_c"),
ZONE4_SOURCE_INPUT_D("Zone 4 Source Input D", null, "z4:input_d"),
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"),
@ -210,6 +237,30 @@ public enum RotelCommand {
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?"),
ZONE1_TREBLE_UP("Zone 1 Treble Up", null, "z1:treble_up"),
ZONE1_TREBLE_DOWN("Zone 1 Treble Down", null, "z1:treble_down"),
ZONE1_TREBLE_SET("Set Zone 1 Treble to level", null, "z1:treble_"),
ZONE1_BASS_UP("Zone 1 Bass Up", null, "z1:bass_up"),
ZONE1_BASS_DOWN("Zone 1 Bass Down", null, "z1:bass_down"),
ZONE1_BASS_SET("Set Zone 1 Bass to level", null, "z1:bass_"),
ZONE2_TREBLE_UP("Zone 2 Treble Up", null, "z2:treble_up"),
ZONE2_TREBLE_DOWN("Zone 2 Treble Down", null, "z2:treble_down"),
ZONE2_TREBLE_SET("Set Zone 2 Treble to level", null, "z2:treble_"),
ZONE2_BASS_UP("Zone 2 Bass Up", null, "z2:bass_up"),
ZONE2_BASS_DOWN("Zone 2 Bass Down", null, "z2:bass_down"),
ZONE2_BASS_SET("Set Zone 2 Bass to level", null, "z2:bass_"),
ZONE3_TREBLE_UP("Zone 3 Treble Up", null, "z3:treble_up"),
ZONE3_TREBLE_DOWN("Zone 3 Treble Down", null, "z3:treble_down"),
ZONE3_TREBLE_SET("Set Zone 3 Treble to level", null, "z3:treble_"),
ZONE3_BASS_UP("Zone 3 Bass Up", null, "z3:bass_up"),
ZONE3_BASS_DOWN("Zone 3 Bass Down", null, "z3:bass_down"),
ZONE3_BASS_SET("Set Zone 3 Bass to level", null, "z3:bass_"),
ZONE4_TREBLE_UP("Zone 4 Treble Up", null, "z4:treble_up"),
ZONE4_TREBLE_DOWN("Zone 4 Treble Down", null, "z4:treble_down"),
ZONE4_TREBLE_SET("Set Zone 4 Treble to level", null, "z4:treble_"),
ZONE4_BASS_UP("Zone 4 Bass Up", null, "z4:bass_up"),
ZONE4_BASS_DOWN("Zone 4 Bass Down", null, "z4:bass_down"),
ZONE4_BASS_SET("Set Zone 4 Bass to level", null, "z4:bass_"),
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"),
@ -234,6 +285,18 @@ public enum RotelCommand {
BALANCE_RIGHT("Balance Right", "balance_right", "balance_r"),
BALANCE_LEFT("Balance Left", "balance_left", "balance_l"),
BALANCE_SET("Set Balance to level", "balance_", "balance_"),
ZONE1_BALANCE_RIGHT("Zone 1 Balance Right", null, "z1:balance_r"),
ZONE1_BALANCE_LEFT("Zone 1 Balance Left", null, "z1:balance_l"),
ZONE1_BALANCE_SET("Set Zone 1 Balance to level", null, "z1:balance_"),
ZONE2_BALANCE_RIGHT("Zone 2 Balance Right", null, "z2:balance_r"),
ZONE2_BALANCE_LEFT("Zone 2 Balance Left", null, "z2:balance_l"),
ZONE2_BALANCE_SET("Set Zone 2 Balance to level", null, "z2:balance_"),
ZONE3_BALANCE_RIGHT("Zone 3 Balance Right", null, "z3:balance_r"),
ZONE3_BALANCE_LEFT("Zone 3 Balance Left", null, "z3:balance_l"),
ZONE3_BALANCE_SET("Set Zone 3 Balance to level", null, "z3:balance_"),
ZONE4_BALANCE_RIGHT("Zone 4 Balance Right", null, "z4:balance_r"),
ZONE4_BALANCE_LEFT("Zone 4 Balance Left", null, "z4:balance_l"),
ZONE4_BALANCE_SET("Set Zone 4 Balance to level", null, "z4:balance_"),
BALANCE("Request current balance setting", "get_balance", "balance?"),
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"),
@ -241,7 +304,9 @@ public enum RotelCommand {
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?");
SPEAKER("Request current active speaker outputs", "get_current_speaker", "speaker?"),
MODEL("Request the model number", null, "model?"),
VERSION("Request the main CPU software version", null, "version?");
public static final byte PRIMARY_COMMAND = (byte) 0x10;

View File

@ -301,14 +301,23 @@ public enum RotelSource {
CAT20_BLUETOOTH(20, "BLUETOOTH", "Bluetooth", RotelCommand.SOURCE_BLUETOOTH),
CAT20_XLR1(20, "XLR1", "XLR 1", RotelCommand.SOURCE_XLR1),
CAT20_XLR2(20, "XLR2", "XLR 2", RotelCommand.SOURCE_XLR1),
CAT20_PCUSB(20, "PCUSB", "PC USB", RotelCommand.SOURCE_PCUSB);
CAT20_PCUSB(20, "PCUSB", "PC USB", RotelCommand.SOURCE_PCUSB),
CAT21_INPUTA(21, "INPUTA", "Input A", RotelCommand.SOURCE_INPUT_A, null, RotelCommand.ZONE1_SOURCE_INPUT_A,
RotelCommand.ZONE2_SOURCE_INPUT_A, RotelCommand.ZONE3_SOURCE_INPUT_A, RotelCommand.ZONE4_SOURCE_INPUT_A),
CAT21_INPUTB(21, "INPUTB", "Input B", RotelCommand.SOURCE_INPUT_B, null, RotelCommand.ZONE1_SOURCE_INPUT_B,
RotelCommand.ZONE2_SOURCE_INPUT_B, RotelCommand.ZONE3_SOURCE_INPUT_B, RotelCommand.ZONE4_SOURCE_INPUT_B),
CAT21_INPUTC(21, "INPUTC", "Input C", RotelCommand.SOURCE_INPUT_C, null, RotelCommand.ZONE1_SOURCE_INPUT_C,
RotelCommand.ZONE2_SOURCE_INPUT_C, RotelCommand.ZONE3_SOURCE_INPUT_C, RotelCommand.ZONE4_SOURCE_INPUT_C),
CAT21_INPUTD(21, "INPUTD", "Input D", RotelCommand.SOURCE_INPUT_D, null, RotelCommand.ZONE1_SOURCE_INPUT_D,
RotelCommand.ZONE2_SOURCE_INPUT_D, RotelCommand.ZONE3_SOURCE_INPUT_D, RotelCommand.ZONE4_SOURCE_INPUT_D);
private int category;
private String name;
private String label;
private @Nullable RotelCommand command;
private @Nullable RotelCommand recordCommand;
private @Nullable RotelCommand mainZoneCommand;
private @Nullable RotelCommand zone1Command;
private @Nullable RotelCommand zone2Command;
private @Nullable RotelCommand zone3Command;
private @Nullable RotelCommand zone4Command;
@ -333,13 +342,13 @@ public enum RotelSource {
* @param label the label of the source
* @param command the command to select the source
* @param recordCommand the command to select the source as source to be recorded
* @param mainZoneCommand the command to select the source in the main zone
* @param zone1Command the command to select the source in the zone 1 or main zone
* @param zone2Command the command to select the source in the zone 2
* @param zone3Command the command to select the source in the zone 3
* @param zone4Command the command to select the source in the zone 4
*/
private RotelSource(int category, String name, String label, @Nullable RotelCommand command,
@Nullable RotelCommand recordCommand, @Nullable RotelCommand mainZoneCommand,
@Nullable RotelCommand recordCommand, @Nullable RotelCommand zone1Command,
@Nullable RotelCommand zone2Command, @Nullable RotelCommand zone3Command,
@Nullable RotelCommand zone4Command) {
this.category = category;
@ -347,7 +356,7 @@ public enum RotelSource {
this.label = label;
this.command = command;
this.recordCommand = recordCommand;
this.mainZoneCommand = mainZoneCommand;
this.zone1Command = zone1Command;
this.zone2Command = zone2Command;
this.zone3Command = zone3Command;
this.zone4Command = zone4Command;
@ -399,47 +408,33 @@ public enum RotelSource {
}
/**
* Get the command to select the source in the main zone
* Get the command to select the source in a zone
*
* @param numZone the zone number, 1 for main zone or zone 1, 2 for zone 2, 3 for zone 3, 4 for zone 4
*
* @return the command
*/
public @Nullable RotelCommand getMainZoneCommand() {
return mainZoneCommand;
}
/**
* Get the command to select the source in the zone 2
*
* @return the command
*/
public @Nullable RotelCommand getZone2Command() {
return zone2Command;
}
/**
* Get the command to select the source in the zone 3
*
* @return the command
*/
public @Nullable RotelCommand getZone3Command() {
return zone3Command;
}
/**
* Get the command to select the source in the zone 4
*
* @return the command
*/
public @Nullable RotelCommand getZone4Command() {
return zone4Command;
public @Nullable RotelCommand getZoneCommand(int numZone) {
switch (numZone) {
case 1:
return zone1Command;
case 2:
return zone2Command;
case 3:
return zone3Command;
case 4:
return zone4Command;
default:
throw new IllegalArgumentException("numZone must be a value between 1 and 4");
}
}
/**
* Get the list of {@link RotelSource} available for a particular category of models
*
* @param category a category of models
* @param type a source type (0 for global source, 1 for main zone, 2 for zone 2, 3 for zone 3, 4 for zone 4 and 5
* for record source)
* @param type a source type (0 for global source, 1 for main zone or zone 1, 2 for zone 2, 3 for zone 3, 4 for zone
* 4 and 5 for record source)
*
* @return the list of {@link RotelSource} available in a zone for a provided category of models
*/
@ -447,9 +442,8 @@ public enum RotelSource {
List<RotelSource> sources = new ArrayList<>();
for (RotelSource value : RotelSource.values()) {
if (value.getCategory() == category && ((type == 0 && value.getCommand() != null)
|| (type == 1 && value.getMainZoneCommand() != null)
|| (type == 2 && value.getZone2Command() != null) || (type == 3 && value.getZone3Command() != null)
|| (type == 4 && value.getZone4Command() != null)
|| (type == 1 && value.getZoneCommand(1) != null) || (type == 2 && value.getZoneCommand(2) != null)
|| (type == 3 && value.getZoneCommand(3) != null) || (type == 4 && value.getZoneCommand(4) != null)
|| (type == 5 && value.getRecordCommand() != null))) {
sources.add(value);
}
@ -481,8 +475,8 @@ public enum RotelSource {
*
* @param category a category of models
* @param command the command used to identify the source
* @param type a source type (0 for global source, 1 for main zone, 2 for zone 2, 3 for zone 3, 4 for zone 4 and 5
* for record source)
* @param type a source type (0 for global source, 1 for main zone or zone 1, 2 for zone 2, 3 for zone 3,
* 4 for zone 4 and 5 for record source)
*
* @return the source associated to the searched command for the provided category of models
*
@ -491,10 +485,10 @@ public enum RotelSource {
public static RotelSource getFromCommand(int category, RotelCommand command, int type) throws RotelException {
for (RotelSource value : RotelSource.values()) {
if (value.getCategory() == category && ((type == 0 && value.getCommand() == command)
|| (type == 1 && value.getMainZoneCommand() == command)
|| (type == 2 && value.getZone2Command() == command)
|| (type == 3 && value.getZone3Command() == command)
|| (type == 4 && value.getZone4Command() == command)
|| (type == 1 && value.getZoneCommand(1) == command)
|| (type == 2 && value.getZoneCommand(2) == command)
|| (type == 3 && value.getZoneCommand(3) == command)
|| (type == 4 && value.getZoneCommand(4) == command)
|| (type == 5 && value.getRecordCommand() == command))) {
return value;
}

View File

@ -12,6 +12,8 @@
*/
package org.openhab.binding.rotel.internal.protocol.ascii;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
@ -58,10 +60,22 @@ public class RotelAsciiV2ProtocolHandler extends RotelAbstractAsciiProtocolHandl
if (value != null) {
switch (cmd) {
case VOLUME_SET:
case ZONE1_VOLUME_SET:
case ZONE2_VOLUME_SET:
case ZONE3_VOLUME_SET:
case ZONE4_VOLUME_SET:
messageStr += String.format("%02d", value);
break;
case BASS_SET:
case ZONE1_BASS_SET:
case ZONE2_BASS_SET:
case ZONE3_BASS_SET:
case ZONE4_BASS_SET:
case TREBLE_SET:
case ZONE1_TREBLE_SET:
case ZONE2_TREBLE_SET:
case ZONE3_TREBLE_SET:
case ZONE4_TREBLE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
@ -71,6 +85,10 @@ public class RotelAsciiV2ProtocolHandler extends RotelAbstractAsciiProtocolHandl
}
break;
case BALANCE_SET:
case ZONE1_BALANCE_SET:
case ZONE2_BALANCE_SET:
case ZONE3_BALANCE_SET:
case ZONE4_BALANCE_SET:
if (value == 0) {
messageStr += "000";
} else if (value > 0) {
@ -97,4 +115,36 @@ public class RotelAsciiV2ProtocolHandler extends RotelAbstractAsciiProtocolHandl
logger.debug("Command \"{}\" => {}", cmd.getName(), messageStr);
return message;
}
@Override
protected void dispatchKeyValue(String key, String value) {
// For distribution amplifiers, we need to split certain values to get the value for each zone
if (model == RotelModel.C8 && value.contains(",")) {
switch (key) {
case KEY_INPUT:
case KEY_VOLUME:
case KEY_MUTE:
case KEY_BASS:
case KEY_TREBLE:
case KEY_BALANCE:
case KEY_FREQ:
String[] splitValues = value.split(",");
int nb = splitValues.length;
if (nb > MAX_NUMBER_OF_ZONES) {
nb = MAX_NUMBER_OF_ZONES;
}
for (int i = 1; i <= nb; i++) {
String val = KEY_INPUT.equals(key) ? String.format("z%d:input_%s", i, splitValues[i - 1])
: splitValues[i - 1];
dispatchKeyValue(String.format("%s_zone%d", key, i), val);
}
break;
default:
super.dispatchKeyValue(key, value);
break;
}
} else {
super.dispatchKeyValue(key, value);
}
}
}

View File

@ -675,7 +675,7 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone2Command();
RotelCommand cmd = source.getZoneCommand(2);
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
@ -709,7 +709,7 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone3Command();
RotelCommand cmd = source.getZoneCommand(3);
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {
@ -743,7 +743,7 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
} else if (!MSG_VALUE_OFF.equalsIgnoreCase(value)) {
RotelSource source = parseSource(value, true);
if (source != null) {
RotelCommand cmd = source.getZone4Command();
RotelCommand cmd = source.getZoneCommand(4);
if (cmd != null) {
value = cmd.getAsciiCommandV2();
if (value != null) {

View File

@ -170,8 +170,14 @@ config.serialPort.description = Serial port to use for connecting to the Rotel d
# channel group types
channel-group.allZones.label = All Zones
channel-group.allZones.description = The controls applied to all zones
channel-group.mainZone.label = Main Zone
channel-group.mainZone.description = The controls of the main zone
channel-group.zone.label = Zone
channel-group.zone.description = The controls of the zone
channel-group.zone1.label = Zone 1
channel-group.zone1.description = The controls of the zone 1
channel-group.zone2.label = Zone 2
channel-group.zone2.description = The controls of the zone 2
channel-group.zone3.label = Zone 3
@ -261,3 +267,7 @@ source.DAB = DAB
source.PLAYFI = PlayFi
source.IRADIO = iRadio
source.NETWORK = Network
source.INPUTA = Input A
source.INPUTB = Input B
source.INPUTC = Input C
source.INPUTD = Input D

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="rotel"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<!-- Rotel C8 Connection Thing Type -->
<thing-type id="c8">
<label>C8 Distribution Amplifier</label>
<description>Connection to the Rotel C8 or C8+ distribution amplifier</description>
<channel-groups>
<channel-group id="allZones" typeId="allZones"/>
<channel-group id="zone1" typeId="zone">
<label>@text/channel-group.zone1.label</label>
<description>@text/channel-group.zone1.description</description>
</channel-group>
<channel-group id="zone2" typeId="zone">
<label>@text/channel-group.zone2.label</label>
<description>@text/channel-group.zone2.description</description>
</channel-group>
<channel-group id="zone3" typeId="zone">
<label>@text/channel-group.zone3.label</label>
<description>@text/channel-group.zone3.description</description>
</channel-group>
<channel-group id="zone4" typeId="zone">
<label>@text/channel-group.zone4.label</label>
<description>@text/channel-group.zone4.description</description>
</channel-group>
</channel-groups>
<properties>
<property name="protocol">ASCII_V2</property>
</properties>
<config-description-ref uri="thing-type:rotel:serialandip2"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -128,6 +128,29 @@
</channels>
</channel-group-type>
<channel-group-type id="allZones">
<label>@text/channel-group.allZones.label</label>
<description>@text/channel-group.allZones.description</description>
<channels>
<channel id="power" typeId="system.power"/>
<channel id="brightness" typeId="brightness"/>
</channels>
</channel-group-type>
<channel-group-type id="zone">
<label>@text/channel-group.zone.label</label>
<description>@text/channel-group.zone.description</description>
<channels>
<channel id="source" typeId="source"/>
<channel id="volume" typeId="system.volume"/>
<channel id="mute" typeId="system.mute"/>
<channel id="bass" typeId="bass"/>
<channel id="treble" typeId="treble"/>
<channel id="balance" typeId="balance"/>
<channel id="frequency" typeId="frequency"/>
</channels>
</channel-group-type>
<channel-type id="source">
<item-type>String</item-type>
<label>Source Input</label>