]> git.basschouten.com Git - openhab-addons.git/commitdiff
[rotel] Support added for model RX-1052 (#16486)
authorlolodomo <lg.hc@free.fr>
Thu, 21 Mar 2024 21:14:03 +0000 (22:14 +0100)
committerGitHub <noreply@github.com>
Thu, 21 Mar 2024 21:14:03 +0000 (22:14 +0100)
* Two new channels to enable/disable speaker A and speaker B

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
17 files changed:
bundles/org.openhab.binding.rotel/README.md
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/RotelBindingConstants.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/RotelHandlerFactory.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/RotelModel.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelCommand.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfo.java [new file with mode: 0644]
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfoType.java [new file with mode: 0644]
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagsMapping.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelSimuConnector.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelSource.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/configuration/RotelThingConfiguration.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/handler/RotelHandler.java
bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/protocol/hex/RotelHexProtocolHandler.java
bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/i18n/rotel.properties
bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/thing/channels.xml
bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/thing/rx1052.xml [new file with mode: 0644]

index 4fa8e652932fa7d3986716bebbcb00cded4185d7..169b0a99fd3741122c6605d75e7a87e25f7ba656 100644 (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                                      |
 
index c19bda03dd66f7af8175437e1565d2260b2369f4..2c5391a1ab60f20a8fbd66a541be48bbcf63ea4f 100644 (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";
index ead0f5d5cd616efedfc438e6023e84f62a5088fe..a70a64824beff031e99d07499172ff1ce6eb47b3 100644 (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;
index 1cae784ec06609799f2668c11945fdc52ddab349..3ed104e5bf6c9605eeeba5fd2745a74127c8c089 100644 (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
-     *
-     * @param flags the flag from the standard response message
+     * Inform whether the information is present in flags
      *
-     * @return true if the multiple input source is ON
-     *
-     * @throws RotelException - If this information is not present in the flags for this model
-     */
-    public boolean isMultiInputOn(byte[] flags) throws RotelException {
-        return flagsMapping.isMultiInputOn(flags);
-    }
-
-    /**
-     * Set the multiple input source 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
+     * @param infoType the type of information
      *
-     * @throws RotelException - If this information is not present in the flags for this model
+     * @return true if the information is available
      */
-    public void setMultiInput(byte[] flags, boolean on) throws RotelException {
-        flagsMapping.setMultiInput(flags, on);
+    public boolean isInfoPresentInFlags(RotelFlagInfoType infoType) {
+        return flagsMapping.isInfoPresent(infoType);
     }
 
     /**
-     * Inform whether the zone 2 is set to ON 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 zone 2 is ON
+     * @return true if the information 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);
+    public boolean isInfoOnInFlags(RotelFlagInfoType infoType, byte[] flags) throws RotelException {
+        return flagsMapping.isInfoOn(infoType, flags);
     }
 
     /**
-     * Set the zone 2 to ON or OFF in the 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
-     */
-    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
+     * @return true if the information was updated in the flags, false if not
      */
-    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);
     }
 
     /**
index 2212ef9e84fa85c11d6d1e6ef8f662d45d852667..dddac41de2435af329658c502e53edad710ea169 100644 (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());
+    }
 }
diff --git a/bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfo.java b/bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfo.java
new file mode 100644 (file)
index 0000000..516fcf6
--- /dev/null
@@ -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) {
+}
diff --git a/bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfoType.java b/bundles/org.openhab.binding.rotel/src/main/java/org/openhab/binding/rotel/internal/communication/RotelFlagInfoType.java
new file mode 100644 (file)
index 0000000..6fb627c
--- /dev/null
@@ -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
+}
index 46fa30dcfbb2a705c0c8cf7fce25f780551cf5d2..c8e2686bcc186ef751041962eca452fea019582c 100644 (file)
  */
 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);
-
-    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;
-
-    /**
-     * 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 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 Map<RotelFlagInfoType, RotelFlagInfo> infoMap;
+
+    public RotelFlagsMapping() {
+        this.infoMap = new HashMap<>();
     }
 
-    /**
-     * Get the multi input 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 multi input indicator is undefined
-     */
-    public boolean isMultiInputOn(byte[] flags) throws RotelException {
-        return RotelFlagsMapping.isBitFlagOn(flags, multiInputFlagNumber, multiInputBitNumber);
-    }
-
-    /**
-     * Set the multi input 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 multi input indicator is undefined
-     */
-    public void setMultiInput(byte[] flags, boolean on) throws RotelException {
-        RotelFlagsMapping.setBitFlag(flags, multiInputFlagNumber, multiInputBitNumber, on);
-    }
-
-    /**
-     * Get the zone 2 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 2 indicator is undefined
-     */
-    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);
+    public RotelFlagsMapping(List<RotelFlagInfo> infos) {
+        this.infoMap = new HashMap<>();
+        for (RotelFlagInfo info : infos) {
+            this.infoMap.put(info.infoType(), info);
+        }
     }
 
     /**
-     * Set the zone 3 indicator
+     * Get the availability of the 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 zone 3 indicator is undefined
+     * @return true if the information is available
      */
-    public void setZone3(byte[] flags, boolean on) throws RotelException {
-        RotelFlagsMapping.setBitFlag(flags, zone3FlagNumber, zone3BitNumber, on);
+    public boolean isInfoPresent(RotelFlagInfoType infoType) {
+        return infoMap.get(infoType) != null;
     }
 
     /**
-     * Get the zone 4 indicator
+     * Get the information
      *
+     * @param infoType the type of information
      * @param flags the table of flags
      *
-     * @return true if the indicator is ON in the flags or false if OFF
+     * @return true if the information is ON in the flags or false if OFF
      *
-     * @throws RotelException in case the zone 4 indicator is undefined
+     * @throws RotelException in case the information is undefined
      */
-    public boolean isZone4On(byte[] flags) throws RotelException {
-        return RotelFlagsMapping.isBitFlagOn(flags, zone4FlagNumber, zone4BitNumber);
+    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());
     }
 
     /**
-     * Set the zone 4 indicator
+     * Set 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
+     * @param on true to set the information to ON or false to set it to OFF
      *
-     * @throws RotelException in case the zone 4 indicator is undefined
+     * @return true if the information was updated, false if not
      */
-    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;
     }
 }
