2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.yamahareceiver.internal.protocol.xml;
15 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*;
17 import java.io.IOException;
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;
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.
37 * Note that yamaha maintains separate presets for each band.
39 * The XML nodes {@code <DAB><Play_Control><Band>FM</Band></Play_Control></DAB>} are used.
41 * No state will be saved in here, but in {@link DabBandState}, {@link PresetInfoState} and {@link PlayInfoState}
44 * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D)
46 public class InputWithTunerDABControlXML extends AbstractInputControlXML
47 implements InputWithTunerBandControl, InputWithPresetControl {
49 private static final String BAND_FM = "FM";
50 private static final String BAND_DAB = "DAB";
52 private final DabBandStateListener observerForBand;
53 private final PresetInfoStateListener observerForPreset;
54 private final PlayInfoStateListener observerForPlayInfo;
56 protected CommandTemplate band = new CommandTemplate("<Play_Control><Band>%s</Band></Play_Control>",
58 protected CommandTemplate preset = new CommandTemplate(
59 "<Play_Control><%s><Preset><Preset_Sel>%d</Preset_Sel></Preset></%s></Play_Control>", "");
62 * Need to remember last band state to drive the preset
64 private DabBandState bandState;
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.
70 * @param inputID The input ID - TUNER is going to be used here.
71 * @param con The Yamaha communication object to send http requests.
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);
78 this.inputElement = "DAB";
80 this.observerForBand = observerForBand;
81 this.observerForPreset = observerForPreset;
82 this.observerForPlayInfo = observerForPlayInfo;
84 if (observerForBand == null && observerForPreset == null && observerForPlayInfo == null) {
85 throw new IllegalArgumentException("At least one observer has to be provided");
90 public void update() throws IOException, ReceivedMessageParseException {
91 Node responseNode = XMLProtocolService.getResponse(comReference.get(),
92 wrInput("<Play_Info>GetParam</Play_Info>"), inputElement);
97 //<YAMAHA_AV rsp="GET" RC="0">
100 // <Feature_Availability>Ready</Feature_Availability>
103 // <Preset_Sel>1</Preset_Sel>
112 // <FM_Mode>Auto</FM_Mode>
114 // <Tuned>Assert</Tuned>
115 // <Stereo>Assert</Stereo>
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>
125 // <Status>Ready</Status>
127 // <Preset_Sel>No Preset</Preset_Sel>
136 // <Category>Primary</Category>
137 // <Audio_Mode>Stereo</Audio_Mode>
143 // <Quality>82</Quality>
144 // <Tune_Aid>45</Tune_Aid>
145 // <Off_Air>Negate</Off_Air>
146 // <DAB_PLUS>Assert</DAB_PLUS>
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'17 23:47</Date_and_Time>
164 DabBandState msgForBand = new DabBandState();
165 PresetInfoState msgForPreset = new PresetInfoState();
166 PlayInfoState msgForPlayInfo = new PlayInfoState();
168 msgForBand.band = getNodeContentOrDefault(responseNode, "Play_Info/Band", msgForBand.band);
169 logger.debug("Band set to {} for input {}", msgForBand.band, inputID);
171 // store last state of band
172 bandState = msgForBand;
174 if (msgForBand.band.isEmpty()) {
175 logger.warn("Band is unknown for input {}, therefore preset and playback information will not be available",
178 Node playInfoNode = getNode(responseNode, "Play_Info/" + msgForBand.band);
180 msgForPreset.presetChannel = getNodeContentOrDefault(playInfoNode, "Preset/Preset_Sel", -1);
181 logger.debug("Preset set to {} for input {}", msgForPreset.presetChannel, inputID);
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);
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);
201 // DAB does not provide channel names, the channel list will be empty
202 msgForPreset.presetChannelNamesChanged = true;
204 if (observerForBand != null) {
205 observerForBand.dabBandUpdated(msgForBand);
207 if (observerForPreset != null) {
208 observerForPreset.presetInfoUpdated(msgForPreset);
210 if (observerForPlayInfo != null) {
211 observerForPlayInfo.playInfoUpdated(msgForPlayInfo);
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));
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);
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));