[rotel] Support added for model RX-1052 (#16486)

* Two new channels to enable/disable speaker A and speaker B

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2024-03-21 22:14:03 +01:00 committed by GitHub
parent d68adfab21
commit 0d308665c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 661 additions and 390 deletions

View File

@ -22,7 +22,7 @@ You can connect it for example to a Raspberry Pi and use [ser2net Linux tool](ht
Recent devices provide a network interface (RJ45 connector).
So you can use the device IP to connect to the device, keeping 9590 as default port.
The binding has been tested with a RSP-1066 and a RSP-1570.
The binding has been tested with an RSP-1066, an RSP-1570 and an RX-1052.
## Supported Things
@ -72,6 +72,7 @@ This binding supports the following thing types:
| rt09 | Connection to the Rotel RT-09 tuner |
| rt11 | Connection to the Rotel RT-11 tuner |
| rt1570 | Connection to the Rotel RT-1570 tuner |
| rx1052 | Connection to the Rotel RX-1052 stereo receiver |
| s5 | Connection to the Rotel Michi S5 stereo amplifier |
| t11 | Connection to the Rotel T11 tuner |
| t14 | Connection to the Rotel T14 tuner |
@ -95,6 +96,7 @@ The thing requires the following configuration parameters:
| Parameter Label | Parameter ID | Description | Accepted values |
|-------------------------|------------------|-------------------------------------------------------|-----------------|
| Serial Port | serialPort | Serial port to use for connecting to the Rotel device | |
| Baud Rate | baudRate | Baud rate to use for connecting to the Rotel device | 19200, 38400 |
| Address | host | Host name or IP address of the Rotel device (IP connection) or the machine connected to the Rotel device (serial over IP) | |
| Port | port | Communication port (IP or serial over IP). For IP connection to the Rotel device, keep the default port (9590) | |
| Protocol Version | Protocol | Choose one of the two protocol versions (depends on your device firmware). Default is ASCII_V2 | ASCII_V1 or ASCII_V2 |
@ -135,7 +137,7 @@ Some have additional parameters listed in the next table:
| rsx1550 | inputLabelCd, inputLabelTuner, inputLabelTape, inputLabelVideo1, inputLabelVideo2, inputLabelVideo3, inputLabelVideo4, inputLabelVideo5, inputLabelMulti |
| rsx1560 | inputLabelCd, inputLabelTuner, inputLabelTape, inputLabelVideo1, inputLabelVideo2, inputLabelVideo3, inputLabelVideo4, inputLabelVideo5, inputLabelMulti |
| rsx1562 | inputLabelCd, inputLabelTuner, inputLabelUsb, inputLabelVideo1, inputLabelVideo2, inputLabelVideo3, inputLabelVideo4, inputLabelVideo5, inputLabelVideo6, inputLabelMulti |
| rx1052 | baudRate, inputLabelVideo1, inputLabelVideo2, inputLabelVideo3, inputLabelVideo4 |
Some notes:
- On Linux, you may get an error stating the serial port cannot be opened when the Rotel binding tries to load. You can get around this by adding the `openhab` user to the `dialout` group like this: `usermod -a -G dialout openhab`.
@ -154,7 +156,7 @@ The following channels are available:
|--------------|---------------------|-----------|---------------------------------------|------------------------------------|
| power, mainZone#power, allZones#power, zone2#power, zone3#power, zone4#power | Power | Switch | Power ON/OFF the equipment or the zone | ON, OFF |
| source, mainZone#source, zone1#source, zone2#source, zone3#source, zone4#source | Source Input | String | Select the source input | CD, TUNER, TAPE, VIDEO1, VIDEO2, VIDEO3, VIDEO4, VIDEO5, VIDEO6, VIDEO7, VIDEO8, USB, PCUSB, MULTI, PHONO, BLUETOOTH, AUX, AUX1, AUX2, AUX1_COAX, AUX1_OPTICAL, COAX1, COAX2, COAX3, OPTICAL1, OPTICAL2, OPTICAL3, XLR, RCD, FM, DAB, PLAYFI, IRADIO, NETWORK, INPUTA, INPUTB, INPUTC, INPUTD |
| mainZone#recordSource | Record Source | String | Select the source to be recorded | CD, TUNER, TAPE, VIDEO1, VIDEO2, VIDEO3, VIDEO4, VIDEO5, VIDEO6, USB, MAIN |
| mainZone#recordSource | Record Source | String | Select the source to be recorded | CD, TUNER, TAPE, VIDEO1, VIDEO2, VIDEO3, VIDEO4, VIDEO5, VIDEO6, USB, PHONO, MAIN |
| dsp, mainZone#dsp | DSP Mode | String | Select the DSP mode | NONE, STEREO3, STEREO5, STEREO7, STEREO9, STEREO11, MUSIC1, MUSIC2, MUSIC3, MUSIC4, PROLOGIC, PLIICINEMA, PLIIMUSIC, PLIIGAME, PLIIXCINEMA, PLIIXMUSIC, PLIIXGAME, PLIIZ, NEO6MUSIC, NEO6CINEMA, ATMOS, NEURALX, BYPASS |
| mainZone#volumeUpDown, zone2#volumeUpDown | Volume | Number | Increase or decrease the volume | INCREASE, DECREASE, value |
| volume, mainZone#volume, zone1#volume, zone2#volume, zone3#volume, zone4#volume | Volume | Dimmer | Adjust the volume | value between 0 and 100 |
@ -221,6 +223,7 @@ Here are the list of channels available for each thing type:
| rt09 | power, source, playControl, brightness |
| rt11 | power, source, radioPreset, brightness |
| rt1570 | power, source, radioPreset, brightness |
| rx1052 | mainZone#power, mainZone#source, mainZone#recordSource, mainZone#volume, mainZone#mute, mainZone#bass, mainZone#treble, mainZone#speakera, mainZone#speakerb, mainZone#line1, mainZone#otherCommand, zone2#power, zone2#source, zone2#volume, zone2#mute, zone3#power, zone3#source, zone3#volume, zone3#mute, zone4#power, zone4#source, zone4#volume, zone4#mute |
| s5 | power, brightness |
| t11 | power, source, radioPreset, brightness |
| t14 | power, source, radioPreset, brightness |
@ -270,6 +273,7 @@ Here are the available commands for the otherCommand channel depending on the th
| rsx1550 | DSP_TOGGLE, PROLOGIC_TOGGLE, DOLBY_TOGGLE, PLII_PANORAMA_TOGGLE, PLII_DIMENSION_UP, PLII_DIMENSION_DOWN, PLII_CENTER_WIDTH_UP, PLII_CENTER_WIDTH_DOWN, DDEX_TOGGLE, NEO6_TOGGLE, NEXT_MODE, TUNE_UP, TUNE_DOWN, PRESET_UP, PRESET_DOWN, FREQUENCY_UP, FREQUENCY_DOWN, MEMORY, BAND_TOGGLE, AM, FM, TUNE_PRESET_TOGGLE, TUNING_MODE_SELECT, PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, TUNER_DISPLAY, RDS_PTY, RDS_TP, RDS_TA, FM_MONO_TOGGLE, ZONE2_TUNE_UP, ZONE2_TUNE_DOWN, ZONE2_PRESET_UP, ZONE2_PRESET_DOWN, ZONE2_FREQUENCY_UP, ZONE2_FREQUENCY_DOWN, ZONE2_BAND_TOGGLE, ZONE2_AM, ZONE2_FM, ZONE2_TUNE_PRESET_TOGGLE, ZONE2_TUNING_MODE_SELECT, ZONE2_PRESET_MODE_SELECT, ZONE2_PRESET_SCAN, ZONE2_FM_MONO_TOGGLE, ZONE3_TUNE_UP, ZONE3_TUNE_DOWN, ZONE3_PRESET_UP, ZONE3_PRESET_DOWN, ZONE3_FREQUENCY_UP, ZONE3_FREQUENCY_DOWN, ZONE3_BAND_TOGGLE, ZONE3_AM, ZONE3_FM, ZONE3_TUNE_PRESET_TOGGLE, ZONE3_TUNING_MODE_SELECT, ZONE3_PRESET_MODE_SELECT, ZONE3_PRESET_SCAN, ZONE3_FM_MONO_TOGGLE, ZONE4_TUNE_UP, ZONE4_TUNE_DOWN, ZONE4_PRESET_UP, ZONE4_PRESET_DOWN, ZONE4_FREQUENCY_UP, ZONE4_FREQUENCY_DOWN, ZONE4_BAND_TOGGLE, ZONE4_AM, ZONE4_FM, ZONE4_TUNE_PRESET_TOGGLE, ZONE4_TUNING_MODE_SELECT, ZONE4_PRESET_MODE_SELECT, ZONE4_PRESET_SCAN, ZONE4_FM_MONO_TOGGLE, KEY1, KEY2, KEY3, KEY4, KEY5, KEY6, KEY7, KEY8, KEY9, KEY0, ZONE2_KEY1, ZONE2_KEY2, ZONE2_KEY3, ZONE2_KEY4, ZONE2_KEY5, ZONE2_KEY6, ZONE2_KEY7, ZONE2_KEY8, ZONE2_KEY9, ZONE2_KEY0, ZONE3_KEY1, ZONE3_KEY2, ZONE3_KEY3, ZONE3_KEY4, ZONE3_KEY5, ZONE3_KEY6, ZONE3_KEY7, ZONE3_KEY8, ZONE3_KEY9, ZONE3_KEY0, ZONE4_KEY1, ZONE4_KEY2, ZONE4_KEY3, ZONE4_KEY4, ZONE4_KEY5, ZONE4_KEY6, ZONE4_KEY7, ZONE4_KEY8, ZONE4_KEY9, ZONE4_KEY0, MENU, UP, DOWN, LEFT, RIGHT, ENTER, RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT, DYNAMIC_RANGE, DIGITAL_INPUT_SELECT, ZONE_TOGGLE, CENTER_TRIM, SUB_TRIM, SURROUND_TRIM, CINEMA_EQ_TOGGLE, POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE, ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, OUTPUT_RESOLUTION, HDMI_AMP_MODE, HDMI_TV_MODE, RESET_FACTORY |
| rsx1560 | DSP_TOGGLE, PROLOGIC_TOGGLE, DOLBY_TOGGLE, PLII_PANORAMA_TOGGLE, PLII_DIMENSION_UP, PLII_DIMENSION_DOWN, PLII_CENTER_WIDTH_UP, PLII_CENTER_WIDTH_DOWN, DDEX_TOGGLE, NEO6_TOGGLE, NEXT_MODE, TUNE_UP, TUNE_DOWN, PRESET_UP, PRESET_DOWN, FREQUENCY_UP, FREQUENCY_DOWN, MEMORY, BAND_TOGGLE, AM, FM, TUNE_PRESET_TOGGLE, TUNING_MODE_SELECT, PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, TUNER_DISPLAY, RDS_PTY, RDS_TP, RDS_TA, FM_MONO_TOGGLE, ZONE2_TUNE_UP, ZONE2_TUNE_DOWN, ZONE2_PRESET_UP, ZONE2_PRESET_DOWN, ZONE2_FREQUENCY_UP, ZONE2_FREQUENCY_DOWN, ZONE2_BAND_TOGGLE, ZONE2_AM, ZONE2_FM, ZONE2_TUNE_PRESET_TOGGLE, ZONE2_TUNING_MODE_SELECT, ZONE2_PRESET_MODE_SELECT, ZONE2_PRESET_SCAN, ZONE2_FM_MONO_TOGGLE, ZONE3_TUNE_UP, ZONE3_TUNE_DOWN, ZONE3_PRESET_UP, ZONE3_PRESET_DOWN, ZONE3_FREQUENCY_UP, ZONE3_FREQUENCY_DOWN, ZONE3_BAND_TOGGLE, ZONE3_AM, ZONE3_FM, ZONE3_TUNE_PRESET_TOGGLE, ZONE3_TUNING_MODE_SELECT, ZONE3_PRESET_MODE_SELECT, ZONE3_PRESET_SCAN, ZONE3_FM_MONO_TOGGLE, ZONE4_TUNE_UP, ZONE4_TUNE_DOWN, ZONE4_PRESET_UP, ZONE4_PRESET_DOWN, ZONE4_FREQUENCY_UP, ZONE4_FREQUENCY_DOWN, ZONE4_BAND_TOGGLE, ZONE4_AM, ZONE4_FM, ZONE4_TUNE_PRESET_TOGGLE, ZONE4_TUNING_MODE_SELECT, ZONE4_PRESET_MODE_SELECT, ZONE4_PRESET_SCAN, ZONE4_FM_MONO_TOGGLE, KEY1, KEY2, KEY3, KEY4, KEY5, KEY6, KEY7, KEY8, KEY9, KEY0, ZONE2_KEY1, ZONE2_KEY2, ZONE2_KEY3, ZONE2_KEY4, ZONE2_KEY5, ZONE2_KEY6, ZONE2_KEY7, ZONE2_KEY8, ZONE2_KEY9, ZONE2_KEY0, ZONE3_KEY1, ZONE3_KEY2, ZONE3_KEY3, ZONE3_KEY4, ZONE3_KEY5, ZONE3_KEY6, ZONE3_KEY7, ZONE3_KEY8, ZONE3_KEY9, ZONE3_KEY0, ZONE4_KEY1, ZONE4_KEY2, ZONE4_KEY3, ZONE4_KEY4, ZONE4_KEY5, ZONE4_KEY6, ZONE4_KEY7, ZONE4_KEY8, ZONE4_KEY9, ZONE4_KEY0, MENU, UP, DOWN, LEFT, RIGHT, ENTER, RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT, DYNAMIC_RANGE, DIGITAL_INPUT_SELECT, ZONE_TOGGLE, CENTER_TRIM, SUB_TRIM, SURROUND_TRIM, CINEMA_EQ_TOGGLE, POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE, ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, OUTPUT_RESOLUTION, HDMI_AMP_MODE, HDMI_TV_MODE, RESET_FACTORY |
| rsx1562 | STEREO_BYPASS_TOGGLE, DSP_TOGGLE, PROLOGIC_TOGGLE, DOLBY_TOGGLE, PLII_PANORAMA_TOGGLE, PLII_DIMENSION_UP, PLII_DIMENSION_DOWN, PLII_CENTER_WIDTH_UP, PLII_CENTER_WIDTH_DOWN, DDEX_TOGGLE, NEO6_TOGGLE, NEXT_MODE, TUNE_UP, TUNE_DOWN, PRESET_UP, PRESET_DOWN, FREQUENCY_UP, FREQUENCY_DOWN, MEMORY, BAND_TOGGLE, AM, FM, TUNE_PRESET_TOGGLE, TUNING_MODE_SELECT, PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, TUNER_DISPLAY, RDS_PTY, RDS_TP, RDS_TA, FM_MONO_TOGGLE, ZONE2_TUNE_UP, ZONE2_TUNE_DOWN, ZONE2_PRESET_UP, ZONE2_PRESET_DOWN, ZONE2_FREQUENCY_UP, ZONE2_FREQUENCY_DOWN, ZONE2_BAND_TOGGLE, ZONE2_AM, ZONE2_FM, ZONE2_TUNE_PRESET_TOGGLE, ZONE2_TUNING_MODE_SELECT, ZONE2_PRESET_MODE_SELECT, ZONE2_PRESET_SCAN, ZONE2_FM_MONO_TOGGLE, ZONE3_TUNE_UP, ZONE3_TUNE_DOWN, ZONE3_PRESET_UP, ZONE3_PRESET_DOWN, ZONE3_FREQUENCY_UP, ZONE3_FREQUENCY_DOWN, ZONE3_BAND_TOGGLE, ZONE3_AM, ZONE3_FM, ZONE3_TUNE_PRESET_TOGGLE, ZONE3_TUNING_MODE_SELECT, ZONE3_PRESET_MODE_SELECT, ZONE3_PRESET_SCAN, ZONE3_FM_MONO_TOGGLE, ZONE4_TUNE_UP, ZONE4_TUNE_DOWN, ZONE4_PRESET_UP, ZONE4_PRESET_DOWN, ZONE4_FREQUENCY_UP, ZONE4_FREQUENCY_DOWN, ZONE4_BAND_TOGGLE, ZONE4_AM, ZONE4_FM, ZONE4_TUNE_PRESET_TOGGLE, ZONE4_TUNING_MODE_SELECT, ZONE4_PRESET_MODE_SELECT, ZONE4_PRESET_SCAN, ZONE4_FM_MONO_TOGGLE, KEY1, KEY2, KEY3, KEY4, KEY5, KEY6, KEY7, KEY8, KEY9, KEY0, ZONE2_KEY1, ZONE2_KEY2, ZONE2_KEY3, ZONE2_KEY4, ZONE2_KEY5, ZONE2_KEY6, ZONE2_KEY7, ZONE2_KEY8, ZONE2_KEY9, ZONE2_KEY0, ZONE3_KEY1, ZONE3_KEY2, ZONE3_KEY3, ZONE3_KEY4, ZONE3_KEY5, ZONE3_KEY6, ZONE3_KEY7, ZONE3_KEY8, ZONE3_KEY9, ZONE3_KEY0, ZONE4_KEY1, ZONE4_KEY2, ZONE4_KEY3, ZONE4_KEY4, ZONE4_KEY5, ZONE4_KEY6, ZONE4_KEY7, ZONE4_KEY8, ZONE4_KEY9, ZONE4_KEY0, MENU, EXIT, UP_PRESSED, UP_RELEASED, DOWN_PRESSED, DOWN_RELEASED, LEFT_PRESSED, LEFT_RELEASED, RIGHT_PRESSED, RIGHT_RELEASED, ENTER, RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT, DYNAMIC_RANGE, DIGITAL_INPUT_SELECT, ZONE_TOGGLE, CENTER_TRIM, SUB_TRIM, SURROUND_TRIM, CINEMA_EQ_TOGGLE, POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE, ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, OUTPUT_RESOLUTION, HDMI_AMP_MODE, HDMI_TV_MODE, ROOM_EQ_TOGGLE, SPEAKER_SETTING_TOGGLE, RESET_FACTORY |
| rx1052 | TUNE_UP, TUNE_DOWN, PRESET_UP, PRESET_DOWN, FREQUENCY_UP, FREQUENCY_DOWN, MEMORY, BAND_TOGGLE, AM, FM, TUNE_PRESET_TOGGLE, TUNING_MODE_SELECT, PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, FM_MONO_TOGGLE, ZONE2_TUNE_UP, ZONE2_TUNE_DOWN, ZONE2_PRESET_UP, ZONE2_PRESET_DOWN, ZONE2_FREQUENCY_UP, ZONE2_FREQUENCY_DOWN, ZONE2_BAND_TOGGLE, ZONE2_AM, ZONE2_FM, ZONE2_TUNE_PRESET_TOGGLE, ZONE2_TUNING_MODE_SELECT, ZONE2_PRESET_MODE_SELECT, ZONE2_PRESET_SCAN, ZONE2_FM_MONO_TOGGLE, ZONE3_TUNE_UP, ZONE3_TUNE_DOWN, ZONE3_PRESET_UP, ZONE3_PRESET_DOWN, ZONE3_FREQUENCY_UP, ZONE3_FREQUENCY_DOWN, ZONE3_BAND_TOGGLE, ZONE3_AM, ZONE3_FM, ZONE3_TUNE_PRESET_TOGGLE, ZONE3_TUNING_MODE_SELECT, ZONE3_PRESET_MODE_SELECT, ZONE3_PRESET_SCAN, ZONE3_FM_MONO_TOGGLE, ZONE4_TUNE_UP, ZONE4_TUNE_DOWN, ZONE4_PRESET_UP, ZONE4_PRESET_DOWN, ZONE4_FREQUENCY_UP, ZONE4_FREQUENCY_DOWN, ZONE4_BAND_TOGGLE, ZONE4_AM, ZONE4_FM, ZONE4_TUNE_PRESET_TOGGLE, ZONE4_TUNING_MODE_SELECT, ZONE4_PRESET_MODE_SELECT, ZONE4_PRESET_SCAN, ZONE4_FM_MONO_TOGGLE, KEY1, KEY2, KEY3, KEY4, KEY5, KEY6, KEY7, KEY8, KEY9, KEY0, ZONE2_KEY1, ZONE2_KEY2, ZONE2_KEY3, ZONE2_KEY4, ZONE2_KEY5, ZONE2_KEY6, ZONE2_KEY7, ZONE2_KEY8, ZONE2_KEY9, ZONE2_KEY0, ZONE3_KEY1, ZONE3_KEY2, ZONE3_KEY3, ZONE3_KEY4, ZONE3_KEY5, ZONE3_KEY6, ZONE3_KEY7, ZONE3_KEY8, ZONE3_KEY9, ZONE3_KEY0, ZONE4_KEY1, ZONE4_KEY2, ZONE4_KEY3, ZONE4_KEY4, ZONE4_KEY5, ZONE4_KEY6, ZONE4_KEY7, ZONE4_KEY8, ZONE4_KEY9, ZONE4_KEY0, POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE, ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT, ZONE_TOGGLE |
| x3 | PLAY, PAUSE, STOP, TRACK_FWD, TRACK_BACK |
| x5 | PLAY, PAUSE, STOP, TRACK_FWD, TRACK_BACK |

View File

@ -42,6 +42,7 @@ public class RotelBindingConstants {
public static final String THING_TYPE_ID_RSX1550 = "rsx1550";
public static final String THING_TYPE_ID_RSX1560 = "rsx1560";
public static final String THING_TYPE_ID_RSX1562 = "rsx1562";
public static final String THING_TYPE_ID_RX1052 = "rx1052";
public static final String THING_TYPE_ID_A11 = "a11";
public static final String THING_TYPE_ID_A12 = "a12";
public static final String THING_TYPE_ID_A14 = "a14";
@ -91,6 +92,7 @@ public class RotelBindingConstants {
public static final ThingTypeUID THING_TYPE_RSX1550 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_RSX1550);
public static final ThingTypeUID THING_TYPE_RSX1560 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_RSX1560);
public static final ThingTypeUID THING_TYPE_RSX1562 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_RSX1562);
public static final ThingTypeUID THING_TYPE_RX1052 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_RX1052);
public static final ThingTypeUID THING_TYPE_A11 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_A11);
public static final ThingTypeUID THING_TYPE_A12 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_A12);
public static final ThingTypeUID THING_TYPE_A14 = new ThingTypeUID(BINDING_ID, THING_TYPE_ID_A14);
@ -163,6 +165,8 @@ public class RotelBindingConstants {
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_MAIN_SPEAKER_A = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_SPEAKER_A;
public static final String CHANNEL_MAIN_SPEAKER_B = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_SPEAKER_B;
public static final String CHANNEL_MAIN_OTHER_COMMAND = CHANNEL_GROUP_MAIN_ZONE + "#" + CHANNEL_OTHER_COMMAND;
public static final String CHANNEL_GROUP_ZONE1 = "zone1";
@ -332,6 +336,7 @@ public class RotelBindingConstants {
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_POWER_ZONES = "power_zones";
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";

View File

@ -14,10 +14,7 @@ package org.openhab.binding.rotel.internal;
import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -42,17 +39,16 @@ import org.osgi.service.component.annotations.Reference;
@Component(configurationPid = "binding.rotel", service = ThingHandlerFactory.class)
public class RotelHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(Stream
.of(THING_TYPE_RSP1066, THING_TYPE_RSP1068, THING_TYPE_RSP1069, THING_TYPE_RSP1098, THING_TYPE_RSP1570,
THING_TYPE_RSP1572, THING_TYPE_RSX1055, THING_TYPE_RSX1056, THING_TYPE_RSX1057, THING_TYPE_RSX1058,
THING_TYPE_RSX1065, THING_TYPE_RSX1067, THING_TYPE_RSX1550, THING_TYPE_RSX1560, THING_TYPE_RSX1562,
THING_TYPE_A11, THING_TYPE_A12, THING_TYPE_A14, THING_TYPE_CD11, THING_TYPE_CD14, THING_TYPE_RA11,
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_C8,
THING_TYPE_M8, THING_TYPE_P5, THING_TYPE_S5, THING_TYPE_X3, THING_TYPE_X5)
.collect(Collectors.toSet()));
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_RSP1066, THING_TYPE_RSP1068,
THING_TYPE_RSP1069, THING_TYPE_RSP1098, THING_TYPE_RSP1570, THING_TYPE_RSP1572, THING_TYPE_RSX1055,
THING_TYPE_RSX1056, THING_TYPE_RSX1057, THING_TYPE_RSX1058, THING_TYPE_RSX1065, THING_TYPE_RSX1067,
THING_TYPE_RSX1550, THING_TYPE_RSX1560, THING_TYPE_RSX1562, THING_TYPE_RX1052, THING_TYPE_A11,
THING_TYPE_A12, THING_TYPE_A14, THING_TYPE_CD11, THING_TYPE_CD14, THING_TYPE_RA11, 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_C8, THING_TYPE_M8,
THING_TYPE_P5, THING_TYPE_S5, THING_TYPE_X3, THING_TYPE_X5);
private final SerialPortManager serialPortManager;
private final RotelStateDescriptionOptionProvider stateDescriptionProvider;

View File

@ -23,6 +23,8 @@ 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.RotelDsp;
import org.openhab.binding.rotel.internal.communication.RotelFlagInfo;
import org.openhab.binding.rotel.internal.communication.RotelFlagInfoType;
import org.openhab.binding.rotel.internal.communication.RotelFlagsMapping;
import org.openhab.binding.rotel.internal.communication.RotelSource;
import org.openhab.binding.rotel.internal.protocol.RotelProtocol;
@ -44,19 +46,19 @@ public enum RotelModel {
concatenate(DSP_CMDS_SET1, MENU2_CTRL_CMDS, OTHER_CMDS_SET1), (byte) 0xA1, 42, 5, true,
RotelFlagsMapping.MAPPING2),
RSP1069("RSP-1069", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2,
concatenate(DSP_CMDS_SET1, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2), (byte) 0xA2, 42, 5, true,
RotelFlagsMapping.MAPPING5),
concatenate(DSP_CMDS_SET1, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2, OTHER_CMDS_SET4), (byte) 0xA2,
42, 5, true, RotelFlagsMapping.MAPPING3),
RSP1098("RSP-1098", 19200, 1, 1, true, 96, true, 6, false, ZONE_SELECT, 2,
concatenate(DSP_CMDS_SET1, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, List.of(REMOTE_VOLUME_UP, REMOTE_VOLUME_DOWN)),
(byte) 0xA0, 13, 8, true, RotelFlagsMapping.MAPPING1),
RSP1570("RSP-1570", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3,
concatenate(DSP_CMDS_SET2, DSP_CMDS_SET1, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2,
List.of(RESET_FACTORY)),
(byte) 0xA3, 42, 5, true, RotelFlagsMapping.MAPPING5),
OTHER_CMDS_SET4, List.of(RESET_FACTORY)),
(byte) 0xA3, 42, 5, true, RotelFlagsMapping.MAPPING3),
RSP1572("RSP-1572", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4,
concatenate(DSP_CMDS_SET2, DSP_CMDS_SET1, NUMERIC_KEY_CMDS, MENU3_CTRL_CMDS, OTHER_CMDS_SET1,
OTHER_CMDS_SET4),
(byte) 0xA5, 42, 5, true, RotelFlagsMapping.MAPPING5),
OTHER_CMDS_SET2, OTHER_CMDS_SET4, OTHER_CMDS_SET8),
(byte) 0xA5, 42, 5, true, RotelFlagsMapping.MAPPING3),
RSX1055("RSX-1055", 19200, 3, 1, false, 90, false, 12, false, ZONE_SELECT, 1,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET1, NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1),
(byte) 0xC3, 13, 8, true, RotelFlagsMapping.MAPPING1),
@ -65,13 +67,20 @@ public enum RotelModel {
MENU2_CTRL_CMDS, OTHER_CMDS_SET1),
(byte) 0xC5, 13, 8, true, RotelFlagsMapping.MAPPING1),
RSX1057("RSX-1057", 19200, 1, 1, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, ZONE2_TUNER_CMDS_SET2, NUMERIC_KEY_CMDS, ZONE2_NUMERIC_KEY_CMDS,
MENU2_CTRL_CMDS, OTHER_CMDS_SET1),
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, TUNER_CMDS_SET3, ZONE2_TUNER_CMDS_SET2, NUMERIC_KEY_CMDS,
ZONE2_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1),
(byte) 0xC7, 13, 8, true, RotelFlagsMapping.MAPPING1),
RSX1058("RSX-1058", 38400, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 2,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2),
(byte) 0xC8, 13, 8, true, RotelFlagsMapping.MAPPING4),
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, TUNER_CMDS_SET3, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2, OTHER_CMDS_SET4),
(byte) 0xC8, 13, 8, true, new RotelFlagsMapping(List.of(//
new RotelFlagInfo(RotelFlagInfoType.MULTI_INPUT, 3, 1), //
new RotelFlagInfo(RotelFlagInfoType.ZONE2, 1, 7), //
new RotelFlagInfo(RotelFlagInfoType.ZONE3, 2, 7), //
new RotelFlagInfo(RotelFlagInfoType.ZONE4, 6, 2), //
new RotelFlagInfo(RotelFlagInfoType.CENTER, 8, 6), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_LEFT, 8, 4), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_RIGHT, 8, 3)))),
RSX1065("RSX-1065", 19200, 3, 1, false, 96, false, 12, false, ZONE_SELECT, 1,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET1, NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET3),
(byte) 0xC1, 42, 5, true, RotelFlagsMapping.MAPPING2),
@ -80,19 +89,34 @@ public enum RotelModel {
MENU2_CTRL_CMDS, OTHER_CMDS_SET1),
(byte) 0xC4, 42, 5, true, RotelFlagsMapping.MAPPING2),
RSX1550("RSX-1550", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, TUNER_CMDS_SET3, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2, OTHER_CMDS_SET4,
List.of(RESET_FACTORY)),
(byte) 0xC9, 13, 8, true, RotelFlagsMapping.MAPPING3),
(byte) 0xC9, 13, 8, true, new RotelFlagsMapping(List.of(//
new RotelFlagInfo(RotelFlagInfoType.MULTI_INPUT, 4, 7), //
new RotelFlagInfo(RotelFlagInfoType.ZONE2, 1, 7), //
new RotelFlagInfo(RotelFlagInfoType.ZONE3, 2, 7), //
new RotelFlagInfo(RotelFlagInfoType.ZONE4, 6, 2), //
new RotelFlagInfo(RotelFlagInfoType.CENTER, 5, 6), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_LEFT, 5, 4), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_RIGHT, 5, 3)))),
RSX1560("RSX-1560", 115200, 1, 3, true, 96, true, 6, false, RECORD_FONCTION_SELECT, 3,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2,
concatenate(DSP_CMDS_SET1, TUNER_CMDS_SET2, TUNER_CMDS_SET3, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU2_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2, OTHER_CMDS_SET4,
List.of(RESET_FACTORY)),
(byte) 0xCA, 42, 5, true, RotelFlagsMapping.MAPPING5),
(byte) 0xCA, 42, 5, true, RotelFlagsMapping.MAPPING3),
RSX1562("RSX-1562", 115200, 2, 3, true, 96, true, null, false, RECORD_FONCTION_SELECT, 4,
concatenate(DSP_CMDS_SET2, DSP_CMDS_SET1, TUNER_CMDS_SET2, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS,
ZONE234_NUMERIC_KEY_CMDS, MENU3_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET4),
(byte) 0xCC, 42, 5, true, RotelFlagsMapping.MAPPING5),
concatenate(DSP_CMDS_SET2, DSP_CMDS_SET1, TUNER_CMDS_SET2, TUNER_CMDS_SET3, ZONE234_TUNER_CMDS_SET1,
NUMERIC_KEY_CMDS, ZONE234_NUMERIC_KEY_CMDS, MENU3_CTRL_CMDS, OTHER_CMDS_SET1, OTHER_CMDS_SET2,
OTHER_CMDS_SET4, OTHER_CMDS_SET8),
(byte) 0xCC, 42, 5, true, RotelFlagsMapping.MAPPING3),
RX1052("RX-1052", 38400, 22, 3, true, 90, true, 10, false, RECORD_FONCTION_SELECT, -1,
concatenate(TUNER_CMDS_SET2, ZONE234_TUNER_CMDS_SET1, NUMERIC_KEY_CMDS, ZONE234_NUMERIC_KEY_CMDS,
OTHER_CMDS_SET2, OTHER_CMDS_SET9),
(byte) 0x61, 11, 5, false, new RotelFlagsMapping(List.of(//
new RotelFlagInfo(RotelFlagInfoType.ZONE, 1, 1), //
new RotelFlagInfo(RotelFlagInfoType.SPEAKER_A, 1, 3), //
new RotelFlagInfo(RotelFlagInfoType.SPEAKER_B, 1, 2)))),
A11("A11", 115200, 4, 96, true, 10, 15, false, -1, false, true, true, 6, 0, SRC_CTRL_CMDS_SET1,
NO_SPECIAL_CHARACTERS),
A12("A12", 115200, 5, 96, true, 10, 15, false, -1, true, true, true, 6, 0,
@ -154,7 +178,7 @@ public enum RotelModel {
T11("T11", 115200, 12, null, false, null, false, -1, false, true, 6, 0, List.of(), NO_SPECIAL_CHARACTERS),
T14("T14", 115200, 13, null, false, null, false, -1, false, true, 6, 0, List.of(), NO_SPECIAL_CHARACTERS),
C8("C8", 115200, POWER, 21, 3, true, false, 96, true, 10, false, 10, false, null, -1, true, false, true, 4, 0,
List.of(), (byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, NO_SPECIAL_CHARACTERS),
List.of(), (byte) 0, 0, 0, false, new RotelFlagsMapping(), NO_SPECIAL_CHARACTERS),
M8("M8", 115200, 0, null, false, null, false, -1, false, true, 4, 0, List.of(), NO_SPECIAL_CHARACTERS),
P5("P5", 115200, 20, 96, true, 10, 10, false, -1, true, false, true, 4, 0, SRC_CTRL_CMDS_SET1,
NO_SPECIAL_CHARACTERS),
@ -248,7 +272,7 @@ public enum RotelModel {
this(name, baudRate, POWER, sourceCategory, 0, false, false, volumeMax, directVolume, toneLevelMax,
toneLevelMax != null, null, playControl, null, dspCategory, getFrequencyAvailable, false,
getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, otherCommands, (byte) 0, 0, 0, false,
RotelFlagsMapping.NO_MAPPING, specialCharacters);
new RotelFlagsMapping(), specialCharacters);
}
/**
@ -279,7 +303,7 @@ public enum RotelModel {
this(name, baudRate, POWER, sourceCategory, 0, false, false, volumeMax, directVolume, toneLevelMax,
toneLevelMax != null, balanceLevelMax, playControl, null, dspCategory, getFrequencyAvailable,
getSpeakerGroupsAvailable, getDimmerLevelAvailable, diummerLevelMin, diummerLevelMax, otherCommands,
(byte) 0, 0, 0, false, RotelFlagsMapping.NO_MAPPING, specialCharacters);
(byte) 0, 0, 0, false, new RotelFlagsMapping(), specialCharacters);
}
/**
@ -736,103 +760,41 @@ public enum RotelModel {
}
/**
* Inform whether the multiple input source is set to ON in the flags
* Inform whether the information is present in flags
*
* @param flags the flag from the standard response message
* @param infoType the type of information
*
* @return true if the multiple input source is ON
*
* @throws RotelException - If this information is not present in the flags for this model
* @return true if the information is available
*/
public boolean isMultiInputOn(byte[] flags) throws RotelException {
return flagsMapping.isMultiInputOn(flags);
public boolean isInfoPresentInFlags(RotelFlagInfoType infoType) {
return flagsMapping.isInfoPresent(infoType);
}
/**
* Set the multiple input source to ON or OFF in the flags
* Inform whether the information is set to ON in the flags
*
* @param infoType the type of information
* @param flags the flag from the standard response message
*
* @return true if the information is ON
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public boolean isInfoOnInFlags(RotelFlagInfoType infoType, byte[] flags) throws RotelException {
return flagsMapping.isInfoOn(infoType, flags);
}
/**
* Set the information to ON or OFF in the flags
*
* @param infoType the type of information
* @param flags the flag from the standard response message
* @param on true for ON and false for OFF
*
* @throws RotelException - If this information is not present in the flags for this model
* @return true if the information was updated in the flags, false if not
*/
public void setMultiInput(byte[] flags, boolean on) throws RotelException {
flagsMapping.setMultiInput(flags, on);
}
/**
* Inform whether the zone 2 is set to ON in the flags
*
* @param flags the flag from the standard response message
*
* @return true if the zone 2 is ON
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public boolean isZone2On(byte[] flags) throws RotelException {
return flagsMapping.isZone2On(flags);
}
/**
* Set the zone 2 to ON or OFF in the flags
*
* @param flags the flag from the standard response message
* @param on true for ON and false for OFF
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public void setZone2(byte[] flags, boolean on) throws RotelException {
flagsMapping.setZone2(flags, on);
}
/**
* Inform whether the zone 3 is set to ON in the flags
*
* @param flags the flag from the standard response message
*
* @return true if the zone 3 is ON
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public boolean isZone3On(byte[] flags) throws RotelException {
return flagsMapping.isZone3On(flags);
}
/**
* Set the zone 3 to ON or OFF in the flags
*
* @param flags the flag from the standard response message
* @param on true for ON and false for OFF
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public void setZone3(byte[] flags, boolean on) throws RotelException {
flagsMapping.setZone3(flags, on);
}
/**
* Inform whether the zone 4 is set to ON in the flags
*
* @param flags the flag from the standard response message
*
* @return true if the zone 4 is ON
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public boolean isZone4On(byte[] flags) throws RotelException {
return flagsMapping.isZone4On(flags);
}
/**
* Set the zone 4 to ON or OFF in the flags
*
* @param flags the flag from the standard response message
* @param on true for ON and false for OFF
*
* @throws RotelException - If this information is not present in the flags for this model
*/
public void setZone4(byte[] flags, boolean on) throws RotelException {
flagsMapping.setZone4(flags, on);
public boolean setInfoInFlags(RotelFlagInfoType infoType, byte[] flags, boolean on) {
return flagsMapping.setInfo(infoType, flags, on);
}
/**

View File

@ -150,6 +150,7 @@ public enum RotelCommand {
"main_zone_video5"),
MAIN_ZONE_SOURCE_VIDEO6("Main Zone Source Video 6", MAIN_ZONE_CMD, (byte) 0x94, "main_zone_video6",
"main_zone_video6"),
MAIN_ZONE_SOURCE_PHONO("Main Zone Source Phono", MAIN_ZONE_CMD, (byte) 0x35, "main_zone_phono", "main_zone_phono"),
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"),
@ -166,6 +167,7 @@ public enum RotelCommand {
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_PHONO("Record Source Phono", RECORD_SRC_CMD, (byte) 0x35, "record_phono", "record_phono"),
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"),
@ -178,6 +180,7 @@ public enum RotelCommand {
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_PHONO("Zone 2 Source Phono", ZONE2_CMD, (byte) 0x35, "zone2_phono", "zone2_phono"),
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"),
@ -194,6 +197,7 @@ public enum RotelCommand {
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_PHONO("Zone 3 Source Phono", ZONE3_CMD, (byte) 0x35, "zone3_phono", "zone3_phono"),
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"),
@ -210,6 +214,7 @@ public enum RotelCommand {
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_PHONO("Zone 4 Source Phono", ZONE4_CMD, (byte) 0x35, "zone4_phono", "zone4_phono"),
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"),
@ -520,7 +525,8 @@ public enum RotelCommand {
RDS_PTY, RDS_TP, RDS_TA, FM_MONO_TOGGLE);
public static final List<RotelCommand> TUNER_CMDS_SET2 = List.of(TUNE_UP, TUNE_DOWN, PRESET_UP, PRESET_DOWN,
FREQUENCY_UP, FREQUENCY_DOWN, MEMORY, BAND_TOGGLE, AM, FM, TUNE_PRESET_TOGGLE, TUNING_MODE_SELECT,
PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, TUNER_DISPLAY, RDS_PTY, RDS_TP, RDS_TA, FM_MONO_TOGGLE);
PRESET_MODE_SELECT, FREQUENCY_DIRECT, PRESET_SCAN, FM_MONO_TOGGLE);
public static final List<RotelCommand> TUNER_CMDS_SET3 = List.of(TUNER_DISPLAY, RDS_PTY, RDS_TP, RDS_TA);
public static final List<RotelCommand> ZONE2_TUNER_CMDS_SET1 = List.of(ZONE2_TUNE_UP, ZONE2_TUNE_DOWN,
ZONE2_BAND_TOGGLE, ZONE2_AM, ZONE2_FM, ZONE2_TUNE_PRESET_TOGGLE, ZONE2_TUNING_MODE_SELECT,
ZONE2_PRESET_MODE_SELECT, ZONE2_PRESET_SCAN, ZONE2_FM_MONO_TOGGLE);
@ -563,17 +569,18 @@ public enum RotelCommand {
public static final List<RotelCommand> OTHER_CMDS_SET1 = List.of(RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT,
DYNAMIC_RANGE, DIGITAL_INPUT_SELECT, ZONE_TOGGLE, CENTER_TRIM, SUB_TRIM, SURROUND_TRIM, CINEMA_EQ_TOGGLE);
public static final List<RotelCommand> OTHER_CMDS_SET2 = List.of(POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE,
ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, OUTPUT_RESOLUTION, HDMI_AMP_MODE,
HDMI_TV_MODE);
ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE);
public static final List<RotelCommand> OTHER_CMDS_SET3 = List.of(RECORD_FONCTION_SELECT, DYNAMIC_RANGE,
DIGITAL_INPUT_SELECT, ZONE_TOGGLE, CENTER_TRIM, SUB_TRIM, SURROUND_TRIM, CINEMA_EQ_TOGGLE);
public static final List<RotelCommand> OTHER_CMDS_SET4 = List.of(POWER_OFF_ALL_ZONES, PARTY_MODE_TOGGLE,
ZONE2_PARTY_MODE_TOGGLE, ZONE3_PARTY_MODE_TOGGLE, ZONE4_PARTY_MODE_TOGGLE, OUTPUT_RESOLUTION, HDMI_AMP_MODE,
HDMI_TV_MODE, ROOM_EQ_TOGGLE, SPEAKER_SETTING_TOGGLE, RESET_FACTORY);
public static final List<RotelCommand> OTHER_CMDS_SET4 = List.of(OUTPUT_RESOLUTION, HDMI_AMP_MODE, HDMI_TV_MODE);
public static final List<RotelCommand> OTHER_CMDS_SET5 = List.of(POWER_MODE, POWER_MODE_QUICK, POWER_MODE_NORMAL,
RESET_FACTORY);
public static final List<RotelCommand> OTHER_CMDS_SET6 = List.of(POWER_MODE, RESET_FACTORY);
public static final List<RotelCommand> OTHER_CMDS_SET7 = List.of(NEXT_MODE, RESET_FACTORY);
public static final List<RotelCommand> OTHER_CMDS_SET8 = List.of(ROOM_EQ_TOGGLE, SPEAKER_SETTING_TOGGLE,
RESET_FACTORY);
public static final List<RotelCommand> OTHER_CMDS_SET9 = List.of(RECORD_FONCTION_SELECT, TONE_CONTROL_SELECT,
ZONE_TOGGLE);
public static final byte PRIMARY_COMMAND = (byte) 0x10;
@ -766,4 +773,27 @@ public enum RotelCommand {
return Stream.of(list1, list2, list3, list4, list5, list6, list7, list8, list9).flatMap(Collection::stream)
.collect(Collectors.toList());
}
public static List<RotelCommand> concatenate(List<RotelCommand> list1, List<RotelCommand> list2,
List<RotelCommand> list3, List<RotelCommand> list4, List<RotelCommand> list5, List<RotelCommand> list6,
List<RotelCommand> list7, List<RotelCommand> list8, List<RotelCommand> list9, List<RotelCommand> list10) {
return Stream.of(list1, list2, list3, list4, list5, list6, list7, list8, list9, list10)
.flatMap(Collection::stream).collect(Collectors.toList());
}
public static List<RotelCommand> concatenate(List<RotelCommand> list1, List<RotelCommand> list2,
List<RotelCommand> list3, List<RotelCommand> list4, List<RotelCommand> list5, List<RotelCommand> list6,
List<RotelCommand> list7, List<RotelCommand> list8, List<RotelCommand> list9, List<RotelCommand> list10,
List<RotelCommand> list11) {
return Stream.of(list1, list2, list3, list4, list5, list6, list7, list8, list9, list10, list11)
.flatMap(Collection::stream).collect(Collectors.toList());
}
public static List<RotelCommand> concatenate(List<RotelCommand> list1, List<RotelCommand> list2,
List<RotelCommand> list3, List<RotelCommand> list4, List<RotelCommand> list5, List<RotelCommand> list6,
List<RotelCommand> list7, List<RotelCommand> list8, List<RotelCommand> list9, List<RotelCommand> list10,
List<RotelCommand> list11, List<RotelCommand> list12) {
return Stream.of(list1, list2, list3, list4, list5, list6, list7, list8, list9, list10, list11, list12)
.flatMap(Collection::stream).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2010-2024 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;
/**
* Record describing one information in response flags (HEX protocol)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public record RotelFlagInfo(RotelFlagInfoType infoType, int flagNumber, int bitNumber) {
}

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2024 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;
/**
* Represents the different types of information that can be included in response flags (HEX protocol)
*
* @author Laurent Garnier - Initial contribution
*/
@NonNullByDefault
public enum RotelFlagInfoType {
MULTI_INPUT,
ZONE2,
ZONE3,
ZONE4,
ZONE,
CENTER,
SURROUND_LEFT,
SURROUND_RIGHT,
SPEAKER_A,
SPEAKER_B
}

View File

@ -12,6 +12,10 @@
*/
package org.openhab.binding.rotel.internal.communication;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.rotel.internal.RotelException;
@ -23,174 +27,77 @@ import org.openhab.binding.rotel.internal.RotelException;
@NonNullByDefault
public class RotelFlagsMapping {
public static final RotelFlagsMapping MAPPING1 = new RotelFlagsMapping(3, 1, 5, 0, -1, -1, -1, -1, 8, 6, 8, 4, 8,
3);
public static final RotelFlagsMapping MAPPING2 = new RotelFlagsMapping(-1, -1, 4, 7, -1, -1, -1, -1, 5, 6, 5, 4, 5,
3);
public static final RotelFlagsMapping MAPPING3 = new RotelFlagsMapping(4, 7, 1, 7, 2, 7, 6, 2, 5, 6, 5, 4, 5, 3);
public static final RotelFlagsMapping MAPPING4 = new RotelFlagsMapping(3, 1, 1, 7, 2, 7, 6, 2, 8, 6, 8, 4, 8, 3);
public static final RotelFlagsMapping MAPPING5 = new RotelFlagsMapping(-1, -1, 3, 2, 4, 2, 4, 1, 5, 6, 5, 4, 5, 3);
public static final RotelFlagsMapping NO_MAPPING = new RotelFlagsMapping(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1);
public static final RotelFlagsMapping MAPPING1 = new RotelFlagsMapping(
List.of(new RotelFlagInfo(RotelFlagInfoType.MULTI_INPUT, 3, 1), //
new RotelFlagInfo(RotelFlagInfoType.ZONE2, 5, 0), //
new RotelFlagInfo(RotelFlagInfoType.CENTER, 8, 6), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_LEFT, 8, 4), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_RIGHT, 8, 3)));
public static final RotelFlagsMapping MAPPING2 = new RotelFlagsMapping(
List.of(new RotelFlagInfo(RotelFlagInfoType.ZONE2, 4, 7), //
new RotelFlagInfo(RotelFlagInfoType.CENTER, 5, 6), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_LEFT, 5, 4), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_RIGHT, 5, 3)));
public static final RotelFlagsMapping MAPPING3 = new RotelFlagsMapping(
List.of(new RotelFlagInfo(RotelFlagInfoType.ZONE2, 3, 2), //
new RotelFlagInfo(RotelFlagInfoType.ZONE3, 4, 2), //
new RotelFlagInfo(RotelFlagInfoType.ZONE4, 4, 1), //
new RotelFlagInfo(RotelFlagInfoType.CENTER, 5, 6), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_LEFT, 5, 4), //
new RotelFlagInfo(RotelFlagInfoType.SURROUND_RIGHT, 5, 3)));
private int multiInputFlagNumber;
private int multiInputBitNumber;
private int zone2FlagNumber;
private int zone2BitNumber;
private int zone3FlagNumber;
private int zone3BitNumber;
private int zone4FlagNumber;
private int zone4BitNumber;
private int centerFlagNumber;
private int centerBitNumber;
private int surroundLeftFlagNumber;
private int surroundLeftBitNumber;
private int surroundRightFlagNumber;
private int surroundRightBitNumber;
private Map<RotelFlagInfoType, RotelFlagInfo> infoMap;
/**
* Constructor
*
* For each flag number, value 1 means the first flag; a negative value is used for an undefined indicator.
* For each bit number, value is from 0 to 7; a negative value is used for an undefined indicator.
*
* @param multiInputFlagNumber the flag number in the standard feedback message in which to find the multi input
* indicator
* @param multiInputBitNumber the bit number in the flag in which to find the multi input indicator
* @param zone2FlagNumber the flag number in the standard feedback message in which to find the zone 2 indicator
* @param zone2BitNumber the bit number in the flag in which to find the zone 2 indicator
* @param zone3FlagNumber the flag number in the standard feedback message in which to find the zone 3 indicator
* @param zone3BitNumber the bit number in the flag in which to find the zone 3 indicator
* @param zone4FlagNumber the flag number in the standard feedback message in which to find the zone 4 indicator
* @param zone4BitNumber the bit number in the flag in which to find the zone 4 indicator
* @param centerFlagNumber the flag number in the standard feedback message in which to find the center channel
* indicator
* @param centerBitNumber the bit number in the flag in which to find the center channel indicator
* @param surroundLeftFlagNumber the flag number in the standard feedback message in which to find the surround left
* channel indicator
* @param surroundLeftBitNumber the bit number in the flag in which to find the surround left channel indicator
* @param surroundRightFlagNumber the flag number in the standard feedback message in which to find the surround
* right channel indicator
* @param surroundRightBitNumber the bit number in the flag in which to find the surround right channel indicator
*/
private RotelFlagsMapping(int multiInputFlagNumber, int multiInputBitNumber, int zone2FlagNumber,
int zone2BitNumber, int zone3FlagNumber, int zone3BitNumber, int zone4FlagNumber, int zone4BitNumber,
int centerFlagNumber, int centerBitNumber, int surroundLeftFlagNumber, int surroundLeftBitNumber,
int surroundRightFlagNumber, int surroundRightBitNumber) {
this.multiInputFlagNumber = multiInputFlagNumber;
this.multiInputBitNumber = multiInputBitNumber;
this.zone2FlagNumber = zone2FlagNumber;
this.zone2BitNumber = zone2BitNumber;
this.zone3FlagNumber = zone3FlagNumber;
this.zone3BitNumber = zone3BitNumber;
this.zone4FlagNumber = zone4FlagNumber;
this.zone4BitNumber = zone4BitNumber;
this.centerFlagNumber = centerFlagNumber;
this.centerBitNumber = centerBitNumber;
this.surroundLeftFlagNumber = surroundLeftFlagNumber;
this.surroundLeftBitNumber = surroundLeftBitNumber;
this.surroundRightFlagNumber = surroundRightFlagNumber;
this.surroundRightBitNumber = surroundRightBitNumber;
public RotelFlagsMapping() {
this.infoMap = new HashMap<>();
}
public RotelFlagsMapping(List<RotelFlagInfo> infos) {
this.infoMap = new HashMap<>();
for (RotelFlagInfo info : infos) {
this.infoMap.put(info.infoType(), info);
}
}
/**
* Get the multi input indicator
* Get the availability of the information
*
* @param flags the table of flags
*
* @return true if the indicator is ON in the flags or false if OFF
*
* @throws RotelException in case the multi input indicator is undefined
* @return true if the information is available
*/
public boolean isMultiInputOn(byte[] flags) throws RotelException {
return RotelFlagsMapping.isBitFlagOn(flags, multiInputFlagNumber, multiInputBitNumber);
public boolean isInfoPresent(RotelFlagInfoType infoType) {
return infoMap.get(infoType) != null;
}
/**
* Set the multi input indicator
* Get the information
*
* @param infoType the type of information
* @param flags the table of flags
* @param on true to set the indicator to ON or false to set it to OFF
*
* @throws RotelException in case the multi input indicator is undefined
* @return true if the information is ON in the flags or false if OFF
*
* @throws RotelException in case the information is undefined
*/
public void setMultiInput(byte[] flags, boolean on) throws RotelException {
RotelFlagsMapping.setBitFlag(flags, multiInputFlagNumber, multiInputBitNumber, on);
public boolean isInfoOn(RotelFlagInfoType infoType, byte[] flags) throws RotelException {
RotelFlagInfo info = infoMap.get(infoType);
if (info == null || info.flagNumber() > flags.length) {
throw new RotelException("Info " + infoType.name() + " not available in flags");
}
return RotelFlagsMapping.isBitFlagOn(flags, info.flagNumber(), info.bitNumber());
}
/**
* Get the zone 2 indicator
* Set the information
*
* @param infoType the type of information
* @param flags the table of flags
* @param on true to set the information to ON or false to set it to OFF
*
* @return true if the indicator is ON in the flags or false if OFF
*
* @throws RotelException in case the zone 2 indicator is undefined
* @return true if the information was updated, false if not
*/
public boolean isZone2On(byte[] flags) throws RotelException {
return RotelFlagsMapping.isBitFlagOn(flags, zone2FlagNumber, zone2BitNumber);
}
/**
* Set the zone 2 indicator
*
* @param flags the table of flags
* @param on true to set the indicator to ON or false to set it to OFF
*
* @throws RotelException in case the zone 2 indicator is undefined
*/
public void setZone2(byte[] flags, boolean on) throws RotelException {
RotelFlagsMapping.setBitFlag(flags, zone2FlagNumber, zone2BitNumber, on);
}
/**
* Get the zone 3 indicator
*
* @param flags the table of flags
*
* @return true if the indicator is ON in the flags or false if OFF
*
* @throws RotelException in case the zone 3 indicator is undefined
*/
public boolean isZone3On(byte[] flags) throws RotelException {
return RotelFlagsMapping.isBitFlagOn(flags, zone3FlagNumber, zone3BitNumber);
}
/**
* Set the zone 3 indicator
*
* @param flags the table of flags
* @param on true to set the indicator to ON or false to set it to OFF
*
* @throws RotelException in case the zone 3 indicator is undefined
*/
public void setZone3(byte[] flags, boolean on) throws RotelException {
RotelFlagsMapping.setBitFlag(flags, zone3FlagNumber, zone3BitNumber, on);
}
/**
* Get the zone 4 indicator
*
* @param flags the table of flags
*
* @return true if the indicator is ON in the flags or false if OFF
*
* @throws RotelException in case the zone 4 indicator is undefined
*/
public boolean isZone4On(byte[] flags) throws RotelException {
return RotelFlagsMapping.isBitFlagOn(flags, zone4FlagNumber, zone4BitNumber);
}
/**
* Set the zone 4 indicator
*
* @param flags the table of flags
* @param on true to set the indicator to ON or false to set it to OFF
*
* @throws RotelException in case the zone 4 indicator is undefined
*/
public void setZone4(byte[] flags, boolean on) throws RotelException {
RotelFlagsMapping.setBitFlag(flags, zone4FlagNumber, zone4BitNumber, on);
public boolean setInfo(RotelFlagInfoType infoType, byte[] flags, boolean on) {
RotelFlagInfo info = infoMap.get(infoType);
return info == null ? false : RotelFlagsMapping.setBitFlag(flags, info.flagNumber(), info.bitNumber(), on);
}
/**
@ -203,12 +110,15 @@ public class RotelFlagsMapping {
* @throws RotelException in case the center or surround channel indicators are undefined
*/
public boolean isMoreThan2Channels(byte[] flags) throws RotelException {
return (centerFlagNumber >= 1 && centerFlagNumber <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, centerFlagNumber, centerBitNumber))
|| (surroundLeftFlagNumber >= 1 && surroundLeftFlagNumber <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, surroundLeftFlagNumber, surroundLeftBitNumber))
|| (surroundRightFlagNumber >= 1 && surroundRightFlagNumber <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, surroundRightFlagNumber, surroundRightBitNumber));
RotelFlagInfo center = infoMap.get(RotelFlagInfoType.CENTER);
RotelFlagInfo surroundLeft = infoMap.get(RotelFlagInfoType.SURROUND_LEFT);
RotelFlagInfo surroundRight = infoMap.get(RotelFlagInfoType.SURROUND_RIGHT);
return (center != null && center.flagNumber() <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, center.flagNumber(), center.bitNumber()))
|| (surroundLeft != null && surroundLeft.flagNumber() <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, surroundLeft.flagNumber(), surroundLeft.bitNumber()))
|| (surroundRight != null && surroundRight.flagNumber() <= flags.length
&& RotelFlagsMapping.isBitFlagOn(flags, surroundRight.flagNumber(), surroundRight.bitNumber()));
}
/**
@ -241,19 +151,17 @@ public class RotelFlagsMapping {
* @param bitNumber the bit number in the flag to consider
* @param on true to set the bit value to 1 or false to set it to 0
*
* @throws RotelException in case of out of bounds value for the flag number or the bit number
* @return true if the flag was updated, false if not
*/
private static void setBitFlag(byte[] flags, int flagNumber, int bitNumber, boolean on) throws RotelException {
if (flagNumber < 1 || flagNumber > flags.length) {
throw new RotelException("Flag number out of bounds");
}
if (bitNumber < 0 || bitNumber > 7) {
throw new RotelException("Bit number out of bounds");
private static boolean setBitFlag(byte[] flags, int flagNumber, int bitNumber, boolean on) {
if (flagNumber < 1 || flagNumber > flags.length || bitNumber < 0 || bitNumber > 7) {
return false;
}
if (on) {
flags[flagNumber - 1] |= (1 << bitNumber);
} else {
flags[flagNumber - 1] &= ~(1 << bitNumber);
}
return true;
}
}

View File

@ -312,11 +312,11 @@ public class RotelSimuConnector extends RotelConnector {
case RECORD_FONCTION_SELECT:
if (model.getNumberOfZones() > 1 && model.getZoneSelectCmd() == cmd) {
showZone++;
if (showZone >= model.getNumberOfZones()) {
if (showZone > model.getNumberOfZones()) {
showZone = 1;
if (!powers[0]) {
showZone++;
}
}
if (showZone == 1 && !powers[0]) {
showZone++;
}
} else {
showZone = 1;
@ -325,6 +325,9 @@ public class RotelSimuConnector extends RotelConnector {
selectingRecord = powers[0];
showTreble = false;
textLine2 = buildRecordResponse();
if (model.getRespNbChars() == 11) {
text = textLine2;
}
} else if (showZone >= 2 && showZone <= 4) {
selectingRecord = false;
text = textLine2 = buildZonePowerResponse(showZone);
@ -1182,8 +1185,8 @@ public class RotelSimuConnector extends RotelConnector {
// Check if command is a change of record source
try {
recordSource = model.getRecordSourceFromCommand(cmd);
text = buildSourceLine1Response();
textLine2 = buildRecordResponse();
text = model.getRespNbChars() == 11 ? textLine2 : buildSourceLine1Response();
accepted = true;
} catch (RotelException e) {
}
@ -1217,21 +1220,26 @@ public class RotelSimuConnector extends RotelConnector {
if (protocol == RotelProtocol.HEX) {
byte[] chars = Arrays.copyOf(text.getBytes(StandardCharsets.US_ASCII), model.getRespNbChars());
byte[] flags = new byte[model.getRespNbFlags()];
try {
model.setMultiInput(flags, multiinput);
} catch (RotelException e) {
if (model.isInfoPresentInFlags(RotelFlagInfoType.MULTI_INPUT)) {
model.setInfoInFlags(RotelFlagInfoType.MULTI_INPUT, flags, multiinput);
}
try {
model.setZone2(flags, powers[2]);
} catch (RotelException e) {
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE2)) {
model.setInfoInFlags(RotelFlagInfoType.ZONE2, flags, powers[2]);
}
try {
model.setZone3(flags, powers[3]);
} catch (RotelException e) {
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE3)) {
model.setInfoInFlags(RotelFlagInfoType.ZONE3, flags, powers[3]);
}
try {
model.setZone4(flags, powers[4]);
} catch (RotelException e) {
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE4)) {
model.setInfoInFlags(RotelFlagInfoType.ZONE4, flags, powers[4]);
}
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE)) {
model.setInfoInFlags(RotelFlagInfoType.ZONE, flags, powers[2] || powers[3] || powers[4]);
}
if (model.isInfoPresentInFlags(RotelFlagInfoType.SPEAKER_A)) {
model.setInfoInFlags(RotelFlagInfoType.SPEAKER_A, flags, speakerA);
}
if (model.isInfoPresentInFlags(RotelFlagInfoType.SPEAKER_B)) {
model.setInfoInFlags(RotelFlagInfoType.SPEAKER_B, flags, speakerB);
}
int size = 6 + model.getRespNbChars() + model.getRespNbFlags();
byte[] dataBuffer = new byte[size];
@ -1479,9 +1487,18 @@ public class RotelSimuConnector extends RotelConnector {
if (!powers[0]) {
text = "";
} else if (mutes[0]) {
text = "MUTE ON";
text = model.getRespNbChars() == 11 ? " MUTE ON " : "MUTE ON";
} else {
text = getSourceLabel(sources[0], false) + " " + getSourceLabel(recordSource, true);
text = getSourceLabel(sources[0], false);
if (model.getRespNbChars() == 11) {
if ("TUNER".equals(text)) {
text = "104.30M 10 ";
} else {
text += " " + buildVolumeValue(0);
}
} else {
text += " " + getSourceLabel(recordSource, true);
}
}
return text;
}
@ -1507,26 +1524,16 @@ public class RotelSimuConnector extends RotelConnector {
}
private String buildZonePowerResponse(int numZone) {
String zone;
if (numZone == 2) {
zone = model.getNumberOfZones() > 2 ? "ZONE2" : "ZONE";
} else {
zone = String.format("ZONE%d", numZone);
String zone = model.getRespNbChars() == 11 ? " Z" : "ZONE";
if (numZone != 2 || model.getNumberOfZones() > 2) {
zone += String.format("%d", numZone);
}
String state = powers[numZone] ? getSourceLabel(sources[numZone], true) : "OFF";
return zone + " " + state;
}
private String buildVolumeLine1Response() {
String text;
if (volumes[0] == minVolume) {
text = " VOLUME MIN ";
} else if (volumes[0] == maxVolume) {
text = " VOLUME MAX ";
} else {
text = String.format(" VOLUME %02d ", volumes[0]);
}
return text;
return String.format(model.getRespNbChars() == 11 ? "VOLUME %s" : " VOLUME %s ", buildVolumeValue(0));
}
private String buildVolumeLine1RightResponse() {
@ -1535,32 +1542,36 @@ public class RotelSimuConnector extends RotelConnector {
text = "";
} else if (mutes[0]) {
text = "MUTE ON";
} else if (volumes[0] == minVolume) {
text = "VOL MIN";
} else if (volumes[0] == maxVolume) {
text = "VOL MAX";
} else {
text = String.format("VOL %02d", volumes[0]);
text = String.format("VOL %s", buildVolumeValue(0));
}
return text;
}
private String buildZoneVolumeResponse(int numZone) {
String zone;
if (numZone == 2) {
zone = model.getNumberOfZones() > 2 ? "ZONE2" : "ZONE";
} else {
zone = String.format("ZONE%d", numZone);
String zone = model.getRespNbChars() == 11 ? " Z" : "ZONE";
if (numZone != 2 || model.getNumberOfZones() > 2) {
zone += String.format("%d", numZone);
}
String text;
if (mutes[numZone]) {
text = zone + " MUTE ON";
} else if (volumes[numZone] == minVolume) {
text = zone + " VOL MIN";
} else if (volumes[numZone] == maxVolume) {
text = zone + " VOL MAX";
} else {
text = String.format("%s VOL %02d", zone, volumes[numZone]);
text = String.format("%s VOL %s", zone, buildVolumeValue(numZone));
}
return text;
}
private String buildVolumeValue(int numZone) {
String text;
if (volumes[numZone] == minVolume) {
text = "MIN";
} else if (volumes[numZone] == maxVolume) {
text = "MAX";
} else if (model.getRespNbChars() == 11) {
text = String.format(volumes[numZone] < 10 ? " %d" : " %d", volumes[numZone]);
} else {
text = String.format(" %02d", volumes[numZone]);
}
return text;
}
@ -1568,15 +1579,15 @@ public class RotelSimuConnector extends RotelConnector {
private String buildBassLine1Response() {
String text;
if (basses[0] == minToneLevel) {
text = " BASS MIN ";
text = model.getRespNbChars() == 11 ? " BASS MIN " : " BASS MIN ";
} else if (basses[0] == maxToneLevel) {
text = " BASS MAX ";
text = model.getRespNbChars() == 11 ? " BASS MAX " : " BASS MAX ";
} else if (basses[0] == 0) {
text = " BASS 0 ";
text = model.getRespNbChars() == 11 ? " BASS 0 " : " BASS 0 ";
} else if (basses[0] > 0) {
text = String.format(" BASS +%02d ", basses[0]);
text = String.format(model.getRespNbChars() == 11 ? " BASS + %d " : " BASS +%02d ", basses[0]);
} else {
text = String.format(" BASS -%02d ", -basses[0]);
text = String.format(model.getRespNbChars() == 11 ? " BASS - %d " : " BASS -%02d ", -basses[0]);
}
return text;
}
@ -1600,15 +1611,15 @@ public class RotelSimuConnector extends RotelConnector {
private String buildTrebleLine1Response() {
String text;
if (trebles[0] == minToneLevel) {
text = " TREBLE MIN ";
text = model.getRespNbChars() == 11 ? "TREBLE MIN " : " TREBLE MIN ";
} else if (trebles[0] == maxToneLevel) {
text = " TREBLE MAX ";
text = model.getRespNbChars() == 11 ? "TREBLE MAX " : " TREBLE MAX ";
} else if (trebles[0] == 0) {
text = " TREBLE 0 ";
text = model.getRespNbChars() == 11 ? "TREBLE 0 " : " TREBLE 0 ";
} else if (trebles[0] > 0) {
text = String.format(" TREBLE +%02d ", trebles[0]);
text = String.format(model.getRespNbChars() == 11 ? "TREBLE + %d " : " TREBLE +%02d ", trebles[0]);
} else {
text = String.format(" TREBLE -%02d ", -trebles[0]);
text = String.format(model.getRespNbChars() == 11 ? "TREBLE - %d " : " TREBLE -%02d ", trebles[0]);
}
return text;
}

View File

@ -310,7 +310,33 @@ public enum RotelSource {
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);
RotelCommand.ZONE2_SOURCE_INPUT_D, RotelCommand.ZONE3_SOURCE_INPUT_D, RotelCommand.ZONE4_SOURCE_INPUT_D),
CAT22_CD(22, "CD", "CD", RotelCommand.SOURCE_CD, RotelCommand.RECORD_SOURCE_CD, RotelCommand.MAIN_ZONE_SOURCE_CD,
RotelCommand.ZONE2_SOURCE_CD, RotelCommand.ZONE3_SOURCE_CD, RotelCommand.ZONE4_SOURCE_CD),
CAT22_TUNER(22, "TUNER", "TUNER", RotelCommand.SOURCE_TUNER, RotelCommand.RECORD_SOURCE_TUNER,
RotelCommand.MAIN_ZONE_SOURCE_TUNER, RotelCommand.ZONE2_SOURCE_TUNER, RotelCommand.ZONE3_SOURCE_TUNER,
RotelCommand.ZONE4_SOURCE_TUNER),
CAT22_TAPE(22, "TAPE", "TAPE", RotelCommand.SOURCE_TAPE, RotelCommand.RECORD_SOURCE_TAPE,
RotelCommand.MAIN_ZONE_SOURCE_TAPE, RotelCommand.ZONE2_SOURCE_TAPE, RotelCommand.ZONE3_SOURCE_TAPE,
RotelCommand.ZONE4_SOURCE_TAPE),
CAT22_PHONO(22, "PHONO", "PHONO", RotelCommand.SOURCE_PHONO, RotelCommand.RECORD_SOURCE_PHONO,
RotelCommand.MAIN_ZONE_SOURCE_PHONO, RotelCommand.ZONE2_SOURCE_PHONO, RotelCommand.ZONE3_SOURCE_PHONO,
RotelCommand.ZONE4_SOURCE_PHONO),
CAT22_VIDEO1(22, "VIDEO1", "VIDEO 1", RotelCommand.SOURCE_VIDEO1, RotelCommand.RECORD_SOURCE_VIDEO1,
RotelCommand.MAIN_ZONE_SOURCE_VIDEO1, RotelCommand.ZONE2_SOURCE_VIDEO1, RotelCommand.ZONE3_SOURCE_VIDEO1,
RotelCommand.ZONE4_SOURCE_VIDEO1),
CAT22_VIDEO2(22, "VIDEO2", "VIDEO 2", RotelCommand.SOURCE_VIDEO2, RotelCommand.RECORD_SOURCE_VIDEO2,
RotelCommand.MAIN_ZONE_SOURCE_VIDEO2, RotelCommand.ZONE2_SOURCE_VIDEO2, RotelCommand.ZONE3_SOURCE_VIDEO2,
RotelCommand.ZONE4_SOURCE_VIDEO2),
CAT22_VIDEO3(22, "VIDEO3", "VIDEO 3", RotelCommand.SOURCE_VIDEO3, RotelCommand.RECORD_SOURCE_VIDEO3,
RotelCommand.MAIN_ZONE_SOURCE_VIDEO3, RotelCommand.ZONE2_SOURCE_VIDEO3, RotelCommand.ZONE3_SOURCE_VIDEO3,
RotelCommand.ZONE4_SOURCE_VIDEO3),
CAT22_VIDEO4(22, "VIDEO4", "VIDEO 4", RotelCommand.SOURCE_VIDEO4, RotelCommand.RECORD_SOURCE_VIDEO4,
RotelCommand.MAIN_ZONE_SOURCE_VIDEO4, RotelCommand.ZONE2_SOURCE_VIDEO4, RotelCommand.ZONE3_SOURCE_VIDEO4,
RotelCommand.ZONE4_SOURCE_VIDEO4),
CAT22_FOLLOW_MAIN(22, "MAIN", "Follow Main Zone Source", null, RotelCommand.RECORD_SOURCE_MAIN, null,
RotelCommand.ZONE2_SOURCE_MAIN, RotelCommand.ZONE3_SOURCE_MAIN, RotelCommand.ZONE4_SOURCE_MAIN);
private int category;
private String name;

View File

@ -13,6 +13,7 @@
package org.openhab.binding.rotel.internal.configuration;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link RotelThingConfiguration} class contains fields mapping thing configuration parameters.
@ -23,6 +24,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
public class RotelThingConfiguration {
public @NonNullByDefault({}) String serialPort;
public @Nullable Integer baudRate;
public @NonNullByDefault({}) String host;
public @NonNullByDefault({}) Integer port;
public @NonNullByDefault({}) String inputLabelCd;

View File

@ -90,6 +90,7 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
private @Nullable ScheduledFuture<?> reconnectJob;
private @Nullable ScheduledFuture<?> powerOffJob;
private @Nullable ScheduledFuture<?> powerZonesJob;
private @Nullable ScheduledFuture<?>[] powerOnZoneJobs = { null, null, null, null, null };
private RotelModel model;
@ -104,6 +105,7 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
private int currentZone = 1;
private boolean selectingRecord;
private @Nullable Boolean powerZones;
private @Nullable Boolean[] powers = { null, false, false, false, false };
private boolean powerControlPerZone;
private @Nullable RotelSource recordSource;
@ -219,6 +221,9 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
case THING_TYPE_ID_RSX1562:
model = RotelModel.RSX1562;
break;
case THING_TYPE_ID_RX1052:
model = RotelModel.RX1052;
break;
case THING_TYPE_ID_A11:
model = RotelModel.A11;
break;
@ -435,8 +440,9 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
if (USE_SIMULATED_DEVICE) {
connector = new RotelSimuConnector(model, protocolHandler, sourcesLabels, readerThreadName);
} else if (config.serialPort != null) {
connector = new RotelSerialConnector(serialPortManager, config.serialPort, model.getBaudRate(),
protocolHandler, readerThreadName);
connector = new RotelSerialConnector(serialPortManager, config.serialPort,
config.baudRate != null ? config.baudRate : model.getBaudRate(), protocolHandler,
readerThreadName);
} else {
connector = new RotelIpConnector(config.host, config.port, protocolHandler, readerThreadName);
}
@ -493,6 +499,7 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
public void dispose() {
logger.debug("Disposing handler for thing {}", getThing().getUID());
cancelPowerOffJob();
cancelCheckPowerZonesJob();
for (int zone = 0; zone <= model.getNumberOfZones(); zone++) {
cancelPowerOnZoneJob(zone);
}
@ -946,6 +953,7 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
}
break;
case CHANNEL_SPEAKER_A:
case CHANNEL_MAIN_SPEAKER_A:
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
@ -955,6 +963,7 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
}
break;
case CHANNEL_SPEAKER_B:
case CHANNEL_MAIN_SPEAKER_B:
if (!isPowerOn()) {
success = false;
logger.debug("Command {} from channel {} ignored: device in standby", command, channel);
@ -1431,6 +1440,13 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
throw new RotelException("Invalid value");
}
break;
case KEY_POWER_ZONES:
if (POWER_ON.equalsIgnoreCase(value) || STANDBY.equalsIgnoreCase(value)) {
handlePowerZones(POWER_ON.equalsIgnoreCase(value));
} else {
throw new RotelException("Invalid value");
}
break;
case KEY_POWER_ZONE2:
case KEY_POWER_ZONE3:
case KEY_POWER_ZONE4:
@ -1747,21 +1763,29 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
speakerb = false;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
updateChannelState(CHANNEL_MAIN_SPEAKER_A);
updateChannelState(CHANNEL_MAIN_SPEAKER_B);
} else if (MSG_VALUE_SPEAKER_B.equalsIgnoreCase(value)) {
speakera = false;
speakerb = true;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
updateChannelState(CHANNEL_MAIN_SPEAKER_A);
updateChannelState(CHANNEL_MAIN_SPEAKER_B);
} else if (MSG_VALUE_SPEAKER_AB.equalsIgnoreCase(value)) {
speakera = true;
speakerb = true;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
updateChannelState(CHANNEL_MAIN_SPEAKER_A);
updateChannelState(CHANNEL_MAIN_SPEAKER_B);
} else if (MSG_VALUE_OFF.equalsIgnoreCase(value)) {
speakera = false;
speakerb = false;
updateChannelState(CHANNEL_SPEAKER_A);
updateChannelState(CHANNEL_SPEAKER_B);
updateChannelState(CHANNEL_MAIN_SPEAKER_A);
updateChannelState(CHANNEL_MAIN_SPEAKER_B);
} else {
throw new RotelException("Invalid value");
}
@ -1864,11 +1888,30 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
updateChannelState(CHANNEL_MAIN_MUTE);
updateChannelState(CHANNEL_MAIN_BASS);
updateChannelState(CHANNEL_MAIN_TREBLE);
updateChannelState(CHANNEL_MAIN_SPEAKER_A);
updateChannelState(CHANNEL_MAIN_SPEAKER_B);
updateChannelState(CHANNEL_ALL_POWER);
updateChannelState(CHANNEL_ALL_BRIGHTNESS);
}
/**
* Handle the received information that at least one zone power is ON or all zones power is OFF
*/
private void handlePowerZones(boolean power) {
Boolean prev = powerZones;
powerZones = power;
if (prev == null && power) {
// We know that at least one zone is ON but we don't know which ones
scheduleCheckPowerZonesJob();
} else if ((prev == null || prev.booleanValue() != power) && !power) {
cancelCheckPowerZonesJob();
for (int zone = 1; zone <= model.getNumberOfZones(); zone++) {
handlePowerOffZone(zone);
}
}
}
/**
* Handle the received information that a zone power is ON
*/
@ -2178,6 +2221,41 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
}
}
/**
* Schedule the job to run with a few seconds delay
*/
private void scheduleCheckPowerZonesJob() {
logger.debug("Schedule check power zones job");
cancelCheckPowerZonesJob();
powerZonesJob = scheduler.schedule(() -> {
synchronized (sequenceLock) {
logger.debug("Check power zones job");
try {
selectZone(model.getNumberOfZones(), model.getZoneSelectCmd());
} catch (RotelException e) {
logger.debug("Check power zones sequence failed: {}", e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-check-power-zones-sequence");
closeConnection();
} catch (InterruptedException e) {
logger.debug("Check power zones sequence interrupted: {}", e.getMessage());
Thread.currentThread().interrupt();
}
}
}, 2500, TimeUnit.MILLISECONDS);
}
/**
* Cancel the job scheduled when the device power (main zone) or a zone power switched ON
*/
private void cancelCheckPowerZonesJob() {
ScheduledFuture<?> job = powerZonesJob;
if (job != null && !job.isCancelled()) {
job.cancel(true);
powerZonesJob = null;
}
}
/**
* Schedule the reconnection job
*/
@ -2443,11 +2521,13 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
}
break;
case CHANNEL_SPEAKER_A:
case CHANNEL_MAIN_SPEAKER_A:
if (isPowerOn()) {
state = OnOffType.from(speakera);
}
break;
case CHANNEL_SPEAKER_B:
case CHANNEL_MAIN_SPEAKER_B:
if (isPowerOn()) {
state = OnOffType.from(speakerb);
}
@ -2536,7 +2616,11 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
private RotelCommand getVolumeUpCommand(int numZone) {
switch (numZone) {
case 0:
return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_UP : RotelCommand.VOLUME_UP;
// Spec for RX-1052 defines an unusual code for main zone volume up.
// An error in the spec is suspected. The general volume up code is preferred.
return (model.hasOtherThanPrimaryCommands() && model != RotelModel.RX1052)
? RotelCommand.MAIN_ZONE_VOLUME_UP
: RotelCommand.VOLUME_UP;
case 1:
return RotelCommand.ZONE1_VOLUME_UP;
case 2:
@ -2560,7 +2644,10 @@ public class RotelHandler extends BaseThingHandler implements RotelMessageEventL
private RotelCommand getVolumeDownCommand(int numZone) {
switch (numZone) {
case 0:
return model.hasOtherThanPrimaryCommands() ? RotelCommand.MAIN_ZONE_VOLUME_DOWN
// Spec for RX-1052 defines an unusual code for main zone volume down.
// An error in the spec is suspected. The general volume down code is preferred.
return (model.hasOtherThanPrimaryCommands() && model != RotelModel.RX1052)
? RotelCommand.MAIN_ZONE_VOLUME_DOWN
: RotelCommand.VOLUME_DOWN;
case 1:
return RotelCommand.ZONE1_VOLUME_DOWN;

View File

@ -17,6 +17,7 @@ import static org.openhab.binding.rotel.internal.RotelBindingConstants.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -24,6 +25,7 @@ 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.RotelFlagInfoType;
import org.openhab.binding.rotel.internal.communication.RotelFlagsMapping;
import org.openhab.binding.rotel.internal.communication.RotelSource;
import org.openhab.binding.rotel.internal.protocol.RotelAbstractProtocolHandler;
@ -93,11 +95,17 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
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 KEY3_HEX_ZONE2 = "z2 ";
private static final String KEY_HEX_ZONE3 = "zone3 ";
private static final String KEY2_HEX_ZONE3 = "z3 ";
private static final String KEY_HEX_ZONE4 = "zone4 ";
private static final String KEY2_HEX_ZONE4 = "z4 ";
private static final String KEY_HEX_RECORD = "rec ";
private static final String SOURCE = "source";
private static final Pattern PATTERN_TUNER_FREQ_FM = Pattern.compile("\\d{2,3}[\\.,]\\d{1,2}M.*");
private static final Pattern PATTERN_TUNER_FREQ_AM = Pattern.compile("\\d{3,4}K.*");
private final Logger logger = LoggerFactory.getLogger(RotelHexProtocolHandler.class);
private final Map<RotelSource, String> sourcesLabels;
@ -325,25 +333,59 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
}
}
}
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
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE2)) {
try {
dispatchKeyValue(KEY_POWER_ZONE2,
model.isInfoOnInFlags(RotelFlagInfoType.ZONE2, flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Ignore it
}
}
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
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE3)) {
try {
dispatchKeyValue(KEY_POWER_ZONE3,
model.isInfoOnInFlags(RotelFlagInfoType.ZONE3, flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Ignore it
}
}
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
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE4)) {
try {
dispatchKeyValue(KEY_POWER_ZONE4,
model.isInfoOnInFlags(RotelFlagInfoType.ZONE4, flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Ignore it
}
}
if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE)) {
try {
dispatchKeyValue(KEY_POWER_ZONES,
model.isInfoOnInFlags(RotelFlagInfoType.ZONE, flags) ? POWER_ON : STANDBY);
} catch (RotelException e1) {
// Ignore it
}
}
if (model.isInfoPresentInFlags(RotelFlagInfoType.SPEAKER_A)
&& model.isInfoPresentInFlags(RotelFlagInfoType.SPEAKER_B)) {
try {
String speakerValue = MSG_VALUE_OFF;
if (model.isInfoOnInFlags(RotelFlagInfoType.SPEAKER_A, flags)
&& model.isInfoOnInFlags(RotelFlagInfoType.SPEAKER_B, flags)) {
speakerValue = MSG_VALUE_SPEAKER_AB;
} else if (model.isInfoOnInFlags(RotelFlagInfoType.SPEAKER_A, flags)) {
speakerValue = MSG_VALUE_SPEAKER_A;
} else if (model.isInfoOnInFlags(RotelFlagInfoType.SPEAKER_B, flags)) {
speakerValue = MSG_VALUE_SPEAKER_B;
}
dispatchKeyValue(KEY_SPEAKER, speakerValue);
} catch (RotelException e1) {
// Ignore it
}
}
boolean checkMultiIn = false;
boolean checkSource = true;
try {
if (model.isMultiInputOn(flags)) {
if (model.isInfoOnInFlags(RotelFlagInfoType.MULTI_INPUT, flags)) {
checkSource = false;
try {
RotelSource source = model.getSourceFromName(RotelSource.CAT1_MULTI.getName());
@ -373,8 +415,9 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
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)) {
&& !valueLowerCase.startsWith(KEY2_HEX_ZONE2) && !valueLowerCase.startsWith(KEY3_HEX_ZONE2)
&& !valueLowerCase.startsWith(KEY_HEX_ZONE3) && !valueLowerCase.startsWith(KEY2_HEX_ZONE3)
&& !valueLowerCase.startsWith(KEY_HEX_ZONE4) && !valueLowerCase.startsWith(KEY2_HEX_ZONE4)) {
dispatchKeyValue(KEY_POWER, POWER_ON);
}
@ -384,12 +427,12 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
// 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);
parseText(value, checkSource, checkMultiIn, false, false, false, false, false, false, true, false);
// 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);
parseText(value, false, false, false, false, false, false, false, false, true, false);
// Full line 1
value = new String(incomingMessage, idxChars, 21, StandardCharsets.US_ASCII);
@ -398,16 +441,18 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
// 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);
parseText(value, false, false, false, false, false, false, false, false, true, false);
// 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);
parseText(value, false, false, true, true, false, false, true, true, true, false);
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);
parseText(value, checkSource, checkMultiIn, true, model.getRespNbChars() == 11,
model.getRespNbChars() != 11, model.getRespNbChars() == 11, model.hasDspControl(), checkStereo,
false, model.getRespNbChars() == 11);
dispatchKeyValue(KEY_LINE1, value);
}
@ -424,25 +469,28 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
* @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 searchRecordAfterSource true if a record source has to be searched in the text after the found source
* @param searchVolumeAfterSource true if a volume value has to be searched in the text after the 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
* @param searchTunerFreq true if a tuner frequency has to be searched in the text
*/
private void parseText(String text, boolean searchSource, boolean searchMultiIn, boolean searchZone,
boolean searchRecord, boolean searchRecordAfterSource, boolean searchDsp, boolean searchStereo,
boolean multipleInfo) {
boolean searchRecord, boolean searchRecordAfterSource, boolean searchVolumeAfterSource, boolean searchDsp,
boolean searchStereo, boolean multipleInfo, boolean searchTunerFreq) {
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)) {
if (valueLowerCase.startsWith(KEY1_HEX_ZONE2) || valueLowerCase.startsWith(KEY2_HEX_ZONE2)
|| valueLowerCase.startsWith(KEY3_HEX_ZONE2)) {
dispatchKeyValue(KEY_ZONE, "2");
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE3)) {
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE3) || valueLowerCase.startsWith(KEY2_HEX_ZONE3)) {
dispatchKeyValue(KEY_ZONE, "3");
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE4)) {
} else if (valueLowerCase.startsWith(KEY_HEX_ZONE4) || valueLowerCase.startsWith(KEY2_HEX_ZONE4)) {
dispatchKeyValue(KEY_ZONE, "4");
} else {
dispatchKeyValue(KEY_ZONE, "1");
@ -555,19 +603,41 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
} 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(KEY1_HEX_ZONE2)) {
parseZone2(value.substring(KEY1_HEX_ZONE2.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY2_HEX_ZONE2)) {
parseZone2(value.substring(KEY2_HEX_ZONE2.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY3_HEX_ZONE2)) {
parseZone2(value.substring(KEY3_HEX_ZONE2.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY_HEX_ZONE3)) {
parseZone3(value.substring(KEY_HEX_ZONE3.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY2_HEX_ZONE3)) {
parseZone3(value.substring(KEY2_HEX_ZONE3.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY_HEX_ZONE4)) {
parseZone4(value.substring(KEY_HEX_ZONE4.length()), multipleInfo);
} else if (searchZone && valueLowerCase.startsWith(KEY2_HEX_ZONE4)) {
parseZone4(value.substring(KEY2_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);
} else if (searchSource && searchTunerFreq
&& (PATTERN_TUNER_FREQ_FM.matcher(value).matches() || PATTERN_TUNER_FREQ_AM.matcher(value).matches())) {
try {
RotelSource source = model.getSourceFromName("TUNER");
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);
}
}
}
} catch (RotelException e) {
// Ignore it, no tuner source found for this model
}
} else if (searchSource || searchRecordAfterSource || searchVolumeAfterSource) {
parseSourceAndOther(value, searchSource, searchRecordAfterSource, searchVolumeAfterSource, multipleInfo);
}
}
@ -603,8 +673,8 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
return source;
}
private void parseSourceAndRecord(String text, boolean searchSource, boolean searchRecordAfterSource,
boolean multipleInfo) {
private void parseSourceAndOther(String text, boolean searchSource, boolean searchRecordAfterSource,
boolean searchVolumeAfterSource, boolean multipleInfo) {
RotelSource source = parseSource(text, false);
if (source != null) {
if (searchSource) {
@ -620,6 +690,16 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
}
}
if (searchVolumeAfterSource) {
String value = extractNumber(text, getSourceLabel(source).length());
if (!value.isEmpty()) {
dispatchKeyValue(KEY_VOLUME, value);
if (!searchSource && !multipleInfo) {
dispatchKeyValue(KEY_MUTE, MSG_VALUE_OFF);
}
}
}
if (searchRecordAfterSource) {
String value = text.substring(getSourceLabel(source).length()).trim();
source = parseSource(value, true);
@ -659,6 +739,9 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
private void parseZone2(String text, boolean multipleInfo) {
String value = text.trim();
if (!model.isInfoPresentInFlags(RotelFlagInfoType.ZONE2)) {
dispatchKeyValue(KEY_POWER_ZONE2, MSG_VALUE_OFF.equalsIgnoreCase(value) ? STANDBY : MSG_VALUE_ON);
}
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
@ -693,6 +776,9 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
private void parseZone3(String text, boolean multipleInfo) {
String value = text.trim();
if (!model.isInfoPresentInFlags(RotelFlagInfoType.ZONE3)) {
dispatchKeyValue(KEY_POWER_ZONE3, MSG_VALUE_OFF.equalsIgnoreCase(value) ? STANDBY : MSG_VALUE_ON);
}
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,
@ -727,6 +813,9 @@ public class RotelHexProtocolHandler extends RotelAbstractProtocolHandler {
private void parseZone4(String text, boolean multipleInfo) {
String value = text.trim();
if (!model.isInfoPresentInFlags(RotelFlagInfoType.ZONE4)) {
dispatchKeyValue(KEY_POWER_ZONE4, MSG_VALUE_OFF.equalsIgnoreCase(value) ? STANDBY : MSG_VALUE_ON);
}
String valueLowerCase = value.toLowerCase();
if (valueLowerCase.startsWith(KEY1_HEX_VOLUME) || valueLowerCase.startsWith(KEY2_HEX_VOLUME)) {
value = extractNumber(value,

View File

@ -320,4 +320,48 @@
</parameter>
</config-description>
<config-description uri="thing-type:rotel:serial6">
<parameter name="serialPort" type="text" required="false">
<context>serial-port</context>
<limitToOptions>false</limitToOptions>
<label>@text/config.serialPort.label</label>
<description>@text/config.serialPort.description</description>
</parameter>
<parameter name="baudRate" type="integer" required="false">
<label>@text/config.baudRate.label</label>
<description>@text/config.baudRate.description</description>
<limitToOptions>true</limitToOptions>
<options>
<option value="19200">19200</option>
<option value="38400">38400</option>
</options>
<default>38400</default>
</parameter>
<parameter name="host" type="text" required="false">
<context>network-address</context>
<label>@text/config.hostOverIp.label</label>
<description>@text/config.hostOverIp.description</description>
</parameter>
<parameter name="port" type="integer" required="false">
<label>@text/config.portOverIp.label</label>
<description>@text/config.portOverIp.description</description>
</parameter>
<parameter name="inputLabelVideo1" type="text" required="false">
<label>@text/config.inputLabelVideo1.label</label>
<description>@text/config.inputLabelVideo1.description</description>
</parameter>
<parameter name="inputLabelVideo2" type="text" required="false">
<label>@text/config.inputLabelVideo2.label</label>
<description>@text/config.inputLabelVideo2.description</description>
</parameter>
<parameter name="inputLabelVideo3" type="text" required="false">
<label>@text/config.inputLabelVideo3.label</label>
<description>@text/config.inputLabelVideo3.description</description>
</parameter>
<parameter name="inputLabelVideo4" type="text" required="false">
<label>@text/config.inputLabelVideo4.label</label>
<description>@text/config.inputLabelVideo4.description</description>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@ -89,6 +89,8 @@ thing-type.rotel.rt11.label = RT-11 Tuner
thing-type.rotel.rt11.description = Connection to the Rotel RT-11 tuner
thing-type.rotel.rt1570.label = RT-1570 Tuner
thing-type.rotel.rt1570.description = Connection to the Rotel RT-1570 tuner
thing-type.rotel.rx1052.label = RX-1052 Stereo Receiver
thing-type.rotel.rx1052.description = Connection to the Rotel RX-1052 stereo receiver
thing-type.rotel.s5.label = S5 Stereo Amplifier
thing-type.rotel.s5.description = Connection to the Rotel Michi S5 stereo amplifier
thing-type.rotel.t11.label = T11 Tuner
@ -144,6 +146,8 @@ channel-type.rotel.volumeUpDown.description = Increase or decrease the volume
# thing type configuration
config.baudRate.label = Baud Rate
config.baudRate.description = Baud rate to use for connecting to the Rotel device. Select the highest baud rate if your black unit has at least serial number 090-6441001 or your silver unit has at least serial number 990-6441001.
config.host.label = Address
config.host.description = Host name or IP address of the Rotel device (IP connection) or the machine connected to the Rotel device (serial over IP)
config.hostOverIp.label = Address
@ -428,6 +432,7 @@ offline.config-error-invalid-port = Invalid port configuration setting
offline.config-error-invalid-serial-over-ip = Use host and port configuration settings for a serial over IP connection
offline.comm-error-init-sequence = Init sequence failed
offline.comm-error-init-sequence-zone = Init sequence zone {0} failed
offline.comm-error-check-power-zones-sequence = Check power zones sequence failed
offline.comm-error-reading-thread = Reading thread ended
offline.comm-error-sending-command = Sending command failed
offline.comm-error-reconnection = Reconnection failed

View File

@ -90,6 +90,24 @@
</channels>
</channel-group-type>
<channel-group-type id="mainZoneType6">
<label>@text/channel-group.mainZone.label</label>
<description>@text/channel-group.mainZone.description</description>
<channels>
<channel id="power" typeId="system.power"/>
<channel id="source" typeId="source"/>
<channel id="recordSource" typeId="recordSource"/>
<channel id="volume" typeId="system.volume"/>
<channel id="mute" typeId="system.mute"/>
<channel id="bass" typeId="bass"/>
<channel id="treble" typeId="treble"/>
<channel id="speakera" typeId="speakera"/>
<channel id="speakerb" typeId="speakerb"/>
<channel id="line1" typeId="frontPanelLine"/>
<channel id="otherCommand" typeId="otherCommand"/>
</channels>
</channel-group-type>
<channel-group-type id="zone2type1">
<label>@text/channel-group.zone2.label</label>
<description>@text/channel-group.zone2.description</description>

View File

@ -0,0 +1,26 @@
<?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 RX-1052 Connection Thing Type -->
<thing-type id="rx1052">
<label>RX-1052 Stereo Receiver</label>
<description>Connection to the Rotel RX-1052 stereo receiver</description>
<channel-groups>
<channel-group id="mainZone" typeId="mainZoneType6"/>
<channel-group id="zone2" typeId="zone2type1"/>
<channel-group id="zone3" typeId="zone3"/>
<channel-group id="zone4" typeId="zone4"/>
</channel-groups>
<properties>
<property name="protocol">HEX</property>
</properties>
<config-description-ref uri="thing-type:rotel:serial6"/>
</thing-type>
</thing:thing-descriptions>