Java tutorial
/** * Copyright (c) 2010-2017 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.yamahareceiver.internal.protocol.xml; import java.io.IOException; import java.lang.ref.WeakReference; import org.apache.commons.lang.StringUtils; import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection; import org.openhab.binding.yamahareceiver.internal.protocol.InputWithDabBandControl; import org.openhab.binding.yamahareceiver.internal.protocol.InputWithPresetControl; import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException; import org.openhab.binding.yamahareceiver.internal.state.DabBandState; import org.openhab.binding.yamahareceiver.internal.state.DabBandStateListener; import org.openhab.binding.yamahareceiver.internal.state.PlayInfoState; import org.openhab.binding.yamahareceiver.internal.state.PlayInfoStateListener; import org.openhab.binding.yamahareceiver.internal.state.PresetInfoState; import org.openhab.binding.yamahareceiver.internal.state.PresetInfoStateListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * This class implements the Yamaha Receiver protocol related to DAB tuners which allows to control band and preset. * This control is specific to dual band tuners only. * * Note that yamaha maintains separate presets for each band. * * The XML nodes <DAB><Play_Control><Band>FM</Band></Play_Control></DAB> are used. * * No state will be saved in here, but in {@link DabBandState}, {@link PresetInfoState} and {@link PlayInfoState} * instead. * * @author Tomasz Maruszak - [yamaha] Tuner band selection and preset feature for dual band models (RX-S601D) */ public class InputWithDabControlXML implements InputWithDabBandControl, InputWithPresetControl { private Logger logger = LoggerFactory.getLogger(InputWithDabControlXML.class); private static final String BAND_FM = "FM"; private static final String BAND_DAB = "DAB"; protected final WeakReference<AbstractConnection> comReference; protected final String inputID; private final DabBandStateListener observerForBand; private final PresetInfoStateListener observerForPreset; private final PlayInfoStateListener observerForPlayInfo; /** * Need to remember last band state to drive the preset */ private DabBandState bandState; /** * Create a InputWithPlayControl object for altering menu positions and requesting current menu information as well * as controlling the playback and choosing a preset item. * * @param inputID The input ID - TUNER is going to be used here. * @param com The Yamaha communication object to send http requests. */ public InputWithDabControlXML(String inputID, AbstractConnection com, DabBandStateListener observerForBand, PresetInfoStateListener observerForPreset, PlayInfoStateListener observerForPlayInfo) { this.inputID = inputID; this.comReference = new WeakReference<>(com); this.observerForBand = observerForBand; this.observerForPreset = observerForPreset; this.observerForPlayInfo = observerForPlayInfo; if (observerForBand == null && observerForPreset == null && observerForPlayInfo == null) { throw new IllegalArgumentException("At least one observer has to be provided"); } } /** * Wraps the XML message with the <DAB> tag: * <DAB>message</DAB>. * * @param message XML message * @return */ protected String wrInput(String message) { return "<DAB>" + message + "</DAB>"; } @Override public void update() throws IOException, ReceivedMessageParseException { AbstractConnection com = comReference.get(); String response = com.sendReceive(wrInput("<Play_Info>GetParam</Play_Info>")); Document doc = XMLUtils.xml(response); if (doc.getFirstChild() == null) { throw new ReceivedMessageParseException("<Play_Info>GetParam failed: " + response); } // @formatter:off //Sample response: //<YAMAHA_AV rsp="GET" RC="0"> // <DAB> // <Play_Info> // <Feature_Availability>Ready</Feature_Availability> // <FM> // <Preset> // <Preset_Sel>1</Preset_Sel> // </Preset> // <Tuning> // <Freq> // <Val>9945</Val> // <Exp>2</Exp> // <Unit>MHz</Unit> // </Freq> // </Tuning> // <FM_Mode>Auto</FM_Mode> // <Signal_Info> // <Tuned>Assert</Tuned> // <Stereo>Assert</Stereo> // </Signal_Info> // <Meta_Info> // <Program_Type>POP M</Program_Type> // <Program_Service> 22:59</Program_Service> // <Radio_Text>tel. 22 333 33 33 * Trojka * e-mail: trojka@polskieradio.pl</Radio_Text> // <Clock_Time>22:59</Clock_Time> // </Meta_Info> // </FM> // <DAB> // <Status>Ready</Status> // <Preset> // <Preset_Sel>No Preset</Preset_Sel> // </Preset> // <ID>2</ID> // <Signal_Info> // <Freq> // <Val>218640</Val> // <Exp>3</Exp> // <Unit>MHz</Unit> // </Freq> // <Category>Primary</Category> // <Audio_Mode>Stereo</Audio_Mode> // <Bit_Rate> // <Val>128</Val> // <Exp>0</Exp> // <Unit>Kbps</Unit> // </Bit_Rate> // <Quality>82</Quality> // <Tune_Aid>45</Tune_Aid> // <Off_Air>Negate</Off_Air> // <DAB_PLUS>Assert</DAB_PLUS> // </Signal_Info> // <Meta_Info> // <Ch_Label>11B</Ch_Label> // <Service_Label>PR Czwrka</Service_Label> // <DLS>Kluboteka Polskie Radio S.A.</DLS> // <Ensemble_Label>Polskie Radio</Ensemble_Label> // <Program_Type>Pop</Program_Type> // <Date_and_Time>12AUG'17 23:47</Date_and_Time> // </Meta_Info> // </DAB> // <Band>FM</Band> // </Play_Info> // </DAB> //</YAMAHA_AV> // @formatter:on DabBandState msgForBand = new DabBandState(); PresetInfoState msgForPreset = new PresetInfoState(); PlayInfoState msgForPlayInfo = new PlayInfoState(); msgForBand.band = XMLUtils.getNodeContentOrDefault(doc.getFirstChild(), "DAB/Play_Info/Band", msgForBand.band); logger.debug("Band set to {} for input {}", msgForBand.band, inputID); // store last state of band bandState = msgForBand; if (StringUtils.isEmpty(msgForBand.band)) { logger.warn( "Band is unknown for input {}, therefore preset and playback information will not be available", inputID); } else { Node playInfoNode = XMLUtils.getNode(doc.getFirstChild(), "DAB/Play_Info/" + msgForBand.band); msgForPreset.presetChannel = XMLUtils.getNodeContentOrDefault(playInfoNode, "Preset/Preset_Sel", -1); logger.debug("Preset set to {} for input {}", msgForPreset.presetChannel, inputID); Node metaInfoNode = XMLUtils.getNode(playInfoNode, "Meta_Info"); if (metaInfoNode != null) { msgForPlayInfo.album = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Program_Type", msgForPlayInfo.album); if (BAND_FM.equals(msgForBand.band)) { msgForPlayInfo.station = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Program_Service", msgForPlayInfo.station); msgForPlayInfo.artist = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Station", msgForPlayInfo.artist); msgForPlayInfo.song = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Radio_Text", msgForPlayInfo.song); } else if (BAND_DAB.equals(msgForBand.band)) { msgForPlayInfo.station = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Service_Label", msgForPlayInfo.station); msgForPlayInfo.artist = XMLUtils.getNodeContentOrDefault(metaInfoNode, "Ensemble_Label", msgForPlayInfo.artist); msgForPlayInfo.song = XMLUtils.getNodeContentOrDefault(metaInfoNode, "DLS", msgForPlayInfo.song); } } } // DAB does not provide channel names, the channel list will be empty msgForPreset.presetChannelNamesChanged = true; msgForPreset.presetChannelNames = new String[0]; if (observerForBand != null) { observerForBand.dabBandUpdated(msgForBand); } if (observerForPreset != null) { observerForPreset.presetInfoUpdated(msgForPreset); } if (observerForPlayInfo != null) { observerForPlayInfo.playInfoUpdated(msgForPlayInfo); } } @Override public void selectBandByName(String band) throws IOException, ReceivedMessageParseException { // Example: <Play_Control><Band>FM</Band></Play_Control> String cmd = String.format("<Play_Control><Band>%s</Band></Play_Control>", band); comReference.get().send(wrInput(cmd)); update(); } @Override public void selectItemByPresetNumber(int presetChannel) throws IOException, ReceivedMessageParseException { if (bandState == null || bandState.band == null || bandState.band.isEmpty()) { logger.warn("Cannot change preset because the band is unknown for input {}", inputID); return; } // Example: <Play_Control><FM><Preset><Preset_Sel>2</Preset_Sel></Preset></FM></Play_Control> String cmd = String.format( "<Play_Control><%s><Preset><Preset_Sel>%d</Preset_Sel></Preset></%s></Play_Control>", bandState.band, presetChannel, bandState.band); comReference.get().send(wrInput(cmd)); update(); } }