2 * Copyright (c) 2010-2024 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.denonmarantz.internal.connector;
15 import static org.openhab.binding.denonmarantz.internal.DenonMarantzBindingConstants.DB_OFFSET;
17 import java.math.BigDecimal;
18 import java.math.RoundingMode;
19 import java.util.concurrent.ScheduledExecutorService;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.openhab.binding.denonmarantz.internal.DenonMarantzState;
23 import org.openhab.binding.denonmarantz.internal.UnsupportedCommandTypeException;
24 import org.openhab.binding.denonmarantz.internal.config.DenonMarantzConfiguration;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.IncreaseDecreaseType;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.PercentType;
29 import org.openhab.core.library.types.StringType;
30 import org.openhab.core.types.Command;
31 import org.openhab.core.types.RefreshType;
34 * Abstract class containing common functionality for the connectors.
36 * @author Jan-Willem Veldhuis - Initial contribution
39 public abstract class DenonMarantzConnector {
41 private static final BigDecimal POINTFIVE = new BigDecimal("0.5");
42 protected ScheduledExecutorService scheduler;
43 protected DenonMarantzState state;
44 protected DenonMarantzConfiguration config;
46 public abstract void connect();
48 public abstract void dispose();
50 protected abstract void internalSendCommand(String command);
52 public DenonMarantzConnector(DenonMarantzConfiguration config, ScheduledExecutorService scheduler,
53 DenonMarantzState state) {
55 this.scheduler = scheduler;
59 public void sendCustomCommand(Command command) throws UnsupportedCommandTypeException {
61 if (command instanceof StringType) {
62 cmd = command.toString();
64 throw new UnsupportedCommandTypeException();
66 internalSendCommand(cmd);
69 public void sendInputCommand(Command command, int zone) throws UnsupportedCommandTypeException {
78 zonePrefix = "Z" + zone;
81 throw new UnsupportedCommandTypeException("Zone must be in range [1-4], zone: " + zone);
83 String cmd = zonePrefix;
84 if (command instanceof StringType) {
85 cmd += command.toString();
86 } else if (command instanceof RefreshType) {
89 throw new UnsupportedCommandTypeException();
91 internalSendCommand(cmd);
94 public void sendSurroundProgramCommand(Command command) throws UnsupportedCommandTypeException {
96 if (command instanceof RefreshType) {
99 throw new UnsupportedCommandTypeException();
101 internalSendCommand(cmd);
104 public void sendMuteCommand(Command command, int zone) throws UnsupportedCommandTypeException {
105 if (zone < 1 || zone > 4) {
106 throw new UnsupportedCommandTypeException("Zone must be in range [1-4], zone: " + zone);
108 StringBuilder sb = new StringBuilder();
110 sb.append("Z").append(zone);
113 String cmd = sb.toString();
114 if (command == OnOffType.ON) {
116 } else if (command == OnOffType.OFF) {
118 } else if (command instanceof RefreshType) {
121 throw new UnsupportedCommandTypeException();
123 internalSendCommand(cmd);
126 public void sendPowerCommand(Command command, int zone) throws UnsupportedCommandTypeException {
138 zonePrefix = "Z" + zone;
141 throw new UnsupportedCommandTypeException("Zone must be in range [0-4], zone: " + zone);
143 String cmd = zonePrefix;
144 if (command == OnOffType.ON) {
146 } else if (command == OnOffType.OFF) {
147 cmd += (zone == 0) ? "STANDBY" : "OFF";
148 } else if (command instanceof RefreshType) {
151 throw new UnsupportedCommandTypeException();
153 internalSendCommand(cmd);
156 public void sendVolumeCommand(Command command, int zone) throws UnsupportedCommandTypeException {
165 zonePrefix = "Z" + zone;
168 throw new UnsupportedCommandTypeException("Zone must be in range [1-4], zone: " + zone);
170 String cmd = zonePrefix;
171 if (command instanceof RefreshType) {
173 } else if (command == IncreaseDecreaseType.INCREASE) {
175 } else if (command == IncreaseDecreaseType.DECREASE) {
177 } else if (command instanceof DecimalType decimalCommand) {
178 cmd += toDenonValue(decimalCommand);
179 } else if (command instanceof PercentType percentCommand) {
180 cmd += percentToDenonValue(percentCommand.toBigDecimal());
182 throw new UnsupportedCommandTypeException();
184 internalSendCommand(cmd);
187 public void sendVolumeDbCommand(Command command, int zone) throws UnsupportedCommandTypeException {
188 Command dbCommand = command;
189 if (dbCommand instanceof PercentType) {
190 throw new UnsupportedCommandTypeException();
191 } else if (dbCommand instanceof DecimalType) {
192 // convert dB to 'normal' volume by adding the offset of 80
193 dbCommand = new DecimalType(((DecimalType) command).toBigDecimal().add(DB_OFFSET));
195 sendVolumeCommand(dbCommand, zone);
198 protected String toDenonValue(DecimalType number) {
199 String dbString = String.valueOf(number.intValue());
200 BigDecimal num = number.toBigDecimal();
201 if (num.compareTo(BigDecimal.TEN) == -1) {
202 dbString = "0" + dbString;
204 if (num.remainder(BigDecimal.ONE).equals(POINTFIVE)) {
205 dbString = dbString + "5";
210 protected String percentToDenonValue(BigDecimal pct) {
211 // Round to nearest number divisible by 0.5
212 BigDecimal percent = pct.divide(POINTFIVE).setScale(0, RoundingMode.UP).multiply(POINTFIVE)
213 .min(config.getMainVolumeMax()).max(BigDecimal.ZERO);
215 return toDenonValue(new DecimalType(percent));