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