index a7aa9a079ed7a338dc00e27f3d26bbc4d30435c9..c749e421a6b48ef6b78280db6f82658a7e596a36 100644 (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);
+            }
+            if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE2)) {
+                model.setInfoInFlags(RotelFlagInfoType.ZONE2, flags, powers[2]);
+            }
+            if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE3)) {
+                model.setInfoInFlags(RotelFlagInfoType.ZONE3, flags, powers[3]);
+            }
+            if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE4)) {
+                model.setInfoInFlags(RotelFlagInfoType.ZONE4, flags, powers[4]);
             }
-            try {
-                model.setZone2(flags, powers[2]);
-            } catch (RotelException e) {
+            if (model.isInfoPresentInFlags(RotelFlagInfoType.ZONE)) {
+                model.setInfoInFlags(RotelFlagInfoType.ZONE, flags, powers[2] || powers[3] || powers[4]);
             }
-            try {
-                model.setZone3(flags, powers[3]);
-            } catch (RotelException e) {
+            if (model.isInfoPresentInFlags(RotelFlagInfoType.SPEAKER_A)) {
+                model.setInfoInFlags(RotelFlagInfoType.SPEAKER_A, flags, speakerA);
             }
-            try {
-                model.setZone4(flags, powers[4]);
-            } catch (RotelException e) {
+            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 {
+            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 = zone + " VOL MAX";
+            text = "MAX";
+        } else if (model.getRespNbChars() == 11) {
+            text = String.format(volumes[numZone] < 10 ? "  %d" : " %d", volumes[numZone]);
         } else {
-            text = String.format("%s VOL %02d", zone, volumes[numZone]);
+            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;
     }
index dab2a9fd2055d529157d39ded54838abd3d7f5b0..4a54a3ebb7d7a173b3970aeb63b589500a5054eb 100644 (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;
index a50ed0cfe584498a6012527861e6645a4a8e069f..a9542f5467ed949ecc1bae601478b81a2f55bb11 100644 (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;
index a980d64d8c280d02acc87b3f333b2a1684ab1ff1..3285a76d13c92f0dce3dbd389e2ce82298927c6d 100644 (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;
index 5f9ee4f436d88dc58f4136612d46904f003bed60..a87b81268f432248105a03015220b970eb486b7b 100644 (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,
index 197817913f2b9e66af10f1cf91dc28dec01558aa..1e25d49c024d49dacc607ecd802ad3d76fa7f0f3 100644 (file)
                </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>
index 414a3f6e187cae843f32fadb97a36a2f0efe606f..845a2b8725740518d343690d6674dc846ec33f05 100644 (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
index e44c50f9ca02a1c70f66710e5580ec4a081be002..ec9a2076f7fa0e341c8551f4822707cb8c0f2bb7 100644 (file)
                </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>
diff --git a/bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/thing/rx1052.xml b/bundles/org.openhab.binding.rotel/src/main/resources/OH-INF/thing/rx1052.xml
new file mode 100644 (file)
index 0000000..959bfd2
--- /dev/null
@@ -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>