]> git.basschouten.com Git - openhab-addons.git/blob
3d396cde4dcffdd7ed0088153ce6500b0eeae4fc
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.yamahareceiver.internal.protocol.xml;
14
15 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*;
16
17 import java.io.IOException;
18
19 import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection;
20 import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPresetControl;
21 import org.openhab.binding.yamahareceiver.internal.protocol.InputWithTunerBandControl;
22 import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException;
23 import org.openhab.binding.yamahareceiver.internal.state.DabBandState;
24 import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener;
25 import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState;
26 import org.openhab.binding.yamahareceiver.internal.state.PlayInfoState;
27 import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener;
28 import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState;
29 import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener;
30 import org.slf4j.LoggerFactory;
31 import org.w3c.dom.Node;
32
33 /**
34  * This class implements the Yamaha Receiver protocol related to DAB tuners which allows to control band and preset.
35  * This control is specific to dual band tuners only.
36  *
37  * Note that yamaha maintains separate presets for each band.
38  *
39  * The XML nodes {@code <DAB><Play_Control><Band>FM</Band></Play_Control></DAB>} are used.
40  *
41  * No state will be saved in here, but in {@link DabBandState}, {@link PresetInfoState} and {@link PlayInfoState}
42  * instead.
43  *
44  * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D)
45  */
46 public class InputWithTunerDABControlXML extends AbstractInputControlXML
47         implements InputWithTunerBandControl, InputWithPresetControl {
48
49     private static final String BAND_FM = "FM";
50     private static final String BAND_DAB = "DAB";
51
52     private final DabBandStateListener observerForBand;
53     private final PresetInfoStateListener observerForPreset;
54     private final PlayInfoStateListener observerForPlayInfo;
55
56     protected CommandTemplate band = new CommandTemplate("<Play_Control><Band>%s</Band></Play_Control>",
57             "Play_Info/Band");
58     protected CommandTemplate preset = new CommandTemplate(
59             "<Play_Control><%s><Preset><Preset_Sel>%d</Preset_Sel></Preset></%s></Play_Control>", "");
60
61     /**
62      * Need to remember last band state to drive the preset
63      */
64     private DabBandState bandState;
65
66     /**
67      * Create an InputWithPlayControl object for altering menu positions and requesting current menu information as well
68      * as controlling the playback and choosing a preset item.
69      *
70      * @param inputID The input ID - TUNER is going to be used here.
71      * @param con The Yamaha communication object to send http requests.
72      */
73     public InputWithTunerDABControlXML(String inputID, AbstractConnection con, DabBandStateListener observerForBand,
74             PresetInfoStateListener observerForPreset, PlayInfoStateListener observerForPlayInfo,
75             DeviceInformationState deviceInformationState) {
76         super(LoggerFactory.getLogger(InputWithTunerDABControlXML.class), inputID, con, deviceInformationState);
77
78         this.inputElement = "DAB";
79
80         this.observerForBand = observerForBand;
81         this.observerForPreset = observerForPreset;
82         this.observerForPlayInfo = observerForPlayInfo;
83
84         if (observerForBand == null && observerForPreset == null && observerForPlayInfo == null) {
85             throw new IllegalArgumentException("At least one observer has to be provided");
86         }
87     }
88
89     @Override
90     public void update() throws IOException, ReceivedMessageParseException {
91         Node responseNode = XMLProtocolService.getResponse(comReference.get(),
92                 wrInput("<Play_Info>GetParam</Play_Info>"), inputElement);
93
94         // @formatter:off
95
96         //Sample response:
97         //<YAMAHA_AV rsp="GET" RC="0">
98         //    <DAB>
99         //        <Play_Info>
100         //            <Feature_Availability>Ready</Feature_Availability>
101         //            <FM>
102         //                <Preset>
103         //                    <Preset_Sel>1</Preset_Sel>
104         //                </Preset>
105         //                <Tuning>
106         //                    <Freq>
107         //                        <Val>9945</Val>
108         //                        <Exp>2</Exp>
109         //                        <Unit>MHz</Unit>
110         //                    </Freq>
111         //                </Tuning>
112         //                <FM_Mode>Auto</FM_Mode>
113         //                <Signal_Info>
114         //                    <Tuned>Assert</Tuned>
115         //                    <Stereo>Assert</Stereo>
116         //                </Signal_Info>
117         //                <Meta_Info>
118         //                    <Program_Type>POP M</Program_Type>
119         //                    <Program_Service>  22:59</Program_Service>
120         //                    <Radio_Text>tel. 22 333 33 33   * Trojka *   e-mail: trojka@polskieradio.pl</Radio_Text>
121         //                    <Clock_Time>22:59</Clock_Time>
122         //                </Meta_Info>
123         //            </FM>
124         //            <DAB>
125         //                <Status>Ready</Status>
126         //                <Preset>
127         //                    <Preset_Sel>No Preset</Preset_Sel>
128         //                </Preset>
129         //                <ID>2</ID>
130         //                <Signal_Info>
131         //                    <Freq>
132         //                        <Val>218640</Val>
133         //                        <Exp>3</Exp>
134         //                        <Unit>MHz</Unit>
135         //                    </Freq>
136         //                    <Category>Primary</Category>
137         //                    <Audio_Mode>Stereo</Audio_Mode>
138         //                    <Bit_Rate>
139         //                        <Val>128</Val>
140         //                        <Exp>0</Exp>
141         //                        <Unit>Kbps</Unit>
142         //                    </Bit_Rate>
143         //                    <Quality>82</Quality>
144         //                    <Tune_Aid>45</Tune_Aid>
145         //                    <Off_Air>Negate</Off_Air>
146         //                    <DAB_PLUS>Assert</DAB_PLUS>
147         //                </Signal_Info>
148         //                <Meta_Info>
149         //                    <Ch_Label>11B</Ch_Label>
150         //                    <Service_Label>PR Czwórka</Service_Label>
151         //                    <DLS>Kluboteka  Polskie Radio S.A.</DLS>
152         //                    <Ensemble_Label>Polskie Radio</Ensemble_Label>
153         //                    <Program_Type>Pop</Program_Type>
154         //                    <Date_and_Time>12AUG&apos;17 23:47</Date_and_Time>
155         //                </Meta_Info>
156         //            </DAB>
157         //            <Band>FM</Band>
158         //        </Play_Info>
159         //    </DAB>
160         //</YAMAHA_AV>
161
162         // @formatter:on
163
164         DabBandState msgForBand = new DabBandState();
165         PresetInfoState msgForPreset = new PresetInfoState();
166         PlayInfoState msgForPlayInfo = new PlayInfoState();
167
168         msgForBand.band = getNodeContentOrDefault(responseNode, "Play_Info/Band", msgForBand.band);
169         logger.debug("Band set to {} for input {}", msgForBand.band, inputID);
170
171         // store last state of band
172         bandState = msgForBand;
173
174         if (msgForBand.band.isEmpty()) {
175             logger.warn("Band is unknown for input {}, therefore preset and playback information will not be available",
176                     inputID);
177         } else {
178             Node playInfoNode = getNode(responseNode, "Play_Info/" + msgForBand.band);
179
180             msgForPreset.presetChannel = getNodeContentOrDefault(playInfoNode, "Preset/Preset_Sel", -1);
181             logger.debug("Preset set to {} for input {}", msgForPreset.presetChannel, inputID);
182
183             Node metaInfoNode = getNode(playInfoNode, "Meta_Info");
184             if (metaInfoNode != null) {
185                 msgForPlayInfo.album = getNodeContentOrDefault(metaInfoNode, "Program_Type", msgForPlayInfo.album);
186                 if (BAND_DAB.equals(msgForBand.band)) {
187                     msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Service_Label",
188                             msgForPlayInfo.station);
189                     msgForPlayInfo.artist = getNodeContentOrDefault(metaInfoNode, "Ensemble_Label",
190                             msgForPlayInfo.artist);
191                     msgForPlayInfo.song = getNodeContentOrDefault(metaInfoNode, "DLS", msgForPlayInfo.song);
192                 } else {
193                     msgForPlayInfo.station = getNodeContentOrDefault(metaInfoNode, "Program_Service",
194                             msgForPlayInfo.station);
195                     msgForPlayInfo.artist = getNodeContentOrDefault(metaInfoNode, "Station", msgForPlayInfo.artist);
196                     msgForPlayInfo.song = getNodeContentOrDefault(metaInfoNode, "Radio_Text", msgForPlayInfo.song);
197                 }
198             }
199         }
200
201         // DAB does not provide channel names, the channel list will be empty
202         msgForPreset.presetChannelNamesChanged = true;
203
204         if (observerForBand != null) {
205             observerForBand.dabBandUpdated(msgForBand);
206         }
207         if (observerForPreset != null) {
208             observerForPreset.presetInfoUpdated(msgForPreset);
209         }
210         if (observerForPlayInfo != null) {
211             observerForPlayInfo.playInfoUpdated(msgForPlayInfo);
212         }
213     }
214
215     @Override
216     public void selectBandByName(String band) throws IOException, ReceivedMessageParseException {
217         // Example: <Play_Control><Band>FM</Band></Play_Control>
218         String cmd = this.band.apply(band);
219         comReference.get().send(wrInput(cmd));
220         update();
221     }
222
223     @Override
224     public void selectItemByPresetNumber(int presetChannel) throws IOException, ReceivedMessageParseException {
225         if (bandState == null || bandState.band == null || bandState.band.isEmpty()) {
226             logger.warn("Cannot change preset because the band is unknown for input {}", inputID);
227             return;
228         }
229
230         // Example: <Play_Control><FM><Preset><Preset_Sel>2</Preset_Sel></Preset></FM></Play_Control>
231         String cmd = this.preset.apply(bandState.band, presetChannel, bandState.band);
232         comReference.get().send(wrInput(cmd));
233         update();
234     }
235 }