2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.yamahareceiver.internal.protocol.xml;
15 import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.*;
16 import static org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConstants.Models.RX_A2000;
17 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLConstants.*;
18 import static org.openhab.binding.yamahareceiver.internal.protocol.xml.XMLProtocolService.getResponse;
20 import java.io.IOException;
21 import java.lang.ref.WeakReference;
22 import java.util.Arrays;
23 import java.util.HashSet;
26 import org.openhab.binding.yamahareceiver.internal.protocol.AbstractConnection;
27 import org.openhab.binding.yamahareceiver.internal.protocol.ReceivedMessageParseException;
28 import org.openhab.binding.yamahareceiver.internal.protocol.SystemControl;
29 import org.openhab.binding.yamahareceiver.internal.state.DeviceInformationState;
30 import org.openhab.binding.yamahareceiver.internal.state.SystemControlState;
31 import org.openhab.binding.yamahareceiver.internal.state.SystemControlStateListener;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.w3c.dom.Node;
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.
41 * @author David Gräff - Initial contribution
42 * @author Tomasz Maruszak - refactoring, HTR-xxxx Zone_2 compatibility
44 public class SystemControlXML implements SystemControl {
46 private final Logger logger = LoggerFactory.getLogger(SystemControlXML.class);
48 private static final Set<String> MODELS_WITH_PARTY_SUPPORT = new HashSet<>(Arrays.asList(RX_A2000));
50 private WeakReference<AbstractConnection> comReference;
51 private SystemControlStateListener observer;
52 private final DeviceDescriptorXML descriptorXML;
54 protected CommandTemplate power = new CommandTemplate(
55 "<System><Power_Control><Power>%s</Power></Power_Control></System>", "System/Power_Control/Power");
56 protected CommandTemplate partyMode = new CommandTemplate(
57 "<System><Party_Mode><Mode>%s</Mode></Party_Mode></System>", "System/Party_Mode/Mode");
58 protected boolean partyModeSupported;
59 protected CommandTemplate partyModeMute = new CommandTemplate(
60 "<System><Party_Mode><Volume><Mute>%s</Mute></Volume></Party_Mode></System>");
61 protected boolean partyModeMuteSupported;
62 protected CommandTemplate partyModeVolume = new CommandTemplate(
63 "<System><Party_Mode><Volume><Lvl>%s</Lvl></Volume></Party_Mode></System>");
64 protected boolean partyModeVolumeSupported;
66 public SystemControlXML(AbstractConnection xml, SystemControlStateListener observer,
67 DeviceInformationState deviceInformationState) {
68 this.comReference = new WeakReference<>(xml);
69 this.observer = observer;
70 this.descriptorXML = DeviceDescriptorXML.getAttached(deviceInformationState);
72 this.applyModelVariations();
76 * Apply command changes to ensure compatibility with all supported models
78 protected void applyModelVariations() {
79 if (descriptorXML == null) {
80 logger.trace("Device descriptor not available");
84 logger.trace("Compatibility detection");
86 partyModeSupported = descriptorXML.hasFeature(
87 d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName())
88 || d.system.hasCommandEnding("System,Party_Mode,Mode"),
89 () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE));
91 partyModeMuteSupported = descriptorXML.hasFeature(
92 d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName())
93 || d.system.hasCommandEnding("System,Party_Mode,Volume,Mute"),
94 () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_MUTE));
96 partyModeVolumeSupported = descriptorXML.hasFeature(
97 d -> MODELS_WITH_PARTY_SUPPORT.contains(d.getUnitName())
98 || d.system.hasCommandEnding("System,Party_Mode,Volume,Lvl"),
99 () -> logger.debug("The {} channel is not supported on your model", CHANNEL_PARTY_MODE_VOLUME));
103 public void setPower(boolean power) throws IOException, ReceivedMessageParseException {
104 String cmd = this.power.apply(power ? ON : POWER_STANDBY);
105 comReference.get().send(cmd);
110 public void setPartyMode(boolean on) throws IOException, ReceivedMessageParseException {
111 if (!partyModeSupported) {
114 String cmd = this.partyMode.apply(on ? ON : OFF);
115 comReference.get().send(cmd);
120 public void setPartyModeMute(boolean on) throws IOException, ReceivedMessageParseException {
121 if (!partyModeMuteSupported) {
124 String cmd = this.partyModeMute.apply(on ? ON : OFF);
125 comReference.get().send(cmd);
130 public void setPartyModeVolume(boolean increment) throws IOException, ReceivedMessageParseException {
131 if (!partyModeVolumeSupported) {
134 String cmd = this.partyModeVolume.apply(increment ? UP : DOWN);
135 comReference.get().send(cmd);
140 public void update() throws IOException, ReceivedMessageParseException {
141 if (observer == null) {
145 AbstractConnection conn = comReference.get();
147 SystemControlState state = new SystemControlState();
149 Node node = getResponse(conn, power.apply(GET_PARAM), power.getPath());
150 state.power = node != null && ON.equals(node.getTextContent());
152 if (partyModeSupported) {
153 // prevent an unnecessary call
154 node = getResponse(conn, partyMode.apply(GET_PARAM), partyMode.getPath());
155 state.partyMode = node != null && ON.equals(node.getTextContent());
158 logger.debug("System state - power: {}, partyMode: {}", state.power, state.partyMode);
160 observer.systemControlStateChanged(state);