]> git.basschouten.com Git - openhab-addons.git/blob
33c002408d360d4f6910c20a056b72723ba5f50d
[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.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      * 
59      * <pre>
60      * {@code
61      * <Feature_Existence>
62      * <Main_Zone>1</Main_Zone>
63      * <Zone_2>1</Zone_2>
64      * <Zone_3>0</Zone_3>
65      * <Zone_4>0</Zone_4>
66      * <Tuner>0</Tuner>
67      * <DAB>1</DAB>
68      * <HD_Radio>0</HD_Radio>
69      * <Rhapsody>0</Rhapsody>
70      * <Napster>0</Napster>
71      * <SiriusXM>0</SiriusXM>
72      * <Spotify>1</Spotify>
73      * <Pandora>0</Pandora>
74      * <JUKE>1</JUKE>
75      * <MusicCast_Link>1</MusicCast_Link>
76      * <SERVER>1</SERVER>
77      * <NET_RADIO>1</NET_RADIO>
78      * <Bluetooth>1</Bluetooth>
79      * <USB>1</USB>
80      * <iPod_USB>1</iPod_USB>
81      * <AirPlay>1</AirPlay>
82      * </Feature_Existence>
83      * }
84      * </pre>
85      *
86      * @throws IOException
87      */
88     @Override
89     public void update() throws IOException, ReceivedMessageParseException {
90         XMLConnection con = (XMLConnection) comReference.get();
91
92         Node systemConfigNode = getResponse(con, SYSTEM_STATUS_CONFIG_CMD, SYSTEM_STATUS_CONFIG_PATH);
93
94         state.host = con.getHost();
95         state.name = getNodeContentOrEmpty(systemConfigNode, "Model_Name");
96         state.id = getNodeContentOrEmpty(systemConfigNode, "System_ID");
97         state.version = getNodeContentOrEmpty(systemConfigNode, "Version");
98
99         state.zones.clear();
100         state.features.clear();
101         state.properties.clear();
102
103         // Get and store the Yamaha Description XML. This will be used to detect proper command naming in other areas.
104         DeviceDescriptorXML descriptor = new DeviceDescriptorXML();
105         descriptor.load(con);
106         descriptor.attach(state);
107
108         Node featureNode = getNode(systemConfigNode, "Feature_Existence");
109         if (featureNode != null) {
110             for (Zone zone : Zone.values()) {
111                 checkFeature(featureNode, zone.toString(), zone, state.zones);
112             }
113
114             XMLConstants.FEATURE_BY_YNC_TAG
115                     .forEach((name, feature) -> checkFeature(featureNode, name, feature, state.features));
116
117         } else {
118             // on older models (RX-V3900) the Feature_Existence element does not exist
119
120             descriptor.zones.forEach((zone, x) -> state.zones.add(zone));
121             descriptor.features.forEach((feature, x) -> state.features.add(feature));
122         }
123
124         detectZoneBSupport(con);
125
126         logger.debug("Found zones: {}, features: {}", state.zones, state.features);
127     }
128
129     /**
130      * Detect if Zone_B is supported (HTR-4069). This will allow Zone_2 to be emulated by the Zone_B feature.
131      *
132      * @param con
133      * @throws IOException
134      * @throws ReceivedMessageParseException
135      */
136     private void detectZoneBSupport(XMLConnection con) throws IOException, ReceivedMessageParseException {
137         if (state.zones.contains(Main_Zone) && !state.zones.contains(Zone_2)) {
138             // Detect if Zone_B is supported (HTR-4069). This will allow Zone_2 to be emulated.
139
140             // Retrieve Main_Zone basic status, from which we will know this AVR supports Zone_B feature.
141             Node basicStatusNode = getZoneResponse(con, Main_Zone, ZONE_BASIC_STATUS_CMD, ZONE_BASIC_STATUS_PATH);
142             String power = getNodeContentOrEmpty(basicStatusNode, "Power_Control/Zone_B_Power_Info");
143             if (!power.isEmpty()) {
144                 logger.debug("Zone_2 emulation enabled via Zone_B");
145                 state.zones.add(Zone_2);
146                 state.features.add(Feature.ZONE_B);
147             }
148         }
149     }
150
151     private boolean isFeatureSupported(Node node, String name) {
152         String value = getNodeContentOrEmpty(node, name);
153         return "1".equals(value) || "Available".equals(value);
154     }
155
156     private <T> void checkFeature(Node node, String name, T value, Set<T> set) {
157         if (isFeatureSupported(node, name)) {
158             set.add(value);
159         }
160     }
161 }