# Monoprice Whole House Audio Binding
-This binding can be used to control a Monoprice MPR-SG6Z (10761), Monoprice Passive Matrix (39261) & Dayton Audio DAX66 whole house multi-zone amplifier system.
-All amplifier functions available through the serial port interface can be controlled by the binding.
-Up to 18 zones can be controlled when 3 amps are connected together (if not all zones on the amp are used they can be excluded via configuration).
-Activating the 'Page All Zones' feature can only be done through the +12v trigger input on the back of the amplifier.
+This binding can be used to control the following types of whole house multi-zone amplifier systems:
+
+- Monoprice MPR-SG6Z (10761), Monoprice Passive Matrix (39261), Dayton Audio DAX66 or compatible clones
+- Monoprice 31028 or OSD Audio PAM1270 **(untested)**
+- Dayton Audio DAX88 **(untested)**
+- Xantech MRC88, MX88, MRAUDIO8X8 or CM8X8 **(untested)**
The binding supports two different kinds of connections:
-- serial connection,
+- serial port connection
- serial over IP connection
For users without a serial port on the server side, you can use a USB to serial adapter.
You don't need to have your whole house amplifier device directly connected to your openHAB server.
-Some newer versions of the amplifier have a built-in ethernet port that supports serial over IP.
+Some newer versions of the amplifier have a built-in Ethernet port that supports serial over IP.
Or you can connect it for example to a Raspberry Pi and use [ser2net Linux tool](https://sourceforge.net/projects/ser2net/) to make the serial connection available on the LAN (serial over IP).
## Supported Things
-Monoprice 10761 & 39261 and Dayton Audio DAX66 Amplifiers use the `amplifier` thing id. Up to 18 zones with 3 linked amps, 6 source inputs.
-Note: Compatible clones (including 4 zone versions) from McLELLAND, Factor, Soundavo, etc. should work as well.
+Monoprice 10761 & 39261 or Dayton Audio DAX66 amplifiers use the `amplifier` thing id. Up to 18 zones with 3 linked amps and 6 source inputs are supported.
+Note: Compatible clones (including 4 zone versions) from McLELLAND, Factor, Soundavo, etc. should work as well.
+
+***The following three thing types were implemented via available documentation only and have not been tested. Please open an issue for any bugs found when using these thing types.***
+
+Monoprice 31028 or OSD Audio PAM1270 70 volt amplifiers use the `monoprice70` thing id. 6 zones per amp (not linkable) and 2 source inputs are supported.
+
+Dayton Audio DAX88 amplifiers use the `dax88` thing id. 8 zones (2 un-amplified) per amp (not linkable) and 8 source inputs are supported.
+
+Xantech MRC88, MX88, MRAUDIO8X8 or CM8X8 amplifiers use the `xantech` thing id. Up to 16 zones with 2 linked amps and 8 source inputs are supported.
+Some Xantech amps provide unsolicited zone updates for keypad actions and may work with the `disableKeypadPolling` option set to true which will prevent un-necessary polling of the amplifier.
+Note: MRC44 amps do not support serial control.
## Discovery
Discovery is not supported.
You have to add all things manually.
-## Binding Configuration
-
-There are no overall binding configuration settings that need to be set.
-All settings are through thing configuration parameters.
-
## Thing Configuration
-The thing has the following configuration parameters:
-
-| Parameter Label | Parameter ID | Description | Accepted values |
-|----------------------|------------------|--------------------------------------------------------------------------------------------------------------------------------|------------------|
-| Serial Port | serialPort | Serial port to use for connecting to the Monoprice whole house amplifier device | Serial port name |
-| Address | host | Host name or IP address of the amplifier or serial over IP device | Host name or IP |
-| Port | port | Communication port (default 8080 for newer amps with built-in serial over IP) | TCP port number |
-| Number of Zones | numZones | (Optional) Number of amplifier zones to utilize in the binding (up to 18 zones with 3 amplifiers connected together) | 1-18; default 6 |
-| Polling Interval | pollingInterval | (Optional) Configures how often (in seconds) to poll the amplifier to check for zone updates | 5-60; default 15 |
-| Ignore Zones | ignoreZones | (Optional) A comma seperated list of Zone numbers that will ignore the 'All Zone' (except All Off) commands | ie: "1,6,10" |
-| Initial All Volume | initialAllVolume | (Optional) When 'All' zones are activated, the volume will reset to this value to prevent excessive blaring of sound ;) | 1-30; default 10 |
-| Source 1 Input Label | inputLabel1 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 1") | A free text name |
-| Source 2 Input Label | inputLabel2 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 2") | A free text name |
-| Source 3 Input Label | inputLabel3 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 3") | A free text name |
-| Source 4 Input Label | inputLabel4 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 4") | A free text name |
-| Source 5 Input Label | inputLabel5 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 5") | A free text name |
-| Source 6 Input Label | inputLabel6 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 6") | A free text name |
+The thing has the following configuration parameters (number of sources and zones is amplifier dependent):
+
+| Parameter Label | Parameter ID | Description | Accepted values |
+|------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------|------------------|
+| Serial Port | serialPort | Serial port to use for connecting to the whole house amplifier device | Serial port name |
+| Address | host | Host name or IP address of the amplifier or serial over IP device | Host name or IP |
+| Port | port | Communication port (8080 for newer amps with built-in serial over IP) | TCP port number |
+| Number of Zones | numZones | (Optional) Number of amplifier zones to utilize in the binding (See Supported Things for max number of zones per Thing type) | 1-18; default 6 |
+| Polling Interval | pollingInterval | (Optional) Configures how often (in seconds) to poll the amplifier to check for zone updates | 5-60; default 15 |
+| Ignore Zones | ignoreZones | (Optional) A comma separated list of Zone numbers that will ignore the 'All Zone' (except All Off) commands | ie: "1,6,10" |
+| Initial All Volume | initialAllVolume | (Optional) When 'All' zones are activated, the volume will reset to this value to prevent excessive blaring of sound ;) | 1-30; default 10 |
+| Source 1 Input Label | inputLabel1 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 1") | A free text name |
+| Source 2 Input Label | inputLabel2 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 2") | A free text name |
+| Source 3 Input Label | inputLabel3 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 3") | A free text name |
+| Source 4 Input Label | inputLabel4 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 4") | A free text name |
+| Source 5 Input Label | inputLabel5 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 5") | A free text name |
+| Source 6 Input Label | inputLabel6 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 6") | A free text name |
+| Source 7 Input Label | inputLabel7 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 7") | A free text name |
+| Source 8 Input Label | inputLabel8 | (Optional) Friendly name for the input source to be displayed in the UI (ie: Chromecast, Radio, CD, etc.) (default "Source 8") | A free text name |
+| Disable Keypad Polling | disableKeypadPolling | Set to **true** if physical keypads are not used so the binding will not needlessly poll the amplifier zones for changes | true or false |
Some notes:
+- On the 10761/DAX66 amp, activating the 'Page All Zones' feature can only be done through the +12v trigger input on the back of the amplifier.
+
- On Linux, you may get an error stating the serial port cannot be opened when the MonopriceAudio binding tries to load.
- You can get around this by adding the `openhab` user to the `dialout` group like this: `usermod -a -G dialout openhab`.
- Also on Linux you may have issues with the USB if using two serial USB devices e.g. MonopriceAudio and RFXcom.
- See the [general documentation about serial port configuration](/docs/administration/serial.html) for more on symlinking the USB ports.
-- Here is an example of ser2net.conf you can use to share your serial port /dev/ttyUSB0 on IP port 8080 using [ser2net Linux tool](https://sourceforge.net/projects/ser2net/):
+- Here is an example of ser2net.conf (for ser2net version < 4) you can use to share your serial port /dev/ttyUSB0 on IP port 8080 using [ser2net Linux tool](https://sourceforge.net/projects/ser2net/):
```text
8080:raw:0:/dev/ttyUSB0:9600 8DATABITS NONE 1STOPBIT LOCAL
```
+- Here is an example of ser2net.yaml (for ser2net version >= 4) you can use to share your serial port /dev/ttyUSB0 on IP port 8080 using [ser2net Linux tool](https://sourceforge.net/projects/ser2net/):
+
+```yaml
+connection: &conMono
+ accepter: tcp,8080
+ enable: on
+ options:
+ kickolduser: true
+ connector: serialdev,
+ /dev/ttyUSB0,
+ 9600n81,local
+```
+
## Channels
The following channels are available:
-
-| Channel ID | Item Type | Description |
-|-------------------------------|-----------|---------------------------------------------------------------------------------------------------------------|
-| all#allpower | Switch | Turn all zones on or off simultaneously (those specified by the ignoreZones config option will not turn on) |
-| all#allsource | Number | Select the input source for all zones simultaneously (1-6) (except ignoreZones) |
-| all#allvolume | Dimmer | Control the volume for all zones simultaneously (0-100%) [translates to 0-38] (except ignoreZones) |
-| all#allmute | Switch | Mute or unmute all zones simultaneously (except ignoreZones) |
-| zoneN#power (where N= 1-18) | Switch | Turn the power for a zone on or off |
-| zoneN#source (where N= 1-18) | Number | Select the input source for a zone (1-6) |
-| zoneN#volume (where N= 1-18) | Dimmer | Control the volume for a zone (0-100%) [translates to 0-38] |
-| zoneN#mute (where N= 1-18) | Switch | Mute or unmute a zone |
-| zoneN#treble (where N= 1-18) | Number | Adjust the treble control for a zone (-7 to 7) -7=none, 0=flat, 7=full |
-| zoneN#bass (where N= 1-18) | Number | Adjust the bass control for a zone (-7 to 7) -7=none, 0=flat, 7=full |
-| zoneN#balance (where N= 1-18) | Number | Adjust the balance control for a zone (-10 to 10) -10=left, 0=center, 10=right |
-| zoneN#dnd (where N= 1-18) | Switch | Turn on or off the Do Not Disturb for the zone (for when the amplifier's external page trigger is activated) |
-| zoneN#page (where N= 1-18) | Contact | Indicates if the page input is activated for the zone |
-| zoneN#keypad (where N= 1-18) | Contact | Indicates if the physical keypad is attached to a zone |
+Note that `dnd`, `page` and `keypad` are not available on all thing types.
+
+| Channel ID | Item Type | Description |
+|-------------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------------|
+| all#allpower | Switch | Turn all zones on or off simultaneously (those specified by the ignoreZones config option will not turn on) |
+| all#allsource | Number | Select the input source for all zones simultaneously (1-8) [number of sources is amplifier dependent] (except ignoreZones) |
+| all#allvolume | Dimmer | Control the volume for all zones simultaneously (0-100%) [translates to the particular amplifier's volume range] (except ignoreZones) |
+| all#allmute | Switch | Mute or unmute all zones simultaneously (except ignoreZones) |
+| zoneN#power (where N= 1-18) | Switch | Turn the power for a zone on or off |
+| zoneN#source (where N= 1-18) | Number | Select the input source for a zone (1-8) [number of sources is amplifier dependent] |
+| zoneN#volume (where N= 1-18) | Dimmer | Control the volume for a zone (0-100%) [translates to the particular amplifier's volume range] |
+| zoneN#mute (where N= 1-18) | Switch | Mute or unmute a zone |
+| zoneN#treble (where N= 1-18) | Number | Adjust the treble control for a zone [range is amplifier dependent] |
+| zoneN#bass (where N= 1-18) | Number | Adjust the bass control for a zone [range is amplifier dependent] |
+| zoneN#balance (where N= 1-18) | Number | Adjust the balance control for a zone [0=center, range is amplifier dependent] |
+| zoneN#dnd (where N= 1-18) | Switch | Turn on or off the Do Not Disturb for the zone (for when the amplifier's external page trigger is activated) |
+| zoneN#page (where N= 1-18) | Contact | Indicates if the page input is activated for the zone |
+| zoneN#keypad (where N= 1-18) | Contact | Indicates if the physical keypad is attached to a zone |
## Full Example
monoprice.things:
```java
-// serial port connection
-monopriceaudio:amplifier:myamp "Monoprice WHA" [ serialPort="COM5", pollingInterval=15, numZones=6, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono"]
+// Monoprice 10761, 39261 / DAX66 (serial port connection)
+monopriceaudio:amplifier:myamp "Monoprice WHA" [ serialPort="COM5", pollingInterval=15, numZones=6, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono" ]
+
+// Monoprice 10761, 39261 / DAX66 (serial over IP connection)
+monopriceaudio:amplifier:myamp "Monoprice WHA" [ host="192.168.0.10", port=8080, pollingInterval=15, numZones=6, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono" ]
-// serial over IP connection
-monopriceaudio:amplifier:myamp "Monoprice WHA" [ host="192.168.0.10", port=8080, pollingInterval=15, numZones=6, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono"]
+// Monoprice 31028 or OSD Audio PAM1270
+monopriceaudio:monoprice70:myamp "Monoprice WHA" [ serialPort="COM5", pollingInterval=30, numZones=6, inputLabel1="Source 0 - Bus", inputLabel2="Source 1 - Line" ]
+// Dayton DAX88
+monopriceaudio:dax88:myamp "Dayton WHA" [ serialPort="COM5", pollingInterval=15, numZones=8, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono", inputLabel7="Ipod", inputLabel8="Streaming" ]
+
+// Xantech 8x8
+monopriceaudio:xantech:myamp "Xantech WHA" [ serialPort="COM5", pollingInterval=30, numZones=8, inputLabel1="Chromecast", inputLabel2="Radio", inputLabel3="CD Player", inputLabel4="Bluetooth Audio", inputLabel5="HTPC", inputLabel6="Phono", inputLabel7="Ipod", inputLabel8="Sirius" ]
+
+// Note that host and port can be used with any of the thing types to connect as serial over IP
```
monoprice.items:
```java
+// substitute 'amplifier' for the appropriate thing id if using 31028, DAX88 or Xantech amplifier
+
Switch all_allpower "All Zones Power" { channel="monopriceaudio:amplifier:myamp:all#allpower" }
Number all_source "Source Input [%s]" { channel="monopriceaudio:amplifier:myamp:all#allsource" }
Dimmer all_volume "Volume [%d %%]" { channel="monopriceaudio:amplifier:myamp:all#allvolume" }
Switch z1_page "Page Active: [%s]" { channel="monopriceaudio:amplifier:myamp:zone1#page" }
Switch z1_keypad "Keypad Connected: [%s]" { channel="monopriceaudio:amplifier:myamp:zone1#keypad" }
-// repeat for zones 2-18 (substitute z1 and zone1)
+// repeat for total number of zones used (substitute z1 and zone1)
```
monoprice.sitemap:
// Volume can be a Slider also
Setpoint item=z1_volume minValue=0 maxValue=100 step=1 visibility=[z1_power==ON]
Switch item=z1_mute visibility=[z1_power==ON]
+ // Min and Max values are for the 10761 amp, adjust if using a different model
Setpoint item=z1_treble label="Treble Adjustment [%d]" minValue=-7 maxValue=7 step=1 visibility=[z1_power==ON]
Setpoint item=z1_bass label="Bass Adjustment [%d]" minValue=-7 maxValue=7 step=1 visibility=[z1_power==ON]
Setpoint item=z1_balance label="Balance Adjustment [%d]" minValue=-10 maxValue=10 step=1 visibility=[z1_power==ON]
Text item=z1_keypad label="Keypad Connected: [%s]" visibility=[z1_power==ON]
}
- // repeat for zones 2-18 (substitute z1)
+ // repeat for total number of zones used (substitute z1)
}
```
* used across the whole binding.
*
* @author Michael Lobstein - Initial contribution
+ * @author Michael Lobstein - Add support for additional amplifier types
*/
@NonNullByDefault
public class MonopriceAudioBindingConstants {
public static final String BINDING_ID = "monopriceaudio";
// List of all Thing Type UIDs
- public static final ThingTypeUID THING_TYPE_AMP = new ThingTypeUID(BINDING_ID, "amplifier");
+ // to avoid breaking existing installations, the 10761/DAX66 will still be known as 'amplifier'
+ public static final ThingTypeUID THING_TYPE_MP = new ThingTypeUID(BINDING_ID, "amplifier");
+ public static final ThingTypeUID THING_TYPE_MP70 = new ThingTypeUID(BINDING_ID, "monoprice70");
+ public static final ThingTypeUID THING_TYPE_DAX88 = new ThingTypeUID(BINDING_ID, "dax88");
+ public static final ThingTypeUID THING_TYPE_XT = new ThingTypeUID(BINDING_ID, "xantech");
// List of all Channel types
public static final String CHANNEL_TYPE_POWER = "power";
public static final String CHANNEL_TYPE_ALLSOURCE = "allsource";
public static final String CHANNEL_TYPE_ALLVOLUME = "allvolume";
public static final String CHANNEL_TYPE_ALLMUTE = "allmute";
+
+ // misc
+ public static final String ONE = "1";
+ public static final String ZERO = "0";
+ public static final String EMPTY = "";
+ public static final int NIL = -1;
}
import static org.openhab.binding.monopriceaudio.internal.MonopriceAudioBindingConstants.*;
-import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.monopriceaudio.internal.communication.AmplifierModel;
import org.openhab.binding.monopriceaudio.internal.handler.MonopriceAudioHandler;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Thing;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* The {@link MonopriceAudioHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Michael Lobstein - Initial contribution
+ * @author Michael Lobstein - Add support for additional amplifier types
*/
@NonNullByDefault
@Component(configurationPid = "binding.monopriceaudio", service = ThingHandlerFactory.class)
public class MonopriceAudioHandlerFactory extends BaseThingHandlerFactory {
- private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_AMP);
+ private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_MP, THING_TYPE_MP70,
+ THING_TYPE_DAX88, THING_TYPE_XT);
private final SerialPortManager serialPortManager;
private final MonopriceAudioStateDescriptionOptionProvider stateDescriptionProvider;
+ private final Logger logger = LoggerFactory.getLogger(MonopriceAudioHandlerFactory.class);
+
@Activate
public MonopriceAudioHandlerFactory(final @Reference MonopriceAudioStateDescriptionOptionProvider provider,
final @Reference SerialPortManager serialPortManager) {
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
- if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
- return new MonopriceAudioHandler(thing, stateDescriptionProvider, serialPortManager);
+ if (THING_TYPE_MP.equals(thingTypeUID)) {
+ return new MonopriceAudioHandler(thing, AmplifierModel.MONOPRICE, stateDescriptionProvider,
+ serialPortManager);
+ }
+
+ if (THING_TYPE_MP70.equals(thingTypeUID)) {
+ return new MonopriceAudioHandler(thing, AmplifierModel.MONOPRICE70, stateDescriptionProvider,
+ serialPortManager);
+ }
+
+ if (THING_TYPE_DAX88.equals(thingTypeUID)) {
+ return new MonopriceAudioHandler(thing, AmplifierModel.DAX88, stateDescriptionProvider, serialPortManager);
+ }
+
+ if (THING_TYPE_XT.equals(thingTypeUID)) {
+ return new MonopriceAudioHandler(thing, AmplifierModel.XANTECH, stateDescriptionProvider,
+ serialPortManager);
}
+ logger.warn("Unknown thing type: {}: {}", thingTypeUID.getId(), thingTypeUID.getBindingId());
return null;
}
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2023 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.monopriceaudio.internal.communication;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.monopriceaudio.internal.configuration.MonopriceAudioThingConfiguration;
+import org.openhab.binding.monopriceaudio.internal.dto.MonopriceAudioZoneDTO;
+import org.openhab.core.types.StateOption;
+
+/**
+ * The {@link AmplifierModel} is responsible for mapping low level communications for each supported amplifier model.
+ *
+ * @author Michael Lobstein - Initial contribution
+ */
+@NonNullByDefault
+public enum AmplifierModel {
+
+ // Monoprice 10761/Dayton Audio DAX66
+ MONOPRICE("<", "\r", "?", "", "#>", "PR", "CH", "VO", "MU", "TR", "BS", "BL", "DT", 38, -7, 7, 7, -10, 10, 10, 18,
+ 6, true, List.of("11", "12", "13", "14", "15", "16", "21", "22", "23", "24", "25", "26", "31", "32", "33",
+ "34", "35", "36")) {
+ @Override
+ public MonopriceAudioZoneDTO getZoneData(String newZoneData) {
+ return getMonopriceZoneData(newZoneData);
+ }
+
+ @Override
+ public List<StateOption> getSourceLabels(MonopriceAudioThingConfiguration config) {
+ return List.of(new StateOption("1", config.inputLabel1), new StateOption("2", config.inputLabel2),
+ new StateOption("3", config.inputLabel3), new StateOption("4", config.inputLabel4),
+ new StateOption("5", config.inputLabel5), new StateOption("6", config.inputLabel6));
+ }
+ },
+ // Dayton Audio DAX88
+ DAX88("<", "\r", "?", "", ">", "PR", "CH", "VO", "MU", "TR", "BS", "BL", "DT", 38, -12, 12, 12, -10, 10, 10, 8, 8,
+ true, List.of("01", "02", "03", "04", "05", "06", "07", "08")) {
+ @Override
+ public MonopriceAudioZoneDTO getZoneData(String newZoneData) {
+ return getMonopriceZoneData(newZoneData);
+ }
+
+ @Override
+ public List<StateOption> getSourceLabels(MonopriceAudioThingConfiguration config) {
+ return List.of(new StateOption("1", config.inputLabel1), new StateOption("2", config.inputLabel2),
+ new StateOption("3", config.inputLabel3), new StateOption("4", config.inputLabel4),
+ new StateOption("5", config.inputLabel5), new StateOption("6", config.inputLabel6),
+ new StateOption("7", config.inputLabel7), new StateOption("8", config.inputLabel8));
+ }
+ },
+ MONOPRICE70("!", "+\r", "?", "ZS", "?", "PR", "IS", "VO", "MU", "TR", "BS", "BA", "", 38, -7, 7, 7, -32, 31, 32, 6,
+ 2, false, List.of("1", "2", "3", "4", "5", "6")) {
+ @Override
+ public MonopriceAudioZoneDTO getZoneData(String newZoneData) {
+ MonopriceAudioZoneDTO zoneData = new MonopriceAudioZoneDTO();
+
+ Matcher matcher = MONOPRICE70_PATTERN.matcher(newZoneData);
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setVolume(Integer.parseInt(matcher.group(2)));
+ zoneData.setPower(matcher.group(3));
+ zoneData.setMute(matcher.group(4));
+ zoneData.setSource(matcher.group(5));
+ return zoneData;
+ }
+
+ matcher = MONOPRICE70_TREB_PATTERN.matcher(newZoneData);
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setTreble(Integer.parseInt(matcher.group(2)));
+ return zoneData;
+ }
+
+ matcher = MONOPRICE70_BASS_PATTERN.matcher(newZoneData);
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setBass(Integer.parseInt(matcher.group(2)));
+ return zoneData;
+ }
+
+ matcher = MONOPRICE70_BALN_PATTERN.matcher(newZoneData);
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setBalance(Integer.parseInt(matcher.group(2)));
+ return zoneData;
+ }
+
+ return zoneData;
+ }
+
+ @Override
+ public List<StateOption> getSourceLabels(MonopriceAudioThingConfiguration config) {
+ return List.of(new StateOption("0", config.inputLabel1), new StateOption("1", config.inputLabel2));
+ }
+ },
+ XANTECH("!", "+\r", "?", "ZD", "#", "PR", "SS", "VO", "MU", "TR", "BS", "BL", "", 38, -7, 7, 7, -32, 31, 32, 16, 8,
+ false, List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16")) {
+ @Override
+ public MonopriceAudioZoneDTO getZoneData(String newZoneData) {
+ MonopriceAudioZoneDTO zoneData = new MonopriceAudioZoneDTO();
+ Matcher matcher = XANTECH_PATTERN.matcher(newZoneData);
+
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setPower(matcher.group(2));
+ zoneData.setSource(matcher.group(3));
+ zoneData.setVolume(Integer.parseInt(matcher.group(4)));
+ zoneData.setMute(matcher.group(5));
+ zoneData.setTreble(Integer.parseInt(matcher.group(6)));
+ zoneData.setBass(Integer.parseInt(matcher.group(7)));
+ zoneData.setBalance(Integer.parseInt(matcher.group(8)));
+ zoneData.setKeypad(matcher.group(9));
+ zoneData.setPage(matcher.group(10));
+ }
+ return zoneData;
+ }
+
+ @Override
+ public List<StateOption> getSourceLabels(MonopriceAudioThingConfiguration config) {
+ return List.of(new StateOption("1", config.inputLabel1), new StateOption("2", config.inputLabel2),
+ new StateOption("3", config.inputLabel3), new StateOption("4", config.inputLabel4),
+ new StateOption("5", config.inputLabel5), new StateOption("6", config.inputLabel6),
+ new StateOption("7", config.inputLabel7), new StateOption("8", config.inputLabel8));
+ }
+ };
+
+ // Used by 10761/DAX66 and DAX88
+ private static MonopriceAudioZoneDTO getMonopriceZoneData(String newZoneData) {
+ MonopriceAudioZoneDTO zoneData = new MonopriceAudioZoneDTO();
+ Matcher matcher = MONOPRICE_PATTERN.matcher(newZoneData);
+
+ if (matcher.find()) {
+ zoneData.setZone(matcher.group(1));
+ zoneData.setPage(matcher.group(2));
+ zoneData.setPower(matcher.group(3));
+ zoneData.setMute(matcher.group(4));
+ zoneData.setDnd(matcher.group(5));
+ zoneData.setVolume(Integer.parseInt(matcher.group(6)));
+ zoneData.setTreble(Integer.parseInt(matcher.group(7)));
+ zoneData.setBass(Integer.parseInt(matcher.group(8)));
+ zoneData.setBalance(Integer.parseInt(matcher.group(9)));
+ zoneData.setSource(matcher.group(10));
+ zoneData.setKeypad(matcher.group(11));
+ }
+ return zoneData;
+ }
+
+ // Monoprice 10761/DAX66 status string: #>1200010000130809100601
+ // DAX88 status string is the same but does not have leading '#': >xxaabbccddeeffgghhiijj
+ private static final Pattern MONOPRICE_PATTERN = Pattern
+ .compile("^#?>(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})");
+
+ // Monoprice 31028 / PAM1270 status string: ?6ZS VO8 PO1 MU0 IS0+ (does not include treble, bass & balance)
+ private static final Pattern MONOPRICE70_PATTERN = Pattern
+ .compile("^\\?(\\d{1})ZS VO(\\d{1,2}) PO(\\d{1}) MU(\\d{1}) IS(\\d{1})+");
+ private static final Pattern MONOPRICE70_TREB_PATTERN = Pattern.compile("^\\?(\\d{1})TR(\\d{1,2})+");
+ private static final Pattern MONOPRICE70_BASS_PATTERN = Pattern.compile("^\\?(\\d{1})BS(\\d{1,2})+");
+ private static final Pattern MONOPRICE70_BALN_PATTERN = Pattern.compile("^\\?(\\d{1})BA(\\d{1,2})+");
+
+ // Xantech status string: #1ZS PR0 SS1 VO0 MU1 TR7 BS7 BA32 LS0 PS0+
+ private static final Pattern XANTECH_PATTERN = Pattern.compile(
+ "^#(\\d{1,2})ZS PR(\\d{1}) SS(\\d{1}) VO(\\d{1,2}) MU(\\d{1}) TR(\\d{1,2}) BS(\\d{1,2}) BA(\\d{1,2}) LS(\\d{1}) PS(\\d{1})+");
+
+ private String cmdPrefix;
+ private String cmdSuffix;
+ private String queryPrefix;
+ private String querySuffix;
+ private String respPrefix;
+ private String powerCmd;
+ private String sourceCmd;
+ private String volumeCmd;
+ private String muteCmd;
+ private String trebleCmd;
+ private String bassCmd;
+ private String balanceCmd;
+ private String dndCmd;
+ private int maxVol;
+ private int minTone;
+ private int maxTone;
+ private int toneOffset;
+ private int minBal;
+ private int maxBal;
+ private int balOffset;
+ private int maxZones;
+ private int numSources;
+ private boolean padNumbers;
+ private List<String> zoneIds;
+ private Map<String, String> zoneIdMap = new HashMap<>();
+
+ private static final String ON_STR = "1";
+ private static final String OFF_STR = "0";
+
+ private static final String ON_STR_PAD = "01";
+ private static final String OFF_STR_PAD = "00";
+
+ /**
+ * Constructor for all the enum parameters
+ *
+ **/
+ AmplifierModel(String cmdPrefix, String cmdSuffix, String queryPrefix, String querySuffix, String respPrefix,
+ String powerCmd, String sourceCmd, String volumeCmd, String muteCmd, String trebleCmd, String bassCmd,
+ String balanceCmd, String dndCmd, int maxVol, int minTone, int maxTone, int toneOffset, int minBal,
+ int maxBal, int balOffset, int maxZones, int numSources, boolean padNumbers, List<String> zoneIds) {
+ this.cmdPrefix = cmdPrefix;
+ this.cmdSuffix = cmdSuffix;
+ this.queryPrefix = queryPrefix;
+ this.querySuffix = querySuffix;
+ this.respPrefix = respPrefix;
+ this.powerCmd = powerCmd;
+ this.sourceCmd = sourceCmd;
+ this.volumeCmd = volumeCmd;
+ this.muteCmd = muteCmd;
+ this.trebleCmd = trebleCmd;
+ this.bassCmd = bassCmd;
+ this.balanceCmd = balanceCmd;
+ this.dndCmd = dndCmd;
+ this.maxVol = maxVol;
+ this.minTone = minTone;
+ this.maxTone = maxTone;
+ this.toneOffset = toneOffset;
+ this.minBal = minBal;
+ this.maxBal = maxBal;
+ this.balOffset = balOffset;
+ this.maxZones = maxZones;
+ this.numSources = numSources;
+ this.padNumbers = padNumbers;
+ this.zoneIds = zoneIds;
+
+ int i = 1;
+ for (String zoneId : zoneIds) {
+ zoneIdMap.put(zoneId, "zone" + i);
+ i++;
+ }
+ }
+
+ public abstract MonopriceAudioZoneDTO getZoneData(String newZoneData);
+
+ public abstract List<StateOption> getSourceLabels(MonopriceAudioThingConfiguration config);
+
+ public String getZoneIdFromZoneName(String zoneName) {
+ for (String zoneId : zoneIdMap.keySet()) {
+ if (zoneIdMap.get(zoneId).equals(zoneName)) {
+ return zoneId;
+ }
+ }
+ return "";
+ }
+
+ public String getZoneName(String zoneId) {
+ String zoneName = zoneIdMap.get(zoneId);
+ if (zoneName != null) {
+ return zoneName;
+ } else {
+ return "";
+ }
+ }
+
+ public String getCmdPrefix() {
+ return cmdPrefix;
+ }
+
+ public String getQueryPrefix() {
+ return queryPrefix;
+ }
+
+ public String getQuerySuffix() {
+ return querySuffix;
+ }
+
+ public String getRespPrefix() {
+ return respPrefix;
+ }
+
+ public String getPowerCmd() {
+ return powerCmd;
+ }
+
+ public String getSourceCmd() {
+ return sourceCmd;
+ }
+
+ public String getVolumeCmd() {
+ return volumeCmd;
+ }
+
+ public String getMuteCmd() {
+ return muteCmd;
+ }
+
+ public String getTrebleCmd() {
+ return trebleCmd;
+ }
+
+ public String getBassCmd() {
+ return bassCmd;
+ }
+
+ public String getBalanceCmd() {
+ return balanceCmd;
+ }
+
+ public String getDndCmd() {
+ return dndCmd;
+ }
+
+ public int getMaxVol() {
+ return maxVol;
+ }
+
+ public int getMinTone() {
+ return minTone;
+ }
+
+ public int getMaxTone() {
+ return maxTone;
+ }
+
+ public int getMinBal() {
+ return minBal;
+ }
+
+ public int getMaxBal() {
+ return maxBal;
+ }
+
+ public int getBalOffset() {
+ return balOffset;
+ }
+
+ public int getToneOffset() {
+ return toneOffset;
+ }
+
+ public int getMaxZones() {
+ return maxZones;
+ }
+
+ public int getNumSources() {
+ return numSources;
+ }
+
+ public String getCmdSuffix() {
+ return cmdSuffix;
+ }
+
+ public List<String> getZoneIds() {
+ return zoneIds;
+ }
+
+ public String getFormattedValue(Integer value) {
+ if (padNumbers) {
+ return String.format("%02d", value);
+ } else {
+ return value.toString();
+ }
+ }
+
+ public String getOnStr() {
+ if (padNumbers) {
+ return ON_STR_PAD;
+ } else {
+ return ON_STR;
+ }
+ }
+
+ public String getOffStr() {
+ if (padNumbers) {
+ return OFF_STR_PAD;
+ } else {
+ return OFF_STR;
+ }
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.monopriceaudio.internal.communication;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * Represents the different kinds of commands
- *
- * @author Michael Lobstein - Initial contribution
- */
-@NonNullByDefault
-public enum MonopriceAudioCommand {
- QUERY("?"),
- POWER("PR"),
- SOURCE("CH"),
- VOLUME("VO"),
- MUTE("MU"),
- TREBLE("TR"),
- BASS("BS"),
- BALANCE("BL"),
- DND("DT");
-
- private final String value;
-
- MonopriceAudioCommand(String value) {
- this.value = value;
- }
-
- /**
- * Get the command name
- *
- * @return the command name
- */
- public String getValue() {
- return value;
- }
-}
*/
package org.openhab.binding.monopriceaudio.internal.communication;
+import static org.openhab.binding.monopriceaudio.internal.MonopriceAudioBindingConstants.*;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
*
* @author Laurent Garnier - Initial contribution
* @author Michael Lobstein - Adapted for the MonopriceAudio binding
+ * @author Michael Lobstein - Add support for additional amplifier types
*/
@NonNullByDefault
public abstract class MonopriceAudioConnector {
- public static final String READ_ERROR = "Command Error.";
-
// Message types
public static final String KEY_ZONE_UPDATE = "zone_update";
- // Special keys used by the binding
- public static final String KEY_ERROR = "error";
- public static final String MSG_VALUE_ON = "on";
-
- private static final Pattern PATTERN = Pattern.compile("^.*#>(\\d{22})$", Pattern.DOTALL);
- private static final String BEGIN_CMD = "<";
- private static final String END_CMD = "\r";
+ public static final String KEY_PING = "ping";
private final Logger logger = LoggerFactory.getLogger(MonopriceAudioConnector.class);
/** true if the connection is established, false if not */
private boolean connected;
+ private boolean pingResponseOnly;
+
+ private @Nullable AmplifierModel amp;
private @Nullable Thread readerThread;
*/
protected void setConnected(boolean connected) {
this.connected = connected;
+ this.pingResponseOnly = false;
+ }
+
+ /**
+ * Set the AmplifierModel
+ *
+ * @param amp the AmplifierModel being used
+ */
+ protected void setAmplifierModel(AmplifierModel amp) {
+ this.amp = amp;
}
/**
* Stop the thread that handles the feedback messages and close the opened input and output streams
*/
protected void cleanup() {
+ this.pingResponseOnly = false;
Thread readerThread = this.readerThread;
OutputStream dataOut = this.dataOut;
if (dataOut != null) {
try {
readerThread.join(3000);
} catch (InterruptedException e) {
- logger.warn("Error joining readerThread: {}", e.getMessage());
+ logger.debug("Error joining readerThread: {}", e.getMessage());
}
this.readerThread = null;
}
}
}
+ /**
+ * Get only ping success events from the connector. If amplifier does not have keypads or supports
+ * unsolicited updates, the use of this method will cause the connector to only send ping success events until the
+ * next time the connection is reset.
+ *
+ * @throws MonopriceAudioException - In case of any problem
+ */
+ public void sendPing() throws MonopriceAudioException {
+ pingResponseOnly = true;
+ // poll zone 1 status only to see if the amp responds
+ queryZone(amp.getZoneIds().get(0));
+ }
+
/**
* Get the status of a zone
*
*
* @throws MonopriceAudioException - In case of any problem
*/
- public void queryZone(MonopriceAudioZone zone) throws MonopriceAudioException {
- sendCommand(zone, MonopriceAudioCommand.QUERY, null);
+ public void queryZone(String zoneId) throws MonopriceAudioException {
+ sendCommand(amp.getQueryPrefix() + zoneId + amp.getQuerySuffix());
}
/**
- * Request the MonopriceAudio controller to execute a command
+ * Monoprice 31028 and OSD Audio PAM1270 amps do not report treble, bass and balance with the main status inquiry,
+ * so we must send three extra commands to retrieve those values
+ *
+ * @param zone the zone to query for current treble, bass and balance status
+ *
+ * @throws MonopriceAudioException - In case of any problem
+ */
+ public void queryTrebBassBalance(String zoneId) throws MonopriceAudioException {
+ sendCommand(amp.getQueryPrefix() + zoneId + amp.getTrebleCmd());
+ sendCommand(amp.getQueryPrefix() + zoneId + amp.getBassCmd());
+ sendCommand(amp.getQueryPrefix() + zoneId + amp.getBalanceCmd());
+ }
+
+ /**
+ * Request the MonopriceAudio amplifier to execute a raw command
*
- * @param zone the zone for which the command is to be run
* @param cmd the command to execute
- * @param value the integer value to consider for volume, bass, treble, etc. adjustment
*
* @throws MonopriceAudioException - In case of any problem
*/
- public void sendCommand(MonopriceAudioZone zone, MonopriceAudioCommand cmd, @Nullable Integer value)
+ public void sendCommand(String cmd) throws MonopriceAudioException {
+ sendCommand(null, cmd, null);
+ }
+
+ /**
+ * Request the MonopriceAudio amplifier to execute a command
+ *
+ * @param zoneId the zone for which the command is to be run
+ * @param cmd the command to execute
+ * @param value the integer value to consider for power, volume, bass, treble, etc. adjustment
+ *
+ * @throws MonopriceAudioException - In case of any problem
+ */
+ public void sendCommand(@Nullable String zoneId, String cmd, @Nullable Integer value)
throws MonopriceAudioException {
- String messageStr = "";
-
- if (cmd == MonopriceAudioCommand.QUERY) {
- // query special case (ie: ? + zoneId)
- messageStr = cmd.getValue() + zone.getZoneId();
- } else if (value != null) {
- // if the command passed a value, append it to the messageStr
- messageStr = BEGIN_CMD + zone.getZoneId() + cmd.getValue() + String.format("%02d", value);
+ String messageStr;
+
+ if (zoneId != null && value != null) {
+ // if the command passed a value, build messageStr with prefix, zoneId, command, value and suffix
+ messageStr = amp.getCmdPrefix() + zoneId + cmd + amp.getFormattedValue(value) + amp.getCmdSuffix();
} else {
- throw new MonopriceAudioException("Send command \"" + messageStr + "\" failed: passed in value is null");
+ // otherwise send the raw cmd from the query() methods
+ messageStr = cmd + amp.getCmdSuffix();
}
- messageStr += END_CMD;
logger.debug("Send command {}", messageStr);
OutputStream dataOut = this.dataOut;
dataOut.write(messageStr.getBytes(StandardCharsets.US_ASCII));
dataOut.flush();
} catch (IOException e) {
- throw new MonopriceAudioException("Send command \"" + cmd.getValue() + "\" failed: " + e.getMessage(), e);
+ throw new MonopriceAudioException("Send command \"" + messageStr + "\" failed: " + e.getMessage(), e);
}
}
* @param incomingMessage the received message
*/
public void handleIncomingMessage(byte[] incomingMessage) {
- String message = new String(incomingMessage, StandardCharsets.US_ASCII).trim();
+ if (pingResponseOnly) {
+ dispatchKeyValue(KEY_PING, EMPTY);
+ return;
+ }
- logger.debug("handleIncomingMessage: {}", message);
+ String message = new String(incomingMessage, StandardCharsets.US_ASCII).trim();
- if (READ_ERROR.equals(message)) {
- dispatchKeyValue(KEY_ERROR, MSG_VALUE_ON);
+ if (EMPTY.equals(message)) {
return;
}
- // Amp controller sends status string: #>1200010000130809100601
- Matcher matcher = PATTERN.matcher(message);
- if (matcher.find()) {
- // pull out just the digits and send them as an event
- dispatchKeyValue(KEY_ZONE_UPDATE, matcher.group(1));
+ if (message.startsWith(amp.getRespPrefix())) {
+ logger.debug("handleIncomingMessage: {}", message);
+ dispatchKeyValue(KEY_ZONE_UPDATE, message);
} else {
logger.debug("no match on message: {}", message);
}
}
@Override
- public void sendCommand(MonopriceAudioZone zone, MonopriceAudioCommand cmd, @Nullable Integer value) {
+ public void sendCommand(@Nullable String zone, String cmd, @Nullable Integer value) {
logger.warn(
"MonopriceAudio binding incorrectly configured. Please configure for Serial or IP over serial connection");
setConnected(false);
* @param address the IP address of the serial over IP device
* @param port the TCP port to be used
* @param uid the thing uid string
+ * @param amp the AmplifierModel being used
*/
- public MonopriceAudioIpConnector(@Nullable String address, int port, String uid) {
+ public MonopriceAudioIpConnector(@Nullable String address, int port, String uid, AmplifierModel amp) {
this.address = address;
this.port = port;
this.uid = uid;
+ setAmplifierModel(amp);
}
@Override
}
} catch (MonopriceAudioException e) {
logger.debug("Reading failed: {}", e.getMessage(), e);
- connector.handleIncomingMessage(MonopriceAudioConnector.READ_ERROR.getBytes());
}
logger.debug("Data listener stopped");
* @param serialPortManager the serial port manager
* @param serialPortName the serial port name to be used
* @param uid the thing uid string
+ * @param amp the AmplifierModel being used
*/
- public MonopriceAudioSerialConnector(SerialPortManager serialPortManager, String serialPortName, String uid) {
+ public MonopriceAudioSerialConnector(SerialPortManager serialPortManager, String serialPortName, String uid,
+ AmplifierModel amp) {
this.serialPortManager = serialPortManager;
this.serialPortName = serialPortName;
this.uid = uid;
+ setAmplifierModel(amp);
}
@Override
+++ /dev/null
-/**
- * Copyright (c) 2010-2023 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.monopriceaudio.internal.communication;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.monopriceaudio.internal.MonopriceAudioException;
-
-/**
- * Represents the different internal zone IDs of the Monoprice Whole House Amplifier
- *
- * @author Michael Lobstein - Initial contribution
- */
-@NonNullByDefault
-public enum MonopriceAudioZone {
-
- ALL("all"),
- ZONE1("11"),
- ZONE2("12"),
- ZONE3("13"),
- ZONE4("14"),
- ZONE5("15"),
- ZONE6("16"),
- ZONE7("21"),
- ZONE8("22"),
- ZONE9("23"),
- ZONE10("24"),
- ZONE11("25"),
- ZONE12("26"),
- ZONE13("31"),
- ZONE14("32"),
- ZONE15("33"),
- ZONE16("34"),
- ZONE17("35"),
- ZONE18("36");
-
- private final String zoneId;
-
- // make a list of all valid zone names
- public static final List<String> VALID_ZONES = Arrays.stream(values()).filter(z -> z != ALL)
- .map(MonopriceAudioZone::name).collect(Collectors.toList());
-
- // make a list of all valid zone ids
- public static final List<String> VALID_ZONE_IDS = Arrays.stream(values()).filter(z -> z != ALL)
- .map(MonopriceAudioZone::getZoneId).collect(Collectors.toList());
-
- public static MonopriceAudioZone fromZoneId(String zoneId) throws MonopriceAudioException {
- return Arrays.stream(values()).filter(z -> z.zoneId.equalsIgnoreCase(zoneId)).findFirst()
- .orElseThrow(() -> new MonopriceAudioException("Invalid zoneId specified: " + zoneId));
- }
-
- MonopriceAudioZone(String zoneId) {
- this.zoneId = zoneId;
- }
-
- /**
- * Get the zone id
- *
- * @return the zone id
- */
- public String getZoneId() {
- return zoneId;
- }
-}
public @Nullable String inputLabel4;
public @Nullable String inputLabel5;
public @Nullable String inputLabel6;
+ public @Nullable String inputLabel7;
+ public @Nullable String inputLabel8;
+ public boolean disableKeypadPolling = false;
}
*/
package org.openhab.binding.monopriceaudio.internal.dto;
+import static org.openhab.binding.monopriceaudio.internal.MonopriceAudioBindingConstants.*;
+
/**
- * Represents the data elements of a single zone of the Monoprice Whole House Amplifier
+ * Represents the data elements of a single zone of a supported amplifier
*
* @author Michael Lobstein - Initial contribution
*/
public class MonopriceAudioZoneDTO {
- private String zone;
- private String page;
- private String power;
- private String mute;
- private String dnd;
- private int volume;
- private int treble;
- private int bass;
- private int balance;
- private String source;
- private String keypad;
+ private String zone = EMPTY;
+ private String page = EMPTY;
+ private String power = EMPTY;
+ private String mute = EMPTY;
+ private String dnd = EMPTY;
+ private int volume = NIL;
+ private int treble = NIL;
+ private int bass = NIL;
+ private int balance = NIL;
+ private String source = EMPTY;
+ private String keypad = EMPTY;
+
+ public MonopriceAudioZoneDTO() {
+ }
+
+ public MonopriceAudioZoneDTO(String zone) {
+ this.zone = zone;
+ }
public void setZone(String zone) {
this.zone = zone;
}
+ public String getZone() {
+ return this.zone;
+ }
+
public void setPage(String page) {
this.page = page;
}
}
public boolean isPageActive() {
- return ("01").equals(this.page);
+ return this.page.contains(ONE);
}
public void setPower(String power) {
}
public boolean isPowerOn() {
- return ("01").equals(this.power);
+ return this.power.contains(ONE);
}
public void setMute(String mute) {
}
public boolean isMuted() {
- return ("01").equals(this.mute);
+ return this.mute.contains(ONE);
}
public void setDnd(String dnd) {
}
public boolean isDndOn() {
- return ("01").equals(this.dnd);
+ return this.dnd.contains(ONE);
}
public int getVolume() {
}
public boolean isKeypadActive() {
- return ("01").equals(this.keypad);
+ return this.keypad.contains(ONE);
}
@Override
public String toString() {
- // Re-construct the original status message from the controller
// This is used to determine if something changed from the last polling update
- return zone + page + power + mute + dnd + (String.format("%02d", volume)) + (String.format("%02d", treble))
- + (String.format("%02d", bass)) + (String.format("%02d", balance)) + source + keypad;
+ return zone + page + power + mute + dnd + volume + treble + bass + balance + source + keypad;
}
}
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.monopriceaudio.internal.MonopriceAudioException;
import org.openhab.binding.monopriceaudio.internal.MonopriceAudioStateDescriptionOptionProvider;
-import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioCommand;
+import org.openhab.binding.monopriceaudio.internal.communication.AmplifierModel;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioConnector;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioDefaultConnector;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioIpConnector;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioMessageEvent;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioMessageEventListener;
import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioSerialConnector;
-import org.openhab.binding.monopriceaudio.internal.communication.MonopriceAudioZone;
import org.openhab.binding.monopriceaudio.internal.configuration.MonopriceAudioThingConfiguration;
import org.openhab.binding.monopriceaudio.internal.dto.MonopriceAudioZoneDTO;
import org.openhab.core.io.transport.serial.SerialPortManager;
* Based on the Rotel binding by Laurent Garnier
*
* @author Michael Lobstein - Initial contribution
+ * @author Michael Lobstein - Add support for additional amplifier types
*/
@NonNullByDefault
public class MonopriceAudioHandler extends BaseThingHandler implements MonopriceAudioMessageEventListener {
private static final long RECON_POLLING_INTERVAL_SEC = 60;
- private static final long INITIAL_POLLING_DELAY_SEC = 5;
- private static final Pattern PATTERN = Pattern
- .compile("^(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})");
+ private static final long INITIAL_POLLING_DELAY_SEC = 10;
- private static final String ZONE = "ZONE";
+ private static final String ZONE = "zone";
private static final String ALL = "all";
private static final String CHANNEL_DELIMIT = "#";
- private static final String ON_STR = "01";
- private static final String OFF_STR = "00";
+ private static final int ZERO = 0;
private static final int ONE = 1;
- private static final int MAX_ZONES = 18;
- private static final int MAX_SRC = 6;
private static final int MIN_VOLUME = 0;
- private static final int MAX_VOLUME = 38;
- private static final int MIN_TONE = -7;
- private static final int MAX_TONE = 7;
- private static final int MIN_BALANCE = -10;
- private static final int MAX_BALANCE = 10;
- private static final int BALANCE_OFFSET = 10;
- private static final int TONE_OFFSET = 7;
-
- // build a Map with a MonopriceAudioZoneDTO for each zoneId
- private final Map<String, MonopriceAudioZoneDTO> zoneDataMap = MonopriceAudioZone.VALID_ZONE_IDS.stream()
- .collect(Collectors.toMap(s -> s, s -> new MonopriceAudioZoneDTO()));
private final Logger logger = LoggerFactory.getLogger(MonopriceAudioHandler.class);
+ private final AmplifierModel amp;
private final MonopriceAudioStateDescriptionOptionProvider stateDescriptionProvider;
private final SerialPortManager serialPortManager;
private MonopriceAudioConnector connector = new MonopriceAudioDefaultConnector();
+ private Map<String, MonopriceAudioZoneDTO> zoneDataMap = Map.of(ZONE, new MonopriceAudioZoneDTO());
private Set<String> ignoreZones = new HashSet<>();
private long lastPollingUpdate = System.currentTimeMillis();
- private long pollingInterval = 0;
- private int numZones = 0;
- private int allVolume = 1;
- private int initialAllVolume = 0;
+ private long pollingInterval = ZERO;
+ private int numZones = ZERO;
+ private int allVolume = ONE;
+ private int initialAllVolume = ZERO;
+ private boolean disableKeypadPolling = false;
private Object sequenceLock = new Object();
- public MonopriceAudioHandler(Thing thing, MonopriceAudioStateDescriptionOptionProvider stateDescriptionProvider,
+ public MonopriceAudioHandler(Thing thing, AmplifierModel amp,
+ MonopriceAudioStateDescriptionOptionProvider stateDescriptionProvider,
SerialPortManager serialPortManager) {
super(thing);
+ this.amp = amp;
this.stateDescriptionProvider = stateDescriptionProvider;
this.serialPortManager = serialPortManager;
}
final String serialPort = config.serialPort;
final String host = config.host;
final Integer port = config.port;
+ numZones = config.numZones;
final String ignoreZonesConfig = config.ignoreZones;
+ disableKeypadPolling = config.disableKeypadPolling || amp == AmplifierModel.MONOPRICE70;
+
+ // build a Map with a MonopriceAudioZoneDTO for each zoneId
+ zoneDataMap = amp.getZoneIds().stream().limit(numZones)
+ .collect(Collectors.toMap(s -> s, s -> new MonopriceAudioZoneDTO(s)));
// Check configuration settings
- String configError = null;
- if ((serialPort == null || serialPort.isEmpty()) && (host == null || host.isEmpty())) {
- configError = "undefined serialPort and host configuration settings; please set one of them";
- } else if (serialPort != null && (host == null || host.isEmpty())) {
+ if (serialPort != null && host == null && port == null) {
if (serialPort.toLowerCase().startsWith("rfc2217")) {
- configError = "use host and port configuration settings for a serial over IP connection";
- }
- } else {
- if (port == null) {
- configError = "undefined port configuration setting";
- } else if (port <= 0) {
- configError = "invalid port configuration setting";
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.configuration-error-rfc2217");
+ return;
}
- }
-
- if (configError != null) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configError);
+ } else if (serialPort != null && (host != null || port != null)) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
+ "@text/offline.configuration-error-conflict");
return;
}
if (serialPort != null) {
- connector = new MonopriceAudioSerialConnector(serialPortManager, serialPort, uid);
- } else if (port != null) {
- connector = new MonopriceAudioIpConnector(host, port, uid);
+ connector = new MonopriceAudioSerialConnector(serialPortManager, serialPort, uid, amp);
+ } else if (host != null && (port != null && port > ZERO)) {
+ connector = new MonopriceAudioIpConnector(host, port, uid, amp);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "Either Serial port or Host & Port must be specifed");
+ "@text/offline.configuration-error-missing");
return;
}
pollingInterval = config.pollingInterval;
- numZones = config.numZones;
initialAllVolume = config.initialAllVolume;
// If zones were specified to be ignored by the 'all*' commands, use the specified binding
- // zone ids to get the controller's internal zone ids and save those to a list
+ // zone ids to get the amplifier's internal zone ids and save those to a list
if (ignoreZonesConfig != null) {
for (String zone : ignoreZonesConfig.split(",")) {
try {
int zoneInt = Integer.parseInt(zone);
- if (zoneInt >= ONE && zoneInt <= MAX_ZONES) {
+ if (zoneInt >= ONE && zoneInt <= amp.getMaxZones()) {
ignoreZones.add(ZONE + zoneInt);
} else {
- logger.warn("Invalid ignore zone value: {}, value must be between {} and {}", zone, ONE,
- MAX_ZONES);
+ logger.debug("Invalid ignore zone value: {}, value must be between {} and {}", zone, ONE,
+ amp.getMaxZones());
}
} catch (NumberFormatException nfe) {
- logger.warn("Invalid ignore zone value: {}", zone);
+ logger.debug("Invalid ignore zone value: {}", zone);
}
}
}
- // Build a state option list for the source labels
- List<StateOption> sourcesLabels = new ArrayList<>();
- sourcesLabels.add(new StateOption("1", config.inputLabel1));
- sourcesLabels.add(new StateOption("2", config.inputLabel2));
- sourcesLabels.add(new StateOption("3", config.inputLabel3));
- sourcesLabels.add(new StateOption("4", config.inputLabel4));
- sourcesLabels.add(new StateOption("5", config.inputLabel5));
- sourcesLabels.add(new StateOption("6", config.inputLabel6));
-
// Put the source labels on all active zones
List<Integer> activeZones = IntStream.range(1, numZones + 1).boxed().collect(Collectors.toList());
+ List<StateOption> sourceLabels = amp.getSourceLabels(config);
stateDescriptionProvider.setStateOptions(
- new ChannelUID(getThing().getUID(), ALL + CHANNEL_DELIMIT + CHANNEL_TYPE_ALLSOURCE), sourcesLabels);
+ new ChannelUID(getThing().getUID(), ALL + CHANNEL_DELIMIT + CHANNEL_TYPE_ALLSOURCE), sourceLabels);
activeZones.forEach(zoneNum -> {
- stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(),
- ZONE.toLowerCase() + zoneNum + CHANNEL_DELIMIT + CHANNEL_TYPE_SOURCE), sourcesLabels);
+ stateDescriptionProvider.setStateOptions(
+ new ChannelUID(getThing().getUID(), ZONE + zoneNum + CHANNEL_DELIMIT + CHANNEL_TYPE_SOURCE),
+ sourceLabels);
});
// remove the channels for the zones we are not using
- if (numZones < MAX_ZONES) {
+ if (numZones < amp.getMaxZones()) {
List<Channel> channels = new ArrayList<>(this.getThing().getChannels());
- List<Integer> zonesToRemove = IntStream.range(numZones + 1, MAX_ZONES + 1).boxed()
+ List<Integer> zonesToRemove = IntStream.range(numZones + 1, amp.getMaxZones() + 1).boxed()
.collect(Collectors.toList());
zonesToRemove.forEach(zone -> {
- channels.removeIf(c -> (c.getUID().getId().contains(ZONE.toLowerCase() + zone)));
+ channels.removeIf(c -> (c.getUID().getId().contains(ZONE + zone)));
});
updateThing(editThing().withChannels(channels).build());
}
// initialize the all volume state
allVolume = initialAllVolume;
long allVolumePct = Math
- .round((double) (initialAllVolume - MIN_VOLUME) / (double) (MAX_VOLUME - MIN_VOLUME) * 100.0);
+ .round((initialAllVolume - MIN_VOLUME) / (double) (amp.getMaxVol() - MIN_VOLUME) * 100.0);
updateState(ALL + CHANNEL_DELIMIT + CHANNEL_TYPE_ALLVOLUME, new PercentType(BigDecimal.valueOf(allVolumePct)));
scheduleReconnectJob();
public void handleCommand(ChannelUID channelUID, Command command) {
String channel = channelUID.getId();
String[] channelSplit = channel.split(CHANNEL_DELIMIT);
- MonopriceAudioZone zone = MonopriceAudioZone.valueOf(channelSplit[0].toUpperCase());
String channelType = channelSplit[1];
+ String zoneName = channelSplit[0];
+ String zoneId = amp.getZoneIdFromZoneName(zoneName);
if (getThing().getStatus() != ThingStatus.ONLINE) {
logger.debug("Thing is not ONLINE; command {} from channel {} is ignored", command, channel);
}
if (command instanceof RefreshType) {
- MonopriceAudioZoneDTO zoneDTO = zoneDataMap.get(zone.getZoneId());
- if (zoneDTO != null) {
- updateChannelState(zone, channelType, zoneDTO);
- } else {
- logger.info("Could not execute REFRESH command for zone {}: null", zone.getZoneId());
- }
+ updateChannelState(zoneId, channelType);
return;
}
- Stream<String> zoneStream = MonopriceAudioZone.VALID_ZONES.stream().limit(numZones);
+ Stream<String> zoneStream = amp.getZoneIds().stream().limit(numZones);
try {
switch (channelType) {
case CHANNEL_TYPE_POWER:
if (command instanceof OnOffType) {
- connector.sendCommand(zone, MonopriceAudioCommand.POWER, command == OnOffType.ON ? 1 : 0);
- zoneDataMap.get(zone.getZoneId()).setPower(command == OnOffType.ON ? ON_STR : OFF_STR);
+ connector.sendCommand(zoneId, amp.getPowerCmd(), command == OnOffType.ON ? ONE : ZERO);
+ zoneDataMap.get(zoneId)
+ .setPower(command == OnOffType.ON ? amp.getOnStr() : amp.getOffStr());
}
break;
case CHANNEL_TYPE_SOURCE:
if (command instanceof DecimalType) {
- int value = ((DecimalType) command).intValue();
- if (value >= ONE && value <= MAX_SRC) {
- logger.debug("Got source command {} zone {}", value, zone);
- connector.sendCommand(zone, MonopriceAudioCommand.SOURCE, value);
- zoneDataMap.get(zone.getZoneId()).setSource(String.format("%02d", value));
+ final int value = ((DecimalType) command).intValue();
+ if (value >= ONE && value <= amp.getNumSources()) {
+ logger.debug("Got source command {} zone {}", value, zoneId);
+ connector.sendCommand(zoneId, amp.getSourceCmd(), value);
+ zoneDataMap.get(zoneId).setSource(amp.getFormattedValue(value));
}
}
break;
case CHANNEL_TYPE_VOLUME:
if (command instanceof PercentType) {
- int value = (int) Math
- .round(((PercentType) command).doubleValue() / 100.0 * (MAX_VOLUME - MIN_VOLUME))
+ final int value = (int) Math.round(
+ ((PercentType) command).doubleValue() / 100.0 * (amp.getMaxVol() - MIN_VOLUME))
+ MIN_VOLUME;
- logger.debug("Got volume command {} zone {}", value, zone);
- connector.sendCommand(zone, MonopriceAudioCommand.VOLUME, value);
- zoneDataMap.get(zone.getZoneId()).setVolume(value);
+ logger.debug("Got volume command {} zone {}", value, zoneId);
+ connector.sendCommand(zoneId, amp.getVolumeCmd(), value);
+ zoneDataMap.get(zoneId).setVolume(value);
}
break;
case CHANNEL_TYPE_MUTE:
if (command instanceof OnOffType) {
- connector.sendCommand(zone, MonopriceAudioCommand.MUTE, command == OnOffType.ON ? 1 : 0);
- zoneDataMap.get(zone.getZoneId()).setMute(command == OnOffType.ON ? ON_STR : OFF_STR);
+ connector.sendCommand(zoneId, amp.getMuteCmd(), command == OnOffType.ON ? ONE : ZERO);
+ zoneDataMap.get(zoneId).setMute(command == OnOffType.ON ? amp.getOnStr() : amp.getOffStr());
}
break;
case CHANNEL_TYPE_TREBLE:
if (command instanceof DecimalType) {
- int value = ((DecimalType) command).intValue();
- if (value >= MIN_TONE && value <= MAX_TONE) {
- logger.debug("Got treble command {} zone {}", value, zone);
- connector.sendCommand(zone, MonopriceAudioCommand.TREBLE, value + TONE_OFFSET);
- zoneDataMap.get(zone.getZoneId()).setTreble(value + TONE_OFFSET);
+ final int value = ((DecimalType) command).intValue();
+ if (value >= amp.getMinTone() && value <= amp.getMaxTone()) {
+ logger.debug("Got treble command {} zone {}", value, zoneId);
+ connector.sendCommand(zoneId, amp.getTrebleCmd(), value + amp.getToneOffset());
+ zoneDataMap.get(zoneId).setTreble(value + amp.getToneOffset());
}
}
break;
case CHANNEL_TYPE_BASS:
if (command instanceof DecimalType) {
- int value = ((DecimalType) command).intValue();
- if (value >= MIN_TONE && value <= MAX_TONE) {
- logger.debug("Got bass command {} zone {}", value, zone);
- connector.sendCommand(zone, MonopriceAudioCommand.BASS, value + TONE_OFFSET);
- zoneDataMap.get(zone.getZoneId()).setBass(value + TONE_OFFSET);
+ final int value = ((DecimalType) command).intValue();
+ if (value >= amp.getMinTone() && value <= amp.getMaxTone()) {
+ logger.debug("Got bass command {} zone {}", value, zoneId);
+ connector.sendCommand(zoneId, amp.getBassCmd(), value + amp.getToneOffset());
+ zoneDataMap.get(zoneId).setBass(value + amp.getToneOffset());
}
}
break;
case CHANNEL_TYPE_BALANCE:
if (command instanceof DecimalType) {
- int value = ((DecimalType) command).intValue();
- if (value >= MIN_BALANCE && value <= MAX_BALANCE) {
- logger.debug("Got balance command {} zone {}", value, zone);
- connector.sendCommand(zone, MonopriceAudioCommand.BALANCE, value + BALANCE_OFFSET);
- zoneDataMap.get(zone.getZoneId()).setBalance(value + BALANCE_OFFSET);
+ final int value = ((DecimalType) command).intValue();
+ if (value >= amp.getMinBal() && value <= amp.getMaxBal()) {
+ logger.debug("Got balance command {} zone {}", value, zoneId);
+ connector.sendCommand(zoneId, amp.getBalanceCmd(), value + amp.getBalOffset());
+ zoneDataMap.get(zoneId).setBalance(value + amp.getBalOffset());
}
}
break;
case CHANNEL_TYPE_DND:
if (command instanceof OnOffType) {
- connector.sendCommand(zone, MonopriceAudioCommand.DND, command == OnOffType.ON ? 1 : 0);
- zoneDataMap.get(zone.getZoneId()).setDnd(command == OnOffType.ON ? ON_STR : OFF_STR);
+ connector.sendCommand(zoneId, amp.getDndCmd(), command == OnOffType.ON ? ONE : ZERO);
+ zoneDataMap.get(zoneId).setDnd(command == OnOffType.ON ? amp.getOnStr() : amp.getOffStr());
}
break;
case CHANNEL_TYPE_ALLPOWER:
if (command instanceof OnOffType) {
- zoneStream.forEach((zoneName) -> {
- if (command == OnOffType.OFF || !ignoreZones.contains(zoneName)) {
+ final int cmd = command == OnOffType.ON ? ONE : ZERO;
+ zoneStream.forEach((streamZoneId) -> {
+ if (command == OnOffType.OFF || !ignoreZones.contains(amp.getZoneName(streamZoneId))) {
try {
- connector.sendCommand(MonopriceAudioZone.valueOf(zoneName),
- MonopriceAudioCommand.POWER, command == OnOffType.ON ? 1 : 0);
+ connector.sendCommand(streamZoneId, amp.getPowerCmd(), cmd);
+ zoneDataMap.get(streamZoneId).setPower(amp.getFormattedValue(cmd));
+ updateChannelState(streamZoneId, CHANNEL_TYPE_POWER);
+
if (command == OnOffType.ON) {
// reset the volume of each zone to allVolume
- connector.sendCommand(MonopriceAudioZone.valueOf(zoneName),
- MonopriceAudioCommand.VOLUME, allVolume);
+ connector.sendCommand(streamZoneId, amp.getVolumeCmd(), allVolume);
+ zoneDataMap.get(streamZoneId).setVolume(allVolume);
+ updateChannelState(streamZoneId, CHANNEL_TYPE_VOLUME);
}
} catch (MonopriceAudioException e) {
- logger.warn("Error Turning All Zones On: {}", e.getMessage());
+ logger.debug("Error Turning All Zones On: {}", e.getMessage());
}
}
break;
case CHANNEL_TYPE_ALLSOURCE:
if (command instanceof DecimalType) {
- int value = ((DecimalType) command).intValue();
- if (value >= ONE && value <= MAX_SRC) {
- zoneStream.forEach((zoneName) -> {
- if (!ignoreZones.contains(zoneName)) {
+ final int value = ((DecimalType) command).intValue();
+ if (value >= ONE && value <= amp.getNumSources()) {
+ zoneStream.forEach((streamZoneId) -> {
+ if (!ignoreZones.contains(amp.getZoneName(streamZoneId))) {
try {
- connector.sendCommand(MonopriceAudioZone.valueOf(zoneName),
- MonopriceAudioCommand.SOURCE, value);
+ connector.sendCommand(streamZoneId, amp.getSourceCmd(), value);
+ if (zoneDataMap.get(streamZoneId).isPowerOn()
+ && !zoneDataMap.get(streamZoneId).isMuted()) {
+ zoneDataMap.get(streamZoneId).setSource(amp.getFormattedValue(value));
+ updateChannelState(streamZoneId, CHANNEL_TYPE_SOURCE);
+ }
} catch (MonopriceAudioException e) {
- logger.warn("Error Setting Source for All Zones: {}", e.getMessage());
+ logger.debug("Error Setting Source for All Zones: {}", e.getMessage());
}
}
});
break;
case CHANNEL_TYPE_ALLVOLUME:
if (command instanceof PercentType) {
- int value = (int) Math
- .round(((PercentType) command).doubleValue() / 100.0 * (MAX_VOLUME - MIN_VOLUME))
+ allVolume = (int) Math.round(
+ ((PercentType) command).doubleValue() / 100.0 * (amp.getMaxVol() - MIN_VOLUME))
+ MIN_VOLUME;
- allVolume = value;
- zoneStream.forEach((zoneName) -> {
- if (!ignoreZones.contains(zoneName)) {
+ zoneStream.forEach((streamZoneId) -> {
+ if (!ignoreZones.contains(amp.getZoneName(streamZoneId))) {
try {
- connector.sendCommand(MonopriceAudioZone.valueOf(zoneName),
- MonopriceAudioCommand.VOLUME, value);
+ connector.sendCommand(streamZoneId, amp.getVolumeCmd(), allVolume);
+ if (zoneDataMap.get(streamZoneId).isPowerOn()
+ && !zoneDataMap.get(streamZoneId).isMuted()) {
+ zoneDataMap.get(streamZoneId).setVolume(allVolume);
+ updateChannelState(streamZoneId, CHANNEL_TYPE_VOLUME);
+ }
} catch (MonopriceAudioException e) {
- logger.warn("Error Setting Volume for All Zones: {}", e.getMessage());
+ logger.debug("Error Setting Volume for All Zones: {}", e.getMessage());
}
}
});
break;
case CHANNEL_TYPE_ALLMUTE:
if (command instanceof OnOffType) {
- int cmd = command == OnOffType.ON ? 1 : 0;
- zoneStream.forEach((zoneName) -> {
- if (!ignoreZones.contains(zoneName)) {
+ final int cmd = command == OnOffType.ON ? ONE : ZERO;
+ zoneStream.forEach((streamZoneId) -> {
+ if (!ignoreZones.contains(amp.getZoneName(streamZoneId))) {
try {
- connector.sendCommand(MonopriceAudioZone.valueOf(zoneName),
- MonopriceAudioCommand.MUTE, cmd);
+ connector.sendCommand(streamZoneId, amp.getMuteCmd(), cmd);
+ if (zoneDataMap.get(streamZoneId).isPowerOn()) {
+ zoneDataMap.get(streamZoneId).setMute(amp.getFormattedValue(cmd));
+ updateChannelState(streamZoneId, CHANNEL_TYPE_MUTE);
+ }
} catch (MonopriceAudioException e) {
- logger.warn("Error Setting Mute for All Zones: {}", e.getMessage());
+ logger.debug("Error Setting Mute for All Zones: {}", e.getMessage());
}
}
});
logger.trace("Command {} from channel {} succeeded", command, channel);
}
} catch (MonopriceAudioException e) {
- logger.warn("Command {} from channel {} failed: {}", command, channel, e.getMessage());
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Sending command failed");
+ logger.debug("Command {} from channel {} failed: {}", command, channel, e.getMessage());
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
+ "@text/offline.communication-error-failed");
closeConnection();
scheduleReconnectJob();
}
}
/**
- * Open the connection with the MonopriceAudio device
+ * Open the connection to the amplifier
*
* @return true if the connection is opened successfully or false if not
*/
}
/**
- * Close the connection with the MonopriceAudio device
+ * Close the connection to the amplifier
*/
private synchronized void closeConnection() {
if (connector.isConnected()) {
@Override
public void onNewMessageEvent(MonopriceAudioMessageEvent evt) {
String key = evt.getKey();
- String updateData = evt.getValue().trim();
- if (!MonopriceAudioConnector.KEY_ERROR.equals(key)) {
- updateStatus(ThingStatus.ONLINE);
- }
- try {
- switch (key) {
- case MonopriceAudioConnector.KEY_ERROR:
- logger.debug("Reading feedback message failed");
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Reading thread ended");
- closeConnection();
- break;
- case MonopriceAudioConnector.KEY_ZONE_UPDATE:
- String zoneId = updateData.substring(0, 2);
- MonopriceAudioZoneDTO zoneDTO = zoneDataMap.get(zoneId);
- if (MonopriceAudioZone.VALID_ZONE_IDS.contains(zoneId) && zoneDTO != null) {
- MonopriceAudioZone targetZone = MonopriceAudioZone.fromZoneId(zoneId);
- processZoneUpdate(targetZone, zoneDTO, updateData);
+ switch (key) {
+ case MonopriceAudioConnector.KEY_ZONE_UPDATE:
+ MonopriceAudioZoneDTO newZoneData = amp.getZoneData(evt.getValue());
+ MonopriceAudioZoneDTO zoneData = zoneDataMap.get(newZoneData.getZone());
+ if (amp.getZoneIds().contains(newZoneData.getZone()) && zoneData != null) {
+ if (amp == AmplifierModel.MONOPRICE70) {
+ processMonoprice70Update(zoneData, newZoneData);
} else {
- logger.warn("invalid event: {} for key: {} or zone data null", evt.getValue(), key);
+ processZoneUpdate(zoneData, newZoneData);
}
- break;
- default:
- logger.debug("onNewMessageEvent: unhandled key {}", key);
- break;
- }
- } catch (NumberFormatException e) {
- logger.warn("Invalid value {} for key {}", updateData, key);
- } catch (MonopriceAudioException e) {
- logger.warn("Error processing zone update: {}", e.getMessage());
+ } else {
+ logger.debug("invalid event: {} for key: {} or zone data null", evt.getValue(), key);
+ }
+ break;
+
+ case MonopriceAudioConnector.KEY_PING:
+ lastPollingUpdate = System.currentTimeMillis();
+ break;
+
+ default:
+ logger.debug("onNewMessageEvent: unhandled key {}", key);
+ break;
}
}
String error = null;
if (openConnection()) {
- try {
- long prevUpdateTime = lastPollingUpdate;
- connector.queryZone(MonopriceAudioZone.ZONE1);
-
- // prevUpdateTime should have changed if a zone update was received
- if (lastPollingUpdate == prevUpdateTime) {
- error = "Controller not responding to status requests";
+ long prevUpdateTime = lastPollingUpdate;
+ // poll all zones on the amplifier to get current state
+ amp.getZoneIds().stream().limit(numZones).forEach((streamZoneId) -> {
+ try {
+ connector.queryZone(streamZoneId);
+
+ if (amp == AmplifierModel.MONOPRICE70) {
+ connector.queryTrebBassBalance(streamZoneId);
+ }
+ } catch (MonopriceAudioException e) {
+ logger.debug("Polling error: {}", e.getMessage());
+ }
+ });
+
+ if (amp == AmplifierModel.XANTECH) {
+ try {
+ // for xantech send the commands to enable unsolicited updates
+ connector.sendCommand("!ZA1");
+ connector.sendCommand("!ZP10"); // Zone Periodic Auto Update set to 10 secs
+ } catch (MonopriceAudioException e) {
+ logger.debug("Error sending Xantech periodic update commands: {}", e.getMessage());
}
+ }
- } catch (MonopriceAudioException e) {
- error = "First command after connection failed";
- logger.warn("{}: {}", error, e.getMessage());
- closeConnection();
+ // prevUpdateTime should have changed if a zone update was received
+ if (lastPollingUpdate == prevUpdateTime) {
+ error = "@text/offline.communication-error-polling";
}
} else {
- error = "Reconnection failed";
+ error = "@text/offline.communication-error-reconnection";
}
if (error != null) {
+ closeConnection();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, error);
} else {
updateStatus(ThingStatus.ONLINE);
pollingJob = scheduler.scheduleWithFixedDelay(() -> {
synchronized (sequenceLock) {
if (connector.isConnected()) {
- logger.debug("Polling the controller for updated status...");
-
- // poll each zone up to the number of zones specified in the configuration
- MonopriceAudioZone.VALID_ZONES.stream().limit(numZones).forEach((zoneName) -> {
+ logger.debug("Polling the amplifier for updated status...");
+
+ if (!disableKeypadPolling) {
+ // poll each zone up to the number of zones specified in the configuration
+ amp.getZoneIds().stream().limit(numZones).forEach((streamZoneId) -> {
+ try {
+ connector.queryZone(streamZoneId);
+ } catch (MonopriceAudioException e) {
+ logger.debug("Polling error for zone id {}: {}", streamZoneId, e.getMessage());
+ }
+ });
+ } else {
try {
- connector.queryZone(MonopriceAudioZone.valueOf(zoneName));
+ // ping only (no zone updates) to verify the connection is still alive
+ connector.sendPing();
} catch (MonopriceAudioException e) {
- logger.warn("Polling error: {}", e.getMessage());
+ logger.debug("Ping error: {}", e.getMessage());
}
- });
+ }
- // if the last successful polling update was more than 2.25 intervals ago, the controller
+ // if the last successful polling update was more than 2.25 intervals ago, the amplifier
// is either switched off or not responding even though the connection is still good
if ((System.currentTimeMillis() - lastPollingUpdate) > (pollingInterval * 2.25 * 1000)) {
- logger.warn("Controller not responding to status requests");
+ logger.debug("Amplifier not responding to status requests");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
- "Controller not responding to status requests");
+ "@text/offline.communication-error-polling");
closeConnection();
scheduleReconnectJob();
}
}
}
- /**
- * Update the state of a channel
- *
- * @param channel the channel
- */
- private void updateChannelState(MonopriceAudioZone zone, String channelType, MonopriceAudioZoneDTO zoneData) {
- String channel = zone.name().toLowerCase() + CHANNEL_DELIMIT + channelType;
+ private void processZoneUpdate(MonopriceAudioZoneDTO zoneData, MonopriceAudioZoneDTO newZoneData) {
+ // only process the update if something actually changed in this zone since the last polling update
+ if (!newZoneData.toString().equals(zoneData.toString())) {
+ if (!newZoneData.getPage().equals(zoneData.getPage())) {
+ zoneData.setPage(newZoneData.getPage());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_PAGE);
+ }
- if (!isLinked(channel)) {
- return;
- }
+ if (!newZoneData.getPower().equals(zoneData.getPower())) {
+ zoneData.setPower(newZoneData.getPower());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_POWER);
+ }
- State state = UnDefType.UNDEF;
- switch (channelType) {
- case CHANNEL_TYPE_POWER:
- state = zoneData.isPowerOn() ? OnOffType.ON : OnOffType.OFF;
- break;
- case CHANNEL_TYPE_SOURCE:
- state = new DecimalType(zoneData.getSource());
- break;
- case CHANNEL_TYPE_VOLUME:
- long volumePct = Math.round(
- (double) (zoneData.getVolume() - MIN_VOLUME) / (double) (MAX_VOLUME - MIN_VOLUME) * 100.0);
- state = new PercentType(BigDecimal.valueOf(volumePct));
- break;
- case CHANNEL_TYPE_MUTE:
- state = zoneData.isMuted() ? OnOffType.ON : OnOffType.OFF;
- break;
- case CHANNEL_TYPE_TREBLE:
- state = new DecimalType(BigDecimal.valueOf(zoneData.getTreble() - TONE_OFFSET));
- break;
- case CHANNEL_TYPE_BASS:
- state = new DecimalType(BigDecimal.valueOf(zoneData.getBass() - TONE_OFFSET));
- break;
- case CHANNEL_TYPE_BALANCE:
- state = new DecimalType(BigDecimal.valueOf(zoneData.getBalance() - BALANCE_OFFSET));
- break;
- case CHANNEL_TYPE_DND:
- state = zoneData.isDndOn() ? OnOffType.ON : OnOffType.OFF;
- break;
- case CHANNEL_TYPE_PAGE:
- state = zoneData.isPageActive() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
- break;
- case CHANNEL_TYPE_KEYPAD:
- state = zoneData.isKeypadActive() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
- break;
- default:
- break;
- }
- updateState(channel, state);
- }
+ if (!newZoneData.getMute().equals(zoneData.getMute())) {
+ zoneData.setMute(newZoneData.getMute());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_MUTE);
+ }
- private void processZoneUpdate(MonopriceAudioZone zone, MonopriceAudioZoneDTO zoneData, String newZoneData) {
- // only process the update if something actually changed in this zone since the last time through
- if (!newZoneData.equals(zoneData.toString())) {
- // example status string: 1200010000130809100601, matcher pattern from above:
- // "^(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})"
- Matcher matcher = PATTERN.matcher(newZoneData);
- if (matcher.find()) {
- zoneData.setZone(matcher.group(1));
-
- if (!matcher.group(2).equals(zoneData.getPage())) {
- zoneData.setPage(matcher.group(2));
- updateChannelState(zone, CHANNEL_TYPE_PAGE, zoneData);
- }
+ if (!newZoneData.getDnd().equals(zoneData.getDnd())) {
+ zoneData.setDnd(newZoneData.getDnd());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_DND);
+ }
- if (!matcher.group(3).equals(zoneData.getPower())) {
- zoneData.setPower(matcher.group(3));
- updateChannelState(zone, CHANNEL_TYPE_POWER, zoneData);
- }
+ if (newZoneData.getVolume() != zoneData.getVolume()) {
+ zoneData.setVolume(newZoneData.getVolume());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_VOLUME);
+ }
- if (!matcher.group(4).equals(zoneData.getMute())) {
- zoneData.setMute(matcher.group(4));
- updateChannelState(zone, CHANNEL_TYPE_MUTE, zoneData);
- }
+ if (newZoneData.getTreble() != zoneData.getTreble()) {
+ zoneData.setTreble(newZoneData.getTreble());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_TREBLE);
+ }
- if (!matcher.group(5).equals(zoneData.getDnd())) {
- zoneData.setDnd(matcher.group(5));
- updateChannelState(zone, CHANNEL_TYPE_DND, zoneData);
- }
+ if (newZoneData.getBass() != zoneData.getBass()) {
+ zoneData.setBass(newZoneData.getBass());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_BASS);
+ }
- int volume = Integer.parseInt(matcher.group(6));
- if (volume != zoneData.getVolume()) {
- zoneData.setVolume(volume);
- updateChannelState(zone, CHANNEL_TYPE_VOLUME, zoneData);
- }
+ if (newZoneData.getBalance() != zoneData.getBalance()) {
+ zoneData.setBalance(newZoneData.getBalance());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_BALANCE);
+ }
- int treble = Integer.parseInt(matcher.group(7));
- if (treble != zoneData.getTreble()) {
- zoneData.setTreble(treble);
- updateChannelState(zone, CHANNEL_TYPE_TREBLE, zoneData);
- }
+ if (!newZoneData.getSource().equals(zoneData.getSource())) {
+ zoneData.setSource(newZoneData.getSource());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_SOURCE);
+ }
- int bass = Integer.parseInt(matcher.group(8));
- if (bass != zoneData.getBass()) {
- zoneData.setBass(bass);
- updateChannelState(zone, CHANNEL_TYPE_BASS, zoneData);
- }
+ if (!newZoneData.getKeypad().equals(zoneData.getKeypad())) {
+ zoneData.setKeypad(newZoneData.getKeypad());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_KEYPAD);
+ }
- int balance = Integer.parseInt(matcher.group(9));
- if (balance != zoneData.getBalance()) {
- zoneData.setBalance(balance);
- updateChannelState(zone, CHANNEL_TYPE_BALANCE, zoneData);
- }
+ }
+ lastPollingUpdate = System.currentTimeMillis();
+ }
- if (!matcher.group(10).equals(zoneData.getSource())) {
- zoneData.setSource(matcher.group(10));
- updateChannelState(zone, CHANNEL_TYPE_SOURCE, zoneData);
- }
+ private void processMonoprice70Update(MonopriceAudioZoneDTO zoneData, MonopriceAudioZoneDTO newZoneData) {
+ if (newZoneData.getTreble() != NIL) {
+ zoneData.setTreble(newZoneData.getTreble());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_TREBLE);
+ } else if (newZoneData.getBass() != NIL) {
+ zoneData.setBass(newZoneData.getBass());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_BASS);
+ } else if (newZoneData.getBalance() != NIL) {
+ zoneData.setBalance(newZoneData.getBalance());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_BALANCE);
+ } else {
+ zoneData.setPower(newZoneData.getPower());
+ zoneData.setMute(newZoneData.getMute());
+ zoneData.setVolume(newZoneData.getVolume());
+ zoneData.setSource(newZoneData.getSource());
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_POWER);
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_MUTE);
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_VOLUME);
+ updateChannelState(zoneData.getZone(), CHANNEL_TYPE_SOURCE);
- if (!matcher.group(11).equals(zoneData.getKeypad())) {
- zoneData.setKeypad(matcher.group(11));
- updateChannelState(zone, CHANNEL_TYPE_KEYPAD, zoneData);
- }
- } else {
- logger.debug("Invalid zone update message: {}", newZoneData);
+ }
+ lastPollingUpdate = System.currentTimeMillis();
+ }
+
+ /**
+ * Update the state of a channel
+ *
+ * @param zoneId the zone id used to lookup the channel to be updated
+ * @param channelType the channel type to be updated
+ */
+ private void updateChannelState(String zoneId, String channelType) {
+ MonopriceAudioZoneDTO zoneData = zoneDataMap.get(zoneId);
+
+ if (zoneData != null) {
+ String channel = amp.getZoneName(zoneId) + CHANNEL_DELIMIT + channelType;
+
+ if (!isLinked(channel)) {
+ return;
}
+ logger.debug("updating channel state for zone: {}, channel type: {}", zoneId, channelType);
+
+ State state = UnDefType.UNDEF;
+ switch (channelType) {
+ case CHANNEL_TYPE_POWER:
+ state = OnOffType.from(zoneData.isPowerOn());
+ break;
+ case CHANNEL_TYPE_SOURCE:
+ state = new DecimalType(zoneData.getSource());
+ break;
+ case CHANNEL_TYPE_VOLUME:
+ long volumePct = Math.round(
+ (zoneData.getVolume() - MIN_VOLUME) / (double) (amp.getMaxVol() - MIN_VOLUME) * 100.0);
+ state = new PercentType(BigDecimal.valueOf(volumePct));
+ break;
+ case CHANNEL_TYPE_MUTE:
+ state = OnOffType.from(zoneData.isMuted());
+ break;
+ case CHANNEL_TYPE_TREBLE:
+ state = new DecimalType(BigDecimal.valueOf(zoneData.getTreble() - amp.getToneOffset()));
+ break;
+ case CHANNEL_TYPE_BASS:
+ state = new DecimalType(BigDecimal.valueOf(zoneData.getBass() - amp.getToneOffset()));
+ break;
+ case CHANNEL_TYPE_BALANCE:
+ state = new DecimalType(BigDecimal.valueOf(zoneData.getBalance() - amp.getBalOffset()));
+ break;
+ case CHANNEL_TYPE_DND:
+ state = OnOffType.from(zoneData.isDndOn());
+ break;
+ case CHANNEL_TYPE_PAGE:
+ state = zoneData.isPageActive() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
+ break;
+ case CHANNEL_TYPE_KEYPAD:
+ state = zoneData.isKeypadActive() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
+ break;
+ default:
+ break;
+ }
+ updateState(channel, state);
}
- lastPollingUpdate = System.currentTimeMillis();
}
}
<type>binding</type>
<name>Monoprice Whole House Audio Binding</name>
- <description>Controls Monoprice and Dayton Audio Whole House Amplifiers.</description>
+ <description>Controls Monoprice, Dayton Audio and Xantech Whole House Amplifiers.</description>
+
<connection>local</connection>
</addon:addon>
# add-on
addon.monopriceaudio.name = Monoprice Whole House Audio Binding
-addon.monopriceaudio.description = Controls Monoprice and Dayton Audio Whole House Amplifiers.
+addon.monopriceaudio.description = Controls Monoprice, Dayton Audio and Xantech Whole House Amplifiers.
# thing types
thing-type.monopriceaudio.amplifier.group.zone17.description = The Controls for Zone 17
thing-type.monopriceaudio.amplifier.group.zone18.label = Zone 18
thing-type.monopriceaudio.amplifier.group.zone18.description = The Controls for Zone 18
+thing-type.monopriceaudio.dax88.label = Dayton DAX88 Amplifier
+thing-type.monopriceaudio.dax88.description = A Multi-zone Whole House Amplifier System
+thing-type.monopriceaudio.dax88.group.all.label = All Zones
+thing-type.monopriceaudio.dax88.group.all.description = Control All Zones Simultaneously
+thing-type.monopriceaudio.dax88.group.zone1.label = Zone 1
+thing-type.monopriceaudio.dax88.group.zone1.description = The Controls for Zone 1
+thing-type.monopriceaudio.dax88.group.zone2.label = Zone 2
+thing-type.monopriceaudio.dax88.group.zone2.description = The Controls for Zone 2
+thing-type.monopriceaudio.dax88.group.zone3.label = Zone 3
+thing-type.monopriceaudio.dax88.group.zone3.description = The Controls for Zone 3
+thing-type.monopriceaudio.dax88.group.zone4.label = Zone 4
+thing-type.monopriceaudio.dax88.group.zone4.description = The Controls for Zone 4
+thing-type.monopriceaudio.dax88.group.zone5.label = Zone 5
+thing-type.monopriceaudio.dax88.group.zone5.description = The Controls for Zone 5
+thing-type.monopriceaudio.dax88.group.zone6.label = Zone 6
+thing-type.monopriceaudio.dax88.group.zone6.description = The Controls for Zone 6
+thing-type.monopriceaudio.dax88.group.zone7.label = Zone 7
+thing-type.monopriceaudio.dax88.group.zone7.description = The Controls for Zone 7
+thing-type.monopriceaudio.dax88.group.zone8.label = Zone 8
+thing-type.monopriceaudio.dax88.group.zone8.description = The Controls for Zone 8
+thing-type.monopriceaudio.monoprice70.label = Monoprice 31028 70V Amplifier
+thing-type.monopriceaudio.monoprice70.description = A Multi-zone Whole House Amplifier System
+thing-type.monopriceaudio.monoprice70.group.all.label = All Zones
+thing-type.monopriceaudio.monoprice70.group.all.description = Control All Zones Simultaneously
+thing-type.monopriceaudio.monoprice70.group.zone1.label = Zone 1
+thing-type.monopriceaudio.monoprice70.group.zone1.description = The Controls for Zone 1
+thing-type.monopriceaudio.monoprice70.group.zone2.label = Zone 2
+thing-type.monopriceaudio.monoprice70.group.zone2.description = The Controls for Zone 2
+thing-type.monopriceaudio.monoprice70.group.zone3.label = Zone 3
+thing-type.monopriceaudio.monoprice70.group.zone3.description = The Controls for Zone 3
+thing-type.monopriceaudio.monoprice70.group.zone4.label = Zone 4
+thing-type.monopriceaudio.monoprice70.group.zone4.description = The Controls for Zone 4
+thing-type.monopriceaudio.monoprice70.group.zone5.label = Zone 5
+thing-type.monopriceaudio.monoprice70.group.zone5.description = The Controls for Zone 5
+thing-type.monopriceaudio.monoprice70.group.zone6.label = Zone 6
+thing-type.monopriceaudio.monoprice70.group.zone6.description = The Controls for Zone 6
+thing-type.monopriceaudio.xantech.label = Xantech 8x8 Amplifier
+thing-type.monopriceaudio.xantech.description = A Multi-zone Whole House Amplifier System
+thing-type.monopriceaudio.xantech.group.all.label = All Zones
+thing-type.monopriceaudio.xantech.group.all.description = Control All Zones Simultaneously
+thing-type.monopriceaudio.xantech.group.zone1.label = Zone 1
+thing-type.monopriceaudio.xantech.group.zone1.description = The Controls for Zone 1
+thing-type.monopriceaudio.xantech.group.zone2.label = Zone 2
+thing-type.monopriceaudio.xantech.group.zone2.description = The Controls for Zone 2
+thing-type.monopriceaudio.xantech.group.zone3.label = Zone 3
+thing-type.monopriceaudio.xantech.group.zone3.description = The Controls for Zone 3
+thing-type.monopriceaudio.xantech.group.zone4.label = Zone 4
+thing-type.monopriceaudio.xantech.group.zone4.description = The Controls for Zone 4
+thing-type.monopriceaudio.xantech.group.zone5.label = Zone 5
+thing-type.monopriceaudio.xantech.group.zone5.description = The Controls for Zone 5
+thing-type.monopriceaudio.xantech.group.zone6.label = Zone 6
+thing-type.monopriceaudio.xantech.group.zone6.description = The Controls for Zone 6
+thing-type.monopriceaudio.xantech.group.zone7.label = Zone 7
+thing-type.monopriceaudio.xantech.group.zone7.description = The Controls for Zone 7
+thing-type.monopriceaudio.xantech.group.zone8.label = Zone 8
+thing-type.monopriceaudio.xantech.group.zone8.description = The Controls for Zone 8
+thing-type.monopriceaudio.xantech.group.zone9.label = Zone 9
+thing-type.monopriceaudio.xantech.group.zone9.description = The Controls for Zone 9
+thing-type.monopriceaudio.xantech.group.zone10.label = Zone 10
+thing-type.monopriceaudio.xantech.group.zone10.description = The Controls for Zone 10
+thing-type.monopriceaudio.xantech.group.zone11.label = Zone 11
+thing-type.monopriceaudio.xantech.group.zone11.description = The Controls for Zone 11
+thing-type.monopriceaudio.xantech.group.zone12.label = Zone 12
+thing-type.monopriceaudio.xantech.group.zone12.description = The Controls for Zone 12
+thing-type.monopriceaudio.xantech.group.zone13.label = Zone 13
+thing-type.monopriceaudio.xantech.group.zone13.description = The Controls for Zone 13
+thing-type.monopriceaudio.xantech.group.zone14.label = Zone 14
+thing-type.monopriceaudio.xantech.group.zone14.description = The Controls for Zone 14
+thing-type.monopriceaudio.xantech.group.zone15.label = Zone 15
+thing-type.monopriceaudio.xantech.group.zone15.description = The Controls for Zone 15
+thing-type.monopriceaudio.xantech.group.zone16.label = Zone 16
+thing-type.monopriceaudio.xantech.group.zone16.description = The Controls for Zone 16
# thing types config
+thing-type.config.monopriceaudio.amplifier.disableKeypadPolling.label = Disable Keypad Polling
+thing-type.config.monopriceaudio.amplifier.disableKeypadPolling.description = If physical keypads are not used, this option will disable polling the amplifier for zone updates
thing-type.config.monopriceaudio.amplifier.host.label = Address
-thing-type.config.monopriceaudio.amplifier.host.description = Host Name or IP Address of the Amplifier or Serial over IP device
+thing-type.config.monopriceaudio.amplifier.host.description = Host Name or IP Address of the Monoprice Amplifier or Serial over IP device
thing-type.config.monopriceaudio.amplifier.ignoreZones.label = Ignore Zones
thing-type.config.monopriceaudio.amplifier.ignoreZones.description = (Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off) Commands (ie: 1,6,10)
thing-type.config.monopriceaudio.amplifier.initialAllVolume.label = Initial All Volume
thing-type.config.monopriceaudio.amplifier.numZones.label = Number of Zones
thing-type.config.monopriceaudio.amplifier.numZones.description = Number of Zones on the Amplifier to Utilize in the Binding (Up to 18 Zones With 3 Amplifiers Connected Together)
thing-type.config.monopriceaudio.amplifier.pollingInterval.label = Polling Interval
-thing-type.config.monopriceaudio.amplifier.pollingInterval.description = Configures How Often to Poll the Controller to Check for Zone Updates (5-60; Default 15)
+thing-type.config.monopriceaudio.amplifier.pollingInterval.description = Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 15)
thing-type.config.monopriceaudio.amplifier.port.label = Port
-thing-type.config.monopriceaudio.amplifier.port.description = Communication Port for Serial over IP connection to the Amplifier (Default 8080 for amps with built-in Serial over IP)
+thing-type.config.monopriceaudio.amplifier.port.description = Communication Port for Serial over IP connection to the Monoprice Amplifier (8080 for amps with built-in Serial over IP)
thing-type.config.monopriceaudio.amplifier.serialPort.label = Serial Port
thing-type.config.monopriceaudio.amplifier.serialPort.description = Serial Port to Use for Connecting to the Monoprice Amplifier
+thing-type.config.monopriceaudio.dax88.disableKeypadPolling.label = Disable Keypad Polling
+thing-type.config.monopriceaudio.dax88.disableKeypadPolling.description = If physical keypads are not used, this option will disable polling the amplifier for zone updates
+thing-type.config.monopriceaudio.dax88.host.label = Address
+thing-type.config.monopriceaudio.dax88.host.description = Host Name or IP Address of the Machine Connected to the Dayton Amplifier (Serial over IP)
+thing-type.config.monopriceaudio.dax88.ignoreZones.label = Ignore Zones
+thing-type.config.monopriceaudio.dax88.ignoreZones.description = (Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off) Commands (ie: 1,6,10)
+thing-type.config.monopriceaudio.dax88.initialAllVolume.label = Initial All Volume
+thing-type.config.monopriceaudio.dax88.initialAllVolume.description = When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent Excessive Blaring of Sound ;)
+thing-type.config.monopriceaudio.dax88.inputLabel1.label = Source 1 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel1.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel2.label = Source 2 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel2.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel3.label = Source 3 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel3.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel4.label = Source 4 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel4.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel5.label = Source 5 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel5.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel6.label = Source 6 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel6.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel7.label = Source 7 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel7.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.inputLabel8.label = Source 8 Input Label
+thing-type.config.monopriceaudio.dax88.inputLabel8.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.dax88.numZones.label = Number of Zones
+thing-type.config.monopriceaudio.dax88.numZones.description = Number of Zones on the Amplifier to Utilize in the Binding
+thing-type.config.monopriceaudio.dax88.pollingInterval.label = Polling Interval
+thing-type.config.monopriceaudio.dax88.pollingInterval.description = Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 15)
+thing-type.config.monopriceaudio.dax88.port.label = Port
+thing-type.config.monopriceaudio.dax88.port.description = Communication Port for Serial over IP connection to the Dayton Amplifier
+thing-type.config.monopriceaudio.dax88.serialPort.label = Serial Port
+thing-type.config.monopriceaudio.dax88.serialPort.description = Serial Port to Use for Connecting to the Dayton Amplifier
+thing-type.config.monopriceaudio.monoprice70.host.label = Address
+thing-type.config.monopriceaudio.monoprice70.host.description = Host Name or IP Address of the Machine Connected to the Monoprice Amplifier (Serial over IP)
+thing-type.config.monopriceaudio.monoprice70.ignoreZones.label = Ignore Zones
+thing-type.config.monopriceaudio.monoprice70.ignoreZones.description = (Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off) Commands (ie: 1,5,6)
+thing-type.config.monopriceaudio.monoprice70.initialAllVolume.label = Initial All Volume
+thing-type.config.monopriceaudio.monoprice70.initialAllVolume.description = When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent Excessive Blaring of Sound ;)
+thing-type.config.monopriceaudio.monoprice70.inputLabel1.label = Source 0 Input Label
+thing-type.config.monopriceaudio.monoprice70.inputLabel1.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.monoprice70.inputLabel2.label = Source 1 Input Label
+thing-type.config.monopriceaudio.monoprice70.inputLabel2.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.monoprice70.numZones.label = Number of Zones
+thing-type.config.monopriceaudio.monoprice70.numZones.description = Number of Zones on the Amplifier to Utilize in the Binding
+thing-type.config.monopriceaudio.monoprice70.pollingInterval.label = Polling Interval
+thing-type.config.monopriceaudio.monoprice70.pollingInterval.description = Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 30)
+thing-type.config.monopriceaudio.monoprice70.port.label = Port
+thing-type.config.monopriceaudio.monoprice70.port.description = Communication Port for Serial over IP connection to the Monoprice Amplifier
+thing-type.config.monopriceaudio.monoprice70.serialPort.label = Serial Port
+thing-type.config.monopriceaudio.monoprice70.serialPort.description = Serial Port to Use for Connecting to the Monoprice Amplifier
+thing-type.config.monopriceaudio.xantech.disableKeypadPolling.label = Disable Keypad Polling
+thing-type.config.monopriceaudio.xantech.disableKeypadPolling.description = If physical keypads are not used, this option will disable polling the amplifier for zone updates
+thing-type.config.monopriceaudio.xantech.host.label = Address
+thing-type.config.monopriceaudio.xantech.host.description = Host Name or IP Address of the Machine Connected to the Xantech Amplifier (Serial over IP)
+thing-type.config.monopriceaudio.xantech.ignoreZones.label = Ignore Zones
+thing-type.config.monopriceaudio.xantech.ignoreZones.description = (Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off) Commands (ie: 1,6,10)
+thing-type.config.monopriceaudio.xantech.initialAllVolume.label = Initial All Volume
+thing-type.config.monopriceaudio.xantech.initialAllVolume.description = When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent Excessive Blaring of Sound ;)
+thing-type.config.monopriceaudio.xantech.inputLabel1.label = Source 1 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel1.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel2.label = Source 2 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel2.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel3.label = Source 3 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel3.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel4.label = Source 4 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel4.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel5.label = Source 5 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel5.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel6.label = Source 6 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel6.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel7.label = Source 7 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel7.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.inputLabel8.label = Source 8 Input Label
+thing-type.config.monopriceaudio.xantech.inputLabel8.description = Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)
+thing-type.config.monopriceaudio.xantech.numZones.label = Number of Zones
+thing-type.config.monopriceaudio.xantech.numZones.description = Number of Zones on the Amplifier to Utilize in the Binding (Up to 16 Zones With 2 Amplifiers Connected Together)
+thing-type.config.monopriceaudio.xantech.pollingInterval.label = Polling Interval
+thing-type.config.monopriceaudio.xantech.pollingInterval.description = Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 30)
+thing-type.config.monopriceaudio.xantech.port.label = Port
+thing-type.config.monopriceaudio.xantech.port.description = Communication Port for Serial over IP connection to the Xantech Amplifier
+thing-type.config.monopriceaudio.xantech.serialPort.label = Serial Port
+thing-type.config.monopriceaudio.xantech.serialPort.description = Serial Port to Use for Connecting to the Xantech Amplifier
# channel group types
channel-group-type.monopriceaudio.all.label = All Zones
channel-group-type.monopriceaudio.all.description = Control All Zones Simultaneously
+channel-group-type.monopriceaudio.dax88-all.label = All Zones
+channel-group-type.monopriceaudio.dax88-all.description = Control All Zones Simultaneously
+channel-group-type.monopriceaudio.dax88-zone.label = Zone Controls
+channel-group-type.monopriceaudio.dax88-zone.description = The Controls for the Zone
+channel-group-type.monopriceaudio.monoprice70-all.label = All Zones
+channel-group-type.monopriceaudio.monoprice70-all.description = Control All Zones Simultaneously
+channel-group-type.monopriceaudio.monoprice70-zone.label = Zone Controls
+channel-group-type.monopriceaudio.monoprice70-zone.description = The Controls for the Zone
+channel-group-type.monopriceaudio.xantech-all.label = All Zones
+channel-group-type.monopriceaudio.xantech-all.description = Control All Zones Simultaneously
+channel-group-type.monopriceaudio.xantech-zone.label = Zone Controls
+channel-group-type.monopriceaudio.xantech-zone.description = The Controls for the Zone
channel-group-type.monopriceaudio.zone.label = Zone Controls
channel-group-type.monopriceaudio.zone.description = The Controls for the Zone
channel-type.monopriceaudio.balance.description = Adjust the Balance
channel-type.monopriceaudio.bass.label = Bass Adjustment
channel-type.monopriceaudio.bass.description = Adjust the Bass
+channel-type.monopriceaudio.dax88-allpower.label = All On
+channel-type.monopriceaudio.dax88-allpower.description = Turn All Zones On or Off
+channel-type.monopriceaudio.dax88-balance.label = Balance Adjustment
+channel-type.monopriceaudio.dax88-balance.description = Adjust the Balance
+channel-type.monopriceaudio.dax88-bass.label = Bass Adjustment
+channel-type.monopriceaudio.dax88-bass.description = Adjust the Bass
+channel-type.monopriceaudio.dax88-dnd.label = Do Not Disturb
+channel-type.monopriceaudio.dax88-dnd.description = Controls if the Zone Should Ignore an Incoming Audio Page
+channel-type.monopriceaudio.dax88-keypad.label = Keypad Connected
+channel-type.monopriceaudio.dax88-keypad.description = Indicates if a Physical Keypad is Attached to This Zone
+channel-type.monopriceaudio.dax88-keypad.state.option.CLOSED = Disconnected
+channel-type.monopriceaudio.dax88-keypad.state.option.OPEN = Connected
+channel-type.monopriceaudio.dax88-page.label = Page Active
+channel-type.monopriceaudio.dax88-page.description = Indicates if the Page Mode is Active for This Zone
+channel-type.monopriceaudio.dax88-page.state.option.CLOSED = Inactive
+channel-type.monopriceaudio.dax88-page.state.option.OPEN = Active
+channel-type.monopriceaudio.dax88-source.label = Source Input
+channel-type.monopriceaudio.dax88-source.description = Select the Source Input
+channel-type.monopriceaudio.dax88-treble.label = Treble Adjustment
+channel-type.monopriceaudio.dax88-treble.description = Adjust the Treble
channel-type.monopriceaudio.dnd.label = Do Not Disturb
channel-type.monopriceaudio.dnd.description = Controls if the Zone Should Ignore an Incoming Audio Page
channel-type.monopriceaudio.keypad.label = Keypad Connected
channel-type.monopriceaudio.keypad.description = Indicates if a Physical Keypad is Attached to This Zone
channel-type.monopriceaudio.keypad.state.option.CLOSED = Disconnected
channel-type.monopriceaudio.keypad.state.option.OPEN = Connected
+channel-type.monopriceaudio.monoprice70-allpower.label = All On
+channel-type.monopriceaudio.monoprice70-allpower.description = Turn All Zones On or Off
+channel-type.monopriceaudio.monoprice70-balance.label = Balance Adjustment
+channel-type.monopriceaudio.monoprice70-balance.description = Adjust the Balance
+channel-type.monopriceaudio.monoprice70-bass.label = Bass Adjustment
+channel-type.monopriceaudio.monoprice70-bass.description = Adjust the Bass
+channel-type.monopriceaudio.monoprice70-source.label = Source Input
+channel-type.monopriceaudio.monoprice70-source.description = Select the Source Input
+channel-type.monopriceaudio.monoprice70-treble.label = Treble Adjustment
+channel-type.monopriceaudio.monoprice70-treble.description = Adjust the Treble
channel-type.monopriceaudio.page.label = Page Active
channel-type.monopriceaudio.page.description = Indicates if the Page Mode is Active for This Zone
channel-type.monopriceaudio.page.state.option.CLOSED = Inactive
channel-type.monopriceaudio.source.description = Select the Source Input
channel-type.monopriceaudio.treble.label = Treble Adjustment
channel-type.monopriceaudio.treble.description = Adjust the Treble
+channel-type.monopriceaudio.xantech-allpower.label = All On
+channel-type.monopriceaudio.xantech-allpower.description = Turn All Zones On or Off
+channel-type.monopriceaudio.xantech-balance.label = Balance Adjustment
+channel-type.monopriceaudio.xantech-balance.description = Adjust the Balance
+channel-type.monopriceaudio.xantech-bass.label = Bass Adjustment
+channel-type.monopriceaudio.xantech-bass.description = Adjust the Bass
+channel-type.monopriceaudio.xantech-keypad.label = Keypad Connected
+channel-type.monopriceaudio.xantech-keypad.description = Indicates if a Physical Keypad is Attached to This Zone
+channel-type.monopriceaudio.xantech-keypad.state.option.CLOSED = Disconnected
+channel-type.monopriceaudio.xantech-keypad.state.option.OPEN = Connected
+channel-type.monopriceaudio.xantech-page.label = Page Active
+channel-type.monopriceaudio.xantech-page.description = Indicates if the Page Mode is Active for This Zone
+channel-type.monopriceaudio.xantech-page.state.option.CLOSED = Inactive
+channel-type.monopriceaudio.xantech-page.state.option.OPEN = Active
+channel-type.monopriceaudio.xantech-source.label = Source Input
+channel-type.monopriceaudio.xantech-source.description = Select the Source Input
+channel-type.monopriceaudio.xantech-treble.label = Treble Adjustment
+channel-type.monopriceaudio.xantech-treble.description = Adjust the Treble
+
+# message strings
+
+offline.configuration-error-rfc2217 = Use Host and Port configuration settings for a serial over IP connection
+offline.configuration-error-conflict = Serial port cannot be used at the same time that Host & Port is used
+offline.configuration-error-missing = Either Serial port or Host & Port must be specifed
+offline.communication-error-failed = Sending command failed
+offline.communication-error-polling = Amplifier not responding to status requests
+offline.communication-error-reconnection = Reconnection failed
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<thing:thing-descriptions bindingId="monopriceaudio"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
- xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
-
- <!-- Monoprice 10761/39261/DAX66 Amplifier Thing -->
- <thing-type id="amplifier">
- <label>Monoprice 10761 Amplifier, 39261 Matrix or Dayton DAX66 Amplifier</label>
- <description>
- A Multi-zone Whole House Amplifier System
- </description>
-
- <channel-groups>
- <channel-group id="all" typeId="all">
- <label>All Zones</label>
- <description>Control All Zones Simultaneously</description>
- </channel-group>
- <channel-group id="zone1" typeId="zone">
- <label>Zone 1</label>
- <description>The Controls for Zone 1</description>
- </channel-group>
- <channel-group id="zone2" typeId="zone">
- <label>Zone 2</label>
- <description>The Controls for Zone 2</description>
- </channel-group>
- <channel-group id="zone3" typeId="zone">
- <label>Zone 3</label>
- <description>The Controls for Zone 3</description>
- </channel-group>
- <channel-group id="zone4" typeId="zone">
- <label>Zone 4</label>
- <description>The Controls for Zone 4</description>
- </channel-group>
- <channel-group id="zone5" typeId="zone">
- <label>Zone 5</label>
- <description>The Controls for Zone 5</description>
- </channel-group>
- <channel-group id="zone6" typeId="zone">
- <label>Zone 6</label>
- <description>The Controls for Zone 6</description>
- </channel-group>
- <channel-group id="zone7" typeId="zone">
- <label>Zone 7</label>
- <description>The Controls for Zone 7</description>
- </channel-group>
- <channel-group id="zone8" typeId="zone">
- <label>Zone 8</label>
- <description>The Controls for Zone 8</description>
- </channel-group>
- <channel-group id="zone9" typeId="zone">
- <label>Zone 9</label>
- <description>The Controls for Zone 9</description>
- </channel-group>
- <channel-group id="zone10" typeId="zone">
- <label>Zone 10</label>
- <description>The Controls for Zone 10</description>
- </channel-group>
- <channel-group id="zone11" typeId="zone">
- <label>Zone 11</label>
- <description>The Controls for Zone 11</description>
- </channel-group>
- <channel-group id="zone12" typeId="zone">
- <label>Zone 12</label>
- <description>The Controls for Zone 12</description>
- </channel-group>
- <channel-group id="zone13" typeId="zone">
- <label>Zone 13</label>
- <description>The Controls for Zone 13</description>
- </channel-group>
- <channel-group id="zone14" typeId="zone">
- <label>Zone 14</label>
- <description>The Controls for Zone 14</description>
- </channel-group>
- <channel-group id="zone15" typeId="zone">
- <label>Zone 15</label>
- <description>The Controls for Zone 15</description>
- </channel-group>
- <channel-group id="zone16" typeId="zone">
- <label>Zone 16</label>
- <description>The Controls for Zone 16</description>
- </channel-group>
- <channel-group id="zone17" typeId="zone">
- <label>Zone 17</label>
- <description>The Controls for Zone 17</description>
- </channel-group>
- <channel-group id="zone18" typeId="zone">
- <label>Zone 18</label>
- <description>The Controls for Zone 18</description>
- </channel-group>
- </channel-groups>
-
- <config-description>
- <parameter name="serialPort" type="text" required="false">
- <context>serial-port</context>
- <limitToOptions>false</limitToOptions>
- <label>Serial Port</label>
- <description>Serial Port to Use for Connecting to the Monoprice Amplifier</description>
- </parameter>
- <parameter name="host" type="text" required="false">
- <context>network-address</context>
- <label>Address</label>
- <description>Host Name or IP Address of the Amplifier or Serial over IP device</description>
- </parameter>
- <parameter name="port" type="integer" min="1" max="65535" required="false">
- <label>Port</label>
- <description>Communication Port for Serial over IP connection to the Amplifier (Default 8080 for amps with built-in
- Serial over IP)</description>
- <default>8080</default>
- </parameter>
- <parameter name="numZones" type="integer" min="1" max="18" required="true">
- <label>Number of Zones</label>
- <description>Number of Zones on the Amplifier to Utilize in the Binding (Up to 18 Zones With 3 Amplifiers Connected
- Together)</description>
- <default>6</default>
- </parameter>
- <parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
- <label>Polling Interval</label>
- <description>Configures How Often to Poll the Controller to Check for Zone Updates (5-60; Default 15)</description>
- <default>15</default>
- </parameter>
- <parameter name="ignoreZones" type="text" required="false">
- <label>Ignore Zones</label>
- <description>(Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off)
- Commands (ie: 1,6,10)</description>
- </parameter>
- <parameter name="initialAllVolume" type="integer" min="1" max="30" required="false">
- <label>Initial All Volume</label>
- <description>When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent
- Excessive Blaring of Sound ;)</description>
- <default>10</default>
- </parameter>
- <parameter name="inputLabel1" type="text" required="false">
- <label>Source 1 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 1</default>
- </parameter>
- <parameter name="inputLabel2" type="text" required="false">
- <label>Source 2 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 2</default>
- </parameter>
- <parameter name="inputLabel3" type="text" required="false">
- <label>Source 3 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 3</default>
- </parameter>
- <parameter name="inputLabel4" type="text" required="false">
- <label>Source 4 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 4</default>
- </parameter>
- <parameter name="inputLabel5" type="text" required="false">
- <label>Source 5 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 5</default>
- </parameter>
- <parameter name="inputLabel6" type="text" required="false">
- <label>Source 6 Input Label</label>
- <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
- <default>Source 6</default>
- </parameter>
- </config-description>
- </thing-type>
-
- <channel-group-type id="all">
- <label>All Zones</label>
- <description>Control All Zones Simultaneously</description>
- <channels>
- <channel id="allpower" typeId="allpower"/>
- <channel id="allsource" typeId="source"/>
- <channel id="allvolume" typeId="system.volume"/>
- <channel id="allmute" typeId="system.mute"/>
- </channels>
- </channel-group-type>
-
- <channel-group-type id="zone">
- <label>Zone Controls</label>
- <description>The Controls for the Zone</description>
- <channels>
- <channel id="power" typeId="system.power"/>
- <channel id="source" typeId="source"/>
- <channel id="volume" typeId="system.volume"/>
- <channel id="mute" typeId="system.mute"/>
- <channel id="treble" typeId="treble"/>
- <channel id="bass" typeId="bass"/>
- <channel id="balance" typeId="balance"/>
- <channel id="dnd" typeId="dnd"/>
- <channel id="page" typeId="page"/>
- <channel id="keypad" typeId="keypad"/>
- </channels>
- </channel-group-type>
-
- <channel-type id="allpower">
- <item-type>Switch</item-type>
- <label>All On</label>
- <description>Turn All Zones On or Off</description>
- </channel-type>
-
- <channel-type id="source">
- <item-type>Number</item-type>
- <label>Source Input</label>
- <description>Select the Source Input</description>
- <state min="1" max="6"/>
- </channel-type>
-
- <channel-type id="treble">
- <item-type>Number</item-type>
- <label>Treble Adjustment</label>
- <description>Adjust the Treble</description>
- <state min="-7" max="7" step="1" pattern="%d"/>
- </channel-type>
-
- <channel-type id="bass">
- <item-type>Number</item-type>
- <label>Bass Adjustment</label>
- <description>Adjust the Bass</description>
- <state min="-7" max="7" step="1" pattern="%d"/>
- </channel-type>
-
- <channel-type id="balance">
- <item-type>Number</item-type>
- <label>Balance Adjustment</label>
- <description>Adjust the Balance</description>
- <state min="-10" max="10" step="1" pattern="%d"/>
- </channel-type>
-
- <channel-type id="dnd">
- <item-type>Switch</item-type>
- <label>Do Not Disturb</label>
- <description>Controls if the Zone Should Ignore an Incoming Audio Page</description>
- </channel-type>
-
- <channel-type id="page">
- <item-type>Contact</item-type>
- <label>Page Active</label>
- <description>Indicates if the Page Mode is Active for This Zone</description>
- <state readOnly="true">
- <options>
- <option value="CLOSED">Inactive</option>
- <option value="OPEN">Active</option>
- </options>
- </state>
- </channel-type>
-
- <channel-type id="keypad">
- <item-type>Contact</item-type>
- <label>Keypad Connected</label>
- <description>Indicates if a Physical Keypad is Attached to This Zone</description>
- <state readOnly="true">
- <options>
- <option value="CLOSED">Disconnected</option>
- <option value="OPEN">Connected</option>
- </options>
- </state>
- </channel-type>
-
-</thing:thing-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="monopriceaudio"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- Dayton DAX88 Amplifier Thing -->
+ <thing-type id="dax88">
+ <label>Dayton DAX88 Amplifier</label>
+ <description>
+ A Multi-zone Whole House Amplifier System
+ </description>
+
+ <channel-groups>
+ <channel-group id="all" typeId="dax88-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ </channel-group>
+ <channel-group id="zone1" typeId="dax88-zone">
+ <label>Zone 1</label>
+ <description>The Controls for Zone 1</description>
+ </channel-group>
+ <channel-group id="zone2" typeId="dax88-zone">
+ <label>Zone 2</label>
+ <description>The Controls for Zone 2</description>
+ </channel-group>
+ <channel-group id="zone3" typeId="dax88-zone">
+ <label>Zone 3</label>
+ <description>The Controls for Zone 3</description>
+ </channel-group>
+ <channel-group id="zone4" typeId="dax88-zone">
+ <label>Zone 4</label>
+ <description>The Controls for Zone 4</description>
+ </channel-group>
+ <channel-group id="zone5" typeId="dax88-zone">
+ <label>Zone 5</label>
+ <description>The Controls for Zone 5</description>
+ </channel-group>
+ <channel-group id="zone6" typeId="dax88-zone">
+ <label>Zone 6</label>
+ <description>The Controls for Zone 6</description>
+ </channel-group>
+ <channel-group id="zone7" typeId="dax88-zone">
+ <label>Zone 7</label>
+ <description>The Controls for Zone 7</description>
+ </channel-group>
+ <channel-group id="zone8" typeId="dax88-zone">
+ <label>Zone 8</label>
+ <description>The Controls for Zone 8</description>
+ </channel-group>
+ </channel-groups>
+
+ <config-description>
+ <parameter name="serialPort" type="text" required="false">
+ <context>serial-port</context>
+ <limitToOptions>false</limitToOptions>
+ <label>Serial Port</label>
+ <description>Serial Port to Use for Connecting to the Dayton Amplifier</description>
+ </parameter>
+ <parameter name="host" type="text" required="false">
+ <context>network-address</context>
+ <label>Address</label>
+ <description>Host Name or IP Address of the Machine Connected to the Dayton Amplifier (Serial over IP)</description>
+ </parameter>
+ <parameter name="port" type="integer" min="1" max="65535" required="false">
+ <label>Port</label>
+ <description>Communication Port for Serial over IP connection to the Dayton Amplifier</description>
+ </parameter>
+ <parameter name="numZones" type="integer" min="1" max="8" required="true">
+ <label>Number of Zones</label>
+ <description>Number of Zones on the Amplifier to Utilize in the Binding</description>
+ <default>8</default>
+ </parameter>
+ <parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
+ <label>Polling Interval</label>
+ <description>Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 15)</description>
+ <default>15</default>
+ </parameter>
+ <parameter name="ignoreZones" type="text" required="false">
+ <label>Ignore Zones</label>
+ <description>(Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off)
+ Commands (ie: 1,6,10)</description>
+ </parameter>
+ <parameter name="initialAllVolume" type="integer" min="1" max="30" required="false">
+ <label>Initial All Volume</label>
+ <description>When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent
+ Excessive Blaring of Sound ;)</description>
+ <default>10</default>
+ </parameter>
+ <parameter name="inputLabel1" type="text" required="false">
+ <label>Source 1 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 1</default>
+ </parameter>
+ <parameter name="inputLabel2" type="text" required="false">
+ <label>Source 2 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 2</default>
+ </parameter>
+ <parameter name="inputLabel3" type="text" required="false">
+ <label>Source 3 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 3</default>
+ </parameter>
+ <parameter name="inputLabel4" type="text" required="false">
+ <label>Source 4 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 4</default>
+ </parameter>
+ <parameter name="inputLabel5" type="text" required="false">
+ <label>Source 5 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 5</default>
+ </parameter>
+ <parameter name="inputLabel6" type="text" required="false">
+ <label>Source 6 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 6</default>
+ </parameter>
+ <parameter name="inputLabel7" type="text" required="false">
+ <label>Source 7 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 7</default>
+ </parameter>
+ <parameter name="inputLabel8" type="text" required="false">
+ <label>Source 8 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Wi-Fi streaming</default>
+ </parameter>
+ <parameter name="disableKeypadPolling" type="boolean" required="false">
+ <label>Disable Keypad Polling</label>
+ <description>If physical keypads are not used, this option will disable polling the amplifier for zone updates</description>
+ <default>false</default>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <channel-group-type id="dax88-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ <channels>
+ <channel id="allpower" typeId="dax88-allpower"/>
+ <channel id="allsource" typeId="dax88-source"/>
+ <channel id="allvolume" typeId="system.volume"/>
+ <channel id="allmute" typeId="system.mute"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-group-type id="dax88-zone">
+ <label>Zone Controls</label>
+ <description>The Controls for the Zone</description>
+ <channels>
+ <channel id="power" typeId="system.power"/>
+ <channel id="source" typeId="dax88-source"/>
+ <channel id="volume" typeId="system.volume"/>
+ <channel id="mute" typeId="system.mute"/>
+ <channel id="treble" typeId="dax88-treble"/>
+ <channel id="bass" typeId="dax88-bass"/>
+ <channel id="balance" typeId="dax88-balance"/>
+ <channel id="dnd" typeId="dax88-dnd"/>
+ <channel id="page" typeId="dax88-page"/>
+ <channel id="keypad" typeId="dax88-keypad"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-type id="dax88-allpower">
+ <item-type>Switch</item-type>
+ <label>All On</label>
+ <description>Turn All Zones On or Off</description>
+ </channel-type>
+
+ <channel-type id="dax88-source">
+ <item-type>Number</item-type>
+ <label>Source Input</label>
+ <description>Select the Source Input</description>
+ <state min="1" max="8"/>
+ </channel-type>
+
+ <channel-type id="dax88-treble">
+ <item-type>Number</item-type>
+ <label>Treble Adjustment</label>
+ <description>Adjust the Treble</description>
+ <state min="-12" max="12" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="dax88-bass">
+ <item-type>Number</item-type>
+ <label>Bass Adjustment</label>
+ <description>Adjust the Bass</description>
+ <state min="-12" max="12" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="dax88-balance">
+ <item-type>Number</item-type>
+ <label>Balance Adjustment</label>
+ <description>Adjust the Balance</description>
+ <state min="-10" max="10" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="dax88-dnd">
+ <item-type>Switch</item-type>
+ <label>Do Not Disturb</label>
+ <description>Controls if the Zone Should Ignore an Incoming Audio Page</description>
+ </channel-type>
+
+ <channel-type id="dax88-page">
+ <item-type>Contact</item-type>
+ <label>Page Active</label>
+ <description>Indicates if the Page Mode is Active for This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Inactive</option>
+ <option value="OPEN">Active</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="dax88-keypad">
+ <item-type>Contact</item-type>
+ <label>Keypad Connected</label>
+ <description>Indicates if a Physical Keypad is Attached to This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Disconnected</option>
+ <option value="OPEN">Connected</option>
+ </options>
+ </state>
+ </channel-type>
+
+</thing:thing-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="monopriceaudio"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- Monoprice 10761/39261/DAX66 Amplifier Thing -->
+ <thing-type id="amplifier">
+ <label>Monoprice 10761 Amplifier, 39261 Matrix or Dayton DAX66 Amplifier</label>
+ <description>
+ A Multi-zone Whole House Amplifier System
+ </description>
+
+ <channel-groups>
+ <channel-group id="all" typeId="all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ </channel-group>
+ <channel-group id="zone1" typeId="zone">
+ <label>Zone 1</label>
+ <description>The Controls for Zone 1</description>
+ </channel-group>
+ <channel-group id="zone2" typeId="zone">
+ <label>Zone 2</label>
+ <description>The Controls for Zone 2</description>
+ </channel-group>
+ <channel-group id="zone3" typeId="zone">
+ <label>Zone 3</label>
+ <description>The Controls for Zone 3</description>
+ </channel-group>
+ <channel-group id="zone4" typeId="zone">
+ <label>Zone 4</label>
+ <description>The Controls for Zone 4</description>
+ </channel-group>
+ <channel-group id="zone5" typeId="zone">
+ <label>Zone 5</label>
+ <description>The Controls for Zone 5</description>
+ </channel-group>
+ <channel-group id="zone6" typeId="zone">
+ <label>Zone 6</label>
+ <description>The Controls for Zone 6</description>
+ </channel-group>
+ <channel-group id="zone7" typeId="zone">
+ <label>Zone 7</label>
+ <description>The Controls for Zone 7</description>
+ </channel-group>
+ <channel-group id="zone8" typeId="zone">
+ <label>Zone 8</label>
+ <description>The Controls for Zone 8</description>
+ </channel-group>
+ <channel-group id="zone9" typeId="zone">
+ <label>Zone 9</label>
+ <description>The Controls for Zone 9</description>
+ </channel-group>
+ <channel-group id="zone10" typeId="zone">
+ <label>Zone 10</label>
+ <description>The Controls for Zone 10</description>
+ </channel-group>
+ <channel-group id="zone11" typeId="zone">
+ <label>Zone 11</label>
+ <description>The Controls for Zone 11</description>
+ </channel-group>
+ <channel-group id="zone12" typeId="zone">
+ <label>Zone 12</label>
+ <description>The Controls for Zone 12</description>
+ </channel-group>
+ <channel-group id="zone13" typeId="zone">
+ <label>Zone 13</label>
+ <description>The Controls for Zone 13</description>
+ </channel-group>
+ <channel-group id="zone14" typeId="zone">
+ <label>Zone 14</label>
+ <description>The Controls for Zone 14</description>
+ </channel-group>
+ <channel-group id="zone15" typeId="zone">
+ <label>Zone 15</label>
+ <description>The Controls for Zone 15</description>
+ </channel-group>
+ <channel-group id="zone16" typeId="zone">
+ <label>Zone 16</label>
+ <description>The Controls for Zone 16</description>
+ </channel-group>
+ <channel-group id="zone17" typeId="zone">
+ <label>Zone 17</label>
+ <description>The Controls for Zone 17</description>
+ </channel-group>
+ <channel-group id="zone18" typeId="zone">
+ <label>Zone 18</label>
+ <description>The Controls for Zone 18</description>
+ </channel-group>
+ </channel-groups>
+
+ <config-description>
+ <parameter name="serialPort" type="text" required="false">
+ <context>serial-port</context>
+ <limitToOptions>false</limitToOptions>
+ <label>Serial Port</label>
+ <description>Serial Port to Use for Connecting to the Monoprice Amplifier</description>
+ </parameter>
+ <parameter name="host" type="text" required="false">
+ <context>network-address</context>
+ <label>Address</label>
+ <description>Host Name or IP Address of the Monoprice Amplifier or Serial over IP device</description>
+ </parameter>
+ <parameter name="port" type="integer" min="1" max="65535" required="false">
+ <label>Port</label>
+ <description>Communication Port for Serial over IP connection to the Monoprice Amplifier (8080 for amps with
+ built-in Serial over IP)</description>
+ </parameter>
+ <parameter name="numZones" type="integer" min="1" max="18" required="true">
+ <label>Number of Zones</label>
+ <description>Number of Zones on the Amplifier to Utilize in the Binding (Up to 18 Zones With 3 Amplifiers Connected
+ Together)</description>
+ <default>6</default>
+ </parameter>
+ <parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
+ <label>Polling Interval</label>
+ <description>Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 15)</description>
+ <default>15</default>
+ </parameter>
+ <parameter name="ignoreZones" type="text" required="false">
+ <label>Ignore Zones</label>
+ <description>(Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off)
+ Commands (ie: 1,6,10)</description>
+ </parameter>
+ <parameter name="initialAllVolume" type="integer" min="1" max="30" required="false">
+ <label>Initial All Volume</label>
+ <description>When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent
+ Excessive Blaring of Sound ;)</description>
+ <default>10</default>
+ </parameter>
+ <parameter name="inputLabel1" type="text" required="false">
+ <label>Source 1 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 1</default>
+ </parameter>
+ <parameter name="inputLabel2" type="text" required="false">
+ <label>Source 2 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 2</default>
+ </parameter>
+ <parameter name="inputLabel3" type="text" required="false">
+ <label>Source 3 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 3</default>
+ </parameter>
+ <parameter name="inputLabel4" type="text" required="false">
+ <label>Source 4 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 4</default>
+ </parameter>
+ <parameter name="inputLabel5" type="text" required="false">
+ <label>Source 5 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 5</default>
+ </parameter>
+ <parameter name="inputLabel6" type="text" required="false">
+ <label>Source 6 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 6</default>
+ </parameter>
+ <parameter name="disableKeypadPolling" type="boolean" required="false">
+ <label>Disable Keypad Polling</label>
+ <description>If physical keypads are not used, this option will disable polling the amplifier for zone updates</description>
+ <default>false</default>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <channel-group-type id="all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ <channels>
+ <channel id="allpower" typeId="allpower"/>
+ <channel id="allsource" typeId="source"/>
+ <channel id="allvolume" typeId="system.volume"/>
+ <channel id="allmute" typeId="system.mute"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-group-type id="zone">
+ <label>Zone Controls</label>
+ <description>The Controls for the Zone</description>
+ <channels>
+ <channel id="power" typeId="system.power"/>
+ <channel id="source" typeId="source"/>
+ <channel id="volume" typeId="system.volume"/>
+ <channel id="mute" typeId="system.mute"/>
+ <channel id="treble" typeId="treble"/>
+ <channel id="bass" typeId="bass"/>
+ <channel id="balance" typeId="balance"/>
+ <channel id="dnd" typeId="dnd"/>
+ <channel id="page" typeId="page"/>
+ <channel id="keypad" typeId="keypad"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-type id="allpower">
+ <item-type>Switch</item-type>
+ <label>All On</label>
+ <description>Turn All Zones On or Off</description>
+ </channel-type>
+
+ <channel-type id="source">
+ <item-type>Number</item-type>
+ <label>Source Input</label>
+ <description>Select the Source Input</description>
+ <state min="1" max="6"/>
+ </channel-type>
+
+ <channel-type id="treble">
+ <item-type>Number</item-type>
+ <label>Treble Adjustment</label>
+ <description>Adjust the Treble</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="bass">
+ <item-type>Number</item-type>
+ <label>Bass Adjustment</label>
+ <description>Adjust the Bass</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="balance">
+ <item-type>Number</item-type>
+ <label>Balance Adjustment</label>
+ <description>Adjust the Balance</description>
+ <state min="-10" max="10" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="dnd">
+ <item-type>Switch</item-type>
+ <label>Do Not Disturb</label>
+ <description>Controls if the Zone Should Ignore an Incoming Audio Page</description>
+ </channel-type>
+
+ <channel-type id="page">
+ <item-type>Contact</item-type>
+ <label>Page Active</label>
+ <description>Indicates if the Page Mode is Active for This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Inactive</option>
+ <option value="OPEN">Active</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="keypad">
+ <item-type>Contact</item-type>
+ <label>Keypad Connected</label>
+ <description>Indicates if a Physical Keypad is Attached to This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Disconnected</option>
+ <option value="OPEN">Connected</option>
+ </options>
+ </state>
+ </channel-type>
+
+</thing:thing-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="monopriceaudio"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- Monoprice 31028/PAM1270 Amplifier Thing -->
+ <thing-type id="monoprice70">
+ <label>Monoprice 31028 70V Amplifier</label>
+ <description>
+ A Multi-zone Whole House Amplifier System
+ </description>
+
+ <channel-groups>
+ <channel-group id="all" typeId="monoprice70-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ </channel-group>
+ <channel-group id="zone1" typeId="monoprice70-zone">
+ <label>Zone 1</label>
+ <description>The Controls for Zone 1</description>
+ </channel-group>
+ <channel-group id="zone2" typeId="monoprice70-zone">
+ <label>Zone 2</label>
+ <description>The Controls for Zone 2</description>
+ </channel-group>
+ <channel-group id="zone3" typeId="monoprice70-zone">
+ <label>Zone 3</label>
+ <description>The Controls for Zone 3</description>
+ </channel-group>
+ <channel-group id="zone4" typeId="monoprice70-zone">
+ <label>Zone 4</label>
+ <description>The Controls for Zone 4</description>
+ </channel-group>
+ <channel-group id="zone5" typeId="monoprice70-zone">
+ <label>Zone 5</label>
+ <description>The Controls for Zone 5</description>
+ </channel-group>
+ <channel-group id="zone6" typeId="monoprice70-zone">
+ <label>Zone 6</label>
+ <description>The Controls for Zone 6</description>
+ </channel-group>
+ </channel-groups>
+
+ <config-description>
+ <parameter name="serialPort" type="text" required="false">
+ <context>serial-port</context>
+ <limitToOptions>false</limitToOptions>
+ <label>Serial Port</label>
+ <description>Serial Port to Use for Connecting to the Monoprice Amplifier</description>
+ </parameter>
+ <parameter name="host" type="text" required="false">
+ <context>network-address</context>
+ <label>Address</label>
+ <description>Host Name or IP Address of the Machine Connected to the Monoprice Amplifier (Serial over IP)</description>
+ </parameter>
+ <parameter name="port" type="integer" min="1" max="65535" required="false">
+ <label>Port</label>
+ <description>Communication Port for Serial over IP connection to the Monoprice Amplifier</description>
+ </parameter>
+ <parameter name="numZones" type="integer" min="1" max="6" required="true">
+ <label>Number of Zones</label>
+ <description>Number of Zones on the Amplifier to Utilize in the Binding</description>
+ <default>6</default>
+ </parameter>
+ <parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
+ <label>Polling Interval</label>
+ <description>Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 30)</description>
+ <default>30</default>
+ </parameter>
+ <parameter name="ignoreZones" type="text" required="false">
+ <label>Ignore Zones</label>
+ <description>(Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off)
+ Commands (ie: 1,5,6)</description>
+ </parameter>
+ <parameter name="initialAllVolume" type="integer" min="1" max="30" required="false">
+ <label>Initial All Volume</label>
+ <description>When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent
+ Excessive Blaring of Sound ;)</description>
+ <default>10</default>
+ </parameter>
+ <parameter name="inputLabel1" type="text" required="false">
+ <label>Source 0 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 0 - Bus</default>
+ </parameter>
+ <parameter name="inputLabel2" type="text" required="false">
+ <label>Source 1 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 1 - Line</default>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <channel-group-type id="monoprice70-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ <channels>
+ <channel id="allpower" typeId="monoprice70-allpower"/>
+ <channel id="allsource" typeId="monoprice70-source"/>
+ <channel id="allvolume" typeId="system.volume"/>
+ <channel id="allmute" typeId="system.mute"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-group-type id="monoprice70-zone">
+ <label>Zone Controls</label>
+ <description>The Controls for the Zone</description>
+ <channels>
+ <channel id="power" typeId="system.power"/>
+ <channel id="source" typeId="monoprice70-source"/>
+ <channel id="volume" typeId="system.volume"/>
+ <channel id="mute" typeId="system.mute"/>
+ <channel id="treble" typeId="monoprice70-treble"/>
+ <channel id="bass" typeId="monoprice70-bass"/>
+ <channel id="balance" typeId="monoprice70-balance"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-type id="monoprice70-allpower">
+ <item-type>Switch</item-type>
+ <label>All On</label>
+ <description>Turn All Zones On or Off</description>
+ </channel-type>
+
+ <channel-type id="monoprice70-source">
+ <item-type>Number</item-type>
+ <label>Source Input</label>
+ <description>Select the Source Input</description>
+ <state min="0" max="1"/>
+ </channel-type>
+
+ <channel-type id="monoprice70-treble">
+ <item-type>Number</item-type>
+ <label>Treble Adjustment</label>
+ <description>Adjust the Treble</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="monoprice70-bass">
+ <item-type>Number</item-type>
+ <label>Bass Adjustment</label>
+ <description>Adjust the Bass</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="monoprice70-balance">
+ <item-type>Number</item-type>
+ <label>Balance Adjustment</label>
+ <description>Adjust the Balance</description>
+ <state min="-32" max="31" step="1" pattern="%d"/>
+ </channel-type>
+
+</thing:thing-descriptions>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="monopriceaudio"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+ <!-- Xantech 8x8 Amplifier Thing -->
+ <thing-type id="xantech">
+ <label>Xantech 8x8 Amplifier</label>
+ <description>
+ A Multi-zone Whole House Amplifier System
+ </description>
+
+ <channel-groups>
+ <channel-group id="all" typeId="xantech-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ </channel-group>
+ <channel-group id="zone1" typeId="xantech-zone">
+ <label>Zone 1</label>
+ <description>The Controls for Zone 1</description>
+ </channel-group>
+ <channel-group id="zone2" typeId="xantech-zone">
+ <label>Zone 2</label>
+ <description>The Controls for Zone 2</description>
+ </channel-group>
+ <channel-group id="zone3" typeId="xantech-zone">
+ <label>Zone 3</label>
+ <description>The Controls for Zone 3</description>
+ </channel-group>
+ <channel-group id="zone4" typeId="xantech-zone">
+ <label>Zone 4</label>
+ <description>The Controls for Zone 4</description>
+ </channel-group>
+ <channel-group id="zone5" typeId="xantech-zone">
+ <label>Zone 5</label>
+ <description>The Controls for Zone 5</description>
+ </channel-group>
+ <channel-group id="zone6" typeId="xantech-zone">
+ <label>Zone 6</label>
+ <description>The Controls for Zone 6</description>
+ </channel-group>
+ <channel-group id="zone7" typeId="xantech-zone">
+ <label>Zone 7</label>
+ <description>The Controls for Zone 7</description>
+ </channel-group>
+ <channel-group id="zone8" typeId="xantech-zone">
+ <label>Zone 8</label>
+ <description>The Controls for Zone 8</description>
+ </channel-group>
+ <channel-group id="zone9" typeId="xantech-zone">
+ <label>Zone 9</label>
+ <description>The Controls for Zone 9</description>
+ </channel-group>
+ <channel-group id="zone10" typeId="xantech-zone">
+ <label>Zone 10</label>
+ <description>The Controls for Zone 10</description>
+ </channel-group>
+ <channel-group id="zone11" typeId="xantech-zone">
+ <label>Zone 11</label>
+ <description>The Controls for Zone 11</description>
+ </channel-group>
+ <channel-group id="zone12" typeId="xantech-zone">
+ <label>Zone 12</label>
+ <description>The Controls for Zone 12</description>
+ </channel-group>
+ <channel-group id="zone13" typeId="xantech-zone">
+ <label>Zone 13</label>
+ <description>The Controls for Zone 13</description>
+ </channel-group>
+ <channel-group id="zone14" typeId="xantech-zone">
+ <label>Zone 14</label>
+ <description>The Controls for Zone 14</description>
+ </channel-group>
+ <channel-group id="zone15" typeId="xantech-zone">
+ <label>Zone 15</label>
+ <description>The Controls for Zone 15</description>
+ </channel-group>
+ <channel-group id="zone16" typeId="xantech-zone">
+ <label>Zone 16</label>
+ <description>The Controls for Zone 16</description>
+ </channel-group>
+ </channel-groups>
+
+ <config-description>
+ <parameter name="serialPort" type="text" required="false">
+ <context>serial-port</context>
+ <limitToOptions>false</limitToOptions>
+ <label>Serial Port</label>
+ <description>Serial Port to Use for Connecting to the Xantech Amplifier</description>
+ </parameter>
+ <parameter name="host" type="text" required="false">
+ <context>network-address</context>
+ <label>Address</label>
+ <description>Host Name or IP Address of the Machine Connected to the Xantech Amplifier (Serial over IP)</description>
+ </parameter>
+ <parameter name="port" type="integer" min="1" max="65535" required="false">
+ <label>Port</label>
+ <description>Communication Port for Serial over IP connection to the Xantech Amplifier</description>
+ </parameter>
+ <parameter name="numZones" type="integer" min="1" max="16" required="true">
+ <label>Number of Zones</label>
+ <description>Number of Zones on the Amplifier to Utilize in the Binding (Up to 16 Zones With 2 Amplifiers Connected
+ Together)</description>
+ <default>8</default>
+ </parameter>
+ <parameter name="pollingInterval" type="integer" min="5" max="60" unit="s" required="false">
+ <label>Polling Interval</label>
+ <description>Configures How Often to Poll the Amplifier to Check for Zone Updates (5-60; Default 30)</description>
+ <default>30</default>
+ </parameter>
+ <parameter name="ignoreZones" type="text" required="false">
+ <label>Ignore Zones</label>
+ <description>(Optional) A Comma Seperated List of Zone Numbers That Will Ignore the 'All Zone' (Except All Off)
+ Commands (ie: 1,6,10)</description>
+ </parameter>
+ <parameter name="initialAllVolume" type="integer" min="1" max="30" required="false">
+ <label>Initial All Volume</label>
+ <description>When 'All' Zones Are Activated, the Volume Will Reset to This Value (1-30; default 10) to Prevent
+ Excessive Blaring of Sound ;)</description>
+ <default>10</default>
+ </parameter>
+ <parameter name="inputLabel1" type="text" required="false">
+ <label>Source 1 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 1</default>
+ </parameter>
+ <parameter name="inputLabel2" type="text" required="false">
+ <label>Source 2 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 2</default>
+ </parameter>
+ <parameter name="inputLabel3" type="text" required="false">
+ <label>Source 3 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 3</default>
+ </parameter>
+ <parameter name="inputLabel4" type="text" required="false">
+ <label>Source 4 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 4</default>
+ </parameter>
+ <parameter name="inputLabel5" type="text" required="false">
+ <label>Source 5 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 5</default>
+ </parameter>
+ <parameter name="inputLabel6" type="text" required="false">
+ <label>Source 6 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 6</default>
+ </parameter>
+ <parameter name="inputLabel7" type="text" required="false">
+ <label>Source 7 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 7</default>
+ </parameter>
+ <parameter name="inputLabel8" type="text" required="false">
+ <label>Source 8 Input Label</label>
+ <description>Friendly Name for the Input Source to Be Displayed in the UI (ie: Chromecast, Radio, CD, etc.)</description>
+ <default>Source 8</default>
+ </parameter>
+ <parameter name="disableKeypadPolling" type="boolean" required="false">
+ <label>Disable Keypad Polling</label>
+ <description>If physical keypads are not used, this option will disable polling the amplifier for zone updates</description>
+ <default>false</default>
+ </parameter>
+ </config-description>
+ </thing-type>
+
+ <channel-group-type id="xantech-all">
+ <label>All Zones</label>
+ <description>Control All Zones Simultaneously</description>
+ <channels>
+ <channel id="allpower" typeId="xantech-allpower"/>
+ <channel id="allsource" typeId="xantech-source"/>
+ <channel id="allvolume" typeId="system.volume"/>
+ <channel id="allmute" typeId="system.mute"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-group-type id="xantech-zone">
+ <label>Zone Controls</label>
+ <description>The Controls for the Zone</description>
+ <channels>
+ <channel id="power" typeId="system.power"/>
+ <channel id="source" typeId="xantech-source"/>
+ <channel id="volume" typeId="system.volume"/>
+ <channel id="mute" typeId="system.mute"/>
+ <channel id="treble" typeId="xantech-treble"/>
+ <channel id="bass" typeId="xantech-bass"/>
+ <channel id="balance" typeId="xantech-balance"/>
+ <channel id="page" typeId="xantech-page"/>
+ <channel id="keypad" typeId="xantech-keypad"/>
+ </channels>
+ </channel-group-type>
+
+ <channel-type id="xantech-allpower">
+ <item-type>Switch</item-type>
+ <label>All On</label>
+ <description>Turn All Zones On or Off</description>
+ </channel-type>
+
+ <channel-type id="xantech-source">
+ <item-type>Number</item-type>
+ <label>Source Input</label>
+ <description>Select the Source Input</description>
+ <state min="1" max="8"/>
+ </channel-type>
+
+ <channel-type id="xantech-treble">
+ <item-type>Number</item-type>
+ <label>Treble Adjustment</label>
+ <description>Adjust the Treble</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="xantech-bass">
+ <item-type>Number</item-type>
+ <label>Bass Adjustment</label>
+ <description>Adjust the Bass</description>
+ <state min="-7" max="7" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="xantech-balance">
+ <item-type>Number</item-type>
+ <label>Balance Adjustment</label>
+ <description>Adjust the Balance</description>
+ <state min="-32" max="31" step="1" pattern="%d"/>
+ </channel-type>
+
+ <channel-type id="xantech-page">
+ <item-type>Contact</item-type>
+ <label>Page Active</label>
+ <description>Indicates if the Page Mode is Active for This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Inactive</option>
+ <option value="OPEN">Active</option>
+ </options>
+ </state>
+ </channel-type>
+
+ <channel-type id="xantech-keypad">
+ <item-type>Contact</item-type>
+ <label>Keypad Connected</label>
+ <description>Indicates if a Physical Keypad is Attached to This Zone</description>
+ <state readOnly="true">
+ <options>
+ <option value="CLOSED">Disconnected</option>
+ <option value="OPEN">Connected</option>
+ </options>
+ </state>
+ </channel-type>
+
+</thing:thing-descriptions>