# Atlona Binding
This binding integrates Atlona AT-UHD-PRO3 HdBaseT matrix switches [Atlona AT-UHD-PRO3 HdBaseT matrix switches](https://www.atlona.com) into your openHAB installation.
+The older HD model 6x6 matrix [AT-PRO3HD66M] (https://atlona.com/product/at-pro3hd66m/) is also supported.
## Supported Things
| pro3-66m | Thing | The AT-UHD-PRO3-66M 6x6 HDBaseT matrix. |
| pro3-88m | Thing | The AT-UHD-PRO3-88M 8x8 HDBaseT matrix. |
| pro3-1616m | Thing | The AT-UHD-PRO3-1616M 16x16 HDBaseT matrix. |
+| pro3-hd66m | Thing | The AT-PRO3HD66M 6x6 HDBaseT matrix. |
## Discovery
| pro3-1616m | volume11#volumemute | Switch | RW | Mutes/Unmutes audio port #11 |
| pro3-1616m | volume12#volume | Number | RW | Sets the volume of audio port #12 to the specified decibel level (between -79db to +15db) |
| pro3-1616m | volume12#volumemute | Switch | RW | Mutes/Unmutes audio port #12 |
+| | | | | |
+| pro3-hd66m | primary#power | Switch | RW | Matrix Power Switch |
+| pro3-hd66m | primary#panellock | Switch | RW | Sets the front panel locked or unlocked |
+| pro3-hd66m | primary#irenable | Switch | RW | Enables/Disabled the front panel IR |
+| pro3-hd66m | primary#presetcmd | Switch | W | Sends a preset command ('saveX', 'recallX', 'clearX') - see notes below |
+| pro3-hd66m | primary#matrixcmd | Switch | W | Sends a matrix command ('resetmatrix', 'resetports', 'allportsX') - see notes below |
+| pro3-hd66m | port1#portoutput | Number | RW | Sets output port #1 to the specified input port |
+| pro3-hd66m | port2#portoutput | Number | RW | Sets output port #2 to the specified input port |
+| pro3-hd66m | port3#portoutput | Number | RW | Sets output port #3 to the specified input port |
+| pro3-hd66m | port4#portoutput | Number | RW | Sets output port #4 to the specified input port |
+| pro3-hd66m | port5#portoutput | Number | RW | Sets output port #5 to the specified input port |
+| pro3-hd66m | port5#portoutput | Number | RW | Sets output port #6 to the specified input port |
### presetcmd
* The {@link AtlonaBinding} class defines common constants, which are used across the whole binding.
*
* @author Tim Roberts - Initial contribution
+ * @author Michael Lobstein - Add support for AT-PRO3HD66M
*/
@NonNullByDefault
public class AtlonaBindingConstants {
* Thing ID for the AT-UHD-PRO3-1616m (16x16 hdbaset matrix)
*/
public static final ThingTypeUID THING_TYPE_PRO3_1616M = new ThingTypeUID(BINDING_ID, "pro3-1616m");
+
+ /**
+ * Thing ID for the AT-PRO3HD66M (HD 6x6 hdbaset matrix)
+ */
+ public static final ThingTypeUID THING_TYPE_PRO3HD_66M = new ThingTypeUID(BINDING_ID, "pro3-hd66m");
}
import java.util.Collections;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.openhab.binding.atlona.internal.pro3.AtlonaPro3Capabilities;
import org.openhab.binding.atlona.internal.pro3.AtlonaPro3Handler;
* handlers.
*
* @author Tim Roberts - Initial contribution
+ * @author Michael Lobstein - Add support for AT-PRO3HD66M
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.atlona")
public class AtlonaHandlerFactory extends BaseThingHandlerFactory {
/**
* The set of supported Atlona products
*/
- private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
- Stream.of(THING_TYPE_PRO3_44M, THING_TYPE_PRO3_66M, THING_TYPE_PRO3_88M, THING_TYPE_PRO3_1616M)
- .collect(Collectors.toSet()));
+ private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PRO3_44M, THING_TYPE_PRO3_66M,
+ THING_TYPE_PRO3_88M, THING_TYPE_PRO3_1616M, THING_TYPE_PRO3HD_66M);
/**
* {@inheritDoc}
*/
@Override
protected ThingHandler createHandler(Thing thing) {
- if (thing == null) {
- logger.error("createHandler was given a null thing!");
- return null;
- }
-
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_PRO3_44M)) {
- return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(5, 3, Collections.singleton(5)));
+ return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(5, 3, Collections.singleton(5), true));
}
if (thingTypeUID.equals(THING_TYPE_PRO3_66M)) {
- return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(8, 4,
- Collections.unmodifiableSet(Stream.of(6, 8).collect(Collectors.toSet()))));
+ return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(8, 4, Set.of(6, 8), true));
}
if (thingTypeUID.equals(THING_TYPE_PRO3_88M)) {
- return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(10, 6,
- Collections.unmodifiableSet(Stream.of(8, 10).collect(Collectors.toSet()))));
+ return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(10, 6, Set.of(8, 10), true));
}
if (thingTypeUID.equals(THING_TYPE_PRO3_1616M)) {
- return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(5, 3,
- Collections.unmodifiableSet(Stream.of(17, 18, 19, 20).collect(Collectors.toSet()))));
+ return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(5, 3, Set.of(17, 18, 19, 20), true));
+ }
+
+ if (thingTypeUID.equals(THING_TYPE_PRO3HD_66M)) {
+ return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(0, 0, Set.of(1, 2, 3, 4, 5, 6), false));
}
logger.warn("Unknown binding: {}: {}", thingTypeUID.getId(), thingTypeUID.getBindingId());
* @return the {@link State} for the propertyName or null if not found
*/
public State getState(String propertyName) {
- // TODO Auto-generated method stub
return state.get(propertyName);
}
}
* powered, the number of audio ports there are and which (output) ports are HDMI ports.
*
* @author Tim Roberts - Initial contribution
+ * @author Michael Lobstein - Add support for AT-PRO3HD66M
*/
public class AtlonaPro3Capabilities extends AtlonaCapabilities {
*/
private final Set<Integer> hdmiPorts;
+ /**
+ * Indicates if the thing is a 4K/UHD model vs an older HD model
+ */
+ private final boolean isUHDModel;
+
/**
* Constructs the capabilities from the parms
*
* @param nbrAudioPorts a greater than 0 number of audio ports
* @param hdmiPorts a non-null, non-empty set of hdmi ports
*/
- public AtlonaPro3Capabilities(int nbrPowerPorts, int nbrAudioPorts, Set<Integer> hdmiPorts) {
+ public AtlonaPro3Capabilities(int nbrPowerPorts, int nbrAudioPorts, Set<Integer> hdmiPorts, boolean isUHDModel) {
super();
- if (nbrPowerPorts < 1) {
- throw new IllegalArgumentException("nbrPowerPorts must be greater than 0");
- }
-
- if (nbrAudioPorts < 1) {
- throw new IllegalArgumentException("nbrAudioPorts must be greater than 0");
- }
-
if (hdmiPorts == null) {
throw new IllegalArgumentException("hdmiPorts cannot be null");
}
this.nbrPowerPorts = nbrPowerPorts;
this.nbrAudioPorts = nbrAudioPorts;
this.hdmiPorts = Collections.unmodifiableSet(new HashSet<>(hdmiPorts));
+ this.isUHDModel = isUHDModel;
}
/**
Set<Integer> getHdmiPorts() {
return hdmiPorts;
}
+
+ /**
+ * Returns a flag indicating the model type
+ *
+ * @return boolean true if the thing is a 4K/UHD model
+ */
+ boolean isUHDModel() {
+ return isUHDModel;
+ }
}
session.clearListeners();
session.connect();
- response = atlonaHandler.login();
+ if (this.getCapabilities().isUHDModel()) {
+ response = atlonaHandler.loginUHD();
+ } else {
+ response = atlonaHandler.loginHD();
+ }
+
if (response == null) {
final AtlonaPro3Config config = getAtlonaConfig();
if (config != null) {
* web GUI, front panel keystrokes, etc]).
*
* @author Tim Roberts - Initial contribution
+ * @author Michael Lobstein - Add support for AT-PRO3HD66M
*/
class AtlonaPro3PortocolHandler {
private final Logger logger = LoggerFactory.getLogger(AtlonaPro3PortocolHandler.class);
private final Pattern broadCastPattern = Pattern.compile("Broadcast (\\w+)");
private static final String RSP_MATRIX_RESET = "Mreset";
+ // Constants added to support the HD models
+ private static final String RSP_WELCOME = "Welcome to TELNET";
+ private static final String RSP_LOGIN_PLEASE = "Login Please";
+ private static final String RSP_USERNAME = "Username";
+ private static final String RSP_TRY_AGAIN = "Please Try Again";
+ private final Pattern versionHdPattern = Pattern.compile("V(.*)");
+ private final Pattern typeHdPattern = Pattern.compile("AT-PRO3HD(\\d+)M");
+
// ------------------------------------------------------------------------------------------------
// The following isn't part of the atlona protocol and is generated by us
private static final String CMD_PING = "ping";
* @return a null if logged in successfully (or if switch didn't require login). Non-null if an exception occurred.
* @throws IOException an IO exception occurred during login
*/
- String login() throws Exception {
+ String loginUHD() throws Exception {
logger.debug("Logging into atlona switch");
// Void to make sure we retrieve them
modelType = null;
try {
response = callback.getResponse();
if (!response.equals("")) {
- logger.info("Altona protocol violation - didn't start with an inital empty response: '{}'", response);
+ logger.debug("Altona protocol violation - didn't start with an inital empty response: '{}'", response);
}
} catch (Exception e) {
// ignore - may not having given us an initial ""
return null;
}
- // We should have been presented wit a new "\r\nLogin: "
+ // We should have been presented with a new "\r\nLogin: "
response = callback.getResponse();
if (!response.equals("")) {
- logger.info("Altona protocol violation - didn't start with an inital empty response: '{}'", response);
+ logger.debug("Altona protocol violation - didn't start with an inital empty response: '{}'", response);
}
// Get the new "Login: " prompt response
// First make sure we had an empty response (the "\r\n" part)
if (!response.equals("")) {
- logger.info("Altona protocol violation - not an empty response after password: '{}'", response);
+ logger.debug("Altona protocol violation - not an empty response after password: '{}'", response);
}
// Now send an invalid command
return "Password was invalid - please check your atlona setup";
}
+ /**
+ * Attempts to log into the older HD model switches using a slightly different protocol
+ *
+ * @return a null if logged in successfully (or if switch didn't require login). Non-null if an exception occurred.
+ * @throws IOException an IO exception occurred during login
+ */
+ String loginHD() throws Exception {
+ logger.debug("Logging into atlona switch");
+ // Void to make sure we retrieve them
+ modelType = null;
+ version = null;
+
+ NoDispatchingCallback callback = new NoDispatchingCallback();
+ session.addListener(callback);
+
+ // Burn the initial (empty) return
+ String response;
+ try {
+ response = callback.getResponse();
+ if (!response.equals("")) {
+ logger.debug("Altona protocol violation - didn't start with an inital empty response: '{}'", response);
+ }
+ } catch (Exception e) {
+ // ignore - may not having given us an initial ""
+ }
+
+ response = callback.getResponse();
+ if (response.startsWith(RSP_WELCOME)) {
+ logger.debug("Altona AT-PRO3HD66M didn't require a login");
+ postLogin();
+ return null;
+ } else {
+ if (!response.startsWith(RSP_LOGIN_PLEASE)) {
+ logger.debug("Altona protocol violation - didn't start with login prompt '{}'", response);
+ }
+ // Since we were not logged in automatically, a user name is required from the configuration
+ if (config.getUserName() == null || config.getUserName().trim().length() == 0) {
+ return "Atlona PRO3 has enabled Telnet/IP Login but no username was provided in the configuration.";
+ }
+
+ // Make sure we have a password too
+ if (config.getPassword() == null || config.getPassword().trim().length() == 0) {
+ return "Atlona PRO3 has enabled Telnet/IP Login but no password was provided in the configuration.";
+ }
+
+ // Check for an empty response after the login prompt (the "\r\n" part)
+ response = callback.getResponse();
+ if (!response.equals("")) {
+ logger.debug("Altona protocol violation - not an empty response after password: '{}'", response);
+ }
+
+ // Send the username and wait for a ": " response
+ session.sendCommand(config.getUserName());
+
+ // We should have gotten the username response
+ response = callback.getResponse();
+ if (!response.startsWith(RSP_USERNAME)) {
+ logger.debug("Altona protocol violation - invalid response to username: '{}'", response);
+ }
+
+ // Send the password
+ try {
+ session.sendCommand(config.getPassword());
+ response = callback.getResponse();
+ } catch (Exception e) {
+ return "Password was invalid - please check your atlona setup";
+ }
+
+ if (response.startsWith(RSP_TRY_AGAIN)) {
+ return "Username " + config.getUserName() + " is not a valid user on the atlona";
+ }
+
+ if (response.startsWith(RSP_PASSWORD)) {
+ // After the correct password is sent, several empty responses are sent before the welcome message
+ for (int i = 0; i < 8; i++) {
+ response = callback.getResponse();
+
+ // If we get a welcome message, login was successful
+ if (response.startsWith(RSP_WELCOME)) {
+ postLogin();
+ return null;
+ }
+ }
+ }
+ }
+ return "Authentication failed - please check your atlona setup";
+ }
+
/**
* Post successful login stuff - mark us online and refresh from the switch
*/
session.addListener(new NormalResponseCallback());
callback.statusChanged(ThingStatus.ONLINE, ThingStatusDetail.NONE, null);
- // Set broadcast to on to receive notifications when
- // routing changes (via the webpage, or presets or IR, etc)
- sendCommand(CMD_BROADCAST_ON);
+ if (capabilities.isUHDModel()) {
+ // Set broadcast to on to receive notifications when
+ // routing changes (via the webpage, or presets or IR, etc)
+ sendCommand(CMD_BROADCAST_ON);
+ }
// setup the most likely state of these switches (there is no protocol to get them)
refreshAll();
* Pings the server with an (invalid) ping command to keep the connection alive
*/
void ping() {
- sendCommand(CMD_PING);
+ if (capabilities.isUHDModel()) {
+ sendCommand(CMD_PING);
+ } else {
+ // the HD model does not reflect the invalid command string back in the response for us to match later
+ sendCommand(CMD_VERSION);
+ }
}
/**
}
refreshPower();
- refreshAllPortStatuses();
+ if (capabilities.isUHDModel()) {
+ refreshAllPortStatuses();
+ }
final int nbrPowerPorts = capabilities.getNbrPowerPorts();
for (int x = 1; x <= nbrPowerPorts; x++) {
* @param resp the possibly null, possibly empty actual response
*/
private void handleCommandFailure(String resp) {
- logger.info("{}", resp);
+ logger.debug("{}", resp);
}
/**
return;
}
+ m = versionHdPattern.matcher(response);
+ if (m.matches()) {
+ handleVersionResponse(m, response);
+ return;
+ }
+
m = typePattern.matcher(response);
if (m.matches()) {
handleTypeResponse(m, response);
return;
}
+ m = typeHdPattern.matcher(response);
+ if (m.matches()) {
+ handleTypeResponse(m, response);
+ return;
+ }
+
m = portPowerPattern.matcher(response);
if (m.matches()) {
handlePortPowerResponse(m, response);
return;
}
- logger.info("Unhandled response: {}", response);
+ logger.debug("Unhandled response: {}", response);
}
@Override
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<config-description:config-descriptions
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
+ xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
+
+ <config-description uri="thing-type:altona:hdmimatrix">
+ <parameter name="ipAddress" type="text">
+ <context>network-address</context>
+ <label>IP or Host Name</label>
+ <description>IP or Host name of Atlona Matrix Switch</description>
+ <required>true</required>
+ </parameter>
+ <parameter name="userName" type="text">
+ <label>User Name</label>
+ <description>User Name to login with if Telnet Login is on</description>
+ <required>false</required>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="password" type="text">
+ <context>password</context>
+ <label>Password</label>
+ <description>Password to login with if Telnet Login is on</description>
+ <required>false</required>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="polling" type="integer" min="10" max="9999" unit="s" required="false">
+ <label>Polling Interval</label>
+ <description>Interval (in seconds) to poll the actual state of the Matrix</description>
+ <default>600</default>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="ping" type="integer" min="10" max="600" unit="s" required="false">
+ <label>Ping Interval</label>
+ <description>Ping Interval (in seconds) to keep the connection alive</description>
+ <default>30</default>
+ <advanced>true</advanced>
+ </parameter>
+ <parameter name="retryPolling" type="integer" min="10" max="60" unit="s" required="false">
+ <label>Polling Interval to Try to Reconnect</label>
+ <description>Interval (in seconds) to try to (re)connect to the Matrix</description>
+ <default>10</default>
+ <advanced>true</advanced>
+ </parameter>
+ </config-description>
+</config-description:config-descriptions>
</channel-group>
</channel-groups>
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>IP or Host Name</label>
- <description>IP or Host name of Atlona Pro3 44M Switch</description>
- <required>true</required>
- </parameter>
- <parameter name="userName" type="text">
- <label>UserName to Login With</label>
- <description>User Name to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="password" type="text">
- <context>password</context>
- <label>Password to Login With</label>
- <description>Password to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="polling" type="integer">
- <label>Polling Interval</label>
- <description>Interval (in seconds) to poll the actual state of the Matrix</description>
- <default>600</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="ping" type="integer">
- <label>Pint Interval</label>
- <description>Ping Interval (in seconds) to keep the connection alive</description>
- <default>30</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="retryPolling" type="integer">
- <label>Polling Interval to Try to Reconnect</label>
- <description>Interval (in seconds) to try to (re)connect to the matrix</description>
- <default>10</default>
- <advanced>true</advanced>
- </parameter>
- </config-description>
+ <config-description-ref uri="thing-type:altona:hdmimatrix"/>
</thing-type>
<!-- AT-UHD-PRO3-66M -->
</channel-group>
</channel-groups>
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>IP or Host Name</label>
- <description>IP or Host name of Atlona Pro3 66M Switch</description>
- <required>true</required>
- </parameter>
- <parameter name="userName" type="text">
- <label>UserName to Login With</label>
- <description>User Name to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="password" type="text">
- <context>password</context>
- <label>Password to Login With</label>
- <description>Password to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="polling" type="integer">
- <label>Polling Interval</label>
- <description>Interval (in seconds) to poll the actual state of the Matrix</description>
- <default>600</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="ping" type="integer">
- <label>Pint Interval</label>
- <description>Ping Interval (in seconds) to keep the connection alive</description>
- <default>30</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="retryPolling" type="integer">
- <label>Polling Interval to Try to Reconnect</label>
- <description>Interval (in seconds) to try to (re)connect to the matrix</description>
- <default>10</default>
- <advanced>true</advanced>
- </parameter>
- </config-description>
+ <config-description-ref uri="thing-type:altona:hdmimatrix"/>
</thing-type>
<!-- AT-UHD-PRO3-88M -->
</channel-group>
</channel-groups>
- <config-description>
- <parameter name="ipAddress" type="text" required="true">
- <context>network-address</context>
- <label>IP or Host Name</label>
- <description>IP or Host name of Atlona Switch</description>
- <default></default>
- </parameter>
- <parameter name="userName" type="text">
- <label>UserName</label>
- <description>User Name to use (if Telnet Login is ON)</description>
- <default></default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="password" type="text">
- <context>password</context>
- <label>Password to Login With</label>
- <description>Password to use (if Telnet Login is ON)</description>
- <default></default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="polling" type="integer">
- <label>Polling Interval</label>
- <description>Interval (in seconds) to poll the actual state</description>
- <default>600</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="ping" type="integer">
- <label>Pint Interval</label>
- <description>Ping Interval (in seconds) to keep the connection alive</description>
- <default>30</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="retryPolling" type="integer">
- <label>Polling Interval to Try to Reconnect</label>
- <description>Interval (in seconds) to try to (re)connect</description>
- <default>10</default>
- <advanced>true</advanced>
- </parameter>
- </config-description>
+ <config-description-ref uri="thing-type:altona:hdmimatrix"/>
</thing-type>
<!-- AT-UHD-PRO3-1616M -->
</channel-group>
</channel-groups>
- <config-description>
- <parameter name="ipAddress" type="text">
- <context>network-address</context>
- <label>IP or Host Name</label>
- <description>IP or Host name of Atlona Pro3 1616M Switch</description>
- <required>true</required>
- </parameter>
- <parameter name="userName" type="text">
- <label>UserName to Login With</label>
- <description>User Name to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="password" type="text">
- <context>password</context>
- <label>Password to Login With</label>
- <description>Password to login with if Telnet Login is on</description>
- <required>false</required>
- <advanced>true</advanced>
- </parameter>
- <parameter name="polling" type="integer">
- <label>Polling Interval</label>
- <description>Interval (in seconds) to poll the actual state of the Matrix</description>
- <default>600</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="ping" type="integer">
- <label>Pint Interval</label>
- <description>Ping Interval (in seconds) to keep the connection alive</description>
- <default>30</default>
- <advanced>true</advanced>
- </parameter>
- <parameter name="retryPolling" type="integer">
- <label>Polling Interval to Try to Reconnect</label>
- <description>Interval (in seconds) to try to (re)connect to the matrix</description>
- <default>10</default>
- <advanced>true</advanced>
- </parameter>
- </config-description>
+ <config-description-ref uri="thing-type:altona:hdmimatrix"/>
+ </thing-type>
+
+ <!-- AT-PRO3HD66M -->
+ <thing-type id="pro3-hd66m">
+ <label>Atlona Pro3 HD 6x6 HDBaseT Matrix</label>
+ <description>Atlona Pro3 6x6 HDBaseT Matrix (Model AT-PRO3HD66M)</description>
+
+ <channel-groups>
+ <channel-group id="primary" typeId="primarygroup"/>
+ <channel-group id="port1" typeId="hd-portgroup">
+ <label>Port 1</label>
+ <description>Output Port 1 Channels</description>
+ </channel-group>
+ <channel-group id="port2" typeId="hd-portgroup">
+ <label>Port 2</label>
+ <description>Output Port 2 Channels</description>
+ </channel-group>
+ <channel-group id="port3" typeId="hd-portgroup">
+ <label>Port 3</label>
+ <description>Output Port 3 Channels</description>
+ </channel-group>
+ <channel-group id="port4" typeId="hd-portgroup">
+ <label>Port 4</label>
+ <description>Output Port 4 Channels</description>
+ </channel-group>
+ <channel-group id="port5" typeId="hd-portgroup">
+ <label>Port 5</label>
+ <description>Output Port 5 Channels</description>
+ </channel-group>
+ <channel-group id="port6" typeId="hd-portgroup">
+ <label>Port 6</label>
+ <description>Output Port 6 Channels</description>
+ </channel-group>
+ </channel-groups>
+
+ <config-description-ref uri="thing-type:altona:hdmimatrix"/>
</thing-type>
<channel-group-type id="primarygroup">
</channels>
</channel-group-type>
+ <channel-group-type id="hd-portgroup">
+ <label>Port</label>
+ <description>Output Port Channels</description>
+ <channels>
+ <channel id="portoutput" typeId="portoutput"/>
+ </channels>
+ </channel-group-type>
<channel-group-type id="mirrorgroup">
<label>HDMI Port</label>