added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
32
bundles/org.openhab.binding.atlona/.classpath
Normal file
32
bundles/org.openhab.binding.atlona/.classpath
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.atlona/.project
Normal file
23
bundles/org.openhab.binding.atlona/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.atlona</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
13
bundles/org.openhab.binding.atlona/NOTICE
Normal file
13
bundles/org.openhab.binding.atlona/NOTICE
Normal file
@@ -0,0 +1,13 @@
|
||||
This content is produced and maintained by the openHAB project.
|
||||
|
||||
* Project home: https://www.openhab.org
|
||||
|
||||
== Declared Project Licenses
|
||||
|
||||
This program and the accompanying materials are made available under the terms
|
||||
of the Eclipse Public License 2.0 which is available at
|
||||
https://www.eclipse.org/legal/epl-2.0/.
|
||||
|
||||
== Source Code
|
||||
|
||||
https://github.com/openhab/openhab-addons
|
||||
349
bundles/org.openhab.binding.atlona/README.md
Normal file
349
bundles/org.openhab.binding.atlona/README.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# 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.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports the following thing types:
|
||||
|
||||
| Thing | Thing Type | Description |
|
||||
|---------------|------------|---------------------------------------------------------|
|
||||
| pro3-44m | Thing | The AT-UHD-PRO3-44M 4x4 HDBaseT matrix. |
|
||||
| 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. |
|
||||
|
||||
## Discovery
|
||||
|
||||
The Atlona AT-UHD-PRO3 switch can be discovered by starting a scan in the Paper UI and then logging into your switch and pressing the "SDDP" button on the "Network" tab.
|
||||
The "SDDP" (simple device discovery protocol) button will initiate the discovery process.
|
||||
If "Telnet Login" is enabled ("Network" tab from the switch configuration UI), you will need to set the username and password in the configuration of the newly discovered thing before a connection can be made.
|
||||
|
||||
## Binding configuration
|
||||
|
||||
```java
|
||||
atlona:pro3-88m:home [ ipAddress="192.168.1.30", userName="me", password="12345", polling=600, ping=30, retryPolling=10 ]
|
||||
```
|
||||
|
||||
- `ipAddress`: Hostname or IP address of the matrix switch
|
||||
- `userName`: (optional) the username to login with (only if Telnet Login is enabled)
|
||||
- `password`: (optional) the password to login with (only if Telnet Login is enabled)
|
||||
- `polling`: (optional) the time (in seconds) to poll the state from the actual switch (default: 600)
|
||||
- `ping`: (optional) the time (in seconds) to ping the switch to keep our connection alive (default: 30)
|
||||
- `retryPolling`: (optional) the time (in seconds) to retry a connection if the connection has failed (default: 10)
|
||||
|
||||
### username/password
|
||||
|
||||
The userName/password configuration options are optional and are only required if you have your switch set with "Telnet Login" enabled (on the "Network" tab from the switch configuration UI).
|
||||
The user must be a valid user listed on the "Users" tab of the switch configuration UI in this case.
|
||||
|
||||
### polling
|
||||
|
||||
Polling will automatically occur when (re)connecting to the switch to get the initial state of the switch.
|
||||
If you have anything outside of openHAB that can modify the switch state (front panel, IR, telnet session or another automation system), you will likely want to set this setting to a much lower value.
|
||||
|
||||
### ping
|
||||
|
||||
The Atlona switch will time out any IP connection after a specific time (specified by "IP Timeout" on the "Network" tab from the switch configuration UI - 120 by default).
|
||||
The ping setting MUST be lower than that value.
|
||||
If it is higher than the "IP Timeout" value, the switch will timeout our connection and the thing will go OFFLINE (until a reconnect attempt is made).
|
||||
|
||||
## Channels
|
||||
|
||||
| Thing | Channel Type ID | Item Type | Access | Description |
|
||||
|------------|-----------------------------------------------------------------|-----------|--------|-------------------------------------------------------------------------------------------|
|
||||
| pro3-44m | primary#power | Switch | RW | Matrix Power Switch |
|
||||
| pro3-44m | primary#panellock | Switch | RW | Sets the front panel locked or unlocked |
|
||||
| pro3-44m | primary#irenable | Switch | RW | Enables/Disabled the front panel IR |
|
||||
| pro3-44m | primary#presetcmd | Switch | W | Sends a preset command ('saveX', 'recallX', 'clearX') - see notes below |
|
||||
| pro3-44m | primary#matrixcmd | Switch | W | Sends a matrix command ('resetmatrix', 'resetports', 'allportsX') - see notes below |
|
||||
| pro3-44m | port1#portpower | Switch | RW | Enables/Disables output port #1 |
|
||||
| pro3-44m | port1#portoutput | Number | RW | Sets output port #1 to the specified input port |
|
||||
| pro3-44m | port2#portpower | Switch | RW | Enables/Disables output port #2 |
|
||||
| pro3-44m | port2#portoutput | Number | RW | Sets output port #2 to the specified input port |
|
||||
| pro3-44m | port3#portpower | Switch | RW | Enables/Disables output port #3 |
|
||||
| pro3-44m | port3#portoutput | Number | RW | Sets output port #3 to the specified input port |
|
||||
| pro3-44m | port4#portpower | Switch | RW | Enables/Disables output port #4 |
|
||||
| pro3-44m | port4#portoutput | Number | RW | Sets output port #4 to the specified input port |
|
||||
| pro3-44m | port5#portpower | Switch | RW | Enables/Disables output port #5 |
|
||||
| pro3-44m | port5#portoutput | Number | RW | Sets output port #5 to the specified input port |
|
||||
| pro3-44m | mirror5#portmirrorenabled | Number | RW | Sets hdmi port #5 to enable/disable port mirroring |
|
||||
| pro3-44m | mirror5#portmirror | Number | RW | Sets hdmi port #5 to mirror the specified output port (if enabled) |
|
||||
| pro3-44m | volume1#volume | Number | RW | Sets the volume of audio port #1 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-44m | volume1#volumemute | Switch | RW | Mutes/Unmutes audio port #1 |
|
||||
| pro3-44m | volume2#volume | Number | RW | Sets the volume of audio port #2 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-44m | volume2#volumemute | Switch | RW | Mutes/Unmutes audio port #2 |
|
||||
| pro3-44m | volume3#volume | Number | RW | Sets the volume of audio port #3 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-44m | volume3#volumemute | Switch | RW | Mutes/Unmutes audio port #3 |
|
||||
| | | | | |
|
||||
| pro3-66m | ALL OF THE pro3-44M channels (except different mirror settings) | | | |
|
||||
| pro3-66m | port6#portpower | Switch | RW | Enables/Disables output port #6 |
|
||||
| pro3-66m | port6#portoutput | Number | RW | Sets output port #6 to the specified input port |
|
||||
| pro3-66m | port7#portpower | Switch | RW | Enables/Disables output port #7 |
|
||||
| pro3-66m | port7#portoutput | Number | RW | Sets output port #7 to the specified input port |
|
||||
| pro3-66m | port8#portpower | Switch | RW | Enables/Disables output port #8 |
|
||||
| pro3-66m | port8#portoutput | Number | RW | Sets output port #8 to the specified input port |
|
||||
| pro3-66m | mirror6#portmirrorenabled | Number | RW | Sets hdmi port #6 to enable/disable port mirroring |
|
||||
| pro3-66m | mirror6#portmirror | Number | RW | Sets hdmi port #6 to mirror the specified output port (if enabled) |
|
||||
| pro3-66m | mirror8#portmirrorenabled | Number | RW | Sets hdmi port #8 to enable/disable port mirroring |
|
||||
| pro3-66m | mirror8#portmirror | Number | RW | Sets hdmi port #8 to mirror the specified output port (if enabled) |
|
||||
| pro3-66m | volume4#volume | Number | RW | Sets the volume of audio port #4 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-66m | volume4#volumemute | Switch | RW | Mutes/Unmutes audio port #4 |
|
||||
| | | | | |
|
||||
| pro3-88m | ALL OF THE pro3-66M channels (except different mirror settings) | | | |
|
||||
| pro3-88m | port9#portpower | Switch | RW | Enables/Disables output port #9 |
|
||||
| pro3-88m | port9#portoutput | Number | RW | Sets output port #9 to the specified input port |
|
||||
| pro3-88m | port10#portpower | Switch | RW | Enables/Disables output port #10 |
|
||||
| pro3-88m | port10#portoutput | Number | RW | Sets output port #10 to the specified input port |
|
||||
| pro3-88m | mirror8#portmirrorenabled | Number | RW | Sets hdmi port #8 to enable/disable port mirroring |
|
||||
| pro3-88m | mirror8#portmirror | Number | RW | Sets hdmi port #8 to mirror the specified output port (if enabled) |
|
||||
| pro3-88m | mirror10#portmirrorenabled | Number | RW | Sets hdmi port #10 to enable/disable port mirroring |
|
||||
| pro3-88m | mirror10#portmirror | Number | RW | Sets hdmi port #10 to mirror the specified output port (if enabled) |
|
||||
| pro3-88m | volume5#volume | Number | RW | Sets the volume of audio port #5 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-88m | volume5#volumemute | Switch | RW | Mutes/Unmutes audio port #5 |
|
||||
| pro3-88m | volume6#volume | Number | RW | Sets the volume of audio port #6 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-88m | volume6#volumemute | Switch | RW | Mutes/Unmutes audio port #6 |
|
||||
| | | | | |
|
||||
| pro3-1616m | ALL OF THE pro3-88M channels (except different mirror settings) | | | |
|
||||
| pro3-1616m | port11#portpower | Switch | RW | Enables/Disables output port #11 |
|
||||
| pro3-1616m | port11#portoutput | Number | RW | Sets output port #11 to the specified input port |
|
||||
| pro3-1616m | port12#portpower | Switch | RW | Enables/Disables output port #12 |
|
||||
| pro3-1616m | port12#portoutput | Number | RW | Sets output port #12 to the specified input port |
|
||||
| pro3-1616m | port13#portpower | Switch | RW | Enables/Disables output port #13 |
|
||||
| pro3-1616m | port13#portoutput | Number | RW | Sets output port #13 to the specified input port |
|
||||
| pro3-1616m | port14#portpower | Switch | RW | Enables/Disables output port #14 |
|
||||
| pro3-1616m | port14#portoutput | Number | RW | Sets output port #14 to the specified input port |
|
||||
| pro3-1616m | port15#portpower | Switch | RW | Enables/Disables output port #15 |
|
||||
| pro3-1616m | port15#portoutput | Number | RW | Sets output port #15 to the specified input port |
|
||||
| pro3-1616m | port16#portpower | Switch | RW | Enables/Disables output port #16 |
|
||||
| pro3-1616m | port16#portoutput | Number | RW | Sets output port #16 to the specified input port |
|
||||
| pro3-1616m | port17#portpower | Switch | RW | Enables/Disables output port #17 |
|
||||
| pro3-1616m | port17#portoutput | Number | RW | Sets output port #17 to the specified input port |
|
||||
| pro3-1616m | port18#portpower | Switch | RW | Enables/Disables output port #18 |
|
||||
| pro3-1616m | port18#portoutput | Number | RW | Sets output port #18 to the specified input port |
|
||||
| pro3-1616m | port19#portpower | Switch | RW | Enables/Disables output port #19 |
|
||||
| pro3-1616m | port19#portoutput | Number | RW | Sets output port #19 to the specified input port |
|
||||
| pro3-1616m | port20#portpower | Switch | RW | Enables/Disables output port #20 |
|
||||
| pro3-1616m | port20#portoutput | Number | RW | Sets output port #20 to the specified input port |
|
||||
| pro3-1616m | mirror17#portmirrorenabled | Number | RW | Sets hdmi port #17 to enable/disable port mirroring |
|
||||
| pro3-1616m | mirror17#portmirror | Number | RW | Sets hdmi port #17 to mirror the specified output port (if enabled) |
|
||||
| pro3-1616m | mirror18#portmirrorenabled | Number | RW | Sets hdmi port #18 to enable/disable port mirroring |
|
||||
| pro3-1616m | mirror18#portmirror | Number | RW | Sets hdmi port #18 to mirror the specified output port (if enabled) |
|
||||
| pro3-1616m | mirror19#portmirrorenabled | Number | RW | Sets hdmi port #19 to enable/disable port mirroring |
|
||||
| pro3-1616m | mirror19#portmirror | Number | RW | Sets hdmi port #19 to mirror the specified output port (if enabled) |
|
||||
| pro3-1616m | mirror20#portmirrorenabled | Number | RW | Sets hdmi port #20 to enable/disable port mirroring |
|
||||
| pro3-1616m | mirror20#portmirror | Number | RW | Sets hdmi port #20 to mirror the specified output port (if enabled) |
|
||||
| pro3-1616m | volume7#volume | Number | RW | Sets the volume of audio port #7 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-1616m | volume7#volumemute | Switch | RW | Mutes/Unmutes audio port #7 |
|
||||
| pro3-1616m | volume8#volume | Number | RW | Sets the volume of audio port #8 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-1616m | volume8#volumemute | Switch | RW | Mutes/Unmutes audio port #8 |
|
||||
| pro3-1616m | volume9#volume | Number | RW | Sets the volume of audio port #9 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-1616m | volume9#volumemute | Switch | RW | Mutes/Unmutes audio port #9 |
|
||||
| pro3-1616m | volume10#volume | Number | RW | Sets the volume of audio port #10 to the specified decibel level (between -79db to +15db) |
|
||||
| pro3-1616m | volume10#volumemute | Switch | RW | Mutes/Unmutes audio port #10 |
|
||||
| pro3-1616m | volume11#volume | Number | RW | Sets the volume of audio port #11 to the specified decibel level (between -79db to +15db) |
|
||||
| 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 |
|
||||
|
||||
### presetcmd
|
||||
|
||||
The presetcmd channel will take the following commands:
|
||||
|
||||
| Command | Description |
|
||||
|---------|--------------------------------------------|
|
||||
| saveX | Saves the current input/output to preset X |
|
||||
| recallX | Sets the input/output to preset X |
|
||||
| clearX | Clears the preset X |
|
||||
|
||||
Note: if X doesn't exist - nothing will occur.
|
||||
The # of presets allowed depends on the firmware you are using (5 presets up to rev 13, 10 for rev 14 and above).
|
||||
|
||||
### matrixcmd
|
||||
|
||||
The matrixcmd channel will take the following commands:
|
||||
|
||||
| Command | Description |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| resetmatrix | Resets the matrix back to its default values (USE WITH CARE!). Note: some firmware upgrades require a resetmatrix after installing. |
|
||||
| resetports | Resets the ports back to their default values (outputX=inputX) |
|
||||
| allportsX | Sets all the output ports to the input port X |
|
||||
|
||||
Note: if X doesn't exist - nothing will occur.
|
||||
The # of presets allowed depends on the firmware you are using (5 presets up to rev 13, 10 for rev 14 and above).
|
||||
|
||||
## Changes/Warnings
|
||||
|
||||
As of firmware 1.6.03 (rev 13), there are three issues on Atlona firmware (I have notified them on these issues):
|
||||
|
||||
- clearX command does not work. The TCP/IP command "ClearX" as specified in Atlona's protocol will ALWAYS return a "Command Failed". Please avoid this channel until atlona releases a new firmware.
|
||||
|
||||
- There is no way to query what the current status is of: panellock, and irenable. This add-on simply assumes that panellock is off and irenable is on at startup.
|
||||
|
||||
- If you make a change in the switches UI that requires a reboot (mainly changing any of the settings on the "Network" tab in the switch configuration UI), this add-on's connection will be inconsistently closed at different times.
|
||||
The thing will go OFFLINE and then back ONLINE when the reconnect attempt is made - and then it starts all over again. Please make sure you reboot as soon as possible when the switch UI notifies you.
|
||||
|
||||
- a bug in the firmware will sometimes cause memory presets to disappear after a reboot
|
||||
|
||||
As of firmware 1.6.8 (rev 14),
|
||||
|
||||
- The "clearX" command has been fixed and works now.
|
||||
- The number of presets have increased to 10
|
||||
- If telnet mode is enabled, you must use the admin username/password to issue a matrixreset
|
||||
|
||||
## Example
|
||||
|
||||
### Things
|
||||
|
||||
Here is an example with minimal configuration parameters (using default values with no telnet login):
|
||||
|
||||
```java
|
||||
atlona:pro3-88m:home [ ipAddress="192.168.1.30" ]
|
||||
```
|
||||
|
||||
Here is another example with minimal configuration parameters (using default values with telnet login):
|
||||
|
||||
```java
|
||||
atlona:pro3-88m:home [ ipAddress="192.168.1.30", userName="me", password="12345" ]
|
||||
```
|
||||
|
||||
Here is a full configuration example:
|
||||
|
||||
```java
|
||||
atlona:pro3-88m:home [ ipAddress="192.168.1.30", userName="me", password="12345", polling=600, ping=30, retryPolling=10 ]
|
||||
```
|
||||
|
||||
### Items
|
||||
|
||||
Here is an example of items for the AT-UHD-PRO33-88M:
|
||||
|
||||
```java
|
||||
Switch Atlona_Power "Power" { channel = "atlona:pro3-88m:home:primary#power" }
|
||||
Switch Atlona_PanelLock "Panel Lock" { channel = "atlona:pro3-88m:home:primary#panellock" }
|
||||
Switch Atlona_Presets "Preset Command" { channel = "atlona:pro3-88m:home:primary#presetcmd" }
|
||||
Switch Atlona_IRLock "IR Lock" { channel = "atlona:pro3-88m:home:primary#irenable" }
|
||||
Switch Atlona_PortPower1 "Port Power 1" { channel = "atlona:pro3-88m:home:port1#power" }
|
||||
Switch Atlona_PortPower2 "Port Power 2" { channel = "atlona:pro3-88m:home:port2#power" }
|
||||
Switch Atlona_PortPower3 "Port Power 3" { channel = "atlona:pro3-88m:home:port3#power" }
|
||||
Switch Atlona_PortPower4 "Port Power 4" { channel = "atlona:pro3-88m:home:port4#power" }
|
||||
Switch Atlona_PortPower5 "Port Power 5" { channel = "atlona:pro3-88m:home:port5#power" }
|
||||
Switch Atlona_PortPower6 "Port Power 6" { channel = "atlona:pro3-88m:home:port6#power" }
|
||||
Switch Atlona_PortPower7 "Port Power 7" { channel = "atlona:pro3-88m:home:port7#power" }
|
||||
Switch Atlona_PortPower8 "Port Power 8" { channel = "atlona:pro3-88m:home:port8#power" }
|
||||
Switch Atlona_PortPower9 "Port Power 9" { channel = "atlona:pro3-88m:home:port9#power" }
|
||||
Switch Atlona_PortPower10 "Port Power 10" { channel = "atlona:pro3-88m:home:port10#power" }
|
||||
Number Atlona_PortOutput1 "Living Room [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port1#portoutput" }
|
||||
Number Atlona_PortOutput2 "Master Bed [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port2#portoutput" }
|
||||
Number Atlona_PortOutput3 "Kitchen [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port3#portoutput" }
|
||||
Number Atlona_PortOutput4 "Output 4 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port4#portoutput" }
|
||||
Number Atlona_PortOutput5 "Output 5 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port5#portoutput" }
|
||||
Number Atlona_PortOutput6 "Output 6 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port6#portoutput" }
|
||||
Number Atlona_PortOutput7 "Output 7 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port7#portoutput" }
|
||||
Number Atlona_PortOutput8 "Output 8 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port8#portoutput" }
|
||||
Number Atlona_PortOutput9 "Output 9 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port9#portoutput" }
|
||||
Number Atlona_PortOutput10 "Output 10 [MAP(atlonainputports.map):%s]" { channel = "atlona:pro3-88m:home:port10#portoutput" }
|
||||
Number Atlona_PortMirror8 "Hdmi Mirror 8 [MAP(atlonaoutputports.map):%s]" { channel = "atlona:pro3-88m:home:mirror8#portmirror" }
|
||||
Number Atlona_PortMirror10 "Hdmi Mirror 10 [MAP(atlonaoutputports.map):%s]" { channel = "atlona:pro3-88m:home:mirror10#portmirror" }
|
||||
Number Atlona_Volume1 "Volume 1 [%s db]" { channel = "atlona:pro3-88m:home:volume1#volume" }
|
||||
Number Atlona_Volume2 "Volume 2 [%s db]" { channel = "atlona:pro3-88m:home:volume2#volume" }
|
||||
Number Atlona_Volume3 "Volume 3 [%s db]" { channel = "atlona:pro3-88m:home:volume3#volume" }
|
||||
Number Atlona_Volume4 "Volume 4 [%s db]" { channel = "atlona:pro3-88m:home:volume4#volume" }
|
||||
Number Atlona_Volume5 "Volume 5 [%s db]" { channel = "atlona:pro3-88m:home:volume5#volume" }
|
||||
Number Atlona_Volume6 "Volume 6 [%s db]" { channel = "atlona:pro3-88m:home:volume6#volume" }
|
||||
Switch Atlona_VolumeMute1 "Mute 1" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
Switch Atlona_VolumeMute2 "Mute 2" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
Switch Atlona_VolumeMute3 "Mute 3" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
Switch Atlona_VolumeMute4 "Mute 4" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
Switch Atlona_VolumeMute5 "Mute 5" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
Switch Atlona_VolumeMute6 "Mute 6" { channel = "atlona:pro3-88m:home:volume1#volumemute" }
|
||||
```
|
||||
|
||||
### SiteMap
|
||||
|
||||
```perl
|
||||
sitemap demo label="Main Menu" {
|
||||
Frame label="Atlona" {
|
||||
Text label="Device" {
|
||||
Switch item=Atlona_Power
|
||||
Switch item=Atlona_PanelLock
|
||||
Switch item=Atlona_IRLock
|
||||
Text item=Atlona_Presets
|
||||
}
|
||||
Text label="Ports" {
|
||||
Switch item=Atlona_PortPower1
|
||||
Switch item=Atlona_PortPower2
|
||||
Switch item=Atlona_PortPower3
|
||||
Switch item=Atlona_PortPower4
|
||||
Switch item=Atlona_PortPower5
|
||||
Switch item=Atlona_PortPower6
|
||||
Switch item=Atlona_PortPower7
|
||||
Switch item=Atlona_PortPower8
|
||||
Switch item=Atlona_PortPower9
|
||||
Switch item=Atlona_PortPower10
|
||||
Selection item=Atlona_PortOutput1 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput2 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput3 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput4 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput5 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput6 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput7 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput8 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"] visibility=[Atlona_PortMirror8==0]
|
||||
Selection item=Atlona_PortOutput9 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"]
|
||||
Selection item=Atlona_PortOutput10 mappings=[1="CableBox",2="BluRay Player",3="Roku",4="Apple TV",5="Input 5",6="Input 6",7="Input 7",8="Input 8"] visibility=[Atlona_PortMirror10==0]
|
||||
Selection item=Atlona_PortMirror8 mappings=[0="None",1="Living Room",2="Master Bed",3="Kitchen",4="Output 4",5="Output 5",6="Output 6",7="Output 7",9="Output 9"]
|
||||
Selection item=Atlona_PortMirror10 mappings=[0="None",1="Living Room",2="Master Bed",3="Kitchen",4="Output 4",5="Output 5",6="Output 6",7="Output 7",9="Output 9"]
|
||||
}
|
||||
Text label="Audio" {
|
||||
Setpoint item=Atlona_Volume1 minValue=-79 maxValue=15
|
||||
Setpoint item=Atlona_Volume2 minValue=-79 maxValue=15
|
||||
Setpoint item=Atlona_Volume3 minValue=-79 maxValue=15
|
||||
Setpoint item=Atlona_Volume4 minValue=-79 maxValue=15
|
||||
Setpoint item=Atlona_Volume5 minValue=-79 maxValue=15
|
||||
Setpoint item=Atlona_Volume6 minValue=-79 maxValue=15
|
||||
Switch item=Atlona_VolumeMute1
|
||||
Switch item=Atlona_VolumeMute2
|
||||
Switch item=Atlona_VolumeMute3
|
||||
Switch item=Atlona_VolumeMute4
|
||||
Switch item=Atlona_VolumeMute5
|
||||
Switch item=Atlona_VolumeMute6
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Transformation Maps
|
||||
|
||||
The following is some example transformation maps you can create.
|
||||
Be sure they are in sync with the mappings above.
|
||||
|
||||
### atlonainputports.map
|
||||
|
||||
```text
|
||||
1=CableBox
|
||||
2=BluRay Player
|
||||
3=Roku
|
||||
4=Apple TV
|
||||
5=Input 5
|
||||
6=Input 6
|
||||
7=Input 7
|
||||
8=Input 8
|
||||
-=-
|
||||
NULL=-
|
||||
```
|
||||
|
||||
### atlonaoutputports.map
|
||||
|
||||
```text
|
||||
1=Living Room
|
||||
2=Master Bed
|
||||
3=Kitchen
|
||||
4=Output 4
|
||||
5=Output 5
|
||||
6=Output 6
|
||||
7=Output 7
|
||||
8=Output 8
|
||||
9=Output 9
|
||||
10=Output 10
|
||||
-=-
|
||||
NULL=-
|
||||
```
|
||||
17
bundles/org.openhab.binding.atlona/pom.xml
Normal file
17
bundles/org.openhab.binding.atlona/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>3.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.atlona</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Atlona Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.atlona-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-atlona" description="Atlona PRO3 Switch Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.atlona/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link AtlonaBinding} class defines common constants, which are used across the whole binding.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AtlonaBindingConstants {
|
||||
|
||||
/**
|
||||
* The binding identifier for atlona
|
||||
*/
|
||||
public static final String BINDING_ID = "atlona";
|
||||
|
||||
/**
|
||||
* Thing ID for the AT-UHD-PRO3-44m (4x4 hdbaset matrix)
|
||||
*/
|
||||
public static final ThingTypeUID THING_TYPE_PRO3_44M = new ThingTypeUID(BINDING_ID, "pro3-44m");
|
||||
|
||||
/**
|
||||
* Thing ID for the AT-UHD-PRO3-66m (6x6 hdbaset matrix)
|
||||
*/
|
||||
public static final ThingTypeUID THING_TYPE_PRO3_66M = new ThingTypeUID(BINDING_ID, "pro3-66m");
|
||||
|
||||
/**
|
||||
* Thing ID for the AT-UHD-PRO3-88m (8x8 hdbaset matrix)
|
||||
*/
|
||||
public static final ThingTypeUID THING_TYPE_PRO3_88M = new ThingTypeUID(BINDING_ID, "pro3-88m");
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal;
|
||||
|
||||
import org.openhab.binding.atlona.internal.handler.AtlonaHandler;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
*
|
||||
* A callback to {@link AtlonaHandler} that can be used to update the status, properties and state of the thing.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public interface AtlonaHandlerCallback {
|
||||
/**
|
||||
* Callback to the {@link AtlonaHandler} to update the status of the thing.
|
||||
*
|
||||
* @param status a non-null {@link org.openhab.core.thing.ThingStatus}
|
||||
* @param detail a non-null {@link org.openhab.core.thing.ThingStatusDetail}
|
||||
* @param msg a possibly null, possibly empty message
|
||||
*/
|
||||
void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg);
|
||||
|
||||
/**
|
||||
* Callback to the {@link AtlonaHandler} to update the state of an item
|
||||
*
|
||||
* @param channelId the non-null, non-empty channel id
|
||||
* @param state the new non-null {@State}
|
||||
*/
|
||||
void stateChanged(String channelId, State state);
|
||||
|
||||
/**
|
||||
* Callback to the {@link AtlonaHandler} to update the property of a thing
|
||||
*
|
||||
* @param propertyName a non-null, non-empty property name
|
||||
* @param propertyValue a non-null, possibly empty property value
|
||||
*/
|
||||
void setProperty(String propertyName, String propertyValue);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal;
|
||||
|
||||
import static org.openhab.binding.atlona.internal.AtlonaBindingConstants.*;
|
||||
|
||||
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;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link org.openhab.binding.atlona.internal.AtlonaHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.atlona")
|
||||
public class AtlonaHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AtlonaHandlerFactory.class);
|
||||
|
||||
/**
|
||||
* 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()));
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Simply returns true if the given thingTypeUID is within {@link #SUPPORTED_THING_TYPES_UIDS}
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Creates the handler for the given thing given its thingTypeUID
|
||||
*/
|
||||
@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)));
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_PRO3_66M)) {
|
||||
return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(8, 4,
|
||||
Collections.unmodifiableSet(Stream.of(6, 8).collect(Collectors.toSet()))));
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(THING_TYPE_PRO3_88M)) {
|
||||
return new AtlonaPro3Handler(thing, new AtlonaPro3Capabilities(10, 6,
|
||||
Collections.unmodifiableSet(Stream.of(8, 10).collect(Collectors.toSet()))));
|
||||
}
|
||||
|
||||
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()))));
|
||||
}
|
||||
|
||||
logger.warn("Unknown binding: {}: {}", thingTypeUID.getId(), thingTypeUID.getBindingId());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* Defines an implementation of {@link AtlonaHandlerCallback} that will remember the last state
|
||||
* for an channelId and suppress the callback if the state hasn't changed
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class StatefulHandlerCallback implements AtlonaHandlerCallback {
|
||||
|
||||
/** The wrapped callback */
|
||||
private final AtlonaHandlerCallback wrappedCallback;
|
||||
|
||||
/** The state by channel id */
|
||||
private final Map<String, State> state = new ConcurrentHashMap<>();
|
||||
|
||||
private final Lock statusLock = new ReentrantLock();
|
||||
private ThingStatus lastThingStatus;
|
||||
private ThingStatusDetail lastThingStatusDetail;
|
||||
|
||||
/**
|
||||
* Create the callback from the other {@link AtlonaHandlerCallback}
|
||||
*
|
||||
* @param wrappedCallback a non-null {@link AtlonaHandlerCallback}
|
||||
* @throws IllegalArgumentException if wrappedCallback is null
|
||||
*/
|
||||
public StatefulHandlerCallback(AtlonaHandlerCallback wrappedCallback) {
|
||||
if (wrappedCallback == null) {
|
||||
throw new IllegalArgumentException("wrappedCallback cannot be null");
|
||||
}
|
||||
|
||||
this.wrappedCallback = wrappedCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the status changed to simply call the {@link #wrappedCallback}
|
||||
*
|
||||
* @param status the new status
|
||||
* @param detail the new detail
|
||||
* @param msg the new message
|
||||
*/
|
||||
@Override
|
||||
public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
|
||||
statusLock.lock();
|
||||
try {
|
||||
// Simply return we match the last status change (prevents loops if changing to the same status)
|
||||
if (status == lastThingStatus && detail == lastThingStatusDetail) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastThingStatus = status;
|
||||
lastThingStatusDetail = detail;
|
||||
} finally {
|
||||
statusLock.unlock();
|
||||
}
|
||||
// If we got this far - call the underlying one
|
||||
wrappedCallback.statusChanged(status, detail, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the state changed to determine if the state is new or changed and then
|
||||
* to call the {@link #wrappedCallback} if it has
|
||||
*
|
||||
* @param channelId the channel id that changed
|
||||
* @param state the new state
|
||||
*/
|
||||
@Override
|
||||
public void stateChanged(String channelId, State state) {
|
||||
if (StringUtils.isEmpty(channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final State oldState = this.state.get(channelId);
|
||||
|
||||
// If both null OR the same value (enums), nothing changed
|
||||
if (oldState == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If they are equal - nothing changed
|
||||
if (oldState != null && oldState.equals(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Something changed - save the new state and call the underlying wrapped
|
||||
this.state.put(channelId, state);
|
||||
wrappedCallback.stateChanged(channelId, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the state associated with the channel id. If the channelid
|
||||
* doesn't exist (or is null or is empty), this method will do nothing.
|
||||
*
|
||||
* @param channelId the channel id to remove state
|
||||
*/
|
||||
public void removeState(String channelId) {
|
||||
if (StringUtils.isEmpty(channelId)) {
|
||||
return;
|
||||
}
|
||||
state.remove(channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the set property to simply call the {@link #wrappedCallback}
|
||||
*
|
||||
* @param propertyName a non-null, non-empty property name
|
||||
* @param propertyValue a non-null, possibly empty property value
|
||||
*/
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) {
|
||||
wrappedCallback.setProperty(propertyName, propertyValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to get the {@link State} for a given property name
|
||||
*
|
||||
* @param propertyName a possibly null, possibly empty property name
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.atlona.internal.AtlonaBindingConstants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.atlona.internal.pro3.AtlonaPro3Config;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Discovery class for the Atlona PRO3 line. The PRO3 line uses SDDP (simple device discovery protocol) for discovery
|
||||
* (similar to UPNP but defined by Control4). The user should start the discovery process in openhab and then log into
|
||||
* the switch, go to the Network options and press the SDDP button (which initiates the SDDP conversation).
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.atlona")
|
||||
public class AtlonaDiscovery extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AtlonaDiscovery.class);
|
||||
|
||||
/**
|
||||
* Address SDDP broadcasts on
|
||||
*/
|
||||
private static final String SDDP_ADDR = "239.255.255.250";
|
||||
|
||||
/**
|
||||
* Port number SDDP uses
|
||||
*/
|
||||
private static final int SDDP_PORT = 1902;
|
||||
|
||||
/**
|
||||
* SDDP packet should be only 512 in size - make it 600 to give us some room
|
||||
*/
|
||||
private static final int BUFFER_SIZE = 600;
|
||||
|
||||
/**
|
||||
* Socket read timeout (in ms) - allows us to shutdown the listening every TIMEOUT
|
||||
*/
|
||||
private static final int TIMEOUT = 1000;
|
||||
|
||||
/**
|
||||
* Whether we are currently scanning or not
|
||||
*/
|
||||
private boolean scanning;
|
||||
|
||||
/**
|
||||
* The {@link ExecutorService} to run the listening threads on.
|
||||
*/
|
||||
private ExecutorService executorService;
|
||||
|
||||
/**
|
||||
* Constructs the discovery class using the thing IDs that we can discover.
|
||||
*/
|
||||
public AtlonaDiscovery() {
|
||||
super(Collections.unmodifiableSet(
|
||||
Stream.of(THING_TYPE_PRO3_44M, THING_TYPE_PRO3_66M, THING_TYPE_PRO3_88M, THING_TYPE_PRO3_1616M)
|
||||
.collect(Collectors.toSet())),
|
||||
30, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Starts the scan. This discovery will:
|
||||
* <ul>
|
||||
* <li>Request all the network interfaces</li>
|
||||
* <li>For each network interface, create a listening thread using {@link #executorService}</li>
|
||||
* <li>Each listening thread will open up a {@link MulticastSocket} using {@link #SDDP_ADDR} and {@link #SDDP_PORT}
|
||||
* and
|
||||
* will receive any {@link DatagramPacket} that comes in</li>
|
||||
* <li>The {@link DatagramPacket} is then investigated to see if is a SDDP packet and will create a new thing from
|
||||
* it</li>
|
||||
* </ul>
|
||||
* The process will continue until {@link #stopScan()} is called.
|
||||
*/
|
||||
@Override
|
||||
protected void startScan() {
|
||||
if (executorService != null) {
|
||||
stopScan();
|
||||
}
|
||||
|
||||
logger.debug("Starting Discovery");
|
||||
|
||||
try {
|
||||
final InetAddress addr = InetAddress.getByName(SDDP_ADDR);
|
||||
final List<NetworkInterface> networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
|
||||
executorService = Executors.newFixedThreadPool(networkInterfaces.size());
|
||||
scanning = true;
|
||||
for (final NetworkInterface netint : networkInterfaces) {
|
||||
|
||||
executorService.execute(() -> {
|
||||
try {
|
||||
MulticastSocket multiSocket = new MulticastSocket(SDDP_PORT);
|
||||
multiSocket.setSoTimeout(TIMEOUT);
|
||||
multiSocket.setNetworkInterface(netint);
|
||||
multiSocket.joinGroup(addr);
|
||||
|
||||
while (scanning) {
|
||||
DatagramPacket receivePacket = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE);
|
||||
try {
|
||||
multiSocket.receive(receivePacket);
|
||||
|
||||
String message = new String(receivePacket.getData()).trim();
|
||||
if (message != null && message.length() > 0) {
|
||||
messageReceive(message);
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
multiSocket.close();
|
||||
} catch (Exception e) {
|
||||
if (!e.getMessage().contains("No IP addresses bound to interface")) {
|
||||
logger.debug("Error getting ip addresses: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error getting ip addresses: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SDDP message has the following format
|
||||
*
|
||||
* <pre>
|
||||
* NOTIFY ALIVE SDDP/1.0
|
||||
* From: "192.168.1.30:1902"
|
||||
* Host: "AT-UHD-PRO3-88M_B898B0030F4D"
|
||||
* Type: "AT-UHD-PRO3-88M"
|
||||
* Max-Age: 1800
|
||||
* Primary-Proxy: "avswitch"
|
||||
* Proxies: "avswitch"
|
||||
* Manufacturer: "Atlona"
|
||||
* Model: "AT-UHD-PRO3-88M"
|
||||
* Driver: "avswitch_Atlona_AT-UHD-PRO3-88M_IP.c4i"
|
||||
* Config-URL: "http://192.168.1.30/"
|
||||
* </pre>
|
||||
*
|
||||
* First parse the manufacturer, host, model and IP address from the message. For the "Host" field, we parse out the
|
||||
* serial #. For the From field, we parse out the IP address (minus the port #). If we successfully found all four
|
||||
* and the manufacturer is "Atlona" and it's a model we recognize, we then create our thing from it.
|
||||
*
|
||||
* @param message possibly null, possibly empty SDDP message
|
||||
*/
|
||||
private void messageReceive(String message) {
|
||||
if (message == null || message.trim().length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String host = null;
|
||||
String model = null;
|
||||
String from = null;
|
||||
String manufacturer = null;
|
||||
|
||||
for (String msg : message.split("\r\n")) {
|
||||
int idx = msg.indexOf(':');
|
||||
if (idx > 0) {
|
||||
String name = msg.substring(0, idx);
|
||||
|
||||
if (name.equalsIgnoreCase("Host")) {
|
||||
host = msg.substring(idx + 1).trim().replaceAll("\"", "");
|
||||
int sep = host.indexOf('_');
|
||||
if (sep >= 0) {
|
||||
host = host.substring(sep + 1);
|
||||
}
|
||||
} else if (name.equalsIgnoreCase("Model")) {
|
||||
model = msg.substring(idx + 1).trim().replaceAll("\"", "");
|
||||
} else if (name.equalsIgnoreCase("Manufacturer")) {
|
||||
manufacturer = msg.substring(idx + 1).trim().replaceAll("\"", "");
|
||||
} else if (name.equalsIgnoreCase("From")) {
|
||||
from = msg.substring(idx + 1).trim().replaceAll("\"", "");
|
||||
int sep = from.indexOf(':');
|
||||
if (sep >= 0) {
|
||||
from = from.substring(0, sep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!"Atlona".equalsIgnoreCase(manufacturer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (host != null && model != null && from != null) {
|
||||
ThingTypeUID typeId = null;
|
||||
if (model.equalsIgnoreCase("AT-UHD-PRO3-44M")) {
|
||||
typeId = THING_TYPE_PRO3_44M;
|
||||
} else if (model.equalsIgnoreCase("AT-UHD-PRO3-66M")) {
|
||||
typeId = THING_TYPE_PRO3_66M;
|
||||
} else if (model.equalsIgnoreCase("AT-UHD-PRO3-88M")) {
|
||||
typeId = THING_TYPE_PRO3_88M;
|
||||
} else if (model.equalsIgnoreCase("AT-UHD-PRO3-1616M")) {
|
||||
typeId = THING_TYPE_PRO3_1616M;
|
||||
} else {
|
||||
logger.warn("Unknown model #: {}", model);
|
||||
}
|
||||
|
||||
if (typeId != null) {
|
||||
logger.debug("Creating binding for {} ({})", model, from);
|
||||
ThingUID j = new ThingUID(typeId, host);
|
||||
|
||||
Map<String, Object> properties = new HashMap<>(1);
|
||||
properties.put(AtlonaPro3Config.IP_ADDRESS, from);
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(j).withProperties(properties)
|
||||
.withLabel(model + " (" + from + ")").build();
|
||||
thingDiscovered(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Stops the discovery scan. We set {@link #scanning} to false (allowing the listening threads to end naturally
|
||||
* within {@link #TIMEOUT) * 5 time then shutdown the {@link #executorService}
|
||||
*/
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
if (executorService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
scanning = false;
|
||||
|
||||
try {
|
||||
executorService.awaitTermination(TIMEOUT * 5, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
executorService.shutdown();
|
||||
executorService = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.handler;
|
||||
|
||||
/**
|
||||
* Any model specific capabilities class should inherit from this base class. Currently doesn't provide any generic
|
||||
* functionality.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public abstract class AtlonaCapabilities {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.handler;
|
||||
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
|
||||
/**
|
||||
* This abstract class should be the base class for any Atlona product line handler.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public abstract class AtlonaHandler<C extends AtlonaCapabilities> extends BaseThingHandler {
|
||||
|
||||
/**
|
||||
* The model specific capabilities
|
||||
*/
|
||||
private final C capabilities;
|
||||
|
||||
/**
|
||||
* Constructs the handler from the specified thing and capabilities
|
||||
*
|
||||
* @param thing a non-null {@link org.openhab.core.thing.Thing}
|
||||
* @param capabilities a non-null {@link org.openhab.binding.atlona.internal.handler.AtlonaCapabilities}
|
||||
*/
|
||||
public AtlonaHandler(Thing thing, C capabilities) {
|
||||
super(thing);
|
||||
|
||||
if (capabilities == null) {
|
||||
throw new IllegalArgumentException("capabilities cannot be null");
|
||||
}
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model specific capabilities
|
||||
*
|
||||
* @return a non-null {@link org.openhab.binding.atlona.internal.handler.AtlonaCapabilities}
|
||||
*/
|
||||
protected C getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Represents a restartable socket connection to the underlying telnet session. Commands can be sent via
|
||||
* {@link #sendCommand(String)} and responses will be received on any {@link SocketSessionListener}. This implementation
|
||||
* of {@link SocketSession} communicates using a {@link SocketChannel} connection.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class SocketChannelSession implements SocketSession {
|
||||
private final Logger logger = LoggerFactory.getLogger(SocketChannelSession.class);
|
||||
|
||||
/**
|
||||
* The host/ip address to connect to
|
||||
*/
|
||||
private final String host;
|
||||
|
||||
/**
|
||||
* The port to connect to
|
||||
*/
|
||||
private final int port;
|
||||
|
||||
/**
|
||||
* The actual socket being used. Will be null if not connected
|
||||
*/
|
||||
private final AtomicReference<SocketChannel> socketChannel = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* The {@link ResponseReader} that will be used to read from {@link #_readBuffer}
|
||||
*/
|
||||
private final ResponseReader responseReader = new ResponseReader();
|
||||
|
||||
/**
|
||||
* The responses read from the {@link #responseReader}
|
||||
*/
|
||||
private final BlockingQueue<Object> responses = new ArrayBlockingQueue<>(50);
|
||||
|
||||
/**
|
||||
* The dispatcher of responses from {@link #responses}
|
||||
*/
|
||||
private final Dispatcher dispatcher = new Dispatcher();
|
||||
|
||||
/**
|
||||
* The {@link SocketSessionListener} that the {@link #dispatcher} will call
|
||||
*/
|
||||
private List<SocketSessionListener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates the socket session from the given host and port
|
||||
*
|
||||
* @param host a non-null, non-empty host/ip address
|
||||
* @param port the port number between 1 and 65535
|
||||
*/
|
||||
public SocketChannelSession(String host, int port) {
|
||||
if (host == null || host.trim().length() == 0) {
|
||||
throw new IllegalArgumentException("Host cannot be null or empty");
|
||||
}
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
throw new IllegalArgumentException("Port must be between 1 and 65535");
|
||||
}
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(SocketSessionListener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearListeners() {
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeListener(SocketSessionListener listener) {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() throws IOException {
|
||||
disconnect();
|
||||
|
||||
final SocketChannel channel = SocketChannel.open();
|
||||
channel.configureBlocking(true);
|
||||
|
||||
logger.debug("Connecting to {}:{}", host, port);
|
||||
channel.connect(new InetSocketAddress(host, port));
|
||||
|
||||
logger.debug("Waiting for connect");
|
||||
while (!channel.finishConnect()) {
|
||||
try {
|
||||
Thread.sleep(250);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
socketChannel.set(channel);
|
||||
new Thread(dispatcher).start();
|
||||
new Thread(responseReader).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() throws IOException {
|
||||
if (isConnected()) {
|
||||
logger.debug("Disconnecting from {}:{}", host, port);
|
||||
|
||||
final SocketChannel channel = socketChannel.getAndSet(null);
|
||||
channel.close();
|
||||
|
||||
dispatcher.stopRunning();
|
||||
responseReader.stopRunning();
|
||||
|
||||
responses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
final SocketChannel channel = socketChannel.get();
|
||||
return channel != null && channel.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void sendCommand(String command) throws IOException {
|
||||
if (command == null) {
|
||||
throw new IllegalArgumentException("command cannot be null");
|
||||
}
|
||||
|
||||
if (!isConnected()) {
|
||||
throw new IOException("Cannot send message - disconnected");
|
||||
}
|
||||
|
||||
ByteBuffer toSend = ByteBuffer.wrap((command + "\r\n").getBytes());
|
||||
|
||||
final SocketChannel channel = socketChannel.get();
|
||||
if (channel == null) {
|
||||
logger.debug("Cannot send command '{}' - socket channel was closed", command);
|
||||
} else {
|
||||
logger.debug("Sending Command: '{}'", command);
|
||||
channel.write(toSend);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the runnable that will read from the socket and add messages to the responses queue (to be processed by
|
||||
* the dispatcher)
|
||||
*
|
||||
* @author Tim Roberts
|
||||
*
|
||||
*/
|
||||
private class ResponseReader implements Runnable {
|
||||
|
||||
/**
|
||||
* Whether the reader is currently running
|
||||
*/
|
||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Locking to allow proper shutdown of the reader
|
||||
*/
|
||||
private final CountDownLatch running = new CountDownLatch(1);
|
||||
|
||||
/**
|
||||
* Stops the reader. Will wait 5 seconds for the runnable to stop
|
||||
*/
|
||||
public void stopRunning() {
|
||||
if (isRunning.getAndSet(false)) {
|
||||
try {
|
||||
if (!running.await(5, TimeUnit.SECONDS)) {
|
||||
logger.warn("Waited too long for response reader to finish");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the logic to read from the socket until {@link #isRunning} is false. A 'response' is anything that ends
|
||||
* with a carriage-return/newline combo. Additionally, the special "Login: " and "Password: " prompts are
|
||||
* treated as responses for purposes of logging in.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
final StringBuilder sb = new StringBuilder(100);
|
||||
final ByteBuffer readBuffer = ByteBuffer.allocate(1024);
|
||||
|
||||
isRunning.set(true);
|
||||
responses.clear();
|
||||
|
||||
while (isRunning.get()) {
|
||||
try {
|
||||
// if reader is null, sleep and try again
|
||||
if (readBuffer == null) {
|
||||
Thread.sleep(250);
|
||||
continue;
|
||||
}
|
||||
|
||||
final SocketChannel channel = socketChannel.get();
|
||||
if (channel == null) {
|
||||
// socket was closed
|
||||
isRunning.set(false);
|
||||
break;
|
||||
}
|
||||
|
||||
int bytesRead = channel.read(readBuffer);
|
||||
if (bytesRead == -1) {
|
||||
responses.put(new IOException("server closed connection"));
|
||||
isRunning.set(false);
|
||||
break;
|
||||
} else if (bytesRead == 0) {
|
||||
readBuffer.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
readBuffer.flip();
|
||||
while (readBuffer.hasRemaining()) {
|
||||
final char ch = (char) readBuffer.get();
|
||||
sb.append(ch);
|
||||
if (ch == '\n' || ch == ' ') {
|
||||
final String str = sb.toString();
|
||||
if (str.endsWith("\r\n") || str.endsWith("Login: ") || str.endsWith("Password: ")) {
|
||||
sb.setLength(0);
|
||||
final String response = str.substring(0, str.length() - 2);
|
||||
responses.put(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readBuffer.flip();
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing - probably shutting down
|
||||
} catch (AsynchronousCloseException e) {
|
||||
// socket was definitely closed by another thread
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
isRunning.set(false);
|
||||
responses.put(e);
|
||||
} catch (InterruptedException e1) {
|
||||
// Do nothing - probably shutting down
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
running.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The dispatcher runnable is responsible for reading the response queue and dispatching it to the current callable.
|
||||
* Since the dispatcher is ONLY started when a callable is set, responses may pile up in the queue and be dispatched
|
||||
* when a callable is set. Unlike the socket reader, this can be assigned to another thread (no state outside of the
|
||||
* class).
|
||||
*
|
||||
* @author Tim Roberts
|
||||
*/
|
||||
private class Dispatcher implements Runnable {
|
||||
|
||||
/**
|
||||
* Whether the dispatcher is running or not
|
||||
*/
|
||||
private final AtomicBoolean isRunning = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Locking to allow proper shutdown of the reader
|
||||
*/
|
||||
private final CountDownLatch running = new CountDownLatch(1);
|
||||
|
||||
/**
|
||||
* Whether the dispatcher is currently processing a message
|
||||
*/
|
||||
private final AtomicReference<Thread> processingThread = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Stops the reader. Will wait 5 seconds for the runnable to stop (should stop within 1 second based on the poll
|
||||
* timeout below)
|
||||
*/
|
||||
public void stopRunning() {
|
||||
if (isRunning.getAndSet(false)) {
|
||||
// only wait if stopRunning didn't get called as part of processing a message
|
||||
// (which would happen if we are processing an exception that forced a session close)
|
||||
final Thread processingThread = this.processingThread.get();
|
||||
if (processingThread != null && Thread.currentThread() != processingThread) {
|
||||
try {
|
||||
if (!running.await(5, TimeUnit.SECONDS)) {
|
||||
logger.warn("Waited too long for dispatcher to finish");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the logic to dispatch any responses to the current listeners until {@link #isRunning} is false.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
processingThread.set(Thread.currentThread());
|
||||
|
||||
isRunning.set(true);
|
||||
while (isRunning.get()) {
|
||||
try {
|
||||
// if no listeners, we don't want to start dispatching yet.
|
||||
if (listeners.size() == 0) {
|
||||
Thread.sleep(250);
|
||||
continue;
|
||||
}
|
||||
|
||||
final Object response = responses.poll(1, TimeUnit.SECONDS);
|
||||
|
||||
if (response != null) {
|
||||
if (response instanceof String) {
|
||||
try {
|
||||
logger.debug("Dispatching response: {}", response);
|
||||
final SocketSessionListener[] listeners = SocketChannelSession.this.listeners
|
||||
.toArray(new SocketSessionListener[0]);
|
||||
for (SocketSessionListener listener : listeners) {
|
||||
listener.responseReceived((String) response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Exception occurred processing the response '{}': ", response, e);
|
||||
}
|
||||
} else if (response instanceof Exception) {
|
||||
logger.debug("Dispatching exception: {}", response);
|
||||
final SocketSessionListener[] listeners = SocketChannelSession.this.listeners
|
||||
.toArray(new SocketSessionListener[0]);
|
||||
for (SocketSessionListener listener : listeners) {
|
||||
listener.responseException((Exception) response);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Unknown response class: {}", response);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
} catch (Exception e) {
|
||||
logger.debug("Uncaught exception {}", e.getMessage(), e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
isRunning.set(false);
|
||||
processingThread.set(null);
|
||||
running.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.net;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This is a socket session interface that defines the contract for a socket session. A socket session will initiate
|
||||
* communications with the underlying device and provide message back via the {@link SocketSessionListener}
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public interface SocketSession {
|
||||
|
||||
/**
|
||||
* Adds a {@link SocketSessionListener} to call when responses/exceptions have been received
|
||||
*
|
||||
* @param listener a non-null {@link SocketSessionListener} to use
|
||||
*/
|
||||
void addListener(SocketSessionListener listener);
|
||||
|
||||
/**
|
||||
* Clears all listeners
|
||||
*/
|
||||
void clearListeners();
|
||||
|
||||
/**
|
||||
* Removes a {@link SocketSessionListener} from this session
|
||||
*
|
||||
* @param listener a non-null {@link SocketSessionListener} to remove
|
||||
* @return true if removed, false otherwise
|
||||
*/
|
||||
boolean removeListener(SocketSessionListener listener);
|
||||
|
||||
/**
|
||||
* Will attempt to connect to the {@link #_host} on port {@link #_port}. If we are current connected, will
|
||||
* {@link #disconnect()} first. Once connected, the {@link #_writer} and {@link #_reader} will be created, the
|
||||
* {@link #_dispatcher} and {@link #_responseReader} will be started.
|
||||
*
|
||||
* @throws java.io.IOException if an exception occurs during the connection attempt
|
||||
*/
|
||||
void connect() throws IOException;
|
||||
|
||||
/**
|
||||
* Disconnects from the {@link #_host} if we are {@link #isConnected()}. The {@link #_writer}, {@link #_reader} and
|
||||
* {@link #_client}
|
||||
* will be closed and set to null. The {@link #_dispatcher} and {@link #_responseReader} will be stopped, the
|
||||
* {@link #_listeners} will be nulled and the {@link #_responses} will be cleared.
|
||||
*
|
||||
* @throws java.io.IOException if an exception occurs during the disconnect attempt
|
||||
*/
|
||||
void disconnect() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns true if we are connected ({@link #_client} is not null and is connected)
|
||||
*
|
||||
* @return true if connected, false otherwise
|
||||
*/
|
||||
boolean isConnected();
|
||||
|
||||
/**
|
||||
* Sends the specified command to the underlying socket
|
||||
*
|
||||
* @param command a non-null, non-empty command
|
||||
* @throws java.io.IOException an exception that occurred while sending
|
||||
*/
|
||||
void sendCommand(String command) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.net;
|
||||
|
||||
/**
|
||||
* Interface defining a listener to a {@link SocketSession} that will receive responses and/or exceptions from the
|
||||
* socket
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public interface SocketSessionListener {
|
||||
/**
|
||||
* Called when a command has completed with the response for the command
|
||||
*
|
||||
* @param response a non-null, possibly empty response
|
||||
*/
|
||||
public void responseReceived(String response);
|
||||
|
||||
/**
|
||||
* Called when a command finished with an exception or a general exception occurred while reading
|
||||
*
|
||||
* @param e a non-null exception
|
||||
*/
|
||||
public void responseException(Exception e);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.pro3;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.atlona.internal.handler.AtlonaCapabilities;
|
||||
|
||||
/**
|
||||
* The capabilities class for the Atlona PRO3 line. Each PRO3 model differs in the number of (output) ports that can be
|
||||
* powered, the number of audio ports there are and which (output) ports are HDMI ports.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class AtlonaPro3Capabilities extends AtlonaCapabilities {
|
||||
|
||||
/**
|
||||
* Number of power ports
|
||||
*/
|
||||
private final int nbrPowerPorts;
|
||||
|
||||
/**
|
||||
* Number of audio ports
|
||||
*/
|
||||
private final int nbrAudioPorts;
|
||||
|
||||
/**
|
||||
* The set of output ports that are HDMI ports
|
||||
*/
|
||||
private final Set<Integer> hdmiPorts;
|
||||
|
||||
/**
|
||||
* Constructs the capabilities from the parms
|
||||
*
|
||||
* @param nbrPowerPorts a greater than 0 number of power ports
|
||||
* @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) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (hdmiPorts.isEmpty()) {
|
||||
throw new IllegalArgumentException("hdmiPorts cannot be empty");
|
||||
}
|
||||
|
||||
this.nbrPowerPorts = nbrPowerPorts;
|
||||
this.nbrAudioPorts = nbrAudioPorts;
|
||||
this.hdmiPorts = Collections.unmodifiableSet(new HashSet<>(hdmiPorts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of power ports
|
||||
*
|
||||
* @return a greater than 0 number of power ports
|
||||
*/
|
||||
int getNbrPowerPorts() {
|
||||
return nbrPowerPorts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of audio ports
|
||||
*
|
||||
* @return a greater than 0 number of audio ports
|
||||
*/
|
||||
int getNbrAudioPorts() {
|
||||
return nbrAudioPorts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of hdmi ports
|
||||
*
|
||||
* @return a non-null, non-empty immutable set of hdmi ports
|
||||
*/
|
||||
Set<Integer> getHdmiPorts() {
|
||||
return hdmiPorts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.pro3;
|
||||
|
||||
import org.openhab.binding.atlona.internal.discovery.AtlonaDiscovery;
|
||||
|
||||
/**
|
||||
* Configuration class for the Atlona Pro3 line of switchers
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class AtlonaPro3Config {
|
||||
|
||||
/**
|
||||
* Constant field used in {@link AtlonaDiscovery} to set the config property during discovery. Value of this field
|
||||
* needs to match {@link #ipAddress}
|
||||
*/
|
||||
public static final String IP_ADDRESS = "ipAddress";
|
||||
|
||||
/**
|
||||
* IP Address (or host name) of switch
|
||||
*/
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* Optional username to login in with. Only used if the switch has it's "Telnet Login" option turned on
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* Optional password to login in with. Only used if the switch has it's "Telnet Login" option turned on
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Polling time (in seconds) to refresh state from the switch itself. Only useful if something else modifies the
|
||||
* switch (usually through the front panel or the IR link)
|
||||
*/
|
||||
private int polling;
|
||||
|
||||
/**
|
||||
* Ping time (in seconds) to keep the connection alive. Should be less than the IP Timeout on the switch.
|
||||
*/
|
||||
private int ping;
|
||||
|
||||
/**
|
||||
* Polling time (in seconds) to attempt a reconnect if the socket session has failed
|
||||
*/
|
||||
private int retryPolling;
|
||||
|
||||
/**
|
||||
* Returns the IP address or host name of the switch
|
||||
*
|
||||
* @return the IP address or host name of the swtich
|
||||
*/
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IP address or host name of the switch
|
||||
*
|
||||
* @param ipAddress the IP Address or host name of the switch
|
||||
*/
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the username used to login with
|
||||
*
|
||||
* @return the username used to login with
|
||||
*/
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the username used to login with
|
||||
*
|
||||
* @param userName the username used to login with
|
||||
*/
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the password used to login with
|
||||
*
|
||||
* @return the password used to login with
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password used to login with
|
||||
*
|
||||
* @param password the password used to login with
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the polling (in seconds) to refresh state
|
||||
*
|
||||
* @return the polling (in seconds) to refresh state
|
||||
*/
|
||||
public int getPolling() {
|
||||
return polling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the polling (in seconds) to refresh state
|
||||
*
|
||||
* @param polling the polling (in seconds) to refresh state
|
||||
*/
|
||||
public void setPolling(int polling) {
|
||||
this.polling = polling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the polling (in seconds) to reconnect
|
||||
*
|
||||
* @return the polling (in seconds) to reconnect
|
||||
*/
|
||||
public int getRetryPolling() {
|
||||
return retryPolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the polling (in seconds) to reconnect
|
||||
*
|
||||
* @param retryPolling the polling (in seconds to reconnect)
|
||||
*/
|
||||
public void setRetryPolling(int retryPolling) {
|
||||
this.retryPolling = retryPolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ping interval (in seconds)
|
||||
*
|
||||
* @return the ping interval (in seconds)
|
||||
*/
|
||||
public int getPing() {
|
||||
return ping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ping interval (in seconds)
|
||||
*
|
||||
* @param ping the ping interval (in seconds)
|
||||
*/
|
||||
public void setPing(int ping) {
|
||||
this.ping = ping;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.pro3;
|
||||
|
||||
/**
|
||||
* The {@link AtlonaPro3Binding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
class AtlonaPro3Constants {
|
||||
|
||||
// Properties
|
||||
static final String PROPERTY_VERSION = "version";
|
||||
static final String PROPERTY_TYPE = "type";
|
||||
|
||||
static final String GROUP_PRIMARY = "primary";
|
||||
static final String GROUP_PORT = "port";
|
||||
static final String GROUP_MIRROR = "mirror";
|
||||
static final String GROUP_VOLUME = "volume";
|
||||
|
||||
// List of all Channel ids
|
||||
static final String CHANNEL_POWER = "power";
|
||||
static final String CHANNEL_PANELLOCK = "panellock";
|
||||
static final String CHANNEL_IRENABLE = "irenable";
|
||||
static final String CHANNEL_PRESETCMDS = "presetcmd";
|
||||
static final String CHANNEL_MATRIXCMDS = "matrixcmd";
|
||||
|
||||
static final String CHANNEL_PORTPOWER = "portpower";
|
||||
static final String CHANNEL_PORTOUTPUT = "portoutput";
|
||||
|
||||
static final String CHANNEL_PORTMIRROR = "portmirror";
|
||||
static final String CHANNEL_PORTMIRRORENABLED = "portmirrorenabled";
|
||||
|
||||
static final String CHANNEL_VOLUME = "volume";
|
||||
static final String CHANNEL_VOLUME_MUTE = "volumemute";
|
||||
// static final String CHANNEL_RS232 = "rs232cmd";
|
||||
|
||||
static final String CONFIG_HOSTNAME = "hostname";
|
||||
static final String CONFIG_OUTPUT = "output";
|
||||
|
||||
// Preset commands
|
||||
static final String CMD_PRESETSAVE = "save";
|
||||
static final String CMD_PRESETRECALL = "recall";
|
||||
static final String CMD_PRESETCLEAR = "clear";
|
||||
|
||||
// Matrix commands
|
||||
static final String CMD_MATRIXRESET = "resetmatrix";
|
||||
static final String CMD_MATRIXRESETPORTS = "resetports";
|
||||
static final String CMD_MATRIXPORTALL = "allports";
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.pro3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.openhab.binding.atlona.internal.AtlonaHandlerCallback;
|
||||
import org.openhab.binding.atlona.internal.StatefulHandlerCallback;
|
||||
import org.openhab.binding.atlona.internal.handler.AtlonaHandler;
|
||||
import org.openhab.binding.atlona.internal.net.SocketChannelSession;
|
||||
import org.openhab.binding.atlona.internal.net.SocketSession;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link org.openhab.binding.atlona.internal.pro3.AtlonaPro3Handler} is responsible for handling commands, which
|
||||
* are sent to one of the channels.
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class AtlonaPro3Handler extends AtlonaHandler<AtlonaPro3Capabilities> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AtlonaPro3Handler.class);
|
||||
|
||||
/**
|
||||
* The {@link AtlonaPro3PortocolHandler} protocol handler
|
||||
*/
|
||||
private AtlonaPro3PortocolHandler atlonaHandler;
|
||||
|
||||
/**
|
||||
* The {@link SocketSession} telnet session to the switch. Will be null if not connected.
|
||||
*/
|
||||
private SocketSession session;
|
||||
|
||||
/**
|
||||
* The polling job to poll the actual state from the {@link #session}
|
||||
*/
|
||||
private ScheduledFuture<?> polling;
|
||||
|
||||
/**
|
||||
* The retry connection event
|
||||
*/
|
||||
private ScheduledFuture<?> retryConnection;
|
||||
|
||||
/**
|
||||
* The ping event
|
||||
*/
|
||||
private ScheduledFuture<?> ping;
|
||||
|
||||
// List of all the groups patterns we recognize
|
||||
private static final Pattern GROUP_PRIMARY_PATTERN = Pattern.compile("^" + AtlonaPro3Constants.GROUP_PRIMARY + "$");
|
||||
private static final Pattern GROUP_PORT_PATTERN = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.GROUP_PORT + "(\\d{1,2})$");
|
||||
private static final Pattern GROUP_MIRROR_PATTERN = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.GROUP_MIRROR + "(\\d{1,2})$");
|
||||
private static final Pattern GROUP_VOLUME_PATTERN = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.GROUP_VOLUME + "(\\d{1,2})$");
|
||||
|
||||
// List of preset commands we recognize
|
||||
private static final Pattern CMD_PRESETSAVE = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.CMD_PRESETSAVE + "(\\d{1,2})$");
|
||||
private static final Pattern CMD_PRESETRECALL = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.CMD_PRESETRECALL + "(\\d{1,2})$");
|
||||
private static final Pattern CMD_PRESETCLEAR = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.CMD_PRESETCLEAR + "(\\d{1,2})$");
|
||||
|
||||
// List of matrix commands we recognize
|
||||
private static final Pattern CMD_MATRIXRESET = Pattern.compile("^" + AtlonaPro3Constants.CMD_MATRIXRESET + "$");
|
||||
private static final Pattern CMD_MATRIXRESETPORTS = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.CMD_MATRIXRESETPORTS + "$");
|
||||
private static final Pattern CMD_MATRIXPORTALL = Pattern
|
||||
.compile("^" + AtlonaPro3Constants.CMD_MATRIXPORTALL + "(\\d{1,2})$");
|
||||
|
||||
/**
|
||||
* Constructs the handler from the {@link org.openhab.core.thing.Thing} with the number of power ports and
|
||||
* audio ports the switch supports.
|
||||
*
|
||||
* @param thing a non-null {@link org.openhab.core.thing.Thing} the handler is for
|
||||
* @param capabilities a non-null {@link org.openhab.binding.atlona.internal.pro3.AtlonaPro3Capabilities}
|
||||
*/
|
||||
public AtlonaPro3Handler(Thing thing, AtlonaPro3Capabilities capabilities) {
|
||||
super(thing, capabilities);
|
||||
|
||||
if (thing == null) {
|
||||
throw new IllegalArgumentException("thing cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Handles commands to specific channels. This implementation will offload much of its work to the
|
||||
* {@link AtlonaPro3PortocolHandler}. Basically we validate the type of command for the channel then call the
|
||||
* {@link AtlonaPro3PortocolHandler} to handle the actual protocol. Special use case is the {@link RefreshType}
|
||||
* where we call {{@link #handleRefresh(String)} to handle a refresh of the specific channel (which in turn calls
|
||||
* {@link AtlonaPro3PortocolHandler} to handle the actual refresh
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
handleRefresh(channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
final String group = channelUID.getGroupId().toLowerCase();
|
||||
final String id = channelUID.getIdWithoutGroup().toLowerCase();
|
||||
|
||||
Matcher m;
|
||||
if ((m = GROUP_PRIMARY_PATTERN.matcher(group)).matches()) {
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_POWER:
|
||||
if (command instanceof OnOffType) {
|
||||
final boolean makeOn = ((OnOffType) command) == OnOffType.ON;
|
||||
atlonaHandler.setPower(makeOn);
|
||||
} else {
|
||||
logger.debug("Received a POWER channel command with a non OnOffType: {}", command);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AtlonaPro3Constants.CHANNEL_PANELLOCK:
|
||||
if (command instanceof OnOffType) {
|
||||
final boolean makeOn = ((OnOffType) command) == OnOffType.ON;
|
||||
atlonaHandler.setPanelLock(makeOn);
|
||||
} else {
|
||||
logger.debug("Received a PANELLOCK channel command with a non OnOffType: {}", command);
|
||||
}
|
||||
break;
|
||||
|
||||
case AtlonaPro3Constants.CHANNEL_IRENABLE:
|
||||
if (command instanceof OnOffType) {
|
||||
final boolean makeOn = ((OnOffType) command) == OnOffType.ON;
|
||||
atlonaHandler.setIrOn(makeOn);
|
||||
} else {
|
||||
logger.debug("Received a IRLOCK channel command with a non OnOffType: {}", command);
|
||||
}
|
||||
|
||||
break;
|
||||
case AtlonaPro3Constants.CHANNEL_MATRIXCMDS:
|
||||
if (command instanceof StringType) {
|
||||
final String matrixCmd = command.toString();
|
||||
Matcher cmd;
|
||||
try {
|
||||
if ((cmd = CMD_MATRIXRESET.matcher(matrixCmd)).matches()) {
|
||||
atlonaHandler.resetMatrix();
|
||||
} else if ((cmd = CMD_MATRIXRESETPORTS.matcher(matrixCmd)).matches()) {
|
||||
atlonaHandler.resetAllPorts();
|
||||
} else if ((cmd = CMD_MATRIXPORTALL.matcher(matrixCmd)).matches()) {
|
||||
if (cmd.groupCount() == 1) {
|
||||
final int portNbr = Integer.parseInt(cmd.group(1));
|
||||
atlonaHandler.setPortAll(portNbr);
|
||||
} else {
|
||||
logger.debug("Unknown matirx set port command: '{}'", matrixCmd);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.debug("Unknown matrix command: '{}'", cmd);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Could not parse the port number from the command: '{}'", matrixCmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AtlonaPro3Constants.CHANNEL_PRESETCMDS:
|
||||
if (command instanceof StringType) {
|
||||
final String presetCmd = command.toString();
|
||||
Matcher cmd;
|
||||
try {
|
||||
if ((cmd = CMD_PRESETSAVE.matcher(presetCmd)).matches()) {
|
||||
if (cmd.groupCount() == 1) {
|
||||
final int presetNbr = Integer.parseInt(cmd.group(1));
|
||||
atlonaHandler.saveIoSettings(presetNbr);
|
||||
} else {
|
||||
logger.debug("Unknown preset save command: '{}'", presetCmd);
|
||||
}
|
||||
} else if ((cmd = CMD_PRESETRECALL.matcher(presetCmd)).matches()) {
|
||||
if (cmd.groupCount() == 1) {
|
||||
final int presetNbr = Integer.parseInt(cmd.group(1));
|
||||
atlonaHandler.recallIoSettings(presetNbr);
|
||||
} else {
|
||||
logger.debug("Unknown preset recall command: '{}'", presetCmd);
|
||||
}
|
||||
} else if ((cmd = CMD_PRESETCLEAR.matcher(presetCmd)).matches()) {
|
||||
if (cmd.groupCount() == 1) {
|
||||
final int presetNbr = Integer.parseInt(cmd.group(1));
|
||||
atlonaHandler.clearIoSettings(presetNbr);
|
||||
} else {
|
||||
logger.debug("Unknown preset clear command: '{}'", presetCmd);
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.debug("Unknown preset command: '{}'", cmd);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Could not parse the preset number from the command: '{}'", presetCmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.debug("Unknown/Unsupported Primary Channel: {}", channelUID.getAsString());
|
||||
break;
|
||||
}
|
||||
} else if ((m = GROUP_PORT_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int portNbr = Integer.parseInt(m.group(1));
|
||||
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_PORTOUTPUT:
|
||||
if (command instanceof DecimalType) {
|
||||
final int inpNbr = ((DecimalType) command).intValue();
|
||||
atlonaHandler.setPortSwitch(inpNbr, portNbr);
|
||||
} else {
|
||||
logger.debug("Received a PORTOUTPUT channel command with a non DecimalType: {}",
|
||||
command);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AtlonaPro3Constants.CHANNEL_PORTPOWER:
|
||||
if (command instanceof OnOffType) {
|
||||
final boolean makeOn = ((OnOffType) command) == OnOffType.ON;
|
||||
atlonaHandler.setPortPower(portNbr, makeOn);
|
||||
} else {
|
||||
logger.debug("Received a PORTPOWER channel command with a non OnOffType: {}", command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown/Unsupported Port Channel: {}", channelUID.getAsString());
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Port Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
}
|
||||
} else if ((m = GROUP_MIRROR_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int hdmiPortNbr = Integer.parseInt(m.group(1));
|
||||
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_PORTMIRROR:
|
||||
if (command instanceof DecimalType) {
|
||||
final int outPortNbr = ((DecimalType) command).intValue();
|
||||
if (outPortNbr <= 0) {
|
||||
atlonaHandler.removePortMirror(hdmiPortNbr);
|
||||
} else {
|
||||
atlonaHandler.setPortMirror(hdmiPortNbr, outPortNbr);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received a PORTMIRROR channel command with a non DecimalType: {}",
|
||||
command);
|
||||
}
|
||||
|
||||
break;
|
||||
case AtlonaPro3Constants.CHANNEL_PORTMIRRORENABLED:
|
||||
if (command instanceof OnOffType) {
|
||||
if (command == OnOffType.ON) {
|
||||
final StatefulHandlerCallback callback = (StatefulHandlerCallback) atlonaHandler
|
||||
.getCallback();
|
||||
final State state = callback.getState(AtlonaPro3Constants.CHANNEL_PORTMIRROR);
|
||||
int outPortNbr = 1;
|
||||
if (state != null && state instanceof DecimalType) {
|
||||
outPortNbr = ((DecimalType) state).intValue();
|
||||
}
|
||||
atlonaHandler.setPortMirror(hdmiPortNbr, outPortNbr);
|
||||
} else {
|
||||
atlonaHandler.removePortMirror(hdmiPortNbr);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received a PORTMIRROR channel command with a non DecimalType: {}",
|
||||
command);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
logger.debug("Unknown/Unsupported Mirror Channel: {}", channelUID.getAsString());
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Mirror Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
}
|
||||
} else if ((m = GROUP_VOLUME_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int portNbr = Integer.parseInt(m.group(1));
|
||||
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_VOLUME_MUTE:
|
||||
if (command instanceof OnOffType) {
|
||||
atlonaHandler.setVolumeMute(portNbr, ((OnOffType) command) == OnOffType.ON);
|
||||
} else {
|
||||
logger.debug("Received a VOLUME MUTE channel command with a non OnOffType: {}",
|
||||
command);
|
||||
}
|
||||
|
||||
break;
|
||||
case AtlonaPro3Constants.CHANNEL_VOLUME:
|
||||
if (command instanceof DecimalType) {
|
||||
final double level = ((DecimalType) command).doubleValue();
|
||||
atlonaHandler.setVolume(portNbr, level);
|
||||
} else {
|
||||
logger.debug("Received a VOLUME channel command with a non DecimalType: {}", command);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.debug("Unknown/Unsupported Volume Channel: {}", channelUID.getAsString());
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Volume Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unknown/Unsupported Channel: {}", channelUID.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles the {@link RefreshType} command specifically. Calls the {@link AtlonaPro3PortocolHandler} to
|
||||
* handle the actual refresh based on the channel id.
|
||||
*
|
||||
* @param id a non-null, possibly empty channel id to refresh
|
||||
*/
|
||||
private void handleRefresh(ChannelUID channelUID) {
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String group = channelUID.getGroupId().toLowerCase();
|
||||
final String id = channelUID.getIdWithoutGroup().toLowerCase();
|
||||
final StatefulHandlerCallback callback = (StatefulHandlerCallback) atlonaHandler.getCallback();
|
||||
|
||||
Matcher m;
|
||||
if ((m = GROUP_PRIMARY_PATTERN.matcher(group)).matches()) {
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_POWER:
|
||||
callback.removeState(AtlonaPro3Utilities.createChannelID(group, id));
|
||||
atlonaHandler.refreshPower();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} else if ((m = GROUP_PORT_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int portNbr = Integer.parseInt(m.group(1));
|
||||
callback.removeState(AtlonaPro3Utilities.createChannelID(group, portNbr, id));
|
||||
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_PORTOUTPUT:
|
||||
atlonaHandler.refreshPortStatus(portNbr);
|
||||
break;
|
||||
|
||||
case AtlonaPro3Constants.CHANNEL_PORTPOWER:
|
||||
atlonaHandler.refreshPortPower(portNbr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Port Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
|
||||
}
|
||||
} else if ((m = GROUP_MIRROR_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int hdmiPortNbr = Integer.parseInt(m.group(1));
|
||||
callback.removeState(AtlonaPro3Utilities.createChannelID(group, hdmiPortNbr, id));
|
||||
atlonaHandler.refreshPortMirror(hdmiPortNbr);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Mirror Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
|
||||
}
|
||||
} else if ((m = GROUP_VOLUME_PATTERN.matcher(group)).matches()) {
|
||||
if (m.groupCount() == 1) {
|
||||
try {
|
||||
final int portNbr = Integer.parseInt(m.group(1));
|
||||
callback.removeState(AtlonaPro3Utilities.createChannelID(group, portNbr, id));
|
||||
|
||||
switch (id) {
|
||||
case AtlonaPro3Constants.CHANNEL_VOLUME_MUTE:
|
||||
atlonaHandler.refreshVolumeMute(portNbr);
|
||||
break;
|
||||
case AtlonaPro3Constants.CHANNEL_VOLUME:
|
||||
atlonaHandler.refreshVolumeStatus(portNbr);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Bad Volume Channel (can't parse the port nbr): {}", channelUID.getAsString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Initializes the handler. This initialization will read/validate the configuration, then will create the
|
||||
* {@link SocketSession}, initialize the {@link AtlonaPro3PortocolHandler} and will attempt to connect to the switch
|
||||
* (via {{@link #retryConnect()}.
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
final AtlonaPro3Config config = getAtlonaConfig();
|
||||
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.getIpAddress() == null || config.getIpAddress().trim().length() == 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"IP Address of Atlona Pro3 is missing from configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
session = new SocketChannelSession(config.getIpAddress(), 23);
|
||||
atlonaHandler = new AtlonaPro3PortocolHandler(session, config, getCapabilities(),
|
||||
new StatefulHandlerCallback(new AtlonaHandlerCallback() {
|
||||
@Override
|
||||
public void stateChanged(String channelId, State state) {
|
||||
updateState(channelId, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusChanged(ThingStatus status, ThingStatusDetail detail, String msg) {
|
||||
updateStatus(status, detail, msg);
|
||||
|
||||
if (status != ThingStatus.ONLINE) {
|
||||
disconnect(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(String propertyName, String propertyValue) {
|
||||
getThing().setProperty(propertyName, propertyValue);
|
||||
}
|
||||
}));
|
||||
|
||||
// Try initial connection in a scheduled task
|
||||
this.scheduler.schedule(this::connect, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to connect to the switch. If successfully connect, the {@link AtlonaPro3PortocolHandler#login()} will be
|
||||
* called to log into the switch (if needed). Once completed, a polling job will be created to poll the switch's
|
||||
* actual state and a ping job to ping the server. If a connection cannot be established (or login failed), the
|
||||
* connection attempt will be retried later (via {@link #retryConnect()})
|
||||
*/
|
||||
private void connect() {
|
||||
String response = "Server is offline - will try to reconnect later";
|
||||
try {
|
||||
// clear listeners to avoid any 'old' listener from handling initial messages
|
||||
session.clearListeners();
|
||||
session.connect();
|
||||
|
||||
response = atlonaHandler.login();
|
||||
if (response == null) {
|
||||
final AtlonaPro3Config config = getAtlonaConfig();
|
||||
if (config != null) {
|
||||
polling = this.scheduler.scheduleWithFixedDelay(() -> {
|
||||
final ThingStatus status = getThing().getStatus();
|
||||
if (status == ThingStatus.ONLINE) {
|
||||
if (session.isConnected()) {
|
||||
atlonaHandler.refreshAll();
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Atlona PRO3 has disconnected. Will try to reconnect later.");
|
||||
}
|
||||
} else if (status == ThingStatus.OFFLINE) {
|
||||
disconnect(true);
|
||||
}
|
||||
}, config.getPolling(), config.getPolling(), TimeUnit.SECONDS);
|
||||
|
||||
ping = this.scheduler.scheduleWithFixedDelay(() -> {
|
||||
final ThingStatus status = getThing().getStatus();
|
||||
if (status == ThingStatus.ONLINE) {
|
||||
if (session.isConnected()) {
|
||||
atlonaHandler.ping();
|
||||
}
|
||||
}
|
||||
}, config.getPing(), config.getPing(), TimeUnit.SECONDS);
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, response);
|
||||
retryConnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to disconnect from the session and will optionally retry the connection attempt. The {@link #polling}
|
||||
* will be cancelled, the {@link #ping} will be cancelled and both set to null then the {@link #session} will be
|
||||
* disconnected.
|
||||
*
|
||||
* @param retryConnection true to retry connection attempts after the disconnect
|
||||
*/
|
||||
private void disconnect(boolean retryConnection) {
|
||||
// Cancel polling
|
||||
if (polling != null) {
|
||||
polling.cancel(true);
|
||||
polling = null;
|
||||
}
|
||||
|
||||
// Cancel ping
|
||||
if (ping != null) {
|
||||
ping.cancel(true);
|
||||
ping = null;
|
||||
}
|
||||
|
||||
try {
|
||||
session.disconnect();
|
||||
} catch (IOException e) {
|
||||
// ignore - we don't care
|
||||
}
|
||||
|
||||
if (retryConnection) {
|
||||
retryConnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries the connection attempt - schedules a job in {@link AtlonaPro3Config#getRetryPolling()} seconds to call
|
||||
* the
|
||||
* {@link #connect()} method. If a retry attempt is pending, the request is ignored.
|
||||
*/
|
||||
private void retryConnect() {
|
||||
if (retryConnection == null) {
|
||||
final AtlonaPro3Config config = getAtlonaConfig();
|
||||
if (config != null) {
|
||||
logger.info("Will try to reconnect in {} seconds", config.getRetryPolling());
|
||||
retryConnection = this.scheduler.schedule(() -> {
|
||||
retryConnection = null;
|
||||
connect();
|
||||
}, config.getRetryPolling(), TimeUnit.SECONDS);
|
||||
}
|
||||
} else {
|
||||
logger.debug("RetryConnection called when a retry connection is pending - ignoring request");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple gets the {@link AtlonaPro3Config} from the {@link Thing} and will set the status to offline if not found.
|
||||
*
|
||||
* @return a possible null {@link AtlonaPro3Config}
|
||||
*/
|
||||
private AtlonaPro3Config getAtlonaConfig() {
|
||||
final AtlonaPro3Config config = getThing().getConfiguration().as(AtlonaPro3Config.class);
|
||||
|
||||
if (config == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Disposes of the handler. Will simply call {@link #disconnect(boolean)} to disconnect and NOT retry the
|
||||
* connection
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
disconnect(false);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.atlona.internal.pro3;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Tim Roberts - Initial contribution
|
||||
*/
|
||||
public class AtlonaPro3Utilities {
|
||||
/**
|
||||
* Helper method to create a channel id from a group with no port number attached
|
||||
*
|
||||
* @param group a group name
|
||||
* @param channelId the channel id
|
||||
* @return group + "#" + channelId
|
||||
*/
|
||||
public static String createChannelID(String group, String channelId) {
|
||||
return group + "#" + channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a channel id from a group, port number and channel id
|
||||
*
|
||||
* @param group the group name
|
||||
* @param portNbr the port number
|
||||
* @param channelId the channel id
|
||||
* @return group + portNbr + "#" + channelId
|
||||
*/
|
||||
public static String createChannelID(String group, int portNbr, String channelId) {
|
||||
return group + portNbr + "#" + channelId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="atlona" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>Atlona Products</name>
|
||||
<description>Binding for Atlona PRO3 HDBaseT Matrix switches.</description>
|
||||
<author>Tim Roberts</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,613 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="atlona"
|
||||
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">
|
||||
|
||||
<!-- AT-UHD-PRO3-44M -->
|
||||
<thing-type id="pro3-44m">
|
||||
<label>Atlona Pro3 4x4 HDBaseT Matrix</label>
|
||||
<description>Atlona Pro3 4x4 HDBaseT Matrix (Model AT-UHD-PRO3-44M)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="primary" typeId="primarygroup"/>
|
||||
<channel-group id="port1" typeId="portgroup">
|
||||
<label>Port 1</label>
|
||||
<description>Output Port 1 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port2" typeId="portgroup">
|
||||
<label>Port 2</label>
|
||||
<description>Output Port 2 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port3" typeId="portgroup">
|
||||
<label>Port 3</label>
|
||||
<description>Output Port 3 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port4" typeId="portgroup">
|
||||
<label>Port 4</label>
|
||||
<description>Output Port 4 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port5" typeId="portgroup">
|
||||
<label>Port 5</label>
|
||||
<description>Output Port 5 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror5" typeId="mirrorgroup">
|
||||
<label>HDMI Port 5</label>
|
||||
<description>HDMI Port 5 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume1" typeId="volumegroup">
|
||||
<label>Volume 1</label>
|
||||
<description>Volume 1 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume2" typeId="volumegroup">
|
||||
<label>Volume 2</label>
|
||||
<description>Volume 2 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume3" typeId="volumegroup">
|
||||
<label>Volume 3</label>
|
||||
<description>Volume 3 channels</description>
|
||||
</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>
|
||||
</thing-type>
|
||||
|
||||
<!-- AT-UHD-PRO3-66M -->
|
||||
<thing-type id="pro3-66m">
|
||||
<label>Atlona Pro3 6x6 HDBaseT Matrix</label>
|
||||
<description>Atlona Pro3 6x6 HDBaseT Matrix (Model AT-UHD-PRO3-66M)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="primary" typeId="primarygroup"/>
|
||||
<channel-group id="port1" typeId="portgroup">
|
||||
<label>Port 1</label>
|
||||
<description>Output Port 1 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port2" typeId="portgroup">
|
||||
<label>Port 2</label>
|
||||
<description>Output Port 2 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port3" typeId="portgroup">
|
||||
<label>Port 3</label>
|
||||
<description>Output Port 3 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port4" typeId="portgroup">
|
||||
<label>Port 4</label>
|
||||
<description>Output Port 4 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port5" typeId="portgroup">
|
||||
<label>Port 5</label>
|
||||
<description>Output Port 5 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port6" typeId="portgroup">
|
||||
<label>Port 6</label>
|
||||
<description>Output Port 6 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port7" typeId="portgroup">
|
||||
<label>Port 7</label>
|
||||
<description>Output Port 7 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port8" typeId="portgroup">
|
||||
<label>Port 8</label>
|
||||
<description>Output Port 8 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror6" typeId="mirrorgroup">
|
||||
<label>HDMI Port 6</label>
|
||||
<description>HDMI Port 6 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror8" typeId="mirrorgroup">
|
||||
<label>HDMI Port 8</label>
|
||||
<description>HDMI Port 8 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume1" typeId="volumegroup">
|
||||
<label>Volume 1</label>
|
||||
<description>Volume 1 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume2" typeId="volumegroup">
|
||||
<label>Volume 2</label>
|
||||
<description>Volume 2 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume3" typeId="volumegroup">
|
||||
<label>Volume 3</label>
|
||||
<description>Volume 3 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume4" typeId="volumegroup">
|
||||
<label>Volume 4</label>
|
||||
<description>Volume 4 channels</description>
|
||||
</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>
|
||||
</thing-type>
|
||||
|
||||
<!-- AT-UHD-PRO3-88M -->
|
||||
<thing-type id="pro3-88m">
|
||||
<label>Atlona Pro3 8x8 HDBaseT Matrix</label>
|
||||
<description>Atlona Pro3 8x8 HDBaseT Matrix (Model AT-UHD-PRO3-66M)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="primary" typeId="primarygroup"/>
|
||||
<channel-group id="port1" typeId="portgroup">
|
||||
<label>Port 1</label>
|
||||
<description>Output Port 1 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port2" typeId="portgroup">
|
||||
<label>Port 2</label>
|
||||
<description>Output Port 2 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port3" typeId="portgroup">
|
||||
<label>Port 3</label>
|
||||
<description>Output Port 3 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port4" typeId="portgroup">
|
||||
<label>Port 4</label>
|
||||
<description>Output Port 4 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port5" typeId="portgroup">
|
||||
<label>Port 5</label>
|
||||
<description>Output Port 5 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port6" typeId="portgroup">
|
||||
<label>Port 6</label>
|
||||
<description>Output Port 6 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port7" typeId="portgroup">
|
||||
<label>Port 7</label>
|
||||
<description>Output Port 7 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port8" typeId="portgroup">
|
||||
<label>Port 8</label>
|
||||
<description>Output Port 8 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port9" typeId="portgroup">
|
||||
<label>Port 9</label>
|
||||
<description>Output Port 9 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port10" typeId="portgroup">
|
||||
<label>Port 10</label>
|
||||
<description>Output Port 10 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror8" typeId="mirrorgroup">
|
||||
<label>HDMI Port 8</label>
|
||||
<description>HDMI Port 8 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror10" typeId="mirrorgroup">
|
||||
<label>HDMI Port 10</label>
|
||||
<description>HDMI Port 10 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume1" typeId="volumegroup">
|
||||
<label>Volume 1</label>
|
||||
<description>Volume 1 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume2" typeId="volumegroup">
|
||||
<label>Volume 2</label>
|
||||
<description>Volume 2 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume3" typeId="volumegroup">
|
||||
<label>Volume 3</label>
|
||||
<description>Volume 3 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume4" typeId="volumegroup">
|
||||
<label>Volume 4</label>
|
||||
<description>Volume 4 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume5" typeId="volumegroup">
|
||||
<label>Volume 5</label>
|
||||
<description>Volume 5 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume6" typeId="volumegroup">
|
||||
<label>Volume 6</label>
|
||||
<description>Volume 6 channels</description>
|
||||
</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>
|
||||
</thing-type>
|
||||
|
||||
<!-- AT-UHD-PRO3-1616M -->
|
||||
<thing-type id="pro3-1616m">
|
||||
<label>Atlona Pro3 16x16 HDBaseT Matrix</label>
|
||||
<description>Atlona Pro3 16x16 HDBaseT Matrix (Model AT-UHD-PRO3-1616M)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="primary" typeId="primarygroup"/>
|
||||
<channel-group id="port1" typeId="portgroup">
|
||||
<label>Port 1</label>
|
||||
<description>Output Port 1 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port2" typeId="portgroup">
|
||||
<label>Port 2</label>
|
||||
<description>Output Port 2 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port3" typeId="portgroup">
|
||||
<label>Port 3</label>
|
||||
<description>Output Port 3 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port4" typeId="portgroup">
|
||||
<label>Port 4</label>
|
||||
<description>Output Port 4 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port5" typeId="portgroup">
|
||||
<label>Port 5</label>
|
||||
<description>Output Port 5 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port6" typeId="portgroup">
|
||||
<label>Port 6</label>
|
||||
<description>Output Port 6 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port7" typeId="portgroup">
|
||||
<label>Port 7</label>
|
||||
<description>Output Port 7 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port8" typeId="portgroup">
|
||||
<label>Port 8</label>
|
||||
<description>Output Port 8 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port9" typeId="portgroup">
|
||||
<label>Port 9</label>
|
||||
<description>Output Port 9 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port10" typeId="portgroup">
|
||||
<label>Port 10</label>
|
||||
<description>Output Port 10 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port11" typeId="portgroup">
|
||||
<label>Port 11</label>
|
||||
<description>Output Port 11 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port12" typeId="portgroup">
|
||||
<label>Port 12</label>
|
||||
<description>Output Port 12 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port13" typeId="portgroup">
|
||||
<label>Port 13</label>
|
||||
<description>Output Port 13 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port14" typeId="portgroup">
|
||||
<label>Port 14</label>
|
||||
<description>Output Port 14 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port15" typeId="portgroup">
|
||||
<label>Port 15</label>
|
||||
<description>Output Port 15 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port16" typeId="portgroup">
|
||||
<label>Port 16</label>
|
||||
<description>Output Port 16 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port17" typeId="portgroup">
|
||||
<label>Port 17</label>
|
||||
<description>Output Port 17 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port18" typeId="portgroup">
|
||||
<label>Port 18</label>
|
||||
<description>Output Port 18 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port19" typeId="portgroup">
|
||||
<label>Port 19</label>
|
||||
<description>Output Port 19 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="port20" typeId="portgroup">
|
||||
<label>Port 20</label>
|
||||
<description>Output Port 20 Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror17" typeId="mirrorgroup">
|
||||
<label>HDMI Port 17</label>
|
||||
<description>HDMI Port 17 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror18" typeId="mirrorgroup">
|
||||
<label>HDMI Port 18</label>
|
||||
<description>HDMI Port 18 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror19" typeId="mirrorgroup">
|
||||
<label>HDMI Port 19</label>
|
||||
<description>HDMI Port 19 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="mirror20" typeId="mirrorgroup">
|
||||
<label>HDMI Port 20</label>
|
||||
<description>HDMI Port 20 Mirroring Channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume1" typeId="volumegroup">
|
||||
<label>Volume 1</label>
|
||||
<description>Volume 1 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume2" typeId="volumegroup">
|
||||
<label>Volume 2</label>
|
||||
<description>Volume 2 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume3" typeId="volumegroup">
|
||||
<label>Volume 3</label>
|
||||
<description>Volume 3 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume4" typeId="volumegroup">
|
||||
<label>Volume 4</label>
|
||||
<description>Volume 4 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume5" typeId="volumegroup">
|
||||
<label>Volume 5</label>
|
||||
<description>Volume 5 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume6" typeId="volumegroup">
|
||||
<label>Volume 6</label>
|
||||
<description>Volume 6 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume7" typeId="volumegroup">
|
||||
<label>Volume 7</label>
|
||||
<description>Volume 7 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume8" typeId="volumegroup">
|
||||
<label>Volume 8</label>
|
||||
<description>Volume 8 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume9" typeId="volumegroup">
|
||||
<label>Volume 9</label>
|
||||
<description>Volume 9 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume10" typeId="volumegroup">
|
||||
<label>Volume 10</label>
|
||||
<description>Volume 10 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume11" typeId="volumegroup">
|
||||
<label>Volume 11</label>
|
||||
<description>Volume 11 channels</description>
|
||||
</channel-group>
|
||||
<channel-group id="volume12" typeId="volumegroup">
|
||||
<label>Volume 12</label>
|
||||
<description>Volume 12 channels</description>
|
||||
</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>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="primarygroup">
|
||||
<label>Primary Channels</label>
|
||||
<description>Primary Channels</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="power"/>
|
||||
<channel id="panellock" typeId="panellock"/>
|
||||
<channel id="irenable" typeId="irenable"/>
|
||||
<channel id="presetcmd" typeId="presetcmd"/>
|
||||
<channel id="matrixcmd" typeId="matrixcmd"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="portgroup">
|
||||
<label>Port</label>
|
||||
<description>Output Port Channels</description>
|
||||
<channels>
|
||||
<channel id="portpower" typeId="portpower"/>
|
||||
<channel id="portoutput" typeId="portoutput"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
|
||||
<channel-group-type id="mirrorgroup">
|
||||
<label>HDMI Port</label>
|
||||
<description>HDMI Port Mirroring Channels</description>
|
||||
<channels>
|
||||
<channel id="portmirror" typeId="portmirror"/>
|
||||
<channel id="portmirrorenabled" typeId="portmirrorenabled"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="volumegroup">
|
||||
<label>Volume</label>
|
||||
<description>Volume channels</description>
|
||||
<channels>
|
||||
<channel id="volume" typeId="volume"/>
|
||||
<channel id="volumemute" typeId="volumemute"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<!-- Port Channel Type -->
|
||||
<channel-type id="power">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Power</label>
|
||||
<description>Whether the matrix is on or not</description>
|
||||
</channel-type>
|
||||
<channel-type id="panellock" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Panel Lock</label>
|
||||
<description>Whether the front panel buttons are locked or not</description>
|
||||
</channel-type>
|
||||
<channel-type id="irenable" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>IR Enable</label>
|
||||
<description>Enables or Disables IR</description>
|
||||
</channel-type>
|
||||
<channel-type id="presetcmd" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Preset Command</label>
|
||||
<description>Send a preset command ("saveX", "recallX", "clearX")</description>
|
||||
</channel-type>
|
||||
<channel-type id="matrixcmd" advanced="true">
|
||||
<item-type>String</item-type>
|
||||
<label>Matrix Command</label>
|
||||
<description>Send a matrix command ("resetmatrix", "resetports", "allportsX")</description>
|
||||
</channel-type>
|
||||
<channel-type id="portpower" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Output Port Power</label>
|
||||
<description>Turns on/off the output port</description>
|
||||
</channel-type>
|
||||
<channel-type id="portoutput">
|
||||
<item-type>Number</item-type>
|
||||
<label>Output Port</label>
|
||||
<description>Sets the output port to the input port</description>
|
||||
</channel-type>
|
||||
<channel-type id="portmirror">
|
||||
<item-type>Number</item-type>
|
||||
<label>Mirror Port</label>
|
||||
<description>Sets the port to be mirrored on the output port</description>
|
||||
</channel-type>
|
||||
<channel-type id="portmirrorenabled">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mirror Enabled</label>
|
||||
<description>Whether the HDMI port mirroring is enabled or not</description>
|
||||
</channel-type>
|
||||
<channel-type id="volume">
|
||||
<item-type>Number</item-type>
|
||||
<label>Output Volume</label>
|
||||
<description>Sets the volume (in db) of the output port (default: -40db with range from -79db to 15db)</description>
|
||||
<state min="-79" max="15"/>
|
||||
</channel-type>
|
||||
<channel-type id="volumemute">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mute</label>
|
||||
<description>Sets the output to muted or not</description>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user