]> git.basschouten.com Git - openhab-addons.git/blob
9f68db839759ba67bcde6b146209556b402e9b1e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.YamahaReceiverBindingConstants.Zone.*;
16 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.Commands.*;
17 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.*;
18 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLUtils.*;
19
20 import java.io.IOException;
21 import java.lang.ref.WeakReference;
22 import java.util.Set;
23
24 import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Feature;
25 import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Zone;
26 import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection;
27 import org.openhab.binding.yamahareceiver.internal.protocol.DeviceInformation;
28 import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException;
29 import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState;
30 import org.openhab.binding.yamahareceiver.internal.state.SystemControlState;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.w3c.dom.Node;
34
35 /**
36  * The system control protocol class is used to control basic non-zone functionality
37  * of a Yamaha receiver with HTTP/xml.
38  * No state will be saved in here, but in {@link SystemControlState} instead.
39  *
40  * @author David Graeff - Initial contribution
41  * @author Tomasz Maruszak - DAB support, Spotify support, better feature detection
42  */
43 public class DeviceInformationXML implements DeviceInformation {
44     private final Logger logger = LoggerFactory.getLogger(DeviceInformationXML.class);
45
46     private final WeakReference<AbstractConnection> comReference;
47     protected DeviceInformationState state;
48
49     public DeviceInformationXML(AbstractConnection com, DeviceInformationState state) {
50         this.comReference = new WeakReference<>(com);
51         this.state = state;
52     }
53
54     /**
55      * We need that called only once. Will give us name, id, version and zone information.
56      *
57      * Example:
58      * <Feature_Existence>
59      * <Main_Zone>1</Main_Zone>
60      * <Zone_2>1</Zone_2>
61      * <Zone_3>0</Zone_3>
62      * <Zone_4>0</Zone_4>
63      * <Tuner>0</Tuner>
64      * <DAB>1</DAB>
65      * <HD_Radio>0</HD_Radio>
66      * <Rhapsody>0</Rhapsody>
67      * <Napster>0</Napster>
68      * <SiriusXM>0</SiriusXM>
69      * <Spotify>1</Spotify>
70      * <Pandora>0</Pandora>
71      * <JUKE>1</JUKE>
72      * <MusicCast_Link>1</MusicCast_Link>
73      * <SERVER>1</SERVER>
74      * <NET_RADIO>1</NET_RADIO>
75      * <Bluetooth>1</Bluetooth>
76      * <USB>1</USB>
77      * <iPod_USB>1</iPod_USB>
78      * <AirPlay>1</AirPlay>
79      * </Feature_Existence>
80      *
81      * @throws IOException
82      */
83     @Override
84     public void update() throws IOException, ReceivedMessageParseException {
85         XMLConnection con = (XMLConnection) comReference.get();
86
87         Node systemConfigNode = getResponse(con, SYSTEM_STATUS_CONFIG_CMD, SYSTEM_STATUS_CONFIG_PATH);
88
89         state.host = con.getHost();
90         state.name = getNodeContentOrEmpty(systemConfigNode, "Model_Name");
91         state.id = getNodeContentOrEmpty(systemConfigNode, "System_ID");
92         state.version = getNodeContentOrEmpty(systemConfigNode, "Version");
93
94         state.zones.clear();
95         state.features.clear();
96         state.properties.clear();
97
98         // Get and store the Yamaha Description XML. This will be used to detect proper command naming in other areas.
99         DeviceDescriptorXML descriptor = new DeviceDescriptorXML();
100         descriptor.load(con);
101         descriptor.attach(state);
102
103         Node featureNode = getNode(systemConfigNode, "Feature_Existence");
104         if (featureNode != null) {
105             for (Zone zone : Zone.values()) {
106                 checkFeature(featureNode, zone.toString(), zone, state.zones);
107             }
108
109             XMLConstants.FEATURE_BY_YNC_TAG
110                     .forEach((name, feature) -> checkFeature(featureNode, name, feature, state.features));
111
112         } else {
113             // on older models (RX-V3900) the Feature_Existence element does not exist
114
115             descriptor.zones.forEach((zone, x) -> state.zones.add(zone));
116             descriptor.features.forEach((feature, x) -> state.features.add(feature));
117         }
118
119         detectZoneBSupport(con);
120
121         logger.debug("Found zones: {}, features: {}", state.zones, state.features);
122     }
123
124     /**
125      * Detect if Zone_B is supported (HTR-4069). This will allow Zone_2 to be emulated by the Zone_B feature.
126      *
127      * @param con
128      * @throws IOException
129      * @throws ReceivedMessageParseException
130      */
131     private void detectZoneBSupport(XMLConnection con) throws IOException, ReceivedMessageParseException {
132         if (state.zones.contains(Main_Zone) && !state.zones.contains(Zone_2)) {
133             // Detect if Zone_B is supported (HTR-4069). This will allow Zone_2 to be emulated.
134
135             // Retrieve Main_Zone basic status, from which we will know this AVR supports Zone_B feature.
136             Node basicStatusNode = getZoneResponse(con, Main_Zone, ZONE_BASIC_STATUS_CMD, ZONE_BASIC_STATUS_PATH);
137             String power = getNodeContentOrEmpty(basicStatusNode, "Power_Control/Zone_B_Power_Info");
138             if (!power.isEmpty()) {
139                 logger.debug("Zone_2 emulation enabled via Zone_B");
140                 state.zones.add(Zone_2);
141                 state.features.add(Feature.ZONE_B);
142             }
143         }
144     }
145
146     private boolean isFeatureSupported(Node node, String name) {
147         String value = getNodeContentOrEmpty(node, name);
148         boolean supported = value.equals("1") || value.equals("Available");
149         return supported;
150     }
151
152     private <T> void checkFeature(Node node, String name, T value, Set<T> set) {
153         if (isFeatureSupported(node, name)) {
154             set.add(value);
155         }
156     }
157 }