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