mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[emotiva] Initial contribution (#16499)
* [emotiva] Initial contribution Signed-off-by: Espen Fossen <espenaf@junta.no>
This commit is contained in:
parent
790d4d09ca
commit
67e6f8bf30
@ -96,6 +96,7 @@
|
||||
/bundles/org.openhab.binding.electroluxair/ @jannegpriv
|
||||
/bundles/org.openhab.binding.elerotransmitterstick/ @vbier
|
||||
/bundles/org.openhab.binding.elroconnects/ @mherwege
|
||||
/bundles/org.openhab.binding.emotiva/ @espenaf
|
||||
/bundles/org.openhab.binding.energenie/ @hmerk
|
||||
/bundles/org.openhab.binding.energidataservice/ @jlaur
|
||||
/bundles/org.openhab.binding.enigma2/ @gdolfen
|
||||
|
@ -471,6 +471,11 @@
|
||||
<artifactId>org.openhab.binding.elroconnects</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.emotiva</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.energenie</artifactId>
|
||||
|
13
bundles/org.openhab.binding.emotiva/NOTICE
Normal file
13
bundles/org.openhab.binding.emotiva/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
|
190
bundles/org.openhab.binding.emotiva/README.md
Normal file
190
bundles/org.openhab.binding.emotiva/README.md
Normal file
@ -0,0 +1,190 @@
|
||||
# Emotiva Binding
|
||||
|
||||
This binding integrates Emotiva AV processors by using the Emotiva Network Remote Control protocol.
|
||||
|
||||
## Supported Things
|
||||
|
||||
This binding supports Emotiva processors with Emotiva Network Remote Control protocol support.
|
||||
The thing type for all of them is `processor`.
|
||||
|
||||
Tested models: Emotiva XMC-2
|
||||
|
||||
## Discovery
|
||||
|
||||
The binding automatically discovers devices on your network.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The Emotiva Processor thing requires the `ipAddress` it can connect to.
|
||||
There are more parameters which all have defaults set.
|
||||
|
||||
| Parameter | Values | Default |
|
||||
|-----------------------|---------------------------------------------------------------|---------|
|
||||
| ipAddress | IP address of the processor | - |
|
||||
| controlPort | port number, e.g. 7002 | 7002 |
|
||||
| notifyPort | port number, e.g. 7003 | 7003 |
|
||||
| infoPort | port number, e.g. 7004 | 7004 |
|
||||
| setupPortTCP | port number, e.g. 7100 | 7100 |
|
||||
| menuNotifyPort | port number, e.g. 7005 | 7005 |
|
||||
| protocolVersion | Emotiva Network Protocol version, e.g. 3.0 | 2.0 |
|
||||
| keepAlive | Time between notification update from device, in milliseconds | 7500 |
|
||||
| retryConnectInMinutes | Time between connection retry, in minutes | 2 |
|
||||
|
||||
|
||||
## Channels
|
||||
|
||||
The Emotiva Processor supports the following channels (some channels are model specific):
|
||||
|
||||
| Channel Type ID | Item Type | Description |
|
||||
|------------------------------------|--------------------|------------------------------------------------------------|
|
||||
| _Main zone_ | | |
|
||||
| main-zone#power | Switch (RW) | Main zone power on/off |
|
||||
| main-zone#volume | Dimmer (RW) | Main zone volume in percentage (0 to 100) |
|
||||
| main-zone#volume-db | Number (RW) | Main zone volume in dB (-96 to 15) |
|
||||
| main-zone#mute | Switch (RW) | Main zone mute |
|
||||
| main-zone#source | String (RW) | Main zone input (HDMI1, TUNER, ARC, ...) |
|
||||
| _Zone 2_ | | |
|
||||
| zone2#power | Switch (RW) | Zone 2 power on/off |
|
||||
| zone2#volume | Dimmer (RW) | Zone 2 volume in percentage (0 to 100) |
|
||||
| zone2#volume-db | Number (RW) | Zone 2 volume in dB (-80 offset) |
|
||||
| zone2#mute | Switch (RW) | Zone 2 mute |
|
||||
| zone2#input | String (RW) | Zone 2 input |
|
||||
| _General_ | | |
|
||||
| general#power | Switch (RW) | Power on/off |
|
||||
| general#standby | String (W) | Set in standby mode |
|
||||
| general#menu | String (RW) | Enter or exit menu |
|
||||
| general#menu-control | String (W) | Control menu via string commands |
|
||||
| general#up | String (W) | Menu up |
|
||||
| general#down | String (W) | Menu down |
|
||||
| general#left | String (W) | Menu left |
|
||||
| general#right | String (W) | Menu right |
|
||||
| general#enter | String (W) | Menu enter |
|
||||
| general#dim | Switch (RW) | Cycle through FP dimness settings |
|
||||
| general#mode | String (RW) | Select audio mode (auto, dts, ...) |
|
||||
| general#info | String (W) | Show info screen |
|
||||
| general#speaker-preset | String (RW) | Select speaker presets (preset1, preset2) |
|
||||
| general#center | Number (RW) | Center Volume increment up/down (0.5 step) |
|
||||
| general#subwoofer | Number (RW) | Subwoofer Volume increment up/down (0.5 step) |
|
||||
| general#surround | Number (RW) | Surround Volume increment up/down (0.5 step) |
|
||||
| general#back | Number (RW) | Back Volume increment up/down (0.5 step) |
|
||||
| general#loudness | Switch (RW) | Loudness on/off |
|
||||
| general#treble | Number (RW) | Treble Volume increment up/down (0.5 step) |
|
||||
| general#bass | Number (RW) | Bass Volume increment up/down (0.5 step) |
|
||||
| general#frequenncy | Rollershutter (W) | Frequency up/down, (100 kHz step) |
|
||||
| general#seek | Rollershutter (W) | Seek signal up/down |
|
||||
| general#channel | Rollershutter (W) | Channel up/down |
|
||||
| general#tuner-band | String (R) | Tuner band, (AM, FM) |
|
||||
| general#tuner-channel | String (RW) | User–assigned station name |
|
||||
| general#tuner-signal | String (R) | Tuner signal quality |
|
||||
| general#tuner-program | String (R) | Tuner program: "Country", "Rock", ... |
|
||||
| general#tuner-RDS | String (R) | Tuner RDS string |
|
||||
| general#audio-input | String (R) | Audio input source |
|
||||
| general#audio-bitstream | String (R) | Audio input bitstream type: "PCM 2.0", "ATMOS", etc. |
|
||||
| general#audio-bits | String (R) | Audio input bits: "32kHZ 24bits", etc. |
|
||||
| general#video-input | String (R) | Video input source |
|
||||
| general#video-format | String (R) | Video input format: "1920x1080i/60", "3840x2160p/60", etc. |
|
||||
| general#video-space | String (R) | Video input space: "YcbCr 8bits", etc. |
|
||||
| general#input-[1-8] | String (R) | User assigned input names |
|
||||
| general#selected-mode | String (R) | User selected mode for the main zone |
|
||||
| general#selected-movie-music | String (R) | User selected movie or music mode for main zone |
|
||||
| general#mode-ref-stereo | String (R) | Label for mode: Reference Stereo |
|
||||
| general#mode-stereo | String (R) | Label for mode: Stereo |
|
||||
| general#mode-music | String (R) | Label for mode: Music |
|
||||
| general#mode-movie | String (R) | Label for mode: Movie |
|
||||
| general#mode-direct | String (R) | Label for mode: Direct |
|
||||
| general#mode-dolby | String (R) | Label for mode: Dolby |
|
||||
| general#mode-dts | String (R) | Label for mode: DTS |
|
||||
| general#mode-all-stereo | String (R) | Label for mode: All Stereo |
|
||||
| general#mode-auto | String (R) | Label for mode: Auto |
|
||||
| general#mode-surround | String (RW) | Select audio mode (Auto, Stereo, Dolby, ...) |
|
||||
| general#width | Number (RW) | Width Volume increment up/down (0.5 step) |
|
||||
| general#height | Number (RW) | Height Volume increment up/down (0.5 step) |
|
||||
| general#bar | String (R) | Text displayed on front panel bar of device |
|
||||
| general#menu-display-highlight | String (R) | Menu Panel Display: Value in focus |
|
||||
| general#menu-display-top-start | String (R) | Menu Panel Display: Top bar, start cell |
|
||||
| general#menu-display-top-center | String (R) | Menu Panel Display: Top bar, center cell |
|
||||
| general#menu-display-top-end | String (R) | Menu Panel Display: Top bar, end cell |
|
||||
| general#menu-display-middle-start | String (R) | Menu Panel Display: Middle bar, start cell |
|
||||
| general#menu-display-middle-center | String (R) | Menu Panel Display: Middle bar, center cell |
|
||||
| general#menu-display-middle-end | String (R) | Menu Panel Display: Middle bar, end cell |
|
||||
| general#menu-display-bottom-start | String (R) | Menu Panel Display: Bottom bar, start cell |
|
||||
| general#menu-display-bottom-center | String (R) | Menu Panel Display: Bottom bar, center cell |
|
||||
| general#menu-display-bottom-end | String (R) | Menu Panel Display: Bottom bar, end cell |
|
||||
|
||||
(R) = read-only (no updates possible)
|
||||
(W) = write-only
|
||||
(RW) = read-write
|
||||
|
||||
## Full Example
|
||||
|
||||
### `.things` file:
|
||||
|
||||
```perl
|
||||
Thing emotiva:processor:1 "XMC-2" @ "Living room" [ipAddress="10.0.0.100", protocolVersion="3.0"]
|
||||
```
|
||||
|
||||
### `.items` file:
|
||||
|
||||
```perl
|
||||
Switch emotiva-power "Processor" {channel="emotiva:processor:1:general#power"}
|
||||
Dimmer emotiva-volume "Volume [%d %%]" {channel="emotiva:processor:1:main-zone#volume"}
|
||||
Number:Dimensionless emotiva-volume-db "Volume [%d dB]" {channel="emotiva:processor:1:main-zone#volume-db"}
|
||||
Switch emotiva-mute "Mute" {channel="emotiva:processor:1:main-zone#mute"}
|
||||
String emotiva-source "Source [%s]" {channel="emotiva:processor:1:main-zone#input"}
|
||||
String emotiva-mode-surround "Surround Mode: [%s]" {channel="emotiva:processor:1:general#mode-surround"}
|
||||
Number:Dimensionless emotiva-speakers-center "Center Trim [%.1f dB]" {channel="emotiva:processor:1:general#center"}
|
||||
Switch emotiva-zone2power "Zone 2" {channel="emotiva:processor:1:zone2#power"}
|
||||
String emotiva-front-panel-bar "Bar Text" {channel="emotiva:processor:1:general#bar"}
|
||||
String emotiva-menu-control "Menu Control" {channel="emotiva:processor:1:general#menu-control"}
|
||||
String emotiva-menu-hightlight "Menu field focus" {channel="emotiva:processor:1:general#menu-display-highlight"}
|
||||
String emotiva-menu-top-start "" <none> {channel="emotiva:processor:1:general#menu-display-top-start"}
|
||||
String emotiva-menu-top-center "" <none> {channel="emotiva:processor:1:general#menu-display-top-center"}
|
||||
String emotiva-menu-top-end "" <none> {channel="emotiva:processor:1:general#menu-display-top-end"}
|
||||
String emotiva-menu-middle-start "" <none> {channel="emotiva:processor:1:general#menu-display-middle-start"}
|
||||
String emotiva-menu-middle-center "" <none> {channel="emotiva:processor:1:general#menu-display-middle-center"}
|
||||
String emotiva-menu-middle-end "" <none> {channel="emotiva:processor:1:general#menu-display-middle-end"}
|
||||
String emotiva-menu-tottom-start "" <none> {channel="emotiva:processor:1:general#menu-display-bottom-start"}
|
||||
String emotiva-menu-tottom-center "" <none> {channel="emotiva:processor:1:general#menu-display-bottom-center"}
|
||||
String emotiva-menu-tottom-end "" <none> {channel="emotiva:processor:1:general#menu-display-bottom-end"}
|
||||
```
|
||||
|
||||
### `.sitemap` file:
|
||||
|
||||
```perl
|
||||
Group item=emotiva-input label="Processor" icon="receiver" {
|
||||
Default item=emotiva-power
|
||||
Default item=emotiva-mute
|
||||
Setpoint item=emotiva-volume
|
||||
Default item=emotiva-volume-db step=2 minValue=-96.0 maxValue=15.0
|
||||
Selection item=emotiva-source
|
||||
Text item=emotiva-mode-surround
|
||||
Setpoint item=emotiva-speakers-center step=0.5 minValue=-12.0 maxValue=12.0
|
||||
Default item=emotiva-zone2power
|
||||
}
|
||||
Frame label="Front Panel" {
|
||||
Text item=emotiva-front-panel-bar
|
||||
Text item=emotiva-menu-highlight
|
||||
Frame label="" {
|
||||
Text item=emotiva-menu-top-start
|
||||
Text item=emotiva-menu-top-center
|
||||
Text item=emotiva-menu-top-end
|
||||
}
|
||||
Frame label="" {
|
||||
Text item=emotiva-menu-middle-start
|
||||
Text item=emotiva-menu-middle-center
|
||||
Text item=emotiva-menu-middle-end
|
||||
}
|
||||
Frame label="" {
|
||||
Text item=emotiva-menu-bottom-start
|
||||
Text item=emotiva-menu-bottom-center
|
||||
Text item=emotiva-menu-bottom-end
|
||||
}
|
||||
Buttongrid label="Menu Control" staticIcon=material:control-camera item=emotiva-menu_control buttons=[1:1:POWER="Power"=switch-off , 1:2:MENU="Menu", 1:3:INFO="Info" , 2:2:UP="Up"=f7:arrowtriangle_up , 4:2:DOWN="Down"=f7:arrowtriangle_down , 3:1:LEFT="Left"=f7:arrowtriangle_left , 3:3:RIGHT="Right"=f7:arrowtriangle_right , 3:2:ENTER="Select" ]
|
||||
}
|
||||
```
|
||||
|
||||
## Network Remote Control Protocol Reference
|
||||
|
||||
These resources can be useful to learn what to send using the `command` channel:
|
||||
|
||||
- [Emotiva Remote Interface Description](https://www.dropbox.com/sh/lvo9lbhu89jqfdb/AACa4iguvWK3I6ONjIpyM5Zca/Emotiva_Remote_Interface_Description%20V3.1.docx)
|
17
bundles/org.openhab.binding.emotiva/pom.xml
Normal file
17
bundles/org.openhab.binding.emotiva/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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>4.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.emotiva</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Emotiva Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.emotiva-${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-emotiva" description="Emotiva Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.emotiva/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link EmotivaBindingConstants} class defines common constants, which are used across the whole binding.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "emotiva";
|
||||
|
||||
/** Property name to uniquely identify (discovered) things. */
|
||||
static final String UNIQUE_PROPERTY_NAME = "ip4Address";
|
||||
|
||||
/** Default port used to discover Emotiva devices. */
|
||||
static final int DEFAULT_PING_PORT = 7000;
|
||||
|
||||
/** Default port used to receive transponder (discovered) Emotiva devices. */
|
||||
static final int DEFAULT_TRANSPONDER_PORT = 7001;
|
||||
|
||||
/** Default timeout in milliseconds for sending UDP packets. */
|
||||
static final int DEFAULT_UDP_SENDING_TIMEOUT = 1000;
|
||||
|
||||
/** Number of connection attempts, set OFFLINE if no success and a retry job is then started. */
|
||||
static final int DEFAULT_CONNECTION_RETRIES = 3;
|
||||
|
||||
/** Connection retry interval in minutes */
|
||||
static final int DEFAULT_RETRY_INTERVAL_MINUTES = 2;
|
||||
|
||||
/**
|
||||
* Default Emotiva device keep alive in milliseconds. {@link org.openhab.binding.emotiva.internal.dto.ControlDTO}
|
||||
*/
|
||||
static final int DEFAULT_KEEP_ALIVE_IN_MILLISECONDS = 7500;
|
||||
|
||||
/** State name for storing keepAlive timestamp messages */
|
||||
public static final String LAST_SEEN_STATE_NAME = "no-channel#last-seen";
|
||||
|
||||
/**
|
||||
* Default Emotiva device considered list in milliseconds.
|
||||
* {@link org.openhab.binding.emotiva.internal.dto.ControlDTO}
|
||||
*/
|
||||
static final int DEFAULT_KEEP_ALIVE_CONSIDERED_LOST_IN_MILLISECONDS = 30000;
|
||||
|
||||
/** Default Emotiva control message value **/
|
||||
public static final String DEFAULT_CONTROL_MESSAGE_SET_DEFAULT_VALUE = "0";
|
||||
|
||||
/** Default value for ack property in Emotiva control messages **/
|
||||
public static final String DEFAULT_CONTROL_ACK_VALUE = "yes";
|
||||
|
||||
/** Default discovery timeout in seconds **/
|
||||
public static final int DISCOVERY_TIMEOUT_SECONDS = 5;
|
||||
|
||||
/** Default discovery broadcast address **/
|
||||
public static final String DISCOVERY_BROADCAST_ADDRESS = "255.255.255.255";
|
||||
|
||||
/** List of all Thing Type UIDs **/
|
||||
static final ThingTypeUID THING_PROCESSOR = new ThingTypeUID(BINDING_ID, "processor");
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(List.of(THING_PROCESSOR));
|
||||
|
||||
/** Default values for Emotiva channels **/
|
||||
public static final String DEFAULT_EMOTIVA_PROTOCOL_VERSION = "2.0";
|
||||
public static final int DEFAULT_VOLUME_MIN_DECIBEL = -96;
|
||||
public static final int DEFAULT_VOLUME_MAX_DECIBEL = 15;
|
||||
public static final int DEFAULT_TRIM_MIN_DECIBEL = -12;
|
||||
public static final int DEFAULT_TRIM_MAX_DECIBEL = 12;
|
||||
public static final String MAP_SOURCES_MAIN_ZONE = "sources";
|
||||
public static final String MAP_SOURCES_ZONE_2 = "zone2-sources";
|
||||
|
||||
/** Miscellaneous Constants **/
|
||||
public static final int PROTOCOL_V3_LEVEL_MULTIPLIER = 2;
|
||||
public static final String TRIM_SET_COMMAND_SUFFIX = "_trim_set";
|
||||
static final String MENU_PANEL_CHECKBOX_ON = "on";
|
||||
static final String MENU_PANEL_HIGHLIGHTED = "true";
|
||||
static final String EMOTIVA_SOURCE_COMMAND_PREFIX = "source_";
|
||||
|
||||
/** Emotiva Protocol V1 channels **/
|
||||
public static final String CHANNEL_STANDBY = "general#standby";
|
||||
public static final String CHANNEL_MAIN_ZONE_POWER = "main-zone#power";
|
||||
public static final String CHANNEL_SOURCE = "main-zone#source";
|
||||
public static final String CHANNEL_MENU = "general#menu";
|
||||
public static final String CHANNEL_MENU_CONTROL = "general#menu-control";
|
||||
public static final String CHANNEL_MENU_UP = "general#up";
|
||||
public static final String CHANNEL_MENU_DOWN = "general#down";
|
||||
public static final String CHANNEL_MENU_LEFT = "general#left";
|
||||
public static final String CHANNEL_MENU_RIGHT = "general#right";
|
||||
public static final String CHANNEL_MENU_ENTER = "general#enter";
|
||||
public static final String CHANNEL_MUTE = "main-zone#mute";
|
||||
public static final String CHANNEL_DIM = "general#dim";
|
||||
public static final String CHANNEL_MODE = "general#mode";
|
||||
public static final String CHANNEL_CENTER = "general#center";
|
||||
public static final String CHANNEL_SUBWOOFER = "general#subwoofer";
|
||||
public static final String CHANNEL_SURROUND = "general#surround";
|
||||
public static final String CHANNEL_BACK = "general#back";
|
||||
public static final String CHANNEL_MODE_SURROUND = "general#mode-surround";
|
||||
public static final String CHANNEL_SPEAKER_PRESET = "general#speaker-preset";
|
||||
public static final String CHANNEL_MAIN_VOLUME = "main-zone#volume";
|
||||
public static final String CHANNEL_MAIN_VOLUME_DB = "main-zone#volume_db";
|
||||
public static final String CHANNEL_LOUDNESS = "general#loudness";
|
||||
public static final String CHANNEL_ZONE2_POWER = "zone2#power";
|
||||
public static final String CHANNEL_ZONE2_VOLUME = "zone2#volume";
|
||||
public static final String CHANNEL_ZONE2_VOLUME_DB = "zone2#volume-db";
|
||||
public static final String CHANNEL_ZONE2_MUTE = "zone2#mute";
|
||||
public static final String CHANNEL_ZONE2_SOURCE = "zone2#source";
|
||||
public static final String CHANNEL_FREQUENCY = "general#frequency";
|
||||
public static final String CHANNEL_SEEK = "general#seek";
|
||||
public static final String CHANNEL_CHANNEL = "general#channel";
|
||||
public static final String CHANNEL_TUNER_BAND = "general#tuner-band";
|
||||
public static final String CHANNEL_TUNER_CHANNEL = "general#tuner-channel";
|
||||
public static final String CHANNEL_TUNER_CHANNEL_SELECT = "general#tuner-channel-select";
|
||||
public static final String CHANNEL_TUNER_SIGNAL = "general#tuner-signal";
|
||||
public static final String CHANNEL_TUNER_PROGRAM = "general#tuner-program";
|
||||
public static final String CHANNEL_TUNER_RDS = "general#tuner-RDS";
|
||||
public static final String CHANNEL_AUDIO_INPUT = "general#audio-input";
|
||||
public static final String CHANNEL_AUDIO_BITSTREAM = "general#audio-bitstream";
|
||||
public static final String CHANNEL_AUDIO_BITS = "general#audio-bits";
|
||||
public static final String CHANNEL_VIDEO_INPUT = "general#video-input";
|
||||
public static final String CHANNEL_VIDEO_FORMAT = "general#video-format";
|
||||
public static final String CHANNEL_VIDEO_SPACE = "general#video-space";
|
||||
public static final String CHANNEL_INPUT1 = "general#input-1";
|
||||
public static final String CHANNEL_INPUT2 = "general#input-2";
|
||||
public static final String CHANNEL_INPUT3 = "general#input-3";
|
||||
public static final String CHANNEL_INPUT4 = "general#input-4";
|
||||
public static final String CHANNEL_INPUT5 = "general#input-5";
|
||||
public static final String CHANNEL_INPUT6 = "general#input-6";
|
||||
public static final String CHANNEL_INPUT7 = "general#input-7";
|
||||
public static final String CHANNEL_INPUT8 = "general#input-8";
|
||||
public static final String CHANNEL_MODE_REF_STEREO = "general#mode-ref-stereo";
|
||||
public static final String CHANNEL_SURROUND_MODE = "general#surround-mode";
|
||||
public static final String CHANNEL_MODE_STEREO = "general#mode-stereo";
|
||||
public static final String CHANNEL_MODE_MUSIC = "general#mode-music";
|
||||
public static final String CHANNEL_MODE_MOVIE = "general#mode-movie";
|
||||
public static final String CHANNEL_MODE_DIRECT = "general#mode-direct";
|
||||
public static final String CHANNEL_MODE_DOLBY = "general#mode-dolby";
|
||||
public static final String CHANNEL_MODE_DTS = "general#mode-dts";
|
||||
public static final String CHANNEL_MODE_ALL_STEREO = "general#mode-all-stereo";
|
||||
public static final String CHANNEL_MODE_AUTO = "general#mode-auto";
|
||||
|
||||
/** Emotiva Protocol V2 channels **/
|
||||
public static final String CHANNEL_SELECTED_MODE = "general#selected-mode";
|
||||
public static final String CHANNEL_SELECTED_MOVIE_MUSIC = "general#selected-movie-music";
|
||||
|
||||
/** Emotiva Protocol V3 channels **/
|
||||
public static final String CHANNEL_TREBLE = "general#treble";
|
||||
public static final String CHANNEL_BASS = "general#bass";
|
||||
public static final String CHANNEL_WIDTH = "general#width";
|
||||
public static final String CHANNEL_HEIGHT = "general#height";
|
||||
public static final String CHANNEL_BAR = "general#bar";
|
||||
public static final String CHANNEL_MENU_DISPLAY_PREFIX = "general#menu-display";
|
||||
public static final String CHANNEL_MENU_DISPLAY_HIGHLIGHT = "general#menu-display-highlight";
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlRequest;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
import org.openhab.binding.emotiva.internal.protocol.OHChannelToEmotivaCommand;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
|
||||
/**
|
||||
* Helper class for Emotiva commands.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaCommandHelper {
|
||||
|
||||
public static PercentType volumeDecibelToPercentage(String volumeInDecibel) {
|
||||
String volumeTrimmed = volumeInDecibel.replace("dB", "").trim();
|
||||
int clampedValue = clamp(volumeTrimmed, DEFAULT_VOLUME_MIN_DECIBEL, DEFAULT_VOLUME_MAX_DECIBEL);
|
||||
return new PercentType(Math.round((100 - ((float) Math.abs(clampedValue - DEFAULT_VOLUME_MAX_DECIBEL)
|
||||
/ Math.abs(DEFAULT_VOLUME_MIN_DECIBEL - DEFAULT_VOLUME_MAX_DECIBEL)) * 100)));
|
||||
}
|
||||
|
||||
public static double integerToPercentage(int integer) {
|
||||
int clampedValue = clamp(integer, 0, 100);
|
||||
return Math.round((100 - ((float) Math.abs(clampedValue - 100) / Math.abs(-100)) * 100));
|
||||
}
|
||||
|
||||
public static int volumePercentageToDecibel(int volumeInPercentage) {
|
||||
int clampedValue = clamp(volumeInPercentage, 0, 100);
|
||||
return (clampedValue * (DEFAULT_VOLUME_MAX_DECIBEL - DEFAULT_VOLUME_MIN_DECIBEL) / 100)
|
||||
+ DEFAULT_VOLUME_MIN_DECIBEL;
|
||||
}
|
||||
|
||||
public static int volumePercentageToDecibel(String volumeInPercentage) {
|
||||
String volumeInPercentageTrimmed = volumeInPercentage.replace("%", "").trim();
|
||||
int clampedValue = clamp(volumeInPercentageTrimmed, 0, 100);
|
||||
return (clampedValue * (DEFAULT_VOLUME_MAX_DECIBEL - DEFAULT_VOLUME_MIN_DECIBEL) / 100)
|
||||
+ DEFAULT_VOLUME_MIN_DECIBEL;
|
||||
}
|
||||
|
||||
public static double clamp(Number value, double min, double max) {
|
||||
return Math.min(Math.max(value.intValue(), min), max);
|
||||
}
|
||||
|
||||
private static int clamp(String volumeInPercentage, int min, int max) {
|
||||
return Math.min(Math.max(Double.valueOf(volumeInPercentage.trim()).intValue(), min), max);
|
||||
}
|
||||
|
||||
private static int clamp(int volumeInPercentage, int min, int max) {
|
||||
return Math.min(Math.max(Double.valueOf(volumeInPercentage).intValue(), min), max);
|
||||
}
|
||||
|
||||
public static EmotivaControlRequest channelToControlRequest(String id,
|
||||
Map<String, Map<EmotivaControlCommands, String>> commandMaps, EmotivaProtocolVersion protocolVersion) {
|
||||
EmotivaSubscriptionTags channelSubscription = EmotivaSubscriptionTags.fromChannelUID(id);
|
||||
EmotivaControlCommands channelFromCommand = OHChannelToEmotivaCommand.fromChannelUID(id);
|
||||
return new EmotivaControlRequest(id, channelSubscription, channelFromCommand, commandMaps, protocolVersion);
|
||||
}
|
||||
|
||||
public static String getMenuPanelRowLabel(int row) {
|
||||
return switch (row) {
|
||||
case 4 -> "top";
|
||||
case 5 -> "middle";
|
||||
case 6 -> "bottom";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
public static String getMenuPanelColumnLabel(int column) {
|
||||
return switch (column) {
|
||||
case 0 -> "start";
|
||||
case 1 -> "center";
|
||||
case 2 -> "end";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
public static String updateProgress(double progressPercentage) {
|
||||
final int width = 30;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("[");
|
||||
int i = 0;
|
||||
for (; i <= (int) (progressPercentage * width); i++) {
|
||||
sb.append(".");
|
||||
}
|
||||
for (; i < width; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link EmotivaConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaConfiguration {
|
||||
|
||||
public String ipAddress = "";
|
||||
public int controlPort = 7002;
|
||||
public int notifyPort = 7003;
|
||||
public int infoPort = 7004;
|
||||
public int setupPortTCP = 7100;
|
||||
public int menuNotifyPort = 7005;
|
||||
public String protocolVersion = DEFAULT_EMOTIVA_PROTOCOL_VERSION;
|
||||
public int keepAlive = DEFAULT_KEEP_ALIVE_IN_MILLISECONDS;
|
||||
public int retryConnectInMinutes = DEFAULT_RETRY_INTERVAL_MINUTES;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.THING_PROCESSOR;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
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.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link org.openhab.core.thing.binding.ThingHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.emotiva", service = ThingHandlerFactory.class)
|
||||
public class EmotivaHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaHandlerFactory.class);
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_PROCESSOR);
|
||||
private final EmotivaTranslationProvider i18nProvider;
|
||||
|
||||
@Activate
|
||||
public EmotivaHandlerFactory(final @Reference EmotivaTranslationProvider i18nProvider) {
|
||||
this.i18nProvider = i18nProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_PROCESSOR.equals(thingTypeUID)) {
|
||||
try {
|
||||
return new EmotivaProcessorHandler(thing, i18nProvider);
|
||||
} catch (JAXBException e) {
|
||||
logger.debug("Could not create Emotiva Process Handler", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,786 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.channelToControlRequest;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.getMenuPanelColumnLabel;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.getMenuPanelRowLabel;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.updateProgress;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumeDecibelToPercentage;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumePercentageToDecibel;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_am;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.band_fm;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.channel_1;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.none;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.power_on;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.STRING;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus.NOT_VALID;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.protocolFromConfig;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.noSubscriptionToChannel;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.measure.quantity.Frequency;
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.dto.AbstractNotificationDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaAckDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaBarNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaBarNotifyWrapper;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaMenuNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaNotifyWrapper;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaPropertyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaSubscriptionResponse;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUpdateResponse;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlRequest;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaDataType;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaUdpResponse;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
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.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
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 EmotivaProcessorHandler is responsible for handling OpenHAB commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaProcessorHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaProcessorHandler.class);
|
||||
|
||||
private final Map<String, State> stateMap = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
private final EmotivaConfiguration config;
|
||||
|
||||
/**
|
||||
* Emotiva devices have trouble with too many subscriptions in same request, so subscriptions are dividing into
|
||||
* those general group channels, and the rest.
|
||||
*/
|
||||
private final EmotivaSubscriptionTags[] generalSubscription = EmotivaSubscriptionTags.generalChannels();
|
||||
private final EmotivaSubscriptionTags[] nonGeneralSubscriptions = EmotivaSubscriptionTags.nonGeneralChannels();
|
||||
|
||||
private final EnumMap<EmotivaControlCommands, String> sourcesMainZone;
|
||||
private final EnumMap<EmotivaControlCommands, String> sourcesZone2;
|
||||
private final EnumMap<EmotivaSubscriptionTags, String> modes;
|
||||
private final Map<String, Map<EmotivaControlCommands, String>> commandMaps = new ConcurrentHashMap<>();
|
||||
private final EmotivaTranslationProvider i18nProvider;
|
||||
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
private @Nullable ScheduledFuture<?> connectRetryJob;
|
||||
private @Nullable EmotivaUdpSendingService sendingService;
|
||||
private @Nullable EmotivaUdpReceivingService notifyListener;
|
||||
private @Nullable EmotivaUdpReceivingService menuNotifyListener;
|
||||
|
||||
private final int retryConnectInMinutes;
|
||||
|
||||
/**
|
||||
* Thread factory for menu progress bar
|
||||
*/
|
||||
private final NamedThreadFactory listeningThreadFactory = new NamedThreadFactory(BINDING_ID, true);
|
||||
|
||||
private final EmotivaXmlUtils xmlUtils = new EmotivaXmlUtils();
|
||||
|
||||
private boolean udpSenderActive = false;
|
||||
|
||||
public EmotivaProcessorHandler(Thing thing, EmotivaTranslationProvider i18nProvider) throws JAXBException {
|
||||
super(thing);
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.config = getConfigAs(EmotivaConfiguration.class);
|
||||
this.retryConnectInMinutes = config.retryConnectInMinutes;
|
||||
|
||||
sourcesMainZone = new EnumMap<>(EmotivaControlCommands.class);
|
||||
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
|
||||
|
||||
sourcesZone2 = new EnumMap<>(EmotivaControlCommands.class);
|
||||
commandMaps.put(MAP_SOURCES_ZONE_2, sourcesZone2);
|
||||
|
||||
EnumMap<EmotivaControlCommands, String> channels = new EnumMap<>(
|
||||
Map.ofEntries(Map.entry(channel_1, channel_1.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_2, EmotivaControlCommands.channel_2.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_3, EmotivaControlCommands.channel_3.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_4, EmotivaControlCommands.channel_4.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_5, EmotivaControlCommands.channel_5.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_6, EmotivaControlCommands.channel_6.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_7, EmotivaControlCommands.channel_7.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_8, EmotivaControlCommands.channel_8.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_9, EmotivaControlCommands.channel_9.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_10, EmotivaControlCommands.channel_10.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_11, EmotivaControlCommands.channel_11.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_12, EmotivaControlCommands.channel_12.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_13, EmotivaControlCommands.channel_13.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_14, EmotivaControlCommands.channel_14.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_15, EmotivaControlCommands.channel_15.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_16, EmotivaControlCommands.channel_16.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_17, EmotivaControlCommands.channel_17.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_18, EmotivaControlCommands.channel_18.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_19, EmotivaControlCommands.channel_19.getLabel()),
|
||||
Map.entry(EmotivaControlCommands.channel_20, EmotivaControlCommands.channel_20.getLabel())));
|
||||
commandMaps.put(tuner_channel.getEmotivaName(), channels);
|
||||
|
||||
EnumMap<EmotivaControlCommands, String> bands = new EnumMap<>(
|
||||
Map.of(band_am, band_am.getLabel(), band_fm, band_fm.getLabel()));
|
||||
commandMaps.put(tuner_band.getEmotivaName(), bands);
|
||||
|
||||
modes = new EnumMap<>(EmotivaSubscriptionTags.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initialize: '{}'", getThing().getUID());
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/message.processor.connecting");
|
||||
if (config.controlPort < 0) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/message.processor.connection.error.port");
|
||||
return;
|
||||
}
|
||||
if (config.ipAddress.trim().isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/message.processor.connection.error.address-empty");
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
InetAddress.getByName(config.ipAddress);
|
||||
} catch (UnknownHostException ignored) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/message.processor.connection.error.address-invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.execute(this::connect);
|
||||
}
|
||||
|
||||
private synchronized void connect() {
|
||||
final EmotivaConfiguration localConfig = config;
|
||||
try {
|
||||
final EmotivaUdpReceivingService notifyListener = new EmotivaUdpReceivingService(localConfig.notifyPort,
|
||||
localConfig, scheduler);
|
||||
this.notifyListener = notifyListener;
|
||||
notifyListener.connect(this::handleStatusUpdate, true);
|
||||
|
||||
final EmotivaUdpSendingService sendConnector = new EmotivaUdpSendingService(localConfig, scheduler);
|
||||
sendingService = sendConnector;
|
||||
sendConnector.connect(this::handleStatusUpdate, true);
|
||||
|
||||
// Simple retry mechanism to handle minor network issues, if this fails a retry job is created
|
||||
for (int attempt = 1; attempt <= DEFAULT_CONNECTION_RETRIES && !udpSenderActive; attempt++) {
|
||||
try {
|
||||
logger.debug("Connection attempt '{}'", attempt);
|
||||
sendConnector.sendSubscription(generalSubscription, config);
|
||||
sendConnector.sendSubscription(nonGeneralSubscriptions, config);
|
||||
} catch (IOException e) {
|
||||
// network or socket failure, also wait 2 sec and try again
|
||||
}
|
||||
|
||||
for (int delay = 0; delay < 10 && !udpSenderActive; delay++) {
|
||||
Thread.sleep(200); // wait 10 x 200ms = 2sec
|
||||
}
|
||||
}
|
||||
|
||||
if (udpSenderActive) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
||||
final EmotivaUdpReceivingService menuListenerConnector = new EmotivaUdpReceivingService(
|
||||
localConfig.menuNotifyPort, localConfig, scheduler);
|
||||
this.menuNotifyListener = menuListenerConnector;
|
||||
menuListenerConnector.connect(this::handleStatusUpdate, true);
|
||||
|
||||
startPollingKeepAlive();
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"@text/message.processor.connection.failed");
|
||||
disconnect();
|
||||
scheduleConnectRetry(retryConnectInMinutes);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// OH shutdown - don't log anything, Framework will call dispose()
|
||||
} catch (Exception e) {
|
||||
logger.error("Connection to '{}' failed", localConfig.ipAddress, e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR,
|
||||
"@text/message.processor.connection.failed");
|
||||
disconnect();
|
||||
scheduleConnectRetry(retryConnectInMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleConnectRetry(long waitMinutes) {
|
||||
logger.debug("Scheduling connection retry in '{}' minutes", waitMinutes);
|
||||
connectRetryJob = scheduler.schedule(this::connect, waitMinutes, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a polling job for connection to th device, adds the
|
||||
* {@link EmotivaBindingConstants#DEFAULT_KEEP_ALIVE_IN_MILLISECONDS} as a time buffer for checking, to avoid
|
||||
* flapping state or minor network issues.
|
||||
*/
|
||||
private void startPollingKeepAlive() {
|
||||
final ScheduledFuture<?> localRefreshJob = this.pollingJob;
|
||||
if (localRefreshJob == null || localRefreshJob.isCancelled()) {
|
||||
logger.debug("Start polling");
|
||||
|
||||
int delay = stateMap.get(EmotivaSubscriptionTags.keepAlive.name()) != null
|
||||
&& stateMap.get(EmotivaSubscriptionTags.keepAlive.name()) instanceof Number keepAlive
|
||||
? keepAlive.intValue()
|
||||
: config.keepAlive;
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(this::checkKeepAliveTimestamp,
|
||||
delay + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS, delay + DEFAULT_KEEP_ALIVE_IN_MILLISECONDS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkKeepAliveTimestamp() {
|
||||
if (ThingStatus.ONLINE.equals(getThing().getStatusInfo().getStatus())) {
|
||||
State state = stateMap.get(LAST_SEEN_STATE_NAME);
|
||||
if (state instanceof Number value) {
|
||||
Instant lastKeepAliveMessageTimestamp = Instant.ofEpochSecond(value.longValue());
|
||||
Instant deviceGoneGracePeriod = Instant.now().minus(config.keepAlive, ChronoUnit.MILLIS)
|
||||
.minus(DEFAULT_KEEP_ALIVE_CONSIDERED_LOST_IN_MILLISECONDS, ChronoUnit.MILLIS);
|
||||
if (lastKeepAliveMessageTimestamp.isBefore(deviceGoneGracePeriod)) {
|
||||
logger.debug(
|
||||
"Last KeepAlive message received '{}', over grace-period by '{}', consider '{}' gone, setting OFFLINE and disposing",
|
||||
lastKeepAliveMessageTimestamp,
|
||||
Duration.between(lastKeepAliveMessageTimestamp, deviceGoneGracePeriod),
|
||||
thing.getThingTypeUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/message.processor.connection.error.keep-alive");
|
||||
// Connection lost, avoid sending unsubscription messages
|
||||
udpSenderActive = false;
|
||||
disconnect();
|
||||
scheduleConnectRetry(retryConnectInMinutes);
|
||||
}
|
||||
}
|
||||
} else if (ThingStatus.OFFLINE.equals(getThing().getStatusInfo().getStatus())) {
|
||||
logger.debug("Keep alive pool job, '{}' is '{}'", getThing().getThingTypeUID(),
|
||||
getThing().getStatusInfo().getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStatusUpdate(EmotivaUdpResponse emotivaUdpResponse) {
|
||||
udpSenderActive = true;
|
||||
logger.debug("Received data from '{}' with length '{}'", emotivaUdpResponse.ipAddress(),
|
||||
emotivaUdpResponse.answer().length());
|
||||
|
||||
Object object;
|
||||
try {
|
||||
object = xmlUtils.unmarshallToEmotivaDTO(emotivaUdpResponse.answer());
|
||||
} catch (JAXBException e) {
|
||||
logger.debug("Could not unmarshal answer from '{}' with length '{}' and content '{}'",
|
||||
emotivaUdpResponse.ipAddress(), emotivaUdpResponse.answer().length(), emotivaUdpResponse.answer(),
|
||||
e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (object instanceof EmotivaAckDTO answerDto) {
|
||||
// Currently not supported to revert a failed command update, just used for logging for now.
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaAckDTO.class.getSimpleName(), answerDto);
|
||||
|
||||
} else if (object instanceof EmotivaBarNotifyWrapper answerDto) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaBarNotifyWrapper.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
|
||||
List<EmotivaBarNotifyDTO> emotivaBarNotifies = xmlUtils.unmarshallToBarNotify(answerDto.getTags());
|
||||
|
||||
if (!emotivaBarNotifies.isEmpty()) {
|
||||
if (emotivaBarNotifies.get(0).getType() != null) {
|
||||
findChannelDatatypeAndUpdateChannel(CHANNEL_BAR, emotivaBarNotifies.get(0).formattedMessage(),
|
||||
STRING);
|
||||
}
|
||||
}
|
||||
} else if (object instanceof EmotivaNotifyWrapper answerDto) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaNotifyWrapper.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
handleNotificationUpdate(answerDto);
|
||||
} else if (object instanceof EmotivaUpdateResponse answerDto) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaUpdateResponse.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
handleNotificationUpdate(answerDto);
|
||||
} else if (object instanceof EmotivaMenuNotifyDTO answerDto) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaMenuNotifyDTO.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
|
||||
if (answerDto.getRow() != null) {
|
||||
handleMenuNotify(answerDto);
|
||||
} else if (answerDto.getProgress() != null && answerDto.getProgress().getTime() != null) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaMenuNotifyDTO.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
listeningThreadFactory
|
||||
.newThread(() -> handleMenuNotifyProgressMessage(answerDto.getProgress().getTime())).start();
|
||||
}
|
||||
} else if (object instanceof EmotivaSubscriptionResponse answerDto) {
|
||||
logger.trace("Processing received '{}' with '{}'", EmotivaSubscriptionResponse.class.getSimpleName(),
|
||||
emotivaUdpResponse.answer());
|
||||
// Populates static input sources, except input
|
||||
sourcesMainZone.putAll(EmotivaControlCommands.getCommandsFromType(EmotivaCommandType.SOURCE_MAIN_ZONE));
|
||||
sourcesMainZone.remove(EmotivaControlCommands.input);
|
||||
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
|
||||
|
||||
sourcesZone2.putAll(EmotivaControlCommands.getCommandsFromType(EmotivaCommandType.SOURCE_ZONE2));
|
||||
sourcesZone2.remove(EmotivaControlCommands.zone2_input);
|
||||
commandMaps.put(MAP_SOURCES_ZONE_2, sourcesZone2);
|
||||
|
||||
if (answerDto.getProperties() == null) {
|
||||
for (EmotivaNotifyDTO dto : xmlUtils.unmarshallToNotification(answerDto.getTags())) {
|
||||
handleChannelUpdate(dto.getName(), dto.getValue(), dto.getVisible(), dto.getAck());
|
||||
}
|
||||
} else {
|
||||
for (EmotivaPropertyDTO property : answerDto.getProperties()) {
|
||||
handleChannelUpdate(property.getName(), property.getValue(), property.getVisible(),
|
||||
property.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMenuNotify(EmotivaMenuNotifyDTO answerDto) {
|
||||
String highlightValue = "";
|
||||
|
||||
for (var row = 4; row <= 6; row++) {
|
||||
var emotivaMenuRow = answerDto.getRow().get(row);
|
||||
logger.debug("Checking row '{}' with '{}' columns", row, emotivaMenuRow.getCol().size());
|
||||
for (var column = 0; column <= 2; column++) {
|
||||
var emotivaMenuCol = emotivaMenuRow.getCol().get(column);
|
||||
String cellValue = "";
|
||||
if (emotivaMenuCol.getValue() != null) {
|
||||
cellValue = emotivaMenuCol.getValue();
|
||||
}
|
||||
|
||||
if (emotivaMenuCol.getCheckbox() != null) {
|
||||
cellValue = MENU_PANEL_CHECKBOX_ON.equalsIgnoreCase(emotivaMenuCol.getCheckbox().trim()) ? "☑"
|
||||
: "☐";
|
||||
}
|
||||
|
||||
if (emotivaMenuCol.getHighlight() != null
|
||||
&& MENU_PANEL_HIGHLIGHTED.equalsIgnoreCase(emotivaMenuCol.getHighlight().trim())) {
|
||||
logger.debug("Highlight is at row '{}' column '{}' value '{}'", row, column, cellValue);
|
||||
highlightValue = cellValue;
|
||||
}
|
||||
|
||||
var channelName = format("%s-%s-%s", CHANNEL_MENU_DISPLAY_PREFIX, getMenuPanelRowLabel(row),
|
||||
getMenuPanelColumnLabel(column));
|
||||
updateChannelState(channelName, new StringType(cellValue));
|
||||
}
|
||||
}
|
||||
updateChannelState(CHANNEL_MENU_DISPLAY_HIGHLIGHT, new StringType(highlightValue));
|
||||
}
|
||||
|
||||
private void handleMenuNotifyProgressMessage(String progressBarTimeInSeconds) {
|
||||
try {
|
||||
var seconds = Integer.parseInt(progressBarTimeInSeconds);
|
||||
for (var count = 0; seconds >= count; count++) {
|
||||
updateChannelState(CHANNEL_MENU_DISPLAY_HIGHLIGHT,
|
||||
new StringType(updateProgress(EmotivaCommandHelper.integerToPercentage(count))));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Menu progress bar time value '{}' is not a valid integer", progressBarTimeInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetMenuPanelChannels() {
|
||||
logger.debug("Resetting Menu Panel Display");
|
||||
for (var row = 4; row <= 6; row++) {
|
||||
for (var column = 0; column <= 2; column++) {
|
||||
var channelName = format("%s-%s-%s", CHANNEL_MENU_DISPLAY_PREFIX, getMenuPanelRowLabel(row),
|
||||
getMenuPanelColumnLabel(column));
|
||||
updateChannelState(channelName, new StringType(""));
|
||||
}
|
||||
}
|
||||
updateChannelState(CHANNEL_MENU_DISPLAY_HIGHLIGHT, new StringType(""));
|
||||
}
|
||||
|
||||
private void sendEmotivaUpdate(EmotivaControlCommands tags) {
|
||||
EmotivaUdpSendingService localSendingService = sendingService;
|
||||
if (localSendingService != null) {
|
||||
try {
|
||||
localSendingService.sendUpdate(tags, config);
|
||||
} catch (InterruptedIOException e) {
|
||||
logger.debug("Interrupted during sending of EmotivaUpdate message to device '{}'",
|
||||
this.getThing().getThingTypeUID(), e);
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to send EmotivaUpdate message to device '{}'", this.getThing().getThingTypeUID(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNotificationUpdate(AbstractNotificationDTO answerDto) {
|
||||
if (answerDto.getProperties() == null) {
|
||||
for (EmotivaNotifyDTO tag : xmlUtils.unmarshallToNotification(answerDto.getTags())) {
|
||||
try {
|
||||
EmotivaSubscriptionTags tagName = EmotivaSubscriptionTags.valueOf(tag.getName());
|
||||
if (EmotivaSubscriptionTags.hasChannel(tag.getName())) {
|
||||
findChannelDatatypeAndUpdateChannel(tagName.getChannel(), tag.getValue(),
|
||||
tagName.getDataType());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Subscription name '{}' could not be mapped to a channel", tag.getName());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (EmotivaPropertyDTO property : answerDto.getProperties()) {
|
||||
handleChannelUpdate(property.getName(), property.getValue(), property.getVisible(),
|
||||
property.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleChannelUpdate(String emotivaSubscriptionName, String value, String visible, String status) {
|
||||
logger.debug("Handling channel update for '{}' with value '{}'", emotivaSubscriptionName, value);
|
||||
|
||||
if (status.equals(NOT_VALID.name())) {
|
||||
logger.debug("Subscription property '{}' not present in device, skipping", emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("None".equals(value)) {
|
||||
logger.debug("No value present for channel {}, usually means a speaker is not enabled",
|
||||
emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
EmotivaSubscriptionTags.hasChannel(emotivaSubscriptionName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Subscription property '{}' is not know to the binding, might need updating",
|
||||
emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (noSubscriptionToChannel().contains(EmotivaSubscriptionTags.valueOf(emotivaSubscriptionName))) {
|
||||
logger.debug("Initial subscription status update for {}, skipping, only want notifications",
|
||||
emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
EmotivaSubscriptionTags subscriptionTag;
|
||||
try {
|
||||
subscriptionTag = EmotivaSubscriptionTags.valueOf(emotivaSubscriptionName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Property '{}' could not be mapped subscription tag, skipping", emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subscriptionTag.getChannel().isEmpty()) {
|
||||
logger.debug("Subscription property '{}' does not have a corresponding channel, skipping",
|
||||
emotivaSubscriptionName);
|
||||
return;
|
||||
}
|
||||
|
||||
String trimmedValue = value.trim();
|
||||
|
||||
logger.debug("Found subscription '{}' for '{}' and value '{}'", subscriptionTag, emotivaSubscriptionName,
|
||||
trimmedValue);
|
||||
|
||||
// Add/Update user assigned name for inputs
|
||||
if (subscriptionTag.getChannel().startsWith(CHANNEL_INPUT1.substring(0, CHANNEL_INPUT1.indexOf("-") + 1))
|
||||
&& "true".equals(visible)) {
|
||||
logger.debug("Adding '{}' to dynamic source input list", trimmedValue);
|
||||
sourcesMainZone.put(EmotivaControlCommands.matchToInput(subscriptionTag.name()), trimmedValue);
|
||||
commandMaps.put(MAP_SOURCES_MAIN_ZONE, sourcesMainZone);
|
||||
|
||||
logger.debug("sources list is now {}", sourcesMainZone.size());
|
||||
}
|
||||
|
||||
// Add/Update audio modes
|
||||
if (subscriptionTag.getChannel().startsWith(CHANNEL_MODE + "-") && "true".equals(visible)) {
|
||||
String modeName = i18nProvider.getText("channel-type.emotiva.selected-mode.option."
|
||||
+ subscriptionTag.getChannel().substring(subscriptionTag.getChannel().indexOf("_") + 1));
|
||||
logger.debug("Adding '{} ({})' from channel '{}' to dynamic mode list", trimmedValue, modeName,
|
||||
subscriptionTag.getChannel());
|
||||
modes.put(EmotivaSubscriptionTags.fromChannelUID(subscriptionTag.getChannel()), trimmedValue);
|
||||
}
|
||||
|
||||
findChannelDatatypeAndUpdateChannel(subscriptionTag.getChannel(), trimmedValue,
|
||||
subscriptionTag.getDataType());
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("Error updating subscription property '{}'", emotivaSubscriptionName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void findChannelDatatypeAndUpdateChannel(String channelName, String value, EmotivaDataType dataType) {
|
||||
switch (dataType) {
|
||||
case DIMENSIONLESS_DECIBEL -> {
|
||||
var trimmedString = value.replaceAll("[ +]", "");
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, QuantityType.class.getSimpleName(),
|
||||
trimmedString);
|
||||
if (channelName.equals(CHANNEL_MAIN_VOLUME)) {
|
||||
updateVolumeChannels(trimmedString, CHANNEL_MUTE, channelName, CHANNEL_MAIN_VOLUME_DB);
|
||||
} else if (channelName.equals(CHANNEL_ZONE2_VOLUME)) {
|
||||
updateVolumeChannels(trimmedString, CHANNEL_ZONE2_MUTE, channelName, CHANNEL_ZONE2_VOLUME_DB);
|
||||
} else {
|
||||
if (trimmedString.equals("None")) {
|
||||
updateChannelState(channelName, QuantityType.valueOf(0, Units.DECIBEL));
|
||||
} else {
|
||||
updateChannelState(channelName,
|
||||
QuantityType.valueOf(Double.parseDouble(trimmedString), Units.DECIBEL));
|
||||
}
|
||||
}
|
||||
}
|
||||
case DIMENSIONLESS_PERCENT -> {
|
||||
var trimmedString = value.replaceAll("[ +]", "");
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, PercentType.class.getSimpleName(), value);
|
||||
updateChannelState(channelName, PercentType.valueOf(trimmedString));
|
||||
}
|
||||
case FREQUENCY_HERTZ -> {
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, Units.HERTZ.getClass().getSimpleName(),
|
||||
value);
|
||||
if (!value.isEmpty()) {
|
||||
// Getting rid of characters and empty space leaves us with the raw frequency
|
||||
try {
|
||||
String frequencyString = value.replaceAll("[a-zA-Z ]", "");
|
||||
QuantityType<Frequency> hz = QuantityType.valueOf(0, Units.HERTZ);
|
||||
if (value.contains("AM")) {
|
||||
hz = QuantityType.valueOf(Double.parseDouble(frequencyString) * 1000, Units.HERTZ);
|
||||
} else if (value.contains("FM")) {
|
||||
hz = QuantityType.valueOf(Double.parseDouble(frequencyString) * 1000000, Units.HERTZ);
|
||||
}
|
||||
updateChannelState(CHANNEL_TUNER_CHANNEL, hz);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.debug("Could not extract radio tuner frequency from '{}'", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
case GOODBYE -> {
|
||||
logger.info(
|
||||
"Received goodbye notification from '{}'; disconnecting and scheduling av connection retry in '{}' minutes",
|
||||
getThing().getUID(), DEFAULT_RETRY_INTERVAL_MINUTES);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE, "@text/message.processor.goodbye");
|
||||
|
||||
// Device gone, sending unsubscription messages not needed
|
||||
udpSenderActive = false;
|
||||
disconnect();
|
||||
scheduleConnectRetry(retryConnectInMinutes);
|
||||
}
|
||||
case NUMBER_TIME -> {
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, Number.class.getSimpleName(), value);
|
||||
long nowEpochSecond = Instant.now().getEpochSecond();
|
||||
updateChannelState(channelName, new QuantityType<>(nowEpochSecond, Units.SECOND));
|
||||
}
|
||||
case ON_OFF -> {
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, OnOffType.class.getSimpleName(), value);
|
||||
OnOffType switchValue = OnOffType.from(value.trim().toUpperCase());
|
||||
updateChannelState(channelName, switchValue);
|
||||
if (switchValue.equals(OnOffType.OFF) && CHANNEL_MENU.equals(channelName)) {
|
||||
resetMenuPanelChannels();
|
||||
}
|
||||
}
|
||||
case STRING -> {
|
||||
logger.debug("Update channel '{}' to '{}:{}'", channelName, StringType.class.getSimpleName(), value);
|
||||
updateChannelState(channelName, StringType.valueOf(value));
|
||||
}
|
||||
case UNKNOWN -> // Do nothing, types not connect to channels
|
||||
logger.debug("Channel '{}' with UNKNOWN type and value '{}' was not updated", channelName, value);
|
||||
default -> {
|
||||
// datatypes not connect to a channel, so do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChannelState(String channelID, State state) {
|
||||
stateMap.put(channelID, state);
|
||||
logger.trace("Updating channel '{}' with '{}'", channelID, state);
|
||||
updateState(channelID, state);
|
||||
}
|
||||
|
||||
private void updateVolumeChannels(String value, String muteChannel, String volumeChannel, String volumeDbChannel) {
|
||||
if ("Mute".equals(value)) {
|
||||
updateChannelState(muteChannel, OnOffType.ON);
|
||||
} else {
|
||||
updateChannelState(volumeChannel, volumeDecibelToPercentage(value));
|
||||
updateChannelState(volumeDbChannel, QuantityType.valueOf(Double.parseDouble(value), Units.DECIBEL));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command ohCommand) {
|
||||
logger.debug("Handling ohCommand '{}:{}' for '{}'", channelUID.getId(), ohCommand, channelUID.getThingUID());
|
||||
EmotivaUdpSendingService localSendingService = sendingService;
|
||||
|
||||
if (localSendingService != null) {
|
||||
EmotivaControlRequest emotivaRequest = channelToControlRequest(channelUID.getId(), commandMaps,
|
||||
protocolFromConfig(config.protocolVersion));
|
||||
if (ohCommand instanceof RefreshType) {
|
||||
stateMap.remove(channelUID.getId());
|
||||
|
||||
if (emotivaRequest.getDefaultCommand().equals(none)) {
|
||||
logger.debug("Found controlCommand 'none' for request '{}' from channel '{}' with RefreshType",
|
||||
emotivaRequest.getName(), channelUID);
|
||||
} else {
|
||||
logger.debug("Sending EmotivaUpdate for '{}'", emotivaRequest);
|
||||
sendEmotivaUpdate(emotivaRequest.getDefaultCommand());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
EmotivaControlDTO dto = emotivaRequest.createDTO(ohCommand, stateMap.get(channelUID.getId()));
|
||||
localSendingService.send(dto);
|
||||
|
||||
if (emotivaRequest.getName().equals(EmotivaControlCommands.volume.name())) {
|
||||
if (ohCommand instanceof PercentType value) {
|
||||
updateChannelState(CHANNEL_MAIN_VOLUME_DB,
|
||||
QuantityType.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
|
||||
} else if (ohCommand instanceof QuantityType<?> value) {
|
||||
updateChannelState(CHANNEL_MAIN_VOLUME, volumeDecibelToPercentage(value.toString()));
|
||||
}
|
||||
} else if (emotivaRequest.getName().equals(EmotivaControlCommands.zone2_volume.name())) {
|
||||
if (ohCommand instanceof PercentType value) {
|
||||
updateChannelState(CHANNEL_ZONE2_VOLUME_DB,
|
||||
QuantityType.valueOf(volumePercentageToDecibel(value.intValue()), Units.DECIBEL));
|
||||
} else if (ohCommand instanceof QuantityType<?> value) {
|
||||
updateChannelState(CHANNEL_ZONE2_VOLUME, volumeDecibelToPercentage(value.toString()));
|
||||
}
|
||||
} else if (ohCommand instanceof OnOffType value) {
|
||||
if (value.equals(OnOffType.ON) && emotivaRequest.getOnCommand().equals(power_on)) {
|
||||
localSendingService.sendUpdate(EmotivaSubscriptionTags.speakerChannels(), config);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedIOException e) {
|
||||
logger.debug("Interrupted during updating state for channel: '{}:{}:{}'", channelUID.getId(),
|
||||
emotivaRequest.getName(), emotivaRequest.getDataType(), e);
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed updating state for channel '{}:{}:{}'", channelUID.getId(),
|
||||
emotivaRequest.getName(), emotivaRequest.getDataType(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.debug("Disposing '{}'", getThing().getUID());
|
||||
|
||||
disconnect();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private synchronized void disconnect() {
|
||||
final EmotivaUdpSendingService localSendingService = sendingService;
|
||||
if (localSendingService != null) {
|
||||
logger.debug("Disposing active sender");
|
||||
if (udpSenderActive) {
|
||||
try {
|
||||
// Unsubscribe before disconnect
|
||||
localSendingService.sendUnsubscribe(generalSubscription);
|
||||
localSendingService.sendUnsubscribe(nonGeneralSubscriptions);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Failed to unsubscribe for '{}'", config.ipAddress, e);
|
||||
}
|
||||
}
|
||||
|
||||
sendingService = null;
|
||||
try {
|
||||
localSendingService.disconnect();
|
||||
logger.debug("Disconnected udp send connector");
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed to close socket connection for '{}'", config.ipAddress, e);
|
||||
}
|
||||
}
|
||||
udpSenderActive = false;
|
||||
|
||||
final EmotivaUdpReceivingService notifyConnector = notifyListener;
|
||||
if (notifyConnector != null) {
|
||||
notifyListener = null;
|
||||
try {
|
||||
notifyConnector.disconnect();
|
||||
logger.debug("Disconnected notify connector");
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to close socket connection for: '{}:{}'", config.ipAddress, config.notifyPort, e);
|
||||
}
|
||||
}
|
||||
|
||||
final EmotivaUdpReceivingService menuConnector = menuNotifyListener;
|
||||
if (menuConnector != null) {
|
||||
menuNotifyListener = null;
|
||||
try {
|
||||
menuConnector.disconnect();
|
||||
logger.debug("Disconnected menu notify connector");
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to close socket connection for: '{}:{}'", config.ipAddress, config.notifyPort, e);
|
||||
}
|
||||
}
|
||||
|
||||
ScheduledFuture<?> localConnectRetryJob = this.connectRetryJob;
|
||||
if (localConnectRetryJob != null) {
|
||||
localConnectRetryJob.cancel(true);
|
||||
this.connectRetryJob = null;
|
||||
}
|
||||
|
||||
ScheduledFuture<?> localPollingJob = this.pollingJob;
|
||||
if (localPollingJob != null) {
|
||||
localPollingJob.cancel(true);
|
||||
this.pollingJob = null;
|
||||
logger.debug("Polling job canceled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Set.of(InputStateOptionProvider.class);
|
||||
}
|
||||
|
||||
public EnumMap<EmotivaControlCommands, String> getSourcesMainZone() {
|
||||
return sourcesMainZone;
|
||||
}
|
||||
|
||||
public EnumMap<EmotivaControlCommands, String> getSourcesZone2() {
|
||||
return sourcesZone2;
|
||||
}
|
||||
|
||||
public EnumMap<EmotivaSubscriptionTags, String> getModes() {
|
||||
return modes;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* This class provides translated texts.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = EmotivaTranslationProvider.class)
|
||||
public class EmotivaTranslationProvider {
|
||||
|
||||
private final Bundle bundle;
|
||||
private final TranslationProvider i18nProvider;
|
||||
private final LocaleProvider localeProvider;
|
||||
|
||||
@Activate
|
||||
public EmotivaTranslationProvider(@Reference TranslationProvider i18nProvider,
|
||||
@Reference LocaleProvider localeProvider) {
|
||||
this.bundle = FrameworkUtil.getBundle(this.getClass());
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
public EmotivaTranslationProvider(final EmotivaTranslationProvider other) {
|
||||
this.bundle = other.bundle;
|
||||
this.i18nProvider = other.i18nProvider;
|
||||
this.localeProvider = other.localeProvider;
|
||||
}
|
||||
|
||||
public String getText(String key, @Nullable Object... arguments) {
|
||||
Locale locale = localeProvider.getLocale();
|
||||
String message = i18nProvider.getText(bundle, key, this.getDefaultText(key), locale, arguments);
|
||||
if (message != null) {
|
||||
return message;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public @Nullable String getDefaultText(String key) {
|
||||
return i18nProvider.getText(bundle, key, key, Locale.ENGLISH);
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaPingDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaTransponderDTO;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils;
|
||||
import org.openhab.core.common.AbstractUID;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This service is used for discovering Emotiva devices via sending an EmotivaPing UDP message.
|
||||
*
|
||||
* @author Hilbrand Bouwkamp - Initial contribution
|
||||
* @author Espen Fossen - Adapted to Emotiva binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaUdpBroadcastService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaUdpBroadcastService.class);
|
||||
private static final int MAX_PACKET_SIZE = 512;
|
||||
@Nullable
|
||||
private DatagramSocket discoverSocket;
|
||||
private final EmotivaXmlUtils xmlUtils = new EmotivaXmlUtils();
|
||||
|
||||
/**
|
||||
* The address to broadcast EmotivaPing message to.
|
||||
*/
|
||||
private final String broadcastAddress;
|
||||
|
||||
public EmotivaUdpBroadcastService(String broadcastAddress) throws IllegalArgumentException, JAXBException {
|
||||
if (broadcastAddress.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing broadcast address");
|
||||
}
|
||||
this.broadcastAddress = broadcastAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual discovery of Emotiva devices (things).
|
||||
*/
|
||||
public Optional<DiscoveryResult> discoverThings() {
|
||||
try {
|
||||
final DatagramPacket receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
|
||||
// No need to call close first, because the caller of this method already has done it.
|
||||
|
||||
discoverSocket = new DatagramSocket(
|
||||
new InetSocketAddress(EmotivaBindingConstants.DEFAULT_TRANSPONDER_PORT));
|
||||
final InetAddress broadcast = InetAddress.getByName(broadcastAddress);
|
||||
|
||||
byte[] emotivaPingDTO = xmlUtils.marshallEmotivaDTO(new EmotivaPingDTO(PROTOCOL_V3.name()))
|
||||
.getBytes(Charset.defaultCharset());
|
||||
final DatagramPacket discoverPacket = new DatagramPacket(emotivaPingDTO, emotivaPingDTO.length, broadcast,
|
||||
EmotivaBindingConstants.DEFAULT_PING_PORT);
|
||||
|
||||
DatagramSocket localDatagramSocket = discoverSocket;
|
||||
while (localDatagramSocket != null && discoverSocket != null) {
|
||||
localDatagramSocket.setBroadcast(true);
|
||||
localDatagramSocket.setSoTimeout(DEFAULT_UDP_SENDING_TIMEOUT);
|
||||
localDatagramSocket.send(discoverPacket);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Discovery package sent: {}",
|
||||
new String(discoverPacket.getData(), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// Runs until the socket call gets a timeout and throws an exception. When a timeout is triggered it
|
||||
// means
|
||||
// no data was present and nothing new to discover.
|
||||
while (true) {
|
||||
// Set packet length in case a previous call reduced the size.
|
||||
receivePacket.setLength(MAX_PACKET_SIZE);
|
||||
if (discoverSocket == null) {
|
||||
break;
|
||||
} else {
|
||||
localDatagramSocket.receive(receivePacket);
|
||||
}
|
||||
logger.debug("Emotiva device discovery returned package with length '{}'",
|
||||
receivePacket.getLength());
|
||||
if (receivePacket.getLength() > 0) {
|
||||
return thingDiscovered(receivePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
logger.debug("Discovering poller timeout...");
|
||||
} catch (InterruptedIOException e) {
|
||||
logger.debug("Interrupted during discovery: {}", e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.debug("Error during discovery: {}", e.getMessage());
|
||||
} finally {
|
||||
closeDiscoverSocket();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the discovery socket and cleans the value. No need for synchronization as this method is called from a
|
||||
* synchronized context.
|
||||
*/
|
||||
public void closeDiscoverSocket() {
|
||||
final DatagramSocket localDiscoverSocket = discoverSocket;
|
||||
if (localDiscoverSocket != null) {
|
||||
discoverSocket = null;
|
||||
if (!localDiscoverSocket.isClosed()) {
|
||||
localDiscoverSocket.close(); // this interrupts and terminates the listening thread
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a device (thing) with the discovered properties.
|
||||
*
|
||||
* @param packet containing data of detected device
|
||||
*/
|
||||
private Optional<DiscoveryResult> thingDiscovered(DatagramPacket packet) {
|
||||
final String ipAddress = packet.getAddress().getHostAddress();
|
||||
String udpResponse = new String(packet.getData(), 0, packet.getLength() - 1, StandardCharsets.UTF_8);
|
||||
|
||||
Object object;
|
||||
try {
|
||||
object = xmlUtils.unmarshallToEmotivaDTO(udpResponse);
|
||||
} catch (JAXBException e) {
|
||||
logger.debug("Could not unmarshal '{}:{}'", ipAddress, udpResponse.length());
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (object instanceof EmotivaTransponderDTO answerDto) {
|
||||
logger.trace("Processing Received '{}' with '{}' ", EmotivaTransponderDTO.class.getSimpleName(),
|
||||
udpResponse);
|
||||
final ThingUID thingUid = new ThingUID(
|
||||
THING_PROCESSOR + AbstractUID.SEPARATOR + ipAddress.replace(".", ""));
|
||||
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUid)
|
||||
.withThingType(THING_PROCESSOR).withProperty("ipAddress", ipAddress)
|
||||
.withProperty("controlPort", answerDto.getControl().getControlPort())
|
||||
.withProperty("notifyPort", answerDto.getControl().getNotifyPort())
|
||||
.withProperty("infoPort", answerDto.getControl().getInfoPort())
|
||||
.withProperty("setupPortTCP", answerDto.getControl().getSetupPortTCP())
|
||||
.withProperty("menuNotifyPort", answerDto.getControl().getMenuNotifyPort())
|
||||
.withProperty("model", answerDto.getModel())
|
||||
.withProperty("revision", Objects.requireNonNullElse(answerDto.getRevision(), ""))
|
||||
.withProperty("dataRevision", Objects.requireNonNullElse(answerDto.getDataRevision(), ""))
|
||||
.withProperty("protocolVersion",
|
||||
Objects.requireNonNullElse(answerDto.getControl().getVersion(),
|
||||
DEFAULT_EMOTIVA_PROTOCOL_VERSION))
|
||||
.withProperty("keepAlive", answerDto.getControl().getKeepAlive())
|
||||
.withProperty(EmotivaBindingConstants.UNIQUE_PROPERTY_NAME, ipAddress)
|
||||
.withLabel(answerDto.getName())
|
||||
.withRepresentationProperty(EmotivaBindingConstants.UNIQUE_PROPERTY_NAME).build();
|
||||
try {
|
||||
logger.debug("Adding newly discovered thing '{}:{}' with properties '{}'", THING_PROCESSOR, ipAddress,
|
||||
discoveryResult.getProperties());
|
||||
|
||||
return Optional.of(discoveryResult);
|
||||
} catch (Exception e) {
|
||||
logger.debug("Failed adding discovered thing '{}:{}' with properties '{}'", THING_PROCESSOR, ipAddress,
|
||||
discoveryResult.getProperties(), e);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Received message of unknown type in message '{}'", udpResponse);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.SocketException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaUdpResponse;
|
||||
import org.openhab.core.common.NamedThreadFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This service is used for receiving UDP message from Emotiva devices.
|
||||
*
|
||||
* @author Patrick Koenemann - Initial contribution
|
||||
* @author Espen Fossen - Adapted to Emotiva binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaUdpReceivingService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaUdpReceivingService.class);
|
||||
|
||||
/**
|
||||
* Buffer for incoming UDP packages.
|
||||
*/
|
||||
private static final int MAX_PACKET_SIZE = 10240;
|
||||
|
||||
/**
|
||||
* The device IP this connector is listening to / sends to.
|
||||
*/
|
||||
private final String ipAddress;
|
||||
|
||||
/**
|
||||
* The port this connector is listening to notify message.
|
||||
*/
|
||||
private final int receivingPort;
|
||||
|
||||
/**
|
||||
* Service to spawn new threads for handling status updates.
|
||||
*/
|
||||
private final ExecutorService executorService;
|
||||
|
||||
/**
|
||||
* Thread factory for UDP listening thread.
|
||||
*/
|
||||
private final NamedThreadFactory listeningThreadFactory = new NamedThreadFactory(EmotivaBindingConstants.BINDING_ID,
|
||||
true);
|
||||
|
||||
/**
|
||||
* Socket for receiving Notify UDP packages.
|
||||
*/
|
||||
private @Nullable DatagramSocket receivingSocket = null;
|
||||
|
||||
/**
|
||||
* The listener that gets notified upon newly received messages.
|
||||
*/
|
||||
private @Nullable Consumer<EmotivaUdpResponse> listener;
|
||||
|
||||
private int receiveNotifyFailures = 0;
|
||||
private boolean listenerNotifyActive = false;
|
||||
|
||||
/**
|
||||
* Create a listener to an Emotiva device via the given configuration.
|
||||
*
|
||||
* @param receivingPort listening port
|
||||
* @param config Emotiva configuration values
|
||||
*/
|
||||
public EmotivaUdpReceivingService(int receivingPort, EmotivaConfiguration config, ExecutorService executorService) {
|
||||
if (receivingPort <= 0) {
|
||||
throw new IllegalArgumentException("Invalid receivingPort: " + receivingPort);
|
||||
}
|
||||
if (config.ipAddress.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing ipAddress");
|
||||
}
|
||||
this.ipAddress = config.ipAddress;
|
||||
this.receivingPort = receivingPort;
|
||||
this.executorService = executorService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize socket connection to the UDP receive port for the given listener.
|
||||
*
|
||||
* @throws SocketException Is only thrown if <code>logNotThrowException = false</code>.
|
||||
* @throws InterruptedException Typically happens during shutdown.
|
||||
*/
|
||||
public void connect(Consumer<EmotivaUdpResponse> listener, boolean logNotThrowException)
|
||||
throws SocketException, InterruptedException {
|
||||
if (receivingSocket == null) {
|
||||
try {
|
||||
receivingSocket = new DatagramSocket(receivingPort);
|
||||
|
||||
this.listener = listener;
|
||||
|
||||
listeningThreadFactory.newThread(this::listen).start();
|
||||
|
||||
// wait for the listening thread to be active
|
||||
for (int i = 0; i < 20 && !listenerNotifyActive; i++) {
|
||||
Thread.sleep(100); // wait at most 20 * 100ms = 2sec for the listener to be active
|
||||
}
|
||||
if (!listenerNotifyActive) {
|
||||
logger.warn(
|
||||
"Listener thread started but listener is not yet active after 2sec; something seems to be wrong with the JVM thread handling?!");
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
if (logNotThrowException) {
|
||||
logger.warn("Failed to open socket connection on port '{}'", receivingPort);
|
||||
}
|
||||
|
||||
disconnect();
|
||||
|
||||
if (!logNotThrowException) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else if (!Objects.equals(this.listener, listener)) {
|
||||
throw new IllegalStateException("A listening thread is already running");
|
||||
}
|
||||
}
|
||||
|
||||
private void listen() {
|
||||
try {
|
||||
listenUnhandledInterruption();
|
||||
} catch (InterruptedException e) {
|
||||
// OH shutdown - don't log anything, just quit
|
||||
}
|
||||
}
|
||||
|
||||
private void listenUnhandledInterruption() throws InterruptedException {
|
||||
logger.debug("Emotiva listener started for: '{}:{}'", ipAddress, receivingPort);
|
||||
|
||||
final Consumer<EmotivaUdpResponse> localListener = listener;
|
||||
final DatagramSocket localReceivingSocket = receivingSocket;
|
||||
while (localListener != null && localReceivingSocket != null && receivingSocket != null) {
|
||||
try {
|
||||
final DatagramPacket answer = new DatagramPacket(new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
|
||||
|
||||
listenerNotifyActive = true;
|
||||
localReceivingSocket.receive(answer); // receive packet (blocking call)
|
||||
listenerNotifyActive = false;
|
||||
|
||||
final byte[] receivedData = Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1);
|
||||
|
||||
if (receivedData.length == 0) {
|
||||
if (isConnected()) {
|
||||
logger.debug("Nothing received, this may happen during shutdown or some unknown error");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
receiveNotifyFailures = 0; // message successfully received, unset failure counter
|
||||
|
||||
handleReceivedData(answer, receivedData, localListener);
|
||||
} catch (Exception e) {
|
||||
listenerNotifyActive = false;
|
||||
|
||||
if (receivingSocket == null) {
|
||||
logger.debug("Socket closed; stopping listener on port '{}'", receivingPort);
|
||||
} else {
|
||||
logger.debug("Checkin receiveFailures count {}", receiveNotifyFailures);
|
||||
// if we get 3 errors in a row, we should better add a delay to stop spamming the log!
|
||||
if (receiveNotifyFailures++ > EmotivaBindingConstants.DEFAULT_CONNECTION_RETRIES) {
|
||||
logger.debug(
|
||||
"Unexpected error while listening on port '{}'; waiting 10sec before the next attempt to listen on that port",
|
||||
receivingPort, e);
|
||||
for (int i = 0; i < 50 && receivingSocket != null; i++) {
|
||||
Thread.sleep(200); // 50 * 200ms = 10sec
|
||||
}
|
||||
} else {
|
||||
logger.debug("Unexpected error while listening on port '{}'", receivingPort, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReceivedData(DatagramPacket answer, byte[] receivedData,
|
||||
Consumer<EmotivaUdpResponse> localListener) {
|
||||
// log & notify listener in new thread (so that listener loop continues immediately)
|
||||
executorService.execute(() -> {
|
||||
if (answer.getAddress() != null && answer.getLength() > 0) {
|
||||
logger.trace("Received data on port '{}': {}", answer.getPort(), receivedData);
|
||||
EmotivaUdpResponse emotivaUdpResponse = new EmotivaUdpResponse(
|
||||
new String(answer.getData(), 0, answer.getLength()), answer.getAddress().getHostAddress());
|
||||
localListener.accept(emotivaUdpResponse);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the socket connection.
|
||||
*/
|
||||
public void disconnect() {
|
||||
logger.debug("Emotiva listener stopped for: '{}:{}'", ipAddress, receivingPort);
|
||||
listener = null;
|
||||
final DatagramSocket localReceivingSocket = receivingSocket;
|
||||
if (localReceivingSocket != null) {
|
||||
receivingSocket = null;
|
||||
if (!localReceivingSocket.isClosed()) {
|
||||
localReceivingSocket.close(); // this interrupts and terminates the listening thread
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return receivingSocket != null;
|
||||
}
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.DEFAULT_UDP_SENDING_TIMEOUT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaSubscriptionRequest;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUnsubscribeDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUpdateRequest;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaUdpResponse;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This service handles sending UDP message to Emotiva devices.
|
||||
*
|
||||
* @author Patrick Koenemann - Initial contribution
|
||||
* @author Espen Fossen - Adapted to Emotiva binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaUdpSendingService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaUdpSendingService.class);
|
||||
|
||||
/**
|
||||
* Buffer for incoming UDP packages.
|
||||
*/
|
||||
private static final int MAX_PACKET_SIZE = 10240;
|
||||
|
||||
/**
|
||||
* The device IP this connector is listening to / sends to.
|
||||
*/
|
||||
private final String ipAddress;
|
||||
|
||||
/**
|
||||
* The port this connector is sending to.
|
||||
*/
|
||||
private final int sendingControlPort;
|
||||
|
||||
/**
|
||||
* Service to spawn new threads for handling status updates.
|
||||
*/
|
||||
private final ExecutorService executorService;
|
||||
|
||||
/**
|
||||
* Socket for sending UDP packages.
|
||||
*/
|
||||
private @Nullable DatagramSocket sendingSocket = null;
|
||||
|
||||
/**
|
||||
* Sending response listener.
|
||||
*/
|
||||
private @Nullable Consumer<EmotivaUdpResponse> listener;
|
||||
|
||||
private final EmotivaXmlUtils emotivaXmlUtils;
|
||||
|
||||
/**
|
||||
* Create a socket for sending message to Emotiva device via the given configuration.
|
||||
*
|
||||
* @param config Emotiva configuration values
|
||||
*/
|
||||
public EmotivaUdpSendingService(EmotivaConfiguration config, ExecutorService executorService) throws JAXBException {
|
||||
if (config.controlPort <= 0) {
|
||||
throw new IllegalArgumentException("Invalid udpSendingControlPort: " + config.controlPort);
|
||||
}
|
||||
if (config.ipAddress.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Missing ipAddress");
|
||||
}
|
||||
this.ipAddress = config.ipAddress;
|
||||
this.sendingControlPort = config.controlPort;
|
||||
this.executorService = executorService;
|
||||
this.emotivaXmlUtils = new EmotivaXmlUtils();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize socket connection to the UDP sending port
|
||||
*
|
||||
* @throws SocketException Is only thrown if <code>logNotThrowException = false</code>.
|
||||
* @throws InterruptedException Typically happens during shutdown.
|
||||
*/
|
||||
public void connect(Consumer<EmotivaUdpResponse> listener, boolean logNotThrowException)
|
||||
throws SocketException, InterruptedException {
|
||||
try {
|
||||
sendingSocket = new DatagramSocket(sendingControlPort);
|
||||
|
||||
this.listener = listener;
|
||||
} catch (SocketException e) {
|
||||
disconnect();
|
||||
|
||||
if (!logNotThrowException) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReceivedData(DatagramPacket answer, byte[] receivedData,
|
||||
Consumer<EmotivaUdpResponse> localListener) {
|
||||
// log & notify listener in new thread (so that listener loop continues immediately)
|
||||
executorService.execute(() -> {
|
||||
if (answer.getAddress() != null && answer.getLength() > 0) {
|
||||
logger.trace("Received data on port '{}': {}", answer.getPort(), receivedData);
|
||||
EmotivaUdpResponse emotivaUdpResponse = new EmotivaUdpResponse(
|
||||
new String(answer.getData(), 0, answer.getLength()), answer.getAddress().getHostAddress());
|
||||
localListener.accept(emotivaUdpResponse);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the socket connection.
|
||||
*/
|
||||
public void disconnect() {
|
||||
logger.debug("Emotiva sender stopped for '{}'", ipAddress);
|
||||
listener = null;
|
||||
final DatagramSocket localSendingSocket = sendingSocket;
|
||||
if (localSendingSocket != null) {
|
||||
synchronized (this) {
|
||||
if (Objects.equals(sendingSocket, localSendingSocket)) {
|
||||
sendingSocket = null;
|
||||
if (!localSendingSocket.isClosed()) {
|
||||
localSendingSocket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void send(EmotivaControlDTO dto) throws IOException {
|
||||
send(emotivaXmlUtils.marshallJAXBElementObjects(dto));
|
||||
}
|
||||
|
||||
public void sendSubscription(EmotivaSubscriptionTags[] tags, EmotivaConfiguration config) throws IOException {
|
||||
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaSubscriptionRequest(tags, config.protocolVersion)));
|
||||
}
|
||||
|
||||
public void sendUpdate(EmotivaControlCommands defaultCommand, EmotivaConfiguration config) throws IOException {
|
||||
send(emotivaXmlUtils
|
||||
.marshallJAXBElementObjects(new EmotivaUpdateRequest(defaultCommand, config.protocolVersion)));
|
||||
}
|
||||
|
||||
public void sendUpdate(EmotivaSubscriptionTags[] tags, EmotivaConfiguration config) throws IOException {
|
||||
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaUpdateRequest(tags, config.protocolVersion)));
|
||||
}
|
||||
|
||||
public void sendUnsubscribe(EmotivaSubscriptionTags[] defaultCommand) throws IOException {
|
||||
send(emotivaXmlUtils.marshallJAXBElementObjects(new EmotivaUnsubscribeDTO(defaultCommand)));
|
||||
}
|
||||
|
||||
public void send(String msg) throws IOException {
|
||||
logger.trace("Sending message '{}' to {}:{}", msg, ipAddress, sendingControlPort);
|
||||
if (msg.isEmpty()) {
|
||||
throw new IllegalArgumentException("Message must not be empty");
|
||||
}
|
||||
|
||||
final InetAddress ipAddress = InetAddress.getByName(this.ipAddress);
|
||||
byte[] buf = msg.getBytes(Charset.defaultCharset());
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length, ipAddress, sendingControlPort);
|
||||
|
||||
// make sure we are not interrupted by a disconnect while sending this message
|
||||
synchronized (this) {
|
||||
DatagramSocket localDatagramSocket = this.sendingSocket;
|
||||
final DatagramPacket answer = new DatagramPacket(new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
|
||||
final Consumer<EmotivaUdpResponse> localListener = listener;
|
||||
if (localDatagramSocket != null && !localDatagramSocket.isClosed()) {
|
||||
localDatagramSocket.setSoTimeout(DEFAULT_UDP_SENDING_TIMEOUT);
|
||||
localDatagramSocket.send(packet);
|
||||
logger.debug("Sending successful");
|
||||
|
||||
localDatagramSocket.receive(answer);
|
||||
final byte[] receivedData = Arrays.copyOfRange(answer.getData(), 0, answer.getLength() - 1);
|
||||
|
||||
if (receivedData.length == 0) {
|
||||
logger.debug("Nothing received, this may happen during shutdown or some unknown error");
|
||||
}
|
||||
|
||||
if (localListener != null) {
|
||||
handleReceivedData(answer, receivedData, localListener);
|
||||
}
|
||||
} else {
|
||||
throw new SocketException("Datagram Socket closed or not initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.BINDING_ID;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_SOURCE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_ZONE2_SOURCE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.EMOTIVA_SOURCE_COMMAND_PREFIX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.openhab.core.types.StateDescription;
|
||||
import org.openhab.core.types.StateOption;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class provides the list of valid inputs for a source or audio mode.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Espen Fossen - Adapted to Emotiva binding
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class InputStateOptionProvider extends BaseDynamicStateDescriptionProvider implements ThingHandlerService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(InputStateOptionProvider.class);
|
||||
|
||||
private @Nullable EmotivaProcessorHandler handler;
|
||||
|
||||
@Override
|
||||
public void setThingHandler(ThingHandler handler) {
|
||||
this.handler = (EmotivaProcessorHandler) handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ThingHandler getThingHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
|
||||
@Nullable Locale locale) {
|
||||
ChannelTypeUID typeUID = channel.getChannelTypeUID();
|
||||
if (typeUID == null || !BINDING_ID.equals(typeUID.getBindingId()) || original == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<StateOption> options = new ArrayList<>();
|
||||
EmotivaProcessorHandler localHandler = handler;
|
||||
if (localHandler != null) {
|
||||
if (channel.getUID().getId().equals(CHANNEL_SOURCE)) {
|
||||
setStateOptionsForSource(channel, options, localHandler.getSourcesMainZone());
|
||||
} else if (channel.getUID().getId().equals(CHANNEL_ZONE2_SOURCE)) {
|
||||
setStateOptionsForSource(channel, options, localHandler.getSourcesZone2());
|
||||
} else if (channel.getUID().getId().equals(CHANNEL_MODE)) {
|
||||
EnumMap<EmotivaSubscriptionTags, String> modes = localHandler.getModes();
|
||||
Collection<EmotivaSubscriptionTags> modeKeys = modes.keySet();
|
||||
for (EmotivaSubscriptionTags modeKey : modeKeys) {
|
||||
options.add(new StateOption(modeKey.name(), modes.get(modeKey)));
|
||||
}
|
||||
logger.debug("Updated '{}' with '{}'", CHANNEL_MODE, options);
|
||||
setStateOptions(channel.getUID(), options);
|
||||
}
|
||||
}
|
||||
|
||||
return super.getStateDescription(channel, original, locale);
|
||||
}
|
||||
|
||||
private void setStateOptionsForSource(Channel channel, List<StateOption> options,
|
||||
EnumMap<EmotivaControlCommands, String> sources) {
|
||||
Collection<EmotivaControlCommands> sourceKeys = sources.keySet();
|
||||
for (EmotivaControlCommands sourceKey : sourceKeys) {
|
||||
if (sourceKey.name().startsWith(EMOTIVA_SOURCE_COMMAND_PREFIX)) {
|
||||
options.add(new StateOption(sourceKey.name(), sources.get(sourceKey)));
|
||||
} else {
|
||||
options.add(new StateOption(sourceKey.name(), sourceKey.getLabel()));
|
||||
}
|
||||
}
|
||||
logger.debug("Updated '{}' with '{}'", channel.getUID().getId(), options);
|
||||
setStateOptions(channel.getUID(), options);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.EmotivaUdpBroadcastService;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Discovery service for Emotiva devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, configurationPid = "discovery.emotiva")
|
||||
public class EmotivaDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaDiscoveryService.class);
|
||||
|
||||
@Nullable
|
||||
private final EmotivaUdpBroadcastService broadcastService = new EmotivaUdpBroadcastService(
|
||||
DISCOVERY_BROADCAST_ADDRESS);
|
||||
|
||||
public EmotivaDiscoveryService() throws IllegalArgumentException, JAXBException {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIMEOUT_SECONDS, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Start scan for Emotiva devices");
|
||||
EmotivaUdpBroadcastService localBroadcastService = broadcastService;
|
||||
if (localBroadcastService != null) {
|
||||
try {
|
||||
localBroadcastService.discoverThings().ifPresent(this::thingDiscovered);
|
||||
} finally {
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopScan() {
|
||||
logger.debug("Stop scan for Emotiva devices");
|
||||
Objects.requireNonNull(broadcastService).closeDiscoverSocket();
|
||||
super.stopScan();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.annotation.XmlAnyElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
/**
|
||||
* Defines elements used by common request DTO classes.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
public class AbstractJAXBElementDTO {
|
||||
|
||||
@XmlTransient
|
||||
protected List<EmotivaCommandDTO> commands;
|
||||
|
||||
@XmlAnyElement
|
||||
protected List<JAXBElement<String>> jaxbElements;
|
||||
|
||||
public List<EmotivaCommandDTO> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
public void setCommands(List<EmotivaCommandDTO> commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public void setJaxbElements(List<JAXBElement<String>> jaxbElements) {
|
||||
this.jaxbElements = jaxbElements;
|
||||
}
|
||||
|
||||
public JAXBElement<String> createJAXBElement(QName name) {
|
||||
return new JAXBElement<String>(name, String.class, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAnyElement;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
/**
|
||||
* Defines elements used by common notification DTO classes.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
public class AbstractNotificationDTO {
|
||||
|
||||
// Only present with PROTOCOL_V2 or older
|
||||
@XmlAnyElement(lax = true)
|
||||
List<Object> tags;
|
||||
|
||||
// Only present with PROTOCOL_V3 or newer
|
||||
@XmlElement(name = "property")
|
||||
List<EmotivaPropertyDTO> properties;
|
||||
|
||||
public List<EmotivaPropertyDTO> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public List<Object> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Emotiva Control XML object, which is part of the {@link EmotivaTransponderDTO} response.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "control")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ControlDTO {
|
||||
|
||||
@XmlElement(name = "version")
|
||||
String version;
|
||||
@XmlElement(name = "controlPort")
|
||||
int controlPort;
|
||||
@XmlElement(name = "notifyPort")
|
||||
int notifyPort;
|
||||
@XmlElement(name = "infoPort")
|
||||
int infoPort;
|
||||
@XmlElement(name = "menuNotifyPort")
|
||||
int menuNotifyPort;
|
||||
@XmlElement(name = "setupPortTCP")
|
||||
int setupPortTCP;
|
||||
@XmlElement(name = "setupXMLVersion")
|
||||
int setupXMLVersion;
|
||||
@XmlElement(name = "keepAlive")
|
||||
int keepAlive;
|
||||
|
||||
public ControlDTO() {
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getControlPort() {
|
||||
return controlPort;
|
||||
}
|
||||
|
||||
public int getNotifyPort() {
|
||||
return notifyPort;
|
||||
}
|
||||
|
||||
public int getInfoPort() {
|
||||
return infoPort;
|
||||
}
|
||||
|
||||
public int getMenuNotifyPort() {
|
||||
return menuNotifyPort;
|
||||
}
|
||||
|
||||
public int getSetupPortTCP() {
|
||||
return setupPortTCP;
|
||||
}
|
||||
|
||||
public int getSetupXMLVersion() {
|
||||
return setupXMLVersion;
|
||||
}
|
||||
|
||||
public int getKeepAlive() {
|
||||
return keepAlive;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAnyElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaAck message type. Received from Emotiva device whenever a {@link EmotivaControlDTO} with
|
||||
* {@link EmotivaCommandDTO} is sent.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaAck")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaAckDTO {
|
||||
|
||||
@XmlAnyElement(lax = true)
|
||||
private List<Object> commands;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaAckDTO() {
|
||||
}
|
||||
|
||||
public List<Object> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
public void setCommands(List<Object> commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmotivaAckDTO{" + "commands=" + commands + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
/**
|
||||
* The EmotivaBarNotify message type. Received from a device if subscribed to the
|
||||
* {@link org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags#bar_update} type. Uses the
|
||||
* {@link EmotivaBarNotifyWrapper} to handle unmarshalling.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "property")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaBarNotifyDTO {
|
||||
|
||||
@XmlValue
|
||||
private String name = "bar";
|
||||
|
||||
// Possible values “bar”, “centerBar”, “bigText’, “off”
|
||||
@XmlAttribute
|
||||
private String type;
|
||||
@XmlAttribute
|
||||
private String text;
|
||||
@XmlAttribute
|
||||
private String units;
|
||||
@XmlAttribute
|
||||
private String value;
|
||||
@XmlAttribute
|
||||
private String min;
|
||||
@XmlAttribute
|
||||
private String max;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaBarNotifyDTO() {
|
||||
}
|
||||
|
||||
public EmotivaBarNotifyDTO(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getUnits() {
|
||||
return units;
|
||||
}
|
||||
|
||||
public void setUnits(String units) {
|
||||
this.units = units;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public void setMin(String min) {
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
public String getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax(String max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public String formattedMessage() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (type != null) {
|
||||
if (!"off".equals(type)) {
|
||||
if (text != null) {
|
||||
sb.append(text);
|
||||
}
|
||||
if (value != null) {
|
||||
sb.append(" ");
|
||||
try {
|
||||
Double doubleValue = Double.valueOf(value);
|
||||
sb.append(String.format("%.1f", doubleValue));
|
||||
} catch (NumberFormatException e) {
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
if (units != null) {
|
||||
sb.append(" ").append(units);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAnyElement;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* A helper class for receiving {@link EmotivaBarNotifyDTO} messages.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaBarNotify")
|
||||
public class EmotivaBarNotifyWrapper {
|
||||
|
||||
@XmlAttribute
|
||||
private String sequence;
|
||||
|
||||
@XmlAnyElement(lax = true)
|
||||
List<Object> tags;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaBarNotifyWrapper() {
|
||||
}
|
||||
|
||||
public String getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public List<Object> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.DEFAULT_CONTROL_ACK_VALUE;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* The EmotivaCommand DTO. Use by multiple message types to control commands in Emotiva devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "property")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaCommandDTO {
|
||||
|
||||
@XmlValue
|
||||
private String commandName;
|
||||
@XmlAttribute
|
||||
private String value;
|
||||
@XmlAttribute
|
||||
private String visible;
|
||||
@XmlAttribute
|
||||
private String status;
|
||||
@XmlAttribute
|
||||
private String ack;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaCommandDTO() {
|
||||
}
|
||||
|
||||
public EmotivaCommandDTO(EmotivaControlCommands command) {
|
||||
this.commandName = command.name();
|
||||
}
|
||||
|
||||
public EmotivaCommandDTO(EmotivaSubscriptionTags tag) {
|
||||
this.commandName = tag.name();
|
||||
}
|
||||
|
||||
public EmotivaCommandDTO(EmotivaControlCommands command, String value) {
|
||||
this.commandName = command.name();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public EmotivaCommandDTO(EmotivaControlCommands commandName, String value, String ack) {
|
||||
this(commandName, value);
|
||||
this.ack = ack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance based on command. Primarily used by EmotivaControl messages.
|
||||
*
|
||||
* @return EmotivaCommandDTO with ack=yes always added
|
||||
*/
|
||||
public static EmotivaCommandDTO fromTypeWithAck(EmotivaControlCommands command) {
|
||||
EmotivaCommandDTO emotivaCommandDTO = new EmotivaCommandDTO(command);
|
||||
emotivaCommandDTO.setAck(DEFAULT_CONTROL_ACK_VALUE);
|
||||
return emotivaCommandDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance based on command and value. Primarily used by EmotivaControl messages.
|
||||
*
|
||||
* @return EmotivaCommandDTO with ack=yes always added
|
||||
*/
|
||||
public static EmotivaCommandDTO fromTypeWithAck(EmotivaControlCommands command, String value) {
|
||||
EmotivaCommandDTO emotivaCommandDTO = new EmotivaCommandDTO(command);
|
||||
if (value != null) {
|
||||
emotivaCommandDTO.setValue(value);
|
||||
}
|
||||
emotivaCommandDTO.setAck(DEFAULT_CONTROL_ACK_VALUE);
|
||||
return emotivaCommandDTO;
|
||||
}
|
||||
|
||||
public static EmotivaCommandDTO fromType(EmotivaControlCommands command) {
|
||||
return new EmotivaCommandDTO(command);
|
||||
}
|
||||
|
||||
public static EmotivaCommandDTO fromType(EmotivaSubscriptionTags command) {
|
||||
return new EmotivaCommandDTO(command);
|
||||
}
|
||||
|
||||
public static EmotivaCommandDTO fromTypeWithAck(EmotivaSubscriptionTags command) {
|
||||
EmotivaCommandDTO emotivaCommandDTO = new EmotivaCommandDTO(command);
|
||||
emotivaCommandDTO.setAck(DEFAULT_CONTROL_ACK_VALUE);
|
||||
return emotivaCommandDTO;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return commandName;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getAck() {
|
||||
return ack;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.commandName = name;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setVisible(String visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setAck(String ack) {
|
||||
this.ack = ack;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.DEFAULT_CONTROL_MESSAGE_SET_DEFAULT_VALUE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
|
||||
/**
|
||||
* The EmotivaControl message type. Use to send commands via {@link EmotivaCommandDTO} to Emotiva devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaControl")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaControlDTO extends AbstractJAXBElementDTO {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaControlDTO() {
|
||||
}
|
||||
|
||||
public EmotivaControlDTO(List<EmotivaCommandDTO> commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public static EmotivaControlDTO create(EmotivaControlCommands command) {
|
||||
return new EmotivaControlDTO(
|
||||
List.of(EmotivaCommandDTO.fromTypeWithAck(command, DEFAULT_CONTROL_MESSAGE_SET_DEFAULT_VALUE)));
|
||||
}
|
||||
|
||||
public static EmotivaControlDTO create(EmotivaControlCommands command, int value) {
|
||||
return new EmotivaControlDTO(List.of(EmotivaCommandDTO.fromTypeWithAck(command, String.valueOf(value))));
|
||||
}
|
||||
|
||||
public static EmotivaControlDTO create(EmotivaControlCommands command, double value) {
|
||||
return new EmotivaControlDTO(
|
||||
List.of(EmotivaCommandDTO.fromTypeWithAck(command, String.valueOf(Math.round(value * 2) / 2.0))));
|
||||
}
|
||||
|
||||
public static EmotivaControlDTO create(EmotivaControlCommands command, String value) {
|
||||
return new EmotivaControlDTO(List.of(EmotivaCommandDTO.fromTypeWithAck(command, value)));
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Data field use by {@link EmotivaMenuNotifyDTO}.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "col")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaMenuCol {
|
||||
|
||||
@XmlAttribute
|
||||
private String arrow;
|
||||
@XmlAttribute
|
||||
private String checkbox;
|
||||
@XmlAttribute
|
||||
private String fixed;
|
||||
@XmlAttribute
|
||||
private String fixedWidth;
|
||||
@XmlAttribute
|
||||
private String highlight;
|
||||
@XmlAttribute
|
||||
private String offset;
|
||||
@XmlAttribute
|
||||
private String number;
|
||||
@XmlAttribute
|
||||
private String value;
|
||||
|
||||
public EmotivaMenuCol() {
|
||||
}
|
||||
|
||||
public String getArrow() {
|
||||
return arrow;
|
||||
}
|
||||
|
||||
public void setArrow(String arrow) {
|
||||
this.arrow = arrow;
|
||||
}
|
||||
|
||||
public String getCheckbox() {
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
public void setCheckbox(String checkbox) {
|
||||
this.checkbox = checkbox;
|
||||
}
|
||||
|
||||
public String getFixed() {
|
||||
return fixed;
|
||||
}
|
||||
|
||||
public void setFixed(String fixed) {
|
||||
this.fixed = fixed;
|
||||
}
|
||||
|
||||
public String getFixedWidth() {
|
||||
return fixedWidth;
|
||||
}
|
||||
|
||||
public void setFixedWidth(String fixedWidth) {
|
||||
this.fixedWidth = fixedWidth;
|
||||
}
|
||||
|
||||
public String getHighlight() {
|
||||
return highlight;
|
||||
}
|
||||
|
||||
public void setHighlight(String highlight) {
|
||||
this.highlight = highlight;
|
||||
}
|
||||
|
||||
public String getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(String offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaMenuNotify message type. Received from a device if subscribed to the
|
||||
*
|
||||
* @link EmotivaSubscriptionTags#menu_update} type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaMenuNotify")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaMenuNotifyDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String sequence;
|
||||
|
||||
@XmlElement
|
||||
private List<EmotivaMenuRow> row;
|
||||
@XmlElement
|
||||
private EmotivaMenuProgress progress;
|
||||
|
||||
public EmotivaMenuNotifyDTO() {
|
||||
}
|
||||
|
||||
public String getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public void setSequence(String sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
public List<EmotivaMenuRow> getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
public void setRow(List<EmotivaMenuRow> row) {
|
||||
this.row = row;
|
||||
}
|
||||
|
||||
public EmotivaMenuProgress getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void setProgress(EmotivaMenuProgress progress) {
|
||||
this.progress = progress;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Data field use by {@link EmotivaMenuNotifyDTO}.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "progress")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaMenuProgress {
|
||||
|
||||
@XmlAttribute
|
||||
private String time;
|
||||
|
||||
public EmotivaMenuProgress() {
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Data field use by {@link EmotivaMenuNotifyDTO}.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "row")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaMenuRow {
|
||||
|
||||
@XmlAttribute
|
||||
private String number;
|
||||
|
||||
@XmlElement
|
||||
private List<EmotivaMenuCol> col;
|
||||
|
||||
public EmotivaMenuRow() {
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public List<EmotivaMenuCol> getCol() {
|
||||
return col;
|
||||
}
|
||||
|
||||
public void setCol(List<EmotivaMenuCol> col) {
|
||||
this.col = col;
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
/**
|
||||
* The EmotivaNotify message type. Received from a device if subscribed to
|
||||
* {@link org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags} values. Uses
|
||||
* the {@link EmotivaNotifyWrapper} to handle unmarshalling.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "property")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaNotifyDTO {
|
||||
|
||||
@XmlValue
|
||||
private String tagName;
|
||||
@XmlAttribute
|
||||
private String value;
|
||||
@XmlAttribute
|
||||
private String visible;
|
||||
@XmlAttribute
|
||||
private String status;
|
||||
@XmlAttribute
|
||||
private String ack;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaNotifyDTO() {
|
||||
}
|
||||
|
||||
public EmotivaNotifyDTO(String tag) {
|
||||
this.tagName = tag;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return tagName;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.tagName = name;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setVisible(String visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAck() {
|
||||
return ack;
|
||||
}
|
||||
|
||||
public void setAck(String ack) {
|
||||
this.ack = ack;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* Emotiva Notify message type. 2.x version of protocol uses command type as prefix in each line in the body, while 3.x
|
||||
* users property as prefix with name="commandType". 2.x is handled as a element with a special handler unmarshall
|
||||
* handler in {@link org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils}, while 3.x qualifies as a proper xml
|
||||
* element and can be properly unmarshalled by
|
||||
* JAXB.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaNotify")
|
||||
public class EmotivaNotifyWrapper extends AbstractNotificationDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String sequence;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaNotifyWrapper() {
|
||||
}
|
||||
|
||||
public EmotivaNotifyWrapper(String sequence, List<EmotivaPropertyDTO> properties) {
|
||||
this.sequence = sequence;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaPing message type. Use to discover Emotiva devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaPing")
|
||||
public class EmotivaPingDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String protocol;
|
||||
|
||||
public EmotivaPingDTO() {
|
||||
}
|
||||
|
||||
public EmotivaPingDTO(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaProperty DTO. Use by multiple message types to get updates from Emotiva devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "property")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaPropertyDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String name;
|
||||
@XmlAttribute
|
||||
private String value;
|
||||
@XmlAttribute
|
||||
private String visible;
|
||||
@XmlAttribute
|
||||
private String status;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaPropertyDTO() {
|
||||
}
|
||||
|
||||
public EmotivaPropertyDTO(String name, String value, String visible) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public EmotivaPropertyDTO(String name, String value, String visible, String status) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.visible = visible;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return Objects.requireNonNullElse(value, "");
|
||||
}
|
||||
|
||||
public String getVisible() {
|
||||
return Objects.requireNonNullElse(visible, "false");
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return Objects.requireNonNullElse(status, "");
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlValue;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* The EmotivaSubscriptionDTO message type. Used to send commands via {@link EmotivaSubscriptionRequest} to Emotiva
|
||||
* devices.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "property")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaSubscriptionDTO {
|
||||
|
||||
@XmlValue
|
||||
private String propertyName;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaSubscriptionDTO() {
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionDTO(EmotivaSubscriptionTags property) {
|
||||
this.propertyName = property.name();
|
||||
}
|
||||
|
||||
public static EmotivaSubscriptionDTO fromType(EmotivaSubscriptionTags tag) {
|
||||
return new EmotivaSubscriptionDTO(tag);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return propertyName;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* A helper class for sending {@link EmotivaSubscriptionDTO} messages.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaSubscription")
|
||||
public class EmotivaSubscriptionRequest extends AbstractJAXBElementDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String protocol = PROTOCOL_V2.value();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaSubscriptionRequest() {
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionRequest(List<EmotivaCommandDTO> commands, String protocol) {
|
||||
this.protocol = protocol;
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionRequest(EmotivaSubscriptionTags[] emotivaCommandTypes, String protocol) {
|
||||
this.protocol = protocol;
|
||||
List<EmotivaCommandDTO> list = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags commandType : emotivaCommandTypes) {
|
||||
list.add(EmotivaCommandDTO.fromTypeWithAck(commandType));
|
||||
}
|
||||
this.commands = list;
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionRequest(EmotivaSubscriptionTags tag) {
|
||||
this.commands = List.of(EmotivaCommandDTO.fromTypeWithAck(tag));
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionRequest(EmotivaControlCommands commandType, String protocol) {
|
||||
this.protocol = protocol;
|
||||
this.commands = List.of(EmotivaCommandDTO.fromTypeWithAck(commandType));
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAnyElement;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* A helper class for receiving {@link EmotivaSubscriptionDTO} messages.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaSubscription")
|
||||
public class EmotivaSubscriptionResponse {
|
||||
|
||||
// Only present with PROTOCOL_V2 or older
|
||||
@XmlAnyElement(lax = true)
|
||||
List<Object> tags;
|
||||
|
||||
// Only present with PROTOCOL_V3 or newer
|
||||
@XmlElement(name = "property")
|
||||
List<EmotivaPropertyDTO> properties;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaSubscriptionResponse() {
|
||||
}
|
||||
|
||||
public EmotivaSubscriptionResponse(List<EmotivaPropertyDTO> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public List<EmotivaPropertyDTO> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public List<Object> getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaTransponder message type. Received from a device if after a successful device discovery via the
|
||||
* {@link EmotivaPingDTO} message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaTransponder")
|
||||
public class EmotivaTransponderDTO {
|
||||
|
||||
@XmlElement(name = "model")
|
||||
private String model;
|
||||
@XmlElement(name = "revision")
|
||||
private String revision;
|
||||
@XmlElement(name = "dataRevision")
|
||||
private String dataRevision;
|
||||
@XmlElement(name = "name")
|
||||
private String name;
|
||||
@XmlElement(name = "control")
|
||||
private ControlDTO control;
|
||||
|
||||
public java.lang.String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public java.lang.String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public String getDataRevision() {
|
||||
return dataRevision;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ControlDTO getControl() {
|
||||
return control;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* The EmotivaUnsubscriptionDTO message type. Use to remove subscription after registration via {
|
||||
*
|
||||
* @link EmotivaSubscriptionRequest}.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaUnsubscribe")
|
||||
public class EmotivaUnsubscribeDTO extends AbstractJAXBElementDTO {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaUnsubscribeDTO() {
|
||||
}
|
||||
|
||||
public EmotivaUnsubscribeDTO(List<EmotivaCommandDTO> commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public EmotivaUnsubscribeDTO(EmotivaSubscriptionTags[] emotivaCommandTypes) {
|
||||
List<EmotivaCommandDTO> list = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags commandType : emotivaCommandTypes) {
|
||||
list.add(EmotivaCommandDTO.fromType(commandType));
|
||||
}
|
||||
this.commands = list;
|
||||
}
|
||||
|
||||
public EmotivaUnsubscribeDTO(EmotivaSubscriptionTags tag) {
|
||||
this.commands = List.of(EmotivaCommandDTO.fromType(tag));
|
||||
}
|
||||
|
||||
public EmotivaUnsubscribeDTO(EmotivaControlCommands commandType) {
|
||||
this.commands = List.of(EmotivaCommandDTO.fromType(commandType));
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* A helper class for sending EmotivaUpdate messages with {@link EmotivaCommandDTO} commands.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaUpdate")
|
||||
public class EmotivaUpdateRequest extends AbstractJAXBElementDTO {
|
||||
|
||||
@XmlAttribute
|
||||
private String protocol;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaUpdateRequest() {
|
||||
}
|
||||
|
||||
public EmotivaUpdateRequest(List<EmotivaCommandDTO> commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
public EmotivaUpdateRequest(EmotivaControlCommands command, String protocol) {
|
||||
this.protocol = protocol;
|
||||
List<EmotivaCommandDTO> list = new ArrayList<>();
|
||||
list.add(EmotivaCommandDTO.fromType(command));
|
||||
this.commands = list;
|
||||
}
|
||||
|
||||
public EmotivaUpdateRequest(EmotivaSubscriptionTags tag) {
|
||||
this.commands = List.of(EmotivaCommandDTO.fromType(tag));
|
||||
}
|
||||
|
||||
public EmotivaUpdateRequest(EmotivaSubscriptionTags[] tags, String protocol) {
|
||||
this.protocol = protocol;
|
||||
List<EmotivaCommandDTO> list = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags tag : tags) {
|
||||
list.add(EmotivaCommandDTO.fromType(tag));
|
||||
}
|
||||
this.commands = list;
|
||||
}
|
||||
|
||||
public EmotivaUpdateRequest(EmotivaControlCommands commandType) {
|
||||
this.commands = List.of(EmotivaCommandDTO.fromType(commandType));
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* The EmotivaUpdate message type. Received if an {@link EmotivaUpdateRequest} sent to a device.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@XmlRootElement(name = "emotivaUpdate")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class EmotivaUpdateResponse extends AbstractNotificationDTO {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public EmotivaUpdateResponse() {
|
||||
}
|
||||
|
||||
public EmotivaUpdateResponse(List<EmotivaPropertyDTO> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Enum types for commands to send to Emotiva devices. Used by {@link EmotivaControlRequest} to create correct
|
||||
* {@link org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO} command message.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaCommandType {
|
||||
|
||||
CYCLE, // Cycles to multiple states
|
||||
NONE, // Unknown or not in use commands
|
||||
NUMBER,
|
||||
MENU_CONTROL,
|
||||
MODE, // Audio mode
|
||||
SET, // Sets a specific number or string value
|
||||
SOURCE_MAIN_ZONE, // Main Zone sources
|
||||
SOURCE_USER, // Source with possible user assigned name
|
||||
SOURCE_ZONE2, // Zone 2 sources
|
||||
SPEAKER_PRESET, // Speaker preset
|
||||
TOGGLE, // Two state toggle
|
||||
UP_DOWN_SINGLE, // +1/-1
|
||||
UP_DOWN_HALF // +0.5/-0.5
|
||||
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.CYCLE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.MENU_CONTROL;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.MODE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.NONE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.NUMBER;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.SET;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.SOURCE_MAIN_ZONE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.SOURCE_USER;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.SOURCE_ZONE2;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.SPEAKER_PRESET;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.TOGGLE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.UP_DOWN_HALF;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.UP_DOWN_SINGLE;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.DIMENSIONLESS_DECIBEL;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.DIMENSIONLESS_PERCENT;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.NOT_IMPLEMENTED;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.ON_OFF;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.STRING;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.UNKNOWN;
|
||||
|
||||
import java.util.EnumMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Emotiva command name with corresponding command type and UoM data type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaControlCommands {
|
||||
none("", NONE, UNKNOWN),
|
||||
standby("", TOGGLE, ON_OFF),
|
||||
source_tuner("Tuner", SOURCE_USER, STRING),
|
||||
source_1("Input 1", SOURCE_USER, STRING),
|
||||
source_2("Input 2", SOURCE_USER, STRING),
|
||||
source_3("Input 3", SOURCE_USER, STRING),
|
||||
source_4("Input 4", SOURCE_USER, STRING),
|
||||
source_5("Input 5", SOURCE_USER, STRING),
|
||||
source_6("Input 6", SOURCE_USER, STRING),
|
||||
source_7("Input 7", SOURCE_USER, STRING),
|
||||
source_8("Input 8", SOURCE_USER, STRING),
|
||||
menu("", SET, STRING),
|
||||
menu_control("", MENU_CONTROL, STRING), // Not an Emotiva command, just a placeholder
|
||||
up("", SET, STRING),
|
||||
down("", SET, STRING),
|
||||
left("", SET, STRING),
|
||||
right("", SET, STRING),
|
||||
enter("", SET, STRING),
|
||||
dim("", CYCLE, DIMENSIONLESS_PERCENT),
|
||||
mode("", MODE, STRING),
|
||||
info("", SET, UNKNOWN),
|
||||
mute("", SET, ON_OFF),
|
||||
mute_off("", SET, ON_OFF),
|
||||
mute_on("", SET, ON_OFF),
|
||||
music("", SET, STRING),
|
||||
movie("", SET, STRING),
|
||||
center("", SET, DIMENSIONLESS_DECIBEL),
|
||||
subwoofer("", SET, DIMENSIONLESS_DECIBEL),
|
||||
surround("", SET, DIMENSIONLESS_DECIBEL),
|
||||
back("", SET, DIMENSIONLESS_DECIBEL),
|
||||
input("", NONE, STRING),
|
||||
input_up("", SET, STRING),
|
||||
input_down("", SET, STRING),
|
||||
power("", TOGGLE, ON_OFF), // Not an Emotiva command, just a placeholder
|
||||
power_on("", SET, ON_OFF),
|
||||
power_off("", SET, ON_OFF),
|
||||
volume("", SET, DIMENSIONLESS_DECIBEL),
|
||||
set_volume("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
loudness_on("", SET, ON_OFF),
|
||||
loudness_off("", SET, ON_OFF),
|
||||
loudness("", TOGGLE, ON_OFF),
|
||||
speaker_preset("", SPEAKER_PRESET, STRING),
|
||||
mode_up("", SET, STRING),
|
||||
mode_down("", SET, STRING),
|
||||
bass("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL), // Not an Emotiva command, just a placeholder
|
||||
bass_up("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL),
|
||||
bass_down("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL),
|
||||
treble("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL), // Not an Emotiva command, just a placeholder
|
||||
treble_up("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL),
|
||||
treble_down("", UP_DOWN_HALF, DIMENSIONLESS_DECIBEL),
|
||||
zone2_power("", TOGGLE, ON_OFF),
|
||||
zone2_power_off("", SET, ON_OFF),
|
||||
zone2_power_on("", SET, ON_OFF),
|
||||
zone2_volume("", SET, DIMENSIONLESS_DECIBEL),
|
||||
zone2_set_volume("", NUMBER, STRING),
|
||||
zone2_input("", NONE, STRING),
|
||||
zone1_band("", TOGGLE, STRING),
|
||||
band_am("", SET, STRING),
|
||||
band_fm("", SET, STRING),
|
||||
zone2_mute("", TOGGLE, ON_OFF),
|
||||
zone2_mute_off("", SET, ON_OFF),
|
||||
zone2_mute_on("", SET, ON_OFF),
|
||||
zone2_band("", SET, NOT_IMPLEMENTED),
|
||||
frequency("", UP_DOWN_SINGLE, ON_OFF),
|
||||
seek("", UP_DOWN_SINGLE, ON_OFF),
|
||||
channel("", UP_DOWN_SINGLE, ON_OFF),
|
||||
stereo("", SET, STRING),
|
||||
direct("", SET, STRING),
|
||||
dolby("", SET, STRING),
|
||||
dts("", SET, STRING),
|
||||
all_stereo("", SET, STRING),
|
||||
auto("", SET, STRING),
|
||||
reference_stereo("", SET, STRING),
|
||||
surround_mode("", SET, STRING),
|
||||
preset1("Preset 1", SET, STRING),
|
||||
preset2("Preset 2", SET, STRING),
|
||||
dirac("Dirac", SET, STRING),
|
||||
hdmi1("HDMI 1", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi2("HDMI 2", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi3("HDMI 3", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi4("HDMI 4", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi5("HDMI 5", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi6("HDMI 6", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi7("HDMI 7", SOURCE_MAIN_ZONE, STRING),
|
||||
hdmi8("HDMI 8", SOURCE_MAIN_ZONE, STRING),
|
||||
analog1("Analog 1", SOURCE_MAIN_ZONE, STRING),
|
||||
analog2("Analog 2", SOURCE_MAIN_ZONE, STRING),
|
||||
analog3("Analog 3", SOURCE_MAIN_ZONE, STRING),
|
||||
analog4("Analog 4", SOURCE_MAIN_ZONE, STRING),
|
||||
analog5("Analog 5", SOURCE_MAIN_ZONE, STRING),
|
||||
analog71("Analog 7.1", SOURCE_MAIN_ZONE, STRING),
|
||||
ARC("Audio Return Channel", SOURCE_MAIN_ZONE, STRING),
|
||||
coax1("Coax 1", SOURCE_MAIN_ZONE, STRING),
|
||||
coax2("Coax 2", SOURCE_MAIN_ZONE, STRING),
|
||||
coax3("Coax 3", SOURCE_MAIN_ZONE, STRING),
|
||||
coax4("Coax 4", SOURCE_MAIN_ZONE, STRING),
|
||||
front_in("Front In", SOURCE_MAIN_ZONE, STRING),
|
||||
optical1("Optical 1", SOURCE_MAIN_ZONE, STRING),
|
||||
optical2("Optical 2", SOURCE_MAIN_ZONE, STRING),
|
||||
optical3("Optical 3", SOURCE_MAIN_ZONE, STRING),
|
||||
optical4("Optical 4", SOURCE_MAIN_ZONE, STRING),
|
||||
tuner("Tuner 1", SOURCE_MAIN_ZONE, STRING),
|
||||
usb_stream("USB Stream", SOURCE_MAIN_ZONE, STRING),
|
||||
center_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
subwoofer_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
surround_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
back_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
width_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
height_trim_set("", NUMBER, DIMENSIONLESS_DECIBEL),
|
||||
zone2_analog1("Analog 1", SOURCE_ZONE2, STRING),
|
||||
zone2_analog2("Analog 2", SOURCE_ZONE2, STRING),
|
||||
zone2_analog3("Analog 3", SOURCE_ZONE2, STRING),
|
||||
zone2_analog4("Analog 4", SOURCE_ZONE2, STRING),
|
||||
zone2_analog5("Analog 5", SOURCE_ZONE2, STRING),
|
||||
zone2_analog71("Analog 7.1", SOURCE_ZONE2, STRING),
|
||||
zone2_analog8("Analog 8", SOURCE_ZONE2, STRING),
|
||||
zone2_ARC("Audio Return Channel", SOURCE_ZONE2, STRING),
|
||||
zone2_coax1("Coax 1", SOURCE_ZONE2, STRING),
|
||||
zone2_coax2("Coax 2", SOURCE_ZONE2, STRING),
|
||||
zone2_coax3("Coax 3", SOURCE_ZONE2, STRING),
|
||||
zone2_coax4("Coax 4", SOURCE_ZONE2, STRING),
|
||||
zone2_ethernet("Ethernet", SOURCE_ZONE2, STRING),
|
||||
zone2_follow_main("Follow Main", SOURCE_ZONE2, STRING),
|
||||
zone2_front_in("Front In", SOURCE_ZONE2, STRING),
|
||||
zone2_optical1("Optical 1", SOURCE_ZONE2, STRING),
|
||||
zone2_optical2("Optical 2", SOURCE_ZONE2, STRING),
|
||||
zone2_optical3("Optical 3", SOURCE_ZONE2, STRING),
|
||||
zone2_optical4("Optical 4", SOURCE_ZONE2, STRING),
|
||||
channel_1("Channel 1", SET, STRING),
|
||||
channel_2("Channel 2", SET, STRING),
|
||||
channel_3("Channel 3", SET, STRING),
|
||||
channel_4("Channel 4", SET, STRING),
|
||||
channel_5("Channel 5", SET, STRING),
|
||||
channel_6("Channel 6", SET, STRING),
|
||||
channel_7("Channel 7", SET, STRING),
|
||||
channel_8("Channel 8", SET, STRING),
|
||||
channel_9("Channel 9", SET, STRING),
|
||||
channel_10("Channel 10", SET, STRING),
|
||||
channel_11("Channel 11", SET, STRING),
|
||||
channel_12("Channel 12", SET, STRING),
|
||||
channel_13("Channel 13", SET, STRING),
|
||||
channel_14("Channel 14", SET, STRING),
|
||||
channel_15("Channel 15", SET, STRING),
|
||||
channel_16("Channel 16", SET, STRING),
|
||||
channel_17("Channel 17", SET, STRING),
|
||||
channel_18("Channel 18", SET, STRING),
|
||||
channel_19("Channel 19", SET, STRING),
|
||||
channel_20("Channel 20", SET, STRING);
|
||||
|
||||
private final String label;
|
||||
private final EmotivaCommandType commandType;
|
||||
private final EmotivaDataType dataType;
|
||||
|
||||
EmotivaControlCommands(String label, EmotivaCommandType commandType, EmotivaDataType dataType) {
|
||||
this.label = label;
|
||||
this.commandType = commandType;
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
public static EmotivaControlCommands matchToInput(String inputName) {
|
||||
for (EmotivaControlCommands value : values()) {
|
||||
if (inputName.toLowerCase().equals(value.name())) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
if (inputName.startsWith("input_")) {
|
||||
return valueOf(inputName.replace("input_", "source_"));
|
||||
}
|
||||
return none;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public EmotivaCommandType getCommandType() {
|
||||
return commandType;
|
||||
}
|
||||
|
||||
public EmotivaDataType getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public static EnumMap<EmotivaControlCommands, String> getCommandsFromType(EmotivaCommandType filter) {
|
||||
EnumMap<EmotivaControlCommands, String> commands = new EnumMap<>(EmotivaControlCommands.class);
|
||||
for (EmotivaControlCommands value : values()) {
|
||||
if (value.getCommandType().equals(filter)) {
|
||||
StringBuilder sb = new StringBuilder(value.name());
|
||||
sb.setCharAt(0, Character.toUpperCase(value.name().charAt(0)));
|
||||
commands.put(value, sb.toString());
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
}
|
@ -0,0 +1,475 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.clamp;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumePercentageToDecibel;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaCommandType.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.FREQUENCY_HERTZ;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Binds channels to a given command with datatype.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaControlRequest {
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaControlRequest.class);
|
||||
private String name;
|
||||
private final EmotivaDataType dataType;
|
||||
private String channel;
|
||||
private final EmotivaControlCommands defaultCommand;
|
||||
private final EmotivaControlCommands setCommand;
|
||||
private final EmotivaControlCommands onCommand;
|
||||
private final EmotivaControlCommands offCommand;
|
||||
private final EmotivaControlCommands upCommand;
|
||||
private final EmotivaControlCommands downCommand;
|
||||
private double maxValue;
|
||||
private double minValue;
|
||||
private final Map<String, Map<EmotivaControlCommands, String>> commandMaps;
|
||||
private final EmotivaProtocolVersion protocolVersion;
|
||||
|
||||
public EmotivaControlRequest(String channel, EmotivaSubscriptionTags channelSubscription,
|
||||
EmotivaControlCommands controlCommand, Map<String, Map<EmotivaControlCommands, String>> commandMaps,
|
||||
EmotivaProtocolVersion protocolVersion) {
|
||||
if (channelSubscription.equals(EmotivaSubscriptionTags.unknown)) {
|
||||
if (controlCommand.equals(EmotivaControlCommands.none)) {
|
||||
this.defaultCommand = EmotivaControlCommands.none;
|
||||
this.onCommand = EmotivaControlCommands.none;
|
||||
this.offCommand = EmotivaControlCommands.none;
|
||||
this.setCommand = EmotivaControlCommands.none;
|
||||
this.upCommand = EmotivaControlCommands.none;
|
||||
this.downCommand = EmotivaControlCommands.none;
|
||||
} else {
|
||||
this.defaultCommand = controlCommand;
|
||||
this.onCommand = resolveOnCommand(controlCommand);
|
||||
this.offCommand = resolveOffCommand(controlCommand);
|
||||
this.setCommand = resolveSetCommand(controlCommand);
|
||||
this.upCommand = resolveUpCommand(controlCommand);
|
||||
this.downCommand = resolveDownCommand(controlCommand);
|
||||
}
|
||||
} else {
|
||||
this.defaultCommand = resolveControlCommand(channelSubscription.getEmotivaName(), controlCommand);
|
||||
if (controlCommand.equals(EmotivaControlCommands.none)) {
|
||||
this.onCommand = resolveOnCommand(defaultCommand);
|
||||
this.offCommand = resolveOffCommand(defaultCommand);
|
||||
this.setCommand = resolveSetCommand(defaultCommand);
|
||||
this.upCommand = resolveUpCommand(defaultCommand);
|
||||
this.downCommand = resolveDownCommand(defaultCommand);
|
||||
} else {
|
||||
this.onCommand = controlCommand;
|
||||
this.offCommand = controlCommand;
|
||||
this.setCommand = controlCommand;
|
||||
this.upCommand = controlCommand;
|
||||
this.downCommand = controlCommand;
|
||||
}
|
||||
}
|
||||
this.name = defaultCommand.name();
|
||||
this.dataType = defaultCommand.getDataType();
|
||||
this.channel = channel;
|
||||
this.commandMaps = commandMaps;
|
||||
this.protocolVersion = protocolVersion;
|
||||
if (name.equals(EmotivaControlCommands.volume.name())
|
||||
|| name.equals(EmotivaControlCommands.zone2_volume.name())) {
|
||||
minValue = DEFAULT_VOLUME_MIN_DECIBEL;
|
||||
maxValue = DEFAULT_VOLUME_MAX_DECIBEL;
|
||||
} else if (setCommand.name().endsWith(TRIM_SET_COMMAND_SUFFIX)) {
|
||||
minValue = DEFAULT_TRIM_MIN_DECIBEL * 2;
|
||||
maxValue = DEFAULT_TRIM_MAX_DECIBEL * 2;
|
||||
}
|
||||
}
|
||||
|
||||
public EmotivaControlDTO createDTO(Command ohCommand, @Nullable State previousState) {
|
||||
switch (defaultCommand.getCommandType()) {
|
||||
case CYCLE -> {
|
||||
return EmotivaControlDTO.create(defaultCommand);
|
||||
}
|
||||
case MENU_CONTROL -> {
|
||||
if (ohCommand instanceof StringType value) {
|
||||
try {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.valueOf(value.toString().toLowerCase()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
}
|
||||
case MODE -> {
|
||||
if (ohCommand instanceof StringType value) {
|
||||
// Check if value can be interpreted as a mode-<command>
|
||||
try {
|
||||
OHChannelToEmotivaCommand ohChannelToEmotivaCommand = OHChannelToEmotivaCommand
|
||||
.valueOf(value.toString());
|
||||
return EmotivaControlDTO.create(ohChannelToEmotivaCommand.getCommand());
|
||||
} catch (IllegalArgumentException e) {
|
||||
if ("1".equals(value.toString())) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else if ("-1".equals(value.toString())) {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
} else if (ohCommand instanceof Number value) {
|
||||
if (value.intValue() >= 1) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else if (value.intValue() <= -1) {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
case NUMBER -> {
|
||||
if (ohCommand instanceof Number value) {
|
||||
return handleNumberTypes(getSetCommand(), ohCommand, value);
|
||||
} else {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name,
|
||||
NUMBER, ohCommand.getClass().getSimpleName());
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
case NONE -> {
|
||||
switch (channel) {
|
||||
case CHANNEL_TUNER_BAND -> {
|
||||
return matchToCommandMap(ohCommand, tuner_band.getEmotivaName());
|
||||
}
|
||||
case CHANNEL_TUNER_CHANNEL_SELECT -> {
|
||||
return matchToCommandMap(ohCommand, tuner_channel.getEmotivaName());
|
||||
}
|
||||
case CHANNEL_SOURCE -> {
|
||||
return matchToCommandMap(ohCommand, MAP_SOURCES_MAIN_ZONE);
|
||||
}
|
||||
case CHANNEL_ZONE2_SOURCE -> {
|
||||
return matchToCommandMap(ohCommand, MAP_SOURCES_ZONE_2);
|
||||
}
|
||||
default -> {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
}
|
||||
case SET -> {
|
||||
if (ohCommand instanceof StringType value) {
|
||||
return EmotivaControlDTO.create(getSetCommand(), value.toString());
|
||||
} else if (ohCommand instanceof Number value) {
|
||||
return handleNumberTypes(getSetCommand(), ohCommand, value);
|
||||
} else if (ohCommand instanceof OnOffType value) {
|
||||
if (value.equals(OnOffType.ON)) {
|
||||
return EmotivaControlDTO.create(getOnCommand());
|
||||
} else {
|
||||
return EmotivaControlDTO.create(getOffCommand());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name, SET,
|
||||
ohCommand.getClass().getSimpleName());
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
case SPEAKER_PRESET -> {
|
||||
if (ohCommand instanceof StringType value) {
|
||||
try {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.valueOf(value.toString()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// No match found for preset command, default to cycling
|
||||
return EmotivaControlDTO.create(defaultCommand);
|
||||
}
|
||||
} else {
|
||||
return EmotivaControlDTO.create(defaultCommand);
|
||||
}
|
||||
}
|
||||
case TOGGLE -> {
|
||||
if (ohCommand instanceof OnOffType value) {
|
||||
if (value.equals(OnOffType.ON)) {
|
||||
return EmotivaControlDTO.create(getOnCommand());
|
||||
} else {
|
||||
return EmotivaControlDTO.create(getOffCommand());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name,
|
||||
TOGGLE, ohCommand.getClass().getSimpleName());
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
case UP_DOWN_SINGLE -> {
|
||||
if (ohCommand instanceof Number value) {
|
||||
if (dataType.equals(FREQUENCY_HERTZ)) {
|
||||
if (previousState instanceof Number pre) {
|
||||
if (value.doubleValue() > pre.doubleValue()) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else if (value.doubleValue() < pre.doubleValue()) {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.intValue() <= maxValue || value.intValue() >= minValue) {
|
||||
if (value.intValue() >= 1) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else if (value.intValue() <= -1) {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
}
|
||||
// Reached max or min value, not sending anything
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
} else if (ohCommand instanceof StringType value) {
|
||||
if ("1".equals(value.toString())) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else if ("-1".equals(value.toString())) {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
} else if (ohCommand instanceof UpDownType value) {
|
||||
if (value.equals(UpDownType.UP)) {
|
||||
return EmotivaControlDTO.create(getUpCommand(), 1);
|
||||
} else {
|
||||
return EmotivaControlDTO.create(getDownCommand(), -1);
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name,
|
||||
UP_DOWN_SINGLE, ohCommand.getClass().getSimpleName());
|
||||
}
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
case UP_DOWN_HALF -> {
|
||||
if (ohCommand instanceof Number value) {
|
||||
if (value.intValue() <= maxValue || value.intValue() >= minValue) {
|
||||
Number pre = (Number) previousState;
|
||||
if (pre == null) {
|
||||
if (value.doubleValue() > 0) {
|
||||
return EmotivaControlDTO.create(getUpCommand());
|
||||
} else if (value.doubleValue() < 0) {
|
||||
return EmotivaControlDTO.create(getDownCommand());
|
||||
}
|
||||
} else {
|
||||
if (value.doubleValue() > pre.doubleValue()) {
|
||||
return EmotivaControlDTO.create(getUpCommand());
|
||||
} else if (value.doubleValue() < pre.doubleValue()) {
|
||||
return EmotivaControlDTO.create(getDownCommand());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name,
|
||||
UP_DOWN_HALF, ohCommand.getClass().getSimpleName());
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
|
||||
private EmotivaControlDTO matchToCommandMap(Command ohCommand, String mapName) {
|
||||
if (ohCommand instanceof StringType value) {
|
||||
Map<EmotivaControlCommands, String> commandMap = commandMaps.get(mapName);
|
||||
if (commandMap != null) {
|
||||
for (EmotivaControlCommands command : commandMap.keySet()) {
|
||||
String map = commandMap.get(command);
|
||||
if (map != null && map.equals(value.toString())) {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.matchToInput(command.toString()));
|
||||
} else if (command.name().equalsIgnoreCase(value.toString())) {
|
||||
return EmotivaControlDTO.create(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
|
||||
private EmotivaControlDTO handleNumberTypes(EmotivaControlCommands setCommand, Command ohCommand, Number value) {
|
||||
switch (dataType) {
|
||||
case DIMENSIONLESS_PERCENT -> {
|
||||
if (name.equals(EmotivaControlCommands.volume.name())) {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.set_volume,
|
||||
volumePercentageToDecibel(value.intValue()));
|
||||
} else if (name.equals(EmotivaControlCommands.zone2_set_volume.name())) {
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.zone2_set_volume,
|
||||
volumePercentageToDecibel(value.intValue()));
|
||||
} else {
|
||||
return EmotivaControlDTO.create(setCommand, value.intValue());
|
||||
}
|
||||
}
|
||||
case DIMENSIONLESS_DECIBEL -> {
|
||||
if (name.equals(EmotivaControlCommands.volume.name())) {
|
||||
return createForVolumeSetCommand(ohCommand, value, EmotivaControlCommands.set_volume);
|
||||
} else if (name.equals(EmotivaControlCommands.zone2_volume.name())) {
|
||||
return createForVolumeSetCommand(ohCommand, value, EmotivaControlCommands.zone2_set_volume);
|
||||
} else {
|
||||
double doubleValue = setCommand.name().endsWith(TRIM_SET_COMMAND_SUFFIX)
|
||||
? value.doubleValue() * PROTOCOL_V3_LEVEL_MULTIPLIER
|
||||
: value.doubleValue();
|
||||
if (doubleValue >= maxValue) {
|
||||
return EmotivaControlDTO.create(getSetCommand(), maxValue);
|
||||
} else if (doubleValue <= minValue) {
|
||||
return EmotivaControlDTO.create(getSetCommand(), minValue);
|
||||
} else {
|
||||
return EmotivaControlDTO.create(getSetCommand(), doubleValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
case FREQUENCY_HERTZ -> {
|
||||
return EmotivaControlDTO.create(getDefaultCommand(), value.intValue());
|
||||
}
|
||||
default -> {
|
||||
logger.debug("Could not create EmotivaControlDTO for {}:{}:{}, ohCommand is {}", channel, name,
|
||||
setCommand.getDataType(), ohCommand.getClass().getSimpleName());
|
||||
return EmotivaControlDTO.create(EmotivaControlCommands.none);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EmotivaControlDTO createForVolumeSetCommand(Command ohCommand, Number value,
|
||||
EmotivaControlCommands emotivaControlCommands) {
|
||||
if (ohCommand instanceof PercentType) {
|
||||
return EmotivaControlDTO.create(emotivaControlCommands, volumePercentageToDecibel(value.intValue()));
|
||||
} else {
|
||||
return EmotivaControlDTO.create(emotivaControlCommands, clamp(value, minValue, maxValue));
|
||||
}
|
||||
}
|
||||
|
||||
private EmotivaControlCommands resolveUpCommand(EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return EmotivaControlCommands.valueOf("%s_up".formatted(controlCommand.name()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// not found, setting original command
|
||||
return controlCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private EmotivaControlCommands resolveDownCommand(EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return EmotivaControlCommands.valueOf("%s_down".formatted(controlCommand.name()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// not found, setting original command
|
||||
return controlCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private EmotivaControlCommands resolveControlCommand(String name, EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return controlCommand.equals(EmotivaControlCommands.none) ? EmotivaControlCommands.valueOf(name)
|
||||
: controlCommand;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
return EmotivaControlCommands.none;
|
||||
}
|
||||
|
||||
private EmotivaControlCommands resolveOnCommand(EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return EmotivaControlCommands.valueOf("%s_on".formatted(controlCommand.name()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// not found, setting original command
|
||||
return controlCommand;
|
||||
}
|
||||
}
|
||||
|
||||
private EmotivaControlCommands resolveOffCommand(EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return EmotivaControlCommands.valueOf("%s_off".formatted(controlCommand.name()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// not found, using original command
|
||||
return controlCommand;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for commands with _trim_set suffix, which indicate speaker trims with a fixed min/max value.
|
||||
*/
|
||||
private EmotivaControlCommands resolveSetCommand(EmotivaControlCommands controlCommand) {
|
||||
try {
|
||||
return EmotivaControlCommands.valueOf("%s_trim_set".formatted(controlCommand.name()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// not found, using original command
|
||||
return controlCommand;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public EmotivaDataType getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getDefaultCommand() {
|
||||
return defaultCommand;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setChannel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getSetCommand() {
|
||||
return setCommand;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getOnCommand() {
|
||||
return onCommand;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getOffCommand() {
|
||||
return offCommand;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getUpCommand() {
|
||||
return upCommand;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getDownCommand() {
|
||||
return downCommand;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public double getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public EmotivaProtocolVersion getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EmotivaControlRequest{" + "name='" + name + '\'' + ", dataType=" + dataType + ", channel='" + channel
|
||||
+ '\'' + ", defaultCommand=" + defaultCommand + ", setCommand=" + setCommand + ", onCommand="
|
||||
+ onCommand + ", offCommand=" + offCommand + ", upCommand=" + upCommand + ", downCommand=" + downCommand
|
||||
+ ", maxValue=" + maxValue + ", minValue=" + minValue + ", commandMaps=" + commandMaps
|
||||
+ ", protocolVersion=" + protocolVersion + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* This enum is used to describe the value types from Emotiva.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaDataType {
|
||||
DIMENSIONLESS_DECIBEL("decibel"),
|
||||
DIMENSIONLESS_PERCENT("percent"),
|
||||
FREQUENCY_HERTZ("hertz"),
|
||||
NUMBER("number"),
|
||||
NUMBER_TIME("number_time"),
|
||||
GOODBYE("goodbye"),
|
||||
NOT_IMPLEMENTED("not_implemented"),
|
||||
ON_OFF("boolean"),
|
||||
STRING("string"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
private final String name;
|
||||
|
||||
EmotivaDataType(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static EmotivaDataType fromName(String name) {
|
||||
EmotivaDataType result = EmotivaDataType.UNKNOWN;
|
||||
for (EmotivaDataType m : EmotivaDataType.values()) {
|
||||
if (m.name.equals(name)) {
|
||||
result = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Status types for status fields of different message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaPropertyStatus {
|
||||
|
||||
VALID("ack"),
|
||||
NOT_VALID("nak");
|
||||
|
||||
private final String value;
|
||||
|
||||
EmotivaPropertyStatus(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Enum for mapping Emotiva Network Protocol versions.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaProtocolVersion {
|
||||
|
||||
PROTOCOL_V2("2.0"),
|
||||
PROTOCOL_V3("3.0");
|
||||
|
||||
private final String protocolVersion;
|
||||
|
||||
EmotivaProtocolVersion(String protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public static EmotivaProtocolVersion protocolFromConfig(String protocolVersion) {
|
||||
for (EmotivaProtocolVersion value : values()) {
|
||||
if (protocolVersion.equals(value.protocolVersion)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return PROTOCOL_V2;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return protocolVersion;
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Emotiva subscription tags with corresponding UoM data type and channel.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum EmotivaSubscriptionTags {
|
||||
|
||||
/* Protocol V1 notify tags */
|
||||
power("power", ON_OFF, CHANNEL_MAIN_ZONE_POWER),
|
||||
source("source", STRING, CHANNEL_SOURCE),
|
||||
dim("dim", DIMENSIONLESS_PERCENT, CHANNEL_DIM),
|
||||
mode("mode", STRING, CHANNEL_MODE),
|
||||
speaker_preset("speaker-preset", STRING, CHANNEL_SPEAKER_PRESET),
|
||||
center("center", DIMENSIONLESS_DECIBEL, CHANNEL_CENTER),
|
||||
subwoofer("subwoofer", DIMENSIONLESS_DECIBEL, CHANNEL_SUBWOOFER),
|
||||
surround("surround", DIMENSIONLESS_DECIBEL, CHANNEL_SURROUND),
|
||||
back("back", DIMENSIONLESS_DECIBEL, CHANNEL_BACK),
|
||||
volume("volume", DIMENSIONLESS_DECIBEL, CHANNEL_MAIN_VOLUME),
|
||||
loudness("loudness", ON_OFF, CHANNEL_LOUDNESS),
|
||||
treble("treble", DIMENSIONLESS_DECIBEL, CHANNEL_TREBLE),
|
||||
bass("bass", DIMENSIONLESS_DECIBEL, CHANNEL_BASS),
|
||||
zone2_power("zone2-power", ON_OFF, CHANNEL_ZONE2_POWER),
|
||||
zone2_volume("zone2-volume", DIMENSIONLESS_DECIBEL, CHANNEL_ZONE2_VOLUME),
|
||||
zone2_input("zone2-input", STRING, CHANNEL_ZONE2_SOURCE),
|
||||
tuner_band("tuner-band", STRING, CHANNEL_TUNER_BAND),
|
||||
tuner_channel("tuner-channel", FREQUENCY_HERTZ, CHANNEL_TUNER_CHANNEL),
|
||||
tuner_signal("tuner-signal", STRING, CHANNEL_TUNER_SIGNAL),
|
||||
tuner_program("tuner-program", STRING, CHANNEL_TUNER_PROGRAM),
|
||||
tuner_RDS("tuner-RDS", STRING, CHANNEL_TUNER_RDS),
|
||||
audio_input("audio-input", STRING, CHANNEL_AUDIO_INPUT),
|
||||
audio_bitstream("audio-bitstream", STRING, CHANNEL_AUDIO_BITSTREAM),
|
||||
audio_bits("audio-bits", STRING, CHANNEL_AUDIO_BITS),
|
||||
video_input("video-input", STRING, CHANNEL_VIDEO_INPUT),
|
||||
video_format("video-format", STRING, CHANNEL_VIDEO_FORMAT),
|
||||
video_space("video-space", STRING, CHANNEL_VIDEO_SPACE),
|
||||
input_1("input-1", STRING, CHANNEL_INPUT1),
|
||||
input_2("input-2", STRING, CHANNEL_INPUT2),
|
||||
input_3("input-3", STRING, CHANNEL_INPUT3),
|
||||
input_4("input-4", STRING, CHANNEL_INPUT4),
|
||||
input_5("input-5", STRING, CHANNEL_INPUT5),
|
||||
input_6("input-6", STRING, CHANNEL_INPUT6),
|
||||
input_7("input-7", STRING, CHANNEL_INPUT7),
|
||||
input_8("input-8", STRING, CHANNEL_INPUT8),
|
||||
|
||||
/* Protocol V2 notify tags */
|
||||
selected_mode("selected-mode", STRING, CHANNEL_SELECTED_MODE),
|
||||
selected_movie_music("selected-movie-music", STRING, CHANNEL_SELECTED_MOVIE_MUSIC),
|
||||
mode_ref_stereo("mode-ref-stereo", STRING, CHANNEL_MODE_REF_STEREO),
|
||||
mode_stereo("mode-stereo", STRING, CHANNEL_MODE_STEREO),
|
||||
mode_music("mode-music", STRING, CHANNEL_MODE_MUSIC),
|
||||
mode_movie("mode-movie", STRING, CHANNEL_MODE_MOVIE),
|
||||
mode_direct("mode-direct", STRING, CHANNEL_MODE_DIRECT),
|
||||
mode_dolby("mode-dolby", STRING, CHANNEL_MODE_DOLBY),
|
||||
mode_dts("mode-dts", STRING, CHANNEL_MODE_DTS),
|
||||
mode_all_stereo("mode-all-stereo", STRING, CHANNEL_MODE_ALL_STEREO),
|
||||
mode_auto("mode-auto", STRING, CHANNEL_MODE_AUTO),
|
||||
mode_surround("mode-surround", STRING, CHANNEL_MODE_SURROUND),
|
||||
menu("menu", ON_OFF, CHANNEL_MENU),
|
||||
menu_update("menu-update", STRING, CHANNEL_MENU_DISPLAY_PREFIX),
|
||||
|
||||
/* Protocol V3 notify tags */
|
||||
keepAlive("keepAlive", NUMBER_TIME, LAST_SEEN_STATE_NAME),
|
||||
goodBye("goodBye", GOODBYE, ""),
|
||||
bar_update("bar-update", STRING, CHANNEL_BAR),
|
||||
width("width", DIMENSIONLESS_DECIBEL, CHANNEL_WIDTH),
|
||||
height("height", DIMENSIONLESS_DECIBEL, CHANNEL_HEIGHT),
|
||||
|
||||
/* Notify tag not in the documentation */
|
||||
source_tuner("source-tuner", ON_OFF, ""),
|
||||
|
||||
/* No match tag */
|
||||
unknown("unknown", UNKNOWN, "");
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EmotivaSubscriptionTags.class);
|
||||
|
||||
/* For error handling */
|
||||
public static final String UNKNOWN_TAG = "unknown";
|
||||
|
||||
private final String name;
|
||||
private final EmotivaDataType dataType;
|
||||
private final String channel;
|
||||
|
||||
EmotivaSubscriptionTags(String name, EmotivaDataType dataType, String channel) {
|
||||
this.name = name;
|
||||
this.dataType = dataType;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public static boolean hasChannel(String name) {
|
||||
try {
|
||||
EmotivaSubscriptionTags type = EmotivaSubscriptionTags.valueOf(name);
|
||||
if (!type.channel.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// do nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static EmotivaSubscriptionTags fromChannelUID(String id) {
|
||||
for (EmotivaSubscriptionTags value : values()) {
|
||||
if (id.equals(value.getChannel())) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return EmotivaSubscriptionTags.unknown;
|
||||
}
|
||||
|
||||
public static EmotivaSubscriptionTags[] generalChannels() {
|
||||
List<EmotivaSubscriptionTags> tags = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags value : values()) {
|
||||
if (value.channel.startsWith("general")) {
|
||||
tags.add(value);
|
||||
}
|
||||
}
|
||||
return tags.toArray(new EmotivaSubscriptionTags[0]);
|
||||
}
|
||||
|
||||
public static EmotivaSubscriptionTags[] nonGeneralChannels() {
|
||||
List<EmotivaSubscriptionTags> tags = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags value : values()) {
|
||||
if (!value.channel.startsWith("general")) {
|
||||
tags.add(value);
|
||||
}
|
||||
}
|
||||
return tags.toArray(new EmotivaSubscriptionTags[0]);
|
||||
}
|
||||
|
||||
public static EmotivaSubscriptionTags[] speakerChannels() {
|
||||
List<EmotivaSubscriptionTags> tags = new ArrayList<>();
|
||||
for (EmotivaSubscriptionTags value : values()) {
|
||||
if (value.getDataType().equals(DIMENSIONLESS_DECIBEL)) {
|
||||
tags.add(value);
|
||||
}
|
||||
}
|
||||
return tags.toArray(new EmotivaSubscriptionTags[0]);
|
||||
}
|
||||
|
||||
public static List<EmotivaSubscriptionTags> noSubscriptionToChannel() {
|
||||
return List.of(goodBye);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getEmotivaName() {
|
||||
String retVal = name.replaceAll("-", "_");
|
||||
logger.debug("Converting OH channel '{}' to Emotiva command '{}'", name, retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public EmotivaDataType getDataType() {
|
||||
return dataType;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
/**
|
||||
* The class {@link EmotivaUdpResponse} represents UDP response we expect.
|
||||
*
|
||||
* @author Andi Bräu - Initial contribution
|
||||
* @author Espen Fossen - Adpated to Emotiva binding
|
||||
*/
|
||||
public record EmotivaUdpResponse(String answer, String ipAddress) {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EmotivaUdpResponse that = (EmotivaUdpResponse) o;
|
||||
return answer.equals(that.answer) && ipAddress.equals(that.ipAddress);
|
||||
}
|
||||
}
|
@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.UNKNOWN_TAG;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.emotiva.internal.dto.AbstractJAXBElementDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaAckDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaBarNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaBarNotifyWrapper;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaCommandDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaMenuNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaNotifyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaNotifyWrapper;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaPingDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaPropertyDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaSubscriptionRequest;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaSubscriptionResponse;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaTransponderDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUnsubscribeDTO;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUpdateRequest;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaUpdateResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Helper class for marshalling and unmarshalling Emotiva message types.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class EmotivaXmlUtils {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(EmotivaXmlUtils.class);
|
||||
Marshaller marshaller;
|
||||
|
||||
JAXBContext context;
|
||||
|
||||
public EmotivaXmlUtils() throws JAXBException {
|
||||
context = JAXBContext.newInstance(EmotivaAckDTO.class, EmotivaBarNotifyWrapper.class, EmotivaBarNotifyDTO.class,
|
||||
EmotivaCommandDTO.class, EmotivaControlDTO.class, EmotivaMenuNotifyDTO.class,
|
||||
EmotivaNotifyWrapper.class, EmotivaPingDTO.class, EmotivaPropertyDTO.class,
|
||||
EmotivaSubscriptionRequest.class, EmotivaSubscriptionResponse.class, EmotivaTransponderDTO.class,
|
||||
EmotivaUnsubscribeDTO.class, EmotivaUpdateRequest.class, EmotivaUpdateResponse.class);
|
||||
marshaller = context.createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||
}
|
||||
|
||||
public String marshallEmotivaDTO(Object objectInstanceType) {
|
||||
try {
|
||||
StringWriter out = new StringWriter();
|
||||
marshaller.marshal(objectInstanceType, out);
|
||||
return out.toString();
|
||||
} catch (JAXBException e) {
|
||||
LOGGER.debug("Could not marshall class of type {}", objectInstanceType.getClass().getName(), e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String marshallJAXBElementObjects(AbstractJAXBElementDTO jaxbElementDTO) {
|
||||
try {
|
||||
StringWriter out = new StringWriter();
|
||||
|
||||
List<JAXBElement<String>> commandsAsJAXBElement = new ArrayList<>();
|
||||
|
||||
if (jaxbElementDTO.getCommands() != null) {
|
||||
for (EmotivaCommandDTO command : jaxbElementDTO.getCommands()) {
|
||||
if (command.getName() != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (command.getValue() != null) {
|
||||
sb.append(" value=\"").append(command.getValue()).append("\"");
|
||||
}
|
||||
if (command.getStatus() != null) {
|
||||
sb.append(" status=\"").append(command.getStatus()).append("\"");
|
||||
}
|
||||
if (command.getVisible() != null) {
|
||||
sb.append(" visible=\"").append(command.getVisible()).append("\"");
|
||||
}
|
||||
if (command.getAck() != null) {
|
||||
sb.append(" ack=\"").append(command.getAck()).append("\"");
|
||||
}
|
||||
QName name = new QName("%s%s".formatted(command.getName().trim(), sb));
|
||||
commandsAsJAXBElement.add(jaxbElementDTO.createJAXBElement(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace commands with modified JaxbElements for Emotiva compatible marshalling
|
||||
jaxbElementDTO.setJaxbElements(commandsAsJAXBElement);
|
||||
jaxbElementDTO.setCommands(Collections.emptyList());
|
||||
|
||||
marshaller.marshal(jaxbElementDTO, out);
|
||||
|
||||
// Remove JAXB added xsi and xmlns data, not needed
|
||||
return out.toString().replaceAll("xsi:nil=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
|
||||
"");
|
||||
} catch (JAXBException e) {
|
||||
LOGGER.debug("Could not marshall class of type {}", jaxbElementDTO.getClass().getName(), e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public Object unmarshallToEmotivaDTO(String xmlAsString) throws JAXBException {
|
||||
Object object;
|
||||
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
|
||||
if (xmlAsString.isEmpty()) {
|
||||
throw new JAXBException("Could not unmarshall value, xml value is null or empty");
|
||||
}
|
||||
|
||||
StringReader xmlAsStringReader = new StringReader(xmlAsString);
|
||||
StreamSource xmlAsStringStream = new StreamSource(xmlAsStringReader);
|
||||
object = unmarshaller.unmarshal(xmlAsStringStream);
|
||||
return object;
|
||||
}
|
||||
|
||||
public List<EmotivaCommandDTO> unmarshallXmlObjectsToControlCommands(List<Object> objects) {
|
||||
List<EmotivaCommandDTO> commands = new ArrayList<>();
|
||||
for (Object object : objects) {
|
||||
try {
|
||||
Element xmlElement = (Element) object;
|
||||
|
||||
try {
|
||||
EmotivaCommandDTO commandDTO = getEmotivaCommandDTO(xmlElement);
|
||||
commands.add(commandDTO);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Notify tag {} is unknown or not defined, skipping.", xmlElement.getTagName(), e);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
LOGGER.debug("Could not cast object to Element, object is of type {}", object.getClass());
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
public List<EmotivaNotifyDTO> unmarshallToNotification(List<Object> objects) {
|
||||
List<EmotivaNotifyDTO> commands = new ArrayList<>();
|
||||
for (Object object : objects) {
|
||||
try {
|
||||
Element xmlElement = (Element) object;
|
||||
|
||||
try {
|
||||
EmotivaNotifyDTO tagDTO = getEmotivaNotifyTags(xmlElement);
|
||||
commands.add(tagDTO);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Notify tag {} is unknown or not defined, skipping.", xmlElement.getTagName(), e);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
LOGGER.debug("Could not cast object to Element, object is of type {}", object.getClass());
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
public List<EmotivaBarNotifyDTO> unmarshallToBarNotify(List<Object> objects) {
|
||||
List<EmotivaBarNotifyDTO> commands = new ArrayList<>();
|
||||
for (Object object : objects) {
|
||||
try {
|
||||
Element xmlElement = (Element) object;
|
||||
|
||||
try {
|
||||
EmotivaBarNotifyDTO tagDTO = getEmotivaBarNotify(xmlElement);
|
||||
commands.add(tagDTO);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Bar notify type {} is unknown or not defined, skipping.", xmlElement.getTagName(), e);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
LOGGER.debug("Could not cast object to Element, object is of type {}", object.getClass());
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
public List<EmotivaCommandDTO> unmarshallToCommands(String elementAsString) {
|
||||
List<EmotivaCommandDTO> commands = new ArrayList<>();
|
||||
try {
|
||||
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = builderFactory.newDocumentBuilder();
|
||||
|
||||
String[] lines = elementAsString.split("\n");
|
||||
for (String line : lines) {
|
||||
|
||||
if (line.trim().startsWith("<") && line.trim().endsWith("/>")) {
|
||||
Document doc = db.parse(new ByteArrayInputStream(line.getBytes(StandardCharsets.UTF_8)));
|
||||
doc.getDocumentElement();
|
||||
EmotivaCommandDTO commandDTO = getEmotivaCommandDTO(doc.getDocumentElement());
|
||||
commands.add(commandDTO);
|
||||
}
|
||||
}
|
||||
} catch (SAXException | IOException | ParserConfigurationException e) {
|
||||
LOGGER.debug("Error unmarshall elements to commands", e);
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
|
||||
private static EmotivaCommandDTO getEmotivaCommandDTO(Element xmlElement) {
|
||||
EmotivaControlCommands commandType;
|
||||
try {
|
||||
commandType = EmotivaControlCommands.valueOf(xmlElement.getTagName().trim());
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Could not create EmotivaCommand, unknown command {}", xmlElement.getTagName());
|
||||
commandType = EmotivaControlCommands.none;
|
||||
}
|
||||
EmotivaCommandDTO commandDTO = new EmotivaCommandDTO(commandType);
|
||||
if (xmlElement.hasAttribute("status")) {
|
||||
commandDTO.setStatus(xmlElement.getAttribute("status"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("value")) {
|
||||
commandDTO.setValue(xmlElement.getAttribute("value"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("visible")) {
|
||||
commandDTO.setVisible(xmlElement.getAttribute("visible"));
|
||||
}
|
||||
return commandDTO;
|
||||
}
|
||||
|
||||
private static EmotivaBarNotifyDTO getEmotivaBarNotify(Element xmlElement) {
|
||||
EmotivaBarNotifyDTO barNotify = new EmotivaBarNotifyDTO(xmlElement.getTagName().trim());
|
||||
if (xmlElement.hasAttribute("type")) {
|
||||
barNotify.setType(xmlElement.getAttribute("type"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("text")) {
|
||||
barNotify.setText(xmlElement.getAttribute("text"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("units")) {
|
||||
barNotify.setUnits(xmlElement.getAttribute("units"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("value")) {
|
||||
barNotify.setValue(xmlElement.getAttribute("value"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("min")) {
|
||||
barNotify.setMin(xmlElement.getAttribute("min"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("max")) {
|
||||
barNotify.setMax(xmlElement.getAttribute("max"));
|
||||
}
|
||||
return barNotify;
|
||||
}
|
||||
|
||||
private static EmotivaNotifyDTO getEmotivaNotifyTags(Element xmlElement) {
|
||||
String notifyTagName;
|
||||
try {
|
||||
notifyTagName = EmotivaSubscriptionTags.valueOf(xmlElement.getTagName().trim()).name();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.debug("Could not create EmotivaNotify, unknown subscription tag {}", xmlElement.getTagName());
|
||||
notifyTagName = UNKNOWN_TAG;
|
||||
}
|
||||
EmotivaNotifyDTO commandDTO = new EmotivaNotifyDTO(notifyTagName);
|
||||
if (xmlElement.hasAttribute("status")) {
|
||||
commandDTO.setStatus(xmlElement.getAttribute("status"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("value")) {
|
||||
commandDTO.setValue(xmlElement.getAttribute("value"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("visible")) {
|
||||
commandDTO.setVisible(xmlElement.getAttribute("visible"));
|
||||
}
|
||||
if (xmlElement.hasAttribute("ack")) {
|
||||
commandDTO.setAck(xmlElement.getAttribute("ack"));
|
||||
}
|
||||
return commandDTO;
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_CHANNEL;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_FREQUENCY;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_HEIGHT;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MAIN_VOLUME;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MAIN_VOLUME_DB;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_CONTROL;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_DOWN;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_ENTER;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_LEFT;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_RIGHT;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MENU_UP;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_ALL_STEREO;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_AUTO;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_DIRECT;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_DOLBY;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_DTS;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_MOVIE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_MUSIC;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_REF_STEREO;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_STEREO;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MODE_SURROUND;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MUTE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_SEEK;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_SOURCE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_STANDBY;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_SURROUND_MODE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_WIDTH;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_ZONE2_MUTE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_ZONE2_SOURCE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_ZONE2_VOLUME;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_ZONE2_VOLUME_DB;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Maps OH channels with only an indirect connection to an Emotiva command. Only handles 1:1 mappings.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public enum OHChannelToEmotivaCommand {
|
||||
|
||||
standby(CHANNEL_STANDBY, EmotivaControlCommands.standby),
|
||||
source(CHANNEL_SOURCE, EmotivaControlCommands.input),
|
||||
menu(CHANNEL_MENU, EmotivaControlCommands.menu),
|
||||
menu_control(CHANNEL_MENU_CONTROL, EmotivaControlCommands.menu_control),
|
||||
up(CHANNEL_MENU_UP, EmotivaControlCommands.up),
|
||||
down(CHANNEL_MENU_DOWN, EmotivaControlCommands.down),
|
||||
left(CHANNEL_MENU_LEFT, EmotivaControlCommands.left),
|
||||
right(CHANNEL_MENU_RIGHT, EmotivaControlCommands.right),
|
||||
enter(CHANNEL_MENU_ENTER, EmotivaControlCommands.enter),
|
||||
mute(CHANNEL_MUTE, EmotivaControlCommands.mute),
|
||||
volume(CHANNEL_MAIN_VOLUME, EmotivaControlCommands.volume),
|
||||
volume_db(CHANNEL_MAIN_VOLUME_DB, EmotivaControlCommands.volume),
|
||||
zone2_volume(CHANNEL_ZONE2_VOLUME, EmotivaControlCommands.zone2_volume),
|
||||
zone2_volume_db(CHANNEL_ZONE2_VOLUME_DB, EmotivaControlCommands.zone2_volume),
|
||||
zone2_mute(CHANNEL_ZONE2_MUTE, EmotivaControlCommands.zone2_mute),
|
||||
zone2_source(CHANNEL_ZONE2_SOURCE, EmotivaControlCommands.zone2_input),
|
||||
width(CHANNEL_WIDTH, EmotivaControlCommands.width_trim_set),
|
||||
height(CHANNEL_HEIGHT, EmotivaControlCommands.height_trim_set),
|
||||
frequency(CHANNEL_FREQUENCY, EmotivaControlCommands.frequency),
|
||||
seek(CHANNEL_SEEK, EmotivaControlCommands.seek),
|
||||
channel(CHANNEL_CHANNEL, EmotivaControlCommands.channel),
|
||||
mode_ref_stereo(CHANNEL_MODE_REF_STEREO, EmotivaControlCommands.reference_stereo),
|
||||
surround_mode(CHANNEL_SURROUND_MODE, EmotivaControlCommands.surround_mode),
|
||||
mode_surround(CHANNEL_MODE_SURROUND, EmotivaControlCommands.surround_mode),
|
||||
mode_stereo(CHANNEL_MODE_STEREO, EmotivaControlCommands.stereo),
|
||||
mode_music(CHANNEL_MODE_MUSIC, EmotivaControlCommands.music),
|
||||
mode_movie(CHANNEL_MODE_MOVIE, EmotivaControlCommands.movie),
|
||||
mode_direct(CHANNEL_MODE_DIRECT, EmotivaControlCommands.direct),
|
||||
mode_dolby(CHANNEL_MODE_DOLBY, EmotivaControlCommands.dolby),
|
||||
mode_dts(CHANNEL_MODE_DTS, EmotivaControlCommands.dts),
|
||||
mode_all_stereo(CHANNEL_MODE_ALL_STEREO, EmotivaControlCommands.all_stereo),
|
||||
mode_auto(CHANNEL_MODE_AUTO, EmotivaControlCommands.auto);
|
||||
|
||||
private final String ohChannel;
|
||||
private final EmotivaControlCommands command;
|
||||
|
||||
OHChannelToEmotivaCommand(String ohChannel, EmotivaControlCommands command) {
|
||||
this.ohChannel = ohChannel;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return ohChannel;
|
||||
}
|
||||
|
||||
public EmotivaControlCommands getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public static EmotivaControlCommands fromChannelUID(String id) {
|
||||
for (OHChannelToEmotivaCommand value : values()) {
|
||||
if (id.equals(value.ohChannel)) {
|
||||
return value.command;
|
||||
}
|
||||
}
|
||||
return EmotivaControlCommands.none;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="emotiva" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>Emotiva Binding</name>
|
||||
<description>This is the binding for devices from the Emotiva Audio Corporation.</description>
|
||||
<connection>local</connection>
|
||||
|
||||
<discovery-methods>
|
||||
<discovery-method>
|
||||
<service-type>ip</service-type>
|
||||
<discovery-parameters>
|
||||
<discovery-parameter>
|
||||
<name>type</name>
|
||||
<value>ipBroadcast</value>
|
||||
</discovery-parameter>
|
||||
<discovery-parameter>
|
||||
<name>destPort</name>
|
||||
<value>7001</value>
|
||||
</discovery-parameter>
|
||||
<discovery-parameter>
|
||||
<name>timeoutMs</name>
|
||||
<value>1000</value>
|
||||
</discovery-parameter>
|
||||
</discovery-parameters>
|
||||
</discovery-method>
|
||||
</discovery-methods>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:processor:config">
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Network Address</label>
|
||||
<description>IP Network Address where Emotiva device can be Reached.</description>
|
||||
</parameter>
|
||||
<parameter name="controlPort" type="integer" required="false">
|
||||
<context>control-port</context>
|
||||
<label>Control Port</label>
|
||||
<description>Network address port for control (UDP)</description>
|
||||
<default>7002</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="notifyPort" type="integer" required="false">
|
||||
<context>notify-port</context>
|
||||
<label>Notify Port</label>
|
||||
<description>Network address port for notifications (UDP)</description>
|
||||
<default>7003</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="infoPort" type="integer" required="false">
|
||||
<context>info-port</context>
|
||||
<label>Info Port</label>
|
||||
<description>Network address port for info (UDP)</description>
|
||||
<default>7004</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="menuNotifyPort" type="integer" required="false">
|
||||
<context>setup-port</context>
|
||||
<label>Menu Notify Port</label>
|
||||
<description>Network address port for menu notify port (UDP)</description>
|
||||
<default>7005</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="setupPortTCP" type="integer" required="false">
|
||||
<context>setup-port</context>
|
||||
<label>Setup Port</label>
|
||||
<description>Network address port for setup port (TCP)</description>
|
||||
<default>7100</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="protocolVersion" type="text" required="false">
|
||||
<context>protocol-revision</context>
|
||||
<label>Protocol Version</label>
|
||||
<description>Protocol version, only change if you know what your doing</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
<parameter name="retryConnectInMinutes" type="integer" required="false" unit="s">
|
||||
<label>Reconnect Interval</label>
|
||||
<description>The time to wait between reconnection attempts (in minutes)</description>
|
||||
<default>2</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,201 @@
|
||||
addon.emotiva.name = Emotiva Binding
|
||||
addon.emotiva.description = This is the binding for Emotiva Audio Corporation AV processors.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.emotiva.processor.label = Processor
|
||||
thing-type.emotiva.processor.description = Control a Emotiva AV Processor.
|
||||
thing-type.emotiva.processor.group.main-zone.label = Main Zone Control
|
||||
thing-type.emotiva.processor.group.main-zone.description = Channels for the main zone of this device.
|
||||
thing-type.emotiva.processor.group.zone2.label = Zone 2 Control
|
||||
thing-type.emotiva.processor.group.zone2.description = Channels for Zone 2 of this device.
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.emotiva.config.ipAddress.label = IP address
|
||||
thing-type.config.emotiva.config.ipAddress.description = IP address of the device
|
||||
thing-type.config.emotiva.config.controlPort = Control Port
|
||||
thing-type.config.emotiva.config.controlPort.description = UDP port to send commands to the device
|
||||
thing-type.config.emotiva.config.notifyPort.label = Notify Port
|
||||
thing-type.config.emotiva.config.notifyPort.description = UDP port to receive notifications from the device
|
||||
thing-type.config.emotiva.config.infoPort.label = Info Port
|
||||
thing-type.config.emotiva.config.infoPort.description = UDP port
|
||||
thing-type.config.emotiva.config.setupPortTCP.label = Setup TCP Port
|
||||
thing-type.config.emotiva.config.setupPortTCP.description = TCP port for remote setup
|
||||
thing-type.config.emotiva.config.menuNotifyPort.label = Menu Notify Port
|
||||
thing-type.config.emotiva.config.menuNotifyPort.description = UDP port to receive menu notifications from the device
|
||||
thing-type.config.emotiva.config.protocolVersion.label = Emotiva Protocol Version
|
||||
thing-type.config.emotiva.config.protocolVersion.description = Emotiva Network Remote Control protocol version
|
||||
thing-type.config.emotiva.config.keepAlive.label = Keep Alive Notification
|
||||
thing-type.config.emotiva.config.keepAlive.description = The interval, in milliseconds, at which the Emotiva Device will send a "keepAlive" notification
|
||||
|
||||
# channel group types
|
||||
|
||||
channel-group-type.emotiva.general.label = General Control
|
||||
channel-group-type.emotiva.general.description = General channels for this device.
|
||||
channel-group-type.emotiva.zone.label = Zone Control
|
||||
channel-group-type.emotiva.zone.description = Channels for a zone of this device.
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.emotiva.audio-input.label = Audio Input
|
||||
channel-type.emotiva.audio-input.description = Source for audio input
|
||||
channel-type.emotiva.audio-bitstream.label = Audio Input Bitstream Type
|
||||
channel-type.emotiva.audio-bitstream.description = Current audio bitstream, "PCM 2.0", "ATMOS", etc.
|
||||
channel-type.emotiva.audio-bits.label = Audio Input Bits
|
||||
channel-type.emotiva.audio-bits.description = Current audio input bits: "32kHZ 24bits", etc.
|
||||
channel-type.emotiva.bar.label = Front Panel Bar
|
||||
channel-type.emotiva.bar.description = Displays text from the front panel bar of the device
|
||||
channel-type.emotiva.channel.label = Radio Tuner Channel
|
||||
channel-type.emotiva.channel.description = Changes radio tuner channel a station at a time, up or down
|
||||
channel-type.emotiva.frequency.label = Radio Tuner Frequency
|
||||
channel-type.emotiva.frequency.description = Changes radio tuner frequency, up or down
|
||||
channel-type.emotiva.dim.label = Front Panel Dimness
|
||||
channel-type.emotiva.dim.description = Percentage of light on front panel
|
||||
channel-type.emotiva.input-name.label = Input Name
|
||||
channel-type.emotiva.input-name.description = User assigned name for input or mode
|
||||
channel-type.emotiva.loudness.label = Loudness
|
||||
channel-type.emotiva.loudness.description = Loudness ON/OFF
|
||||
channel-type.emotiva.mainPower.label = Power
|
||||
channel-type.emotiva.mainPower.description = Power ON/OFF the device
|
||||
channel-type.emotiva.menu.label = Menu
|
||||
channel-type.emotiva.menu.description = Controls the device menu
|
||||
channel-type.emotiva.mode.label = Mode
|
||||
channel-type.emotiva.mode.description = Sets main zone mode, "Stereo", "Direct", "Auto", etc.
|
||||
channel-type.emotiva.mode-surround.label = Surround Mode
|
||||
channel-type.emotiva.mode-surround.description = Select the surround mode for this zone of the device
|
||||
channel-type.emotiva.mute.label = Mute
|
||||
channel-type.emotiva.mute.description = Enable/Disable Mute on this zone of the device
|
||||
channel-type.emotiva.seek.label = Radio Tuner Seek
|
||||
channel-type.emotiva.seek.description = Enables seek of radio channel, up or down
|
||||
channel-type.emotiva.selected-mode.label = Selected Mode
|
||||
channel-type.emotiva.selected-mode.description = User selected mode for the main zone. An "Auto" value here might not mean the mode channel is in auto.
|
||||
channel-type.emotiva.selected-mode.state.option.all-stereo = All Stereo
|
||||
channel-type.emotiva.selected-mode.state.option.auto = Auto
|
||||
channel-type.emotiva.selected-mode.state.option.direct = Direct
|
||||
channel-type.emotiva.selected-mode.state.option.dolby = Dolby
|
||||
channel-type.emotiva.selected-mode.state.option.dts = DTS
|
||||
channel-type.emotiva.selected-mode.state.option.stereo = Stereo
|
||||
channel-type.emotiva.selected-mode.state.option.surround = Surround
|
||||
channel-type.emotiva.selected-mode.state.option.ref-stereo = Reference Stereo
|
||||
channel-type.emotiva.selected-movie-music.label = Selected Movie Music
|
||||
channel-type.emotiva.selected-movie-music.description = User-selected movie or music mode for main zone: "Movie" or "Music".
|
||||
channel-type.emotiva.selected-movie-music.state.option.movie = Movie
|
||||
channel-type.emotiva.selected-movie-music.state.option.music = Music
|
||||
channel-type.emotiva.speaker-preset.label = Speaker Preset
|
||||
channel-type.emotiva.speaker-preset.description = Speaker Preset Name
|
||||
channel-type.emotiva.speaker-preset.state.option.preset-1 = Speaker Preset 1
|
||||
channel-type.emotiva.speaker-preset.state.option.preset-2 = Speaker Preset 2
|
||||
channel-type.emotiva.source.label = Input Source
|
||||
channel-type.emotiva.source.description = Select the input source for this zone of the device
|
||||
channel-type.emotiva.standby.label = Standby
|
||||
channel-type.emotiva.standby.description = Set device in standby mode
|
||||
channel-type.emotiva.tuner-band.label = Radio Tuner Band
|
||||
channel-type.emotiva.tuner-band.description = Set radio tuner band, "AM" or "FM"
|
||||
channel-type.emotiva.tuner-band.state.option.band-am = AM
|
||||
channel-type.emotiva.tuner-band.state.option.band-fm = FM
|
||||
channel-type.emotiva.tuner-channel.label = Radio Tuner Channel Frequency
|
||||
channel-type.emotiva.tuner-channel.description = Frequency of user selected radio channel
|
||||
channel-type.emotiva.tuner-channel-select.label = Radio Tuner Channel Name
|
||||
channel-type.emotiva.tuner-channel-select.description = Name of user selected radio channel
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-1 = Channel 1
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-2 = Channel 2
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-3 = Channel 3
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-4 = Channel 4
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-5 = Channel 5
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-6 = Channel 6
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-7 = Channel 7
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-8 = Channel 8
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-9 = Channel 9
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-10 = Channel 10
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-11 = Channel 11
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-12 = Channel 12
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-13 = Channel 13
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-14 = Channel 14
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-15 = Channel 15
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-16 = Channel 16
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-17 = Channel 17
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-18 = Channel 18
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-19 = Channel 19
|
||||
channel-type.emotiva.tuner-channel-select.state.option.channel-20 = Channel 20
|
||||
channel-type.emotiva.tuner-program.label = Radio Tuner Program
|
||||
channel-type.emotiva.tuner-program.description = Radio tuner program: "Country", "Rock", "Classical", etc.
|
||||
channel-type.emotiva.tuner-program.state.option.adult-hits = Adult Hits
|
||||
channel-type.emotiva.tuner-program.state.option.alarm = Alarm
|
||||
channel-type.emotiva.tuner-program.state.option.alarm-test = Alarm Test
|
||||
channel-type.emotiva.tuner-program.state.option.children-programmes = Children's Programmes
|
||||
channel-type.emotiva.tuner-program.state.option.classic-rock = Classic Rock
|
||||
channel-type.emotiva.tuner-program.state.option.classical = Classical
|
||||
channel-type.emotiva.tuner-program.state.option.college = College
|
||||
channel-type.emotiva.tuner-program.state.option.country-music = Country Music
|
||||
channel-type.emotiva.tuner-program.state.option.culture = Culture
|
||||
channel-type.emotiva.tuner-program.state.option.current-affairs = Current Affairs
|
||||
channel-type.emotiva.tuner-program.state.option.documentary = Documentary
|
||||
channel-type.emotiva.tuner-program.state.option.drama = Drama
|
||||
channel-type.emotiva.tuner-program.state.option.easy-listening = Easy Listening
|
||||
channel-type.emotiva.tuner-program.state.option.education = Education
|
||||
channel-type.emotiva.tuner-program.state.option.emergency = Emergency
|
||||
channel-type.emotiva.tuner-program.state.option.emergency-test = Emergency Test
|
||||
channel-type.emotiva.tuner-program.state.option.finance = Finance
|
||||
channel-type.emotiva.tuner-program.state.option.folk-music = Folk Music
|
||||
channel-type.emotiva.tuner-program.state.option.information = Information
|
||||
channel-type.emotiva.tuner-program.state.option.jazz = Jazz
|
||||
channel-type.emotiva.tuner-program.state.option.jazz-music = Jazz Music
|
||||
channel-type.emotiva.tuner-program.state.option.language = Language
|
||||
channel-type.emotiva.tuner-program.state.option.leisure = Leisure
|
||||
channel-type.emotiva.tuner-program.state.option.light-classical = Light Classical
|
||||
channel-type.emotiva.tuner-program.state.option.national-music = National Music
|
||||
channel-type.emotiva.tuner-program.state.option.news = News
|
||||
channel-type.emotiva.tuner-program.state.option.nostalgia = Nostalgia
|
||||
channel-type.emotiva.tuner-program.state.option.oldies = Oldies (Music)
|
||||
channel-type.emotiva.tuner-program.state.option.oldies-music = Oldies Music
|
||||
channel-type.emotiva.tuner-program.state.option.other-music = Other Music
|
||||
channel-type.emotiva.tuner-program.state.option.personality = Personality
|
||||
channel-type.emotiva.tuner-program.state.option.phone-in = Phone-in
|
||||
channel-type.emotiva.tuner-program.state.option.popular-music = Popular Music (Pop)
|
||||
channel-type.emotiva.tuner-program.state.option.public = Public
|
||||
channel-type.emotiva.tuner-program.state.option.religion = Religion
|
||||
channel-type.emotiva.tuner-program.state.option.religious-talk = Religious Talk
|
||||
channel-type.emotiva.tuner-program.state.option.rhythm-blues = Rhythm & Blues
|
||||
channel-type.emotiva.tuner-program.state.option.rock = Rock
|
||||
channel-type.emotiva.tuner-program.state.option.rock-music = Rock Music
|
||||
channel-type.emotiva.tuner-program.state.option.science = Science
|
||||
channel-type.emotiva.tuner-program.state.option.serious-classical = Serious Classical
|
||||
channel-type.emotiva.tuner-program.state.option.social-affairs = Social Affairs
|
||||
channel-type.emotiva.tuner-program.state.option.soft-music = Soft Music
|
||||
channel-type.emotiva.tuner-program.state.option.soft-rhythm-blues = Soft Rhythm & Blues
|
||||
channel-type.emotiva.tuner-program.state.option.soft-rock = Soft Rock
|
||||
channel-type.emotiva.tuner-program.state.option.sport = Sport
|
||||
channel-type.emotiva.tuner-program.state.option.talk = Talk
|
||||
channel-type.emotiva.tuner-program.state.option.top-40 = Top 40
|
||||
channel-type.emotiva.tuner-program.state.option.travel = Travel
|
||||
channel-type.emotiva.tuner-program.state.option.weather = Weather
|
||||
channel-type.emotiva.tuner-rds.label = Radio Tuner RDS
|
||||
channel-type.emotiva.tuner-rds.description = Message from Radio Data System (RDS) for selected channel
|
||||
channel-type.emotiva.tuner-signal.label = Radio Tuner Signal
|
||||
channel-type.emotiva.tuner-signal.description = Radio tuner signal quality
|
||||
channel-type.emotiva.video-format.label = Video Input Format
|
||||
channel-type.emotiva.video-format.description = Current video input format: "1920x1080i/60", "3840x2160p/60", etc.
|
||||
channel-type.emotiva.video-input.label = Video Input
|
||||
channel-type.emotiva.video-input.description = Source for video input
|
||||
channel-type.emotiva.video-space.label = Video Input Space
|
||||
channel-type.emotiva.video-space.description = Current video input space: "YcbCr 8bits", etc.
|
||||
channel-type.emotiva.volume.label = Volume
|
||||
channel-type.emotiva.volume.description = Set the volume level of this zone
|
||||
channel-type.emotiva.volume-db.label = Volume (dB)
|
||||
channel-type.emotiva.volume-db.description = Set the volume level (dB).
|
||||
channel-type.emotiva.volume-speaker-db.label = Speaker Trim
|
||||
channel-type.emotiva.volume-speaker-db.description = Increased/Reduced volume for the speaker, treble or bass, in +/-dB
|
||||
channel-type.emotiva.zonePower.label = Power (zone)
|
||||
channel-type.emotiva.zonePower.description = Power ON/OFF this zone of the unit
|
||||
|
||||
|
||||
# User Messages
|
||||
message.processor.connecting = Connecting
|
||||
message.processor.connection.failed = Failed to connect, check network connectivity and configuration
|
||||
message.processor.connection.error.keep-alive = Failed to receive keepAlive message from device, check network connectivity!
|
||||
message.processor.connection.error.port = portNumber is invalid!
|
||||
message.processor.connection.error.address-empty = IP Address must be configured!
|
||||
message.processor.connection.error.address-invalid = IP Address is not valid!
|
||||
message.processor.notfound = Could not find device with ipAddress {0}
|
||||
message.processor.goodbye = Device was shutdown
|
@ -0,0 +1,532 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="emotiva"
|
||||
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">
|
||||
|
||||
<thing-type id="processor">
|
||||
<label>Emotiva Processor</label>
|
||||
<description>Emotiva Processor Thing for Emotiva Binding</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="general" typeId="general"/>
|
||||
<channel-group id="main-zone" typeId="zone">
|
||||
<label>Main Zone Control</label>
|
||||
<description>Channels for the main zone of this processor</description>
|
||||
</channel-group>
|
||||
<channel-group id="zone2" typeId="zone">
|
||||
<label>Zone 2 Control</label>
|
||||
<description>Channels for zone2 of this processor</description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
|
||||
<properties>
|
||||
<property name="model">Unknown Model</property>
|
||||
<property name="revision">Unknown Model Revision</property>
|
||||
<property name="dataRevision">Unknown Data Revision</property>
|
||||
</properties>
|
||||
|
||||
<representation-property>ipAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:processor:config"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="general">
|
||||
<label>General Control</label>
|
||||
<description>General channels for this processor</description>
|
||||
<channels>
|
||||
<channel id="power" typeId="mainPower"/>
|
||||
<channel id="standby" typeId="standby"/>
|
||||
<channel id="menu" typeId="menu"/>
|
||||
<channel id="menu-control" typeId="menu-control"/>
|
||||
<channel id="up" typeId="up"/>
|
||||
<channel id="down" typeId="up"/>
|
||||
<channel id="left" typeId="up"/>
|
||||
<channel id="right" typeId="up"/>
|
||||
<channel id="enter" typeId="up"/>
|
||||
<channel id="dim" typeId="dim"/>
|
||||
<channel id="mode" typeId="mode"/>
|
||||
<channel id="info" typeId="info"/>
|
||||
<channel id="speaker-preset" typeId="speaker-preset"/>
|
||||
<channel id="center" typeId="volume-speaker-db"/>
|
||||
<channel id="subwoofer" typeId="volume-speaker-db"/>
|
||||
<channel id="surround" typeId="volume-speaker-db"/>
|
||||
<channel id="back" typeId="volume-speaker-db"/>
|
||||
<channel id="loudness" typeId="loudness"/>
|
||||
<channel id="treble" typeId="volume-speaker-db"/>
|
||||
<channel id="bass" typeId="volume-speaker-db"/>
|
||||
<channel id="frequency" typeId="frequency"/>
|
||||
<channel id="seek" typeId="seek"/>
|
||||
<channel id="channel" typeId="channel"/>
|
||||
<channel id="tuner-band" typeId="tuner-band"/>
|
||||
<channel id="tuner-channel" typeId="tuner-channel"/>
|
||||
<channel id="tuner-channel-select" typeId="tuner-channel-select"/>
|
||||
<channel id="tuner-signal" typeId="tuner-signal"/>
|
||||
<channel id="tuner-program" typeId="tuner-program"/>
|
||||
<channel id="tuner-rds" typeId="tuner-rds"/>
|
||||
<channel id="audio-input" typeId="audio-input"/>
|
||||
<channel id="audio-bitstream" typeId="audio-bitstream"/>
|
||||
<channel id="audio-bits" typeId="audio-bits"/>
|
||||
<channel id="video-input" typeId="video-input"/>
|
||||
<channel id="video-format" typeId="video-format"/>
|
||||
<channel id="video-space" typeId="video-space"/>
|
||||
<channel id="input-1" typeId="input-name"/>
|
||||
<channel id="input-2" typeId="input-name"/>
|
||||
<channel id="input-3" typeId="input-name"/>
|
||||
<channel id="input-4" typeId="input-name"/>
|
||||
<channel id="input-5" typeId="input-name"/>
|
||||
<channel id="input-6" typeId="input-name"/>
|
||||
<channel id="input-7" typeId="input-name"/>
|
||||
<channel id="input-8" typeId="input-name"/>
|
||||
|
||||
<!-- Channels requiring protocol V2 -->
|
||||
<channel id="selected-mode" typeId="selected-mode"/>
|
||||
<channel id="selected-movie-music" typeId="selected-movie-music"/>
|
||||
<channel id="mode-ref-stereo" typeId="input-name"/>
|
||||
<channel id="mode-stereo" typeId="input-name"/>
|
||||
<channel id="mode-music" typeId="input-name"/>
|
||||
<channel id="mode-movie" typeId="input-name"/>
|
||||
<channel id="mode-direct" typeId="input-name"/>
|
||||
<channel id="mode-dolby" typeId="input-name"/>
|
||||
<channel id="mode-dts" typeId="mode"/>
|
||||
<channel id="mode-all-stereo" typeId="mode"/>
|
||||
<channel id="mode-auto" typeId="mode"/>
|
||||
<channel id="mode-surround" typeId="mode-surround"/>
|
||||
<channel id="menu-display-highlight" typeId="menu-display"/>
|
||||
<channel id="menu-display-top-start" typeId="menu-display"/>
|
||||
<channel id="menu-display-top-center" typeId="menu-display"/>
|
||||
<channel id="menu-display-top-end" typeId="menu-display"/>
|
||||
<channel id="menu-display-middle-start" typeId="menu-display"/>
|
||||
<channel id="menu-display-middle-center" typeId="menu-display"/>
|
||||
<channel id="menu-display-middle-end" typeId="menu-display"/>
|
||||
<channel id="menu-display-bottom-start" typeId="menu-display"/>
|
||||
<channel id="menu-display-bottom-center" typeId="menu-display"/>
|
||||
<channel id="menu-display-bottom-end" typeId="menu-display"/>
|
||||
|
||||
<!-- Channels requiring protocol V3 -->
|
||||
<channel id="width" typeId="volume-speaker-db"/>
|
||||
<channel id="height" typeId="volume-speaker-db"/>
|
||||
<channel id="bar" typeId="bar"/>
|
||||
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="zone">
|
||||
<label>Zone Control</label>
|
||||
<description>Channels for a zone of this processor</description>
|
||||
<channels>
|
||||
<channel id="power" typeId="zonePower"/>
|
||||
<channel id="volume" typeId="volume"/>
|
||||
<channel id="volume-db" typeId="volume-db"/>
|
||||
<channel id="mute" typeId="mute"/>
|
||||
<channel id="source" typeId="source"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-type id="mainPower">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Power</label>
|
||||
<description>Power ON/OFF the device</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="zonePower">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Power (zone)</label>
|
||||
<description>Power ON/OFF this zone of the Processor</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="volume">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Volume</label>
|
||||
<description>Set the volume level of this zone</description>
|
||||
<category>SoundVolume</category>
|
||||
<state min="0" max="100" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="volume-db" advanced="true">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Volume (dB)</label>
|
||||
<description>Set the volume level (dB). Same as [mainVolume - 96]</description>
|
||||
<category>SoundVolume</category>
|
||||
<state min="-96" max="15" step="0.5" pattern="%.1f dB"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="mute">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mute</label>
|
||||
<description>Enable or Disable Mute on this zone of the Processor</description>
|
||||
<category>SoundVolume</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="source">
|
||||
<item-type>String</item-type>
|
||||
<label>Input Source</label>
|
||||
<description>Select the input source for this zone of the Processor</description>
|
||||
<autoUpdatePolicy>recommend</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="standby">
|
||||
<item-type>Switch</item-type>
|
||||
<label>On Standby</label>
|
||||
<description>Set appliance on standby</description>
|
||||
<category>Energy</category>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="menu">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu</label>
|
||||
<description>Menu display ON/OFF for the device</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="menu-control">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Control</label>
|
||||
<description>Menu Control for emulating an Emotiva Remote control</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="up">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Up</label>
|
||||
<description>Menu Control Up</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="down">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Down</label>
|
||||
<description>Menu Control Down</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="left">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Left</label>
|
||||
<description>Menu Control Left</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="right">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Right</label>
|
||||
<description>Menu Control Right</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="enter">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Enter</label>
|
||||
<description>Menu Control Enter</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="volume-speaker-db">
|
||||
<item-type>Number</item-type>
|
||||
<label>Volume Speaker</label>
|
||||
<description>Increased/Reduced volume for a given speaker, in dB</description>
|
||||
<category>SoundVolume</category>
|
||||
<state min="-24" step="0.5" max="24" pattern="%.1f dB"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="dim">
|
||||
<item-type>Number:Dimensionless</item-type>
|
||||
<label>Front Panel Dimness</label>
|
||||
<description>Percentage of dimness: "0", "20", "40", "60", "80", "100"</description>
|
||||
<category>Light</category>
|
||||
<state min="0" step="20" max="100" pattern="%d %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="mode">
|
||||
<item-type>String</item-type>
|
||||
<label>Mode</label>
|
||||
<description>Main zone mode: "Stereo", "Direct", "Auto", etc.</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="all-stereo">all-stereo</option>
|
||||
<option value="auto">auto</option>
|
||||
<option value="direct">direct</option>
|
||||
<option value="dolby">dolby</option>
|
||||
<option value="dts">dts</option>
|
||||
<option value="stereo">stereo</option>
|
||||
<option value="surround">surround</option>
|
||||
<option value="ref-stereo">ref-stereo</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="info">
|
||||
<item-type>String</item-type>
|
||||
<label>Info Screen</label>
|
||||
<description>Shown Info Screen</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="speaker-preset">
|
||||
<item-type>String</item-type>
|
||||
<label>Speaker Preset</label>
|
||||
<description>Speaker preset Name</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="preset-1">preset-1</option>
|
||||
<option value="preset-2">preset-2</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="loudness">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Mute</label>
|
||||
<description>Enable/Disable Loudness on this zone of the Processor</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="frequency">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Radio Tuner Frequency</label>
|
||||
<description>Radio Tuner frequency</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="seek">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Radio Tuner Seek</label>
|
||||
<description>Radio Tuner seek</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="channel">
|
||||
<item-type>Rollershutter</item-type>
|
||||
<label>Tuner Channel</label>
|
||||
<description>Radio Tuner Channel</description>
|
||||
<state min="1" max="20"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-band">
|
||||
<item-type>String</item-type>
|
||||
<label>Radio Tuner Band</label>
|
||||
<description>Radio tuner band: "AM" or "FM"</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="band-am">band-am</option>
|
||||
<option value="band-fm">band-fm</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-channel">
|
||||
<item-type>Number:Frequency</item-type>
|
||||
<label>Radio Tuner Channel Frequency</label>
|
||||
<description>User select radio tuner channel frequency"</description>
|
||||
<state readOnly="true" min="535000" max="108000000" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-channel-select">
|
||||
<item-type>String</item-type>
|
||||
<label>Radio Tuner Channel Name</label>
|
||||
<description>User select radio tuner channel name</description>
|
||||
<state>
|
||||
<options>
|
||||
<option value="channel-1">channel-1</option>
|
||||
<option value="channel-2">channel-2</option>
|
||||
<option value="channel-3">channel-3</option>
|
||||
<option value="channel-4">channel-4</option>
|
||||
<option value="channel-5">channel-5</option>
|
||||
<option value="channel-6">channel-6</option>
|
||||
<option value="channel-7">channel-7</option>
|
||||
<option value="channel-8">channel-8</option>
|
||||
<option value="channel-9">channel-9</option>
|
||||
<option value="channel-10">channel-10</option>
|
||||
<option value="channel-11">channel-11</option>
|
||||
<option value="channel-12">channel-12</option>
|
||||
<option value="channel-13">channel-13</option>
|
||||
<option value="channel-14">channel-14</option>
|
||||
<option value="channel-15">channel-15</option>
|
||||
<option value="channel-16">channel-16</option>
|
||||
<option value="channel-17">channel-17</option>
|
||||
<option value="channel-18">channel-18</option>
|
||||
<option value="channel-19">channel-19</option>
|
||||
<option value="channel-20">channel-20</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-signal">
|
||||
<item-type>String</item-type>
|
||||
<label>Radio Tuner Signal</label>
|
||||
<description>Radio tuner signal quality</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-program">
|
||||
<item-type>String</item-type>
|
||||
<label>Radio Tuner Program</label>
|
||||
<description>Radio tuner program: "Country", "Rock", "Classical", etc.</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="adult-hits">adult-hits</option>
|
||||
<option value="alarm-test">alarm-test</option>
|
||||
<option value="alarm">alarm</option>
|
||||
<option value="children-programmes">children-programmes</option>
|
||||
<option value="classic-rock">classic-rock</option>
|
||||
<option value="classical">classical</option>
|
||||
<option value="college">college</option>
|
||||
<option value="country-music">country-music</option>
|
||||
<option value="culture">culture</option>
|
||||
<option value="current-affairs">current-affairs</option>
|
||||
<option value="documentary">documentary</option>
|
||||
<option value="drama">drama</option>
|
||||
<option value="easy-listening">easy-listening</option>
|
||||
<option value="education">education</option>
|
||||
<option value="emergency-test">emergency-test</option>
|
||||
<option value="emergency">emergency</option>
|
||||
<option value="finance">finance</option>
|
||||
<option value="folk-music">folk-music</option>
|
||||
<option value="information">information</option>
|
||||
<option value="jazz-music">jazz-music</option>
|
||||
<option value="jazz">jazz</option>
|
||||
<option value="language">language</option>
|
||||
<option value="Leisure">leisure</option>
|
||||
<option value="Light Classical">light-classical</option>
|
||||
<option value="National Music">national-music</option>
|
||||
<option value="News">news</option>
|
||||
<option value="Nostalgia">nostalgia</option>
|
||||
<option value="no-program">no-program</option>
|
||||
<option value="oldies">oldies</option>
|
||||
<option value="oldies-music">oldies-music</option>
|
||||
<option value="other-music">other-music</option>
|
||||
<option value="personality">personality</option>
|
||||
<option value="Phone-in">phone-in</option>
|
||||
<option value="popular-music">popular-music</option>
|
||||
<option value="public">public</option>
|
||||
<option value="religion">religion</option>
|
||||
<option value="religious-talk">religious-talk</option>
|
||||
<option value="rhythm-blues">rhythm-blues</option>
|
||||
<option value="rock-music">rock-music</option>
|
||||
<option value="rock">rock</option>
|
||||
<option value="science">Science</option>
|
||||
<option value="serious-classical">serious-classical</option>
|
||||
<option value="social-affairs">social-affairs</option>
|
||||
<option value="soft-music">soft-music</option>
|
||||
<option value="soft-rhythm-blues">soft-rhythm-blues</option>
|
||||
<option value="soft-rock">soft-rock</option>
|
||||
<option value="sport">sport</option>
|
||||
<option value="talk">talk</option>
|
||||
<option value="top-40">top-40</option>
|
||||
<option value="travel">travel</option>
|
||||
<option value="weather">weather</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="tuner-rds">
|
||||
<item-type>String</item-type>
|
||||
<label>Radio Tuner RDS</label>
|
||||
<description>Radio Data System (RDS) tuner string</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="audio-input">
|
||||
<item-type>String</item-type>
|
||||
<label>Audio Input</label>
|
||||
<description>Input source for audio on main zone</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="audio-bitstream">
|
||||
<item-type>String</item-type>
|
||||
<label>Audio Input Bitstream Type</label>
|
||||
<description>Audio input bitstream type: "PCM 2.0", "ATMOS", etc.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="audio-bits">
|
||||
<item-type>String</item-type>
|
||||
<label>Audio Input Bits</label>
|
||||
<description>Audio input bits: "32kHZ 24bits", etc.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="video-input">
|
||||
<item-type>String</item-type>
|
||||
<label>Video Input Source</label>
|
||||
<description>Input source for video on main zone</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="video-format">
|
||||
<item-type>String</item-type>
|
||||
<label>Video Input Format</label>
|
||||
<description>Video input format: "1920x1080i/60", "3840x2160p/60", etc.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="video-space">
|
||||
<item-type>String</item-type>
|
||||
<label>Video Input Space</label>
|
||||
<description>Video input space: "YcbCr 8bits", etc.</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="input-name">
|
||||
<item-type>String</item-type>
|
||||
<label>Custom Input Name</label>
|
||||
<description>Custom Input Name</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<!-- Channels requiring protocol V2 -->
|
||||
<channel-type id="selected-mode">
|
||||
<item-type>String</item-type>
|
||||
<label>User Selected Mode</label>
|
||||
<description>User selected mode for the main zone. An "Auto" value here might not mean the mode channel is in
|
||||
auto:
|
||||
"Stereo", "Direct", "Auto", etc.
|
||||
</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="all-stereo">all-stereo</option>
|
||||
<option value="auto">auto</option>
|
||||
<option value="direct">direct</option>
|
||||
<option value="dolby">dolby</option>
|
||||
<option value="dts">dts</option>
|
||||
<option value="stereo">stereo</option>
|
||||
<option value="surround">surround</option>
|
||||
<option value="ref-stereo">ref-stereo</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="selected-movie-music">
|
||||
<item-type>String</item-type>
|
||||
<label>Media Mode</label>
|
||||
<description>User-selected movie or music mode for main zone: "Movie" or "Music"</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="movie">movie</option>
|
||||
<option value="music">music</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="mode-surround">
|
||||
<item-type>String</item-type>
|
||||
<label>Mode Surround</label>
|
||||
<description>Main zone surround mode: "Auto", "Stereo", "Dolby", ...</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="all-stereo">all-stereo</option>
|
||||
<option value="auto">auto</option>
|
||||
<option value="direct">direct</option>
|
||||
<option value="dolby">dolby</option>
|
||||
<option value="dts">dts</option>
|
||||
<option value="stereo">stereo</option>
|
||||
<option value="surround">surround</option>
|
||||
<option value="ref-stereo">ref-stereo</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="bar">
|
||||
<item-type>String</item-type>
|
||||
<label>Front Panel Bar</label>
|
||||
<description>Text displayed on front panel bar of device</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="menu-display">
|
||||
<item-type>String</item-type>
|
||||
<label>Menu Display</label>
|
||||
<description>Text displayed on a specific menu row and column</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaXmlUtils;
|
||||
|
||||
/**
|
||||
* Abstract helper class for unit tests.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AbstractDTOTestBase {
|
||||
|
||||
protected EmotivaXmlUtils xmlUtils = new EmotivaXmlUtils();
|
||||
|
||||
protected String emotivaAckPowerOff = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaAck>
|
||||
<power_off status="ack"/>
|
||||
</emotivaAck>""";
|
||||
|
||||
protected String emotivaAckPowerOffAndNotRealCommand = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaAck>
|
||||
<power_off status="ack"/>
|
||||
<not_a_real_command status="ack"/>
|
||||
</emotivaAck>""";
|
||||
|
||||
protected String emotivaAckPowerOffAndVolume = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaAck>
|
||||
<power_off status="ack"/>
|
||||
<volume status="ack"/>
|
||||
</emotivaAck>""";
|
||||
|
||||
protected String emotivaCommandoPowerOn = """
|
||||
<power_on status="ack"/>""";
|
||||
|
||||
protected String emotivaNotifyEmotivaPropertyPower = """
|
||||
<property name="tuner_channel" value="FM 106.50MHz" visible="true"/>""";
|
||||
|
||||
protected String emotivaUpdateEmotivaPropertyPower = """
|
||||
<property name="power" value="On" visible="true" status="ack"/>""";
|
||||
|
||||
protected String emotivaControlVolume = """
|
||||
<emotivaControl>
|
||||
<volume value="-1" ack="no" />
|
||||
</emotivaControl>""";
|
||||
|
||||
protected String emotivaNotifyV2KeepAlive = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaNotify sequence="54062">
|
||||
<keepAlive value="7500" visible="true"/>
|
||||
</emotivaNotify>""";
|
||||
|
||||
protected String emotivaNotifyV2UnknownTag = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaNotify sequence="54062">
|
||||
<unknownTag value="0" visible="false"/>
|
||||
</emotivaNotify>""";
|
||||
|
||||
protected String emotivaNotifyV2KeepAliveSequence = "54062";
|
||||
|
||||
protected String emotivaNotifyV3KeepAlive = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaNotify sequence="54062">
|
||||
<property name="keepAlive" value="7500" visible="true"/>
|
||||
</emotivaNotify>""";
|
||||
|
||||
protected String emotivaNotifyV3EmptyMenuValue = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaNotify sequence="23929">
|
||||
<property name="menu" value="" visible="true"/>
|
||||
</emotivaNotify>
|
||||
""";
|
||||
|
||||
protected String emotivaUpdateRequest = """
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<emotivaUpdate protocol="3.0">
|
||||
<power />
|
||||
<source />
|
||||
<volume />
|
||||
<audio_bitstream />
|
||||
<audio_bits />
|
||||
<video_input />
|
||||
<video_format />
|
||||
<video_space />
|
||||
</emotivaUpdate>""";
|
||||
|
||||
protected String emotivaMenuNotify = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaMenuNotify sequence="2378">
|
||||
<row number="0">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Left Display" fixed="no" highlight="no" arrow="up"/>
|
||||
<col number="2" value="Full Status" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="1">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Right Display" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value="Volume" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="2">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Menu Display" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value="Right" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="3">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="OSD Transparent" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value=" 37.5%" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="4">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Friendly Name" fixed="no" highlight="no" arrow="up"/>
|
||||
<col number="2" value="RMC-1" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="5">
|
||||
<col number="0" value="Preferences" fixed="no" highlight="no" arrow="left"/>
|
||||
<col number="1" value="OSD Popups" fixed="no" highlight="yes" arrow="no"/>
|
||||
<col number="2" value="All" fixed="no" highlight="no" arrow="right"/>
|
||||
</row>
|
||||
<row number="6">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="LFE Level" fixed="no" highlight="no" arrow="down"/>
|
||||
<col number="2" value=" 0.0dB" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="7">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Turn-On Input" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value="Last Used" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="8">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Turn-On Volume" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value="Last Used" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="9">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Max Volume" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value=" 11.0dB" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
<row number="10">
|
||||
<col number="0" value="" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="1" value="Front Bright" fixed="no" highlight="no" arrow="no"/>
|
||||
<col number="2" value="100%" fixed="no" highlight="no" arrow="no"/>
|
||||
</row>
|
||||
</emotivaMenuNotify>""";
|
||||
|
||||
protected String emotivaMenuNotifyWithCheckBox = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<emotivaMenuNotify sequence="12129">
|
||||
<row number="0">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="" fixedWidth="false" highlight="false" arrow="up"/>
|
||||
<col number="2" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="1">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="2">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="3">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="Input change" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" checkbox="off" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="4">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="Volume" fixedWidth="false" highlight="false" arrow="up"/>
|
||||
<col number="2" checkbox="on" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="5">
|
||||
<col number="0" value="HDMI CEC" fixedWidth="false" highlight="false" arrow="left"/>
|
||||
<col number="1" value="Enable" fixedWidth="false" highlight="true" arrow="no"/>
|
||||
<col number="2" checkbox="on" highlight="false" arrow="right"/>
|
||||
</row>
|
||||
<row number="6">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="Audio to TV" fixedWidth="false" highlight="false" arrow="down"/>
|
||||
<col number="2" checkbox="off" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="7">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="Power On" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" checkbox="on" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="8">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="Power Off" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" checkbox="on" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="9">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
<row number="10">
|
||||
<col number="0" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="1" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
<col number="2" value="" fixedWidth="false" highlight="false" arrow="no"/>
|
||||
</row>
|
||||
</emotivaMenuNotify>""";
|
||||
|
||||
protected String emotivaMenuNotifyProgress = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaMenuNotify sequence="2405">
|
||||
<progress time="15"/>
|
||||
</emotivaMenuNotify>""";
|
||||
|
||||
protected String emotivaUpdateResponseV2 = """
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<emotivaUpdate protocol="2.0">
|
||||
<power value="On" visible="true" status="ack"/>
|
||||
<source value="HDMI 1" visible="true" status="nak"/>
|
||||
<noKnownTag ack="nak"/>
|
||||
</emotivaUpdate>""";
|
||||
|
||||
protected String emotivaUpdateResponseV3 = """
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<emotivaUpdate protocol="3.0">
|
||||
<property name="power" value="On" visible="true" status="ack"/>
|
||||
<property name="source" value="HDMI 1" visible="true" status="nak"/>
|
||||
<property name="noKnownTag" ack="nak"/>
|
||||
</emotivaUpdate>""";
|
||||
|
||||
protected String emotivaBarNotifyBigText = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<emotivaBarNotify sequence="98">
|
||||
<bar text="XBox One" type="bigText"/>
|
||||
</emotivaBarNotify>""";
|
||||
|
||||
protected String emotivaSubscriptionRequest = """
|
||||
<emotivaSubscription>
|
||||
<selected_mode />
|
||||
<power />
|
||||
<noKnownTag />
|
||||
</emotivaSubscription>""";
|
||||
|
||||
protected String emotivaSubscriptionResponse = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaSubscription>
|
||||
<power status="ack"/>
|
||||
<source value="SHIELD " visible="true" status="ack"/>
|
||||
<menu value="Off" visible="true" status="ack"/>
|
||||
<treble ack="yes" value="+ 1.5" visible="true" status="ack"/>
|
||||
<noKnownTag ack="no"/>
|
||||
</emotivaSubscription>""";
|
||||
|
||||
protected String emotivaPingV2 = """
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<emotivaPing />""";
|
||||
|
||||
protected String emotivaPingV3 = """
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<emotivaPing protocol="3.0"/>""";
|
||||
|
||||
protected String emotivaTransponderResponseV2 = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaTransponder>
|
||||
<model>XMC-1</model>
|
||||
<revision>2.0</revision>
|
||||
<name>Living Room</name>
|
||||
<control>
|
||||
<version>2.0</version>
|
||||
<controlPort>7002</controlPort>
|
||||
<notifyPort>7003</notifyPort>
|
||||
<infoPort>7004</infoPort>
|
||||
<setupPortTCP>7100</setupPortTCP>
|
||||
<keepAlive>10000</keepAlive>
|
||||
</control>
|
||||
</emotivaTransponder>""";
|
||||
|
||||
protected String emotivaTransponderResponseV3 = """
|
||||
<?xml version="1.0"?>
|
||||
<emotivaTransponder>
|
||||
<model>XMC-2</model>
|
||||
<revision>3.0</revision>
|
||||
<name>Living Room</name>
|
||||
<control>
|
||||
<version>3.0</version>
|
||||
<controlPort>7002</controlPort>
|
||||
<notifyPort>7003</notifyPort>
|
||||
<infoPort>7004</infoPort>
|
||||
<setupPortTCP>7100</setupPortTCP>
|
||||
<keepAlive>10000</keepAlive>
|
||||
</control>
|
||||
</emotivaTransponder>""";
|
||||
|
||||
public AbstractDTOTestBase() throws JAXBException {
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MAIN_VOLUME;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_MUTE;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_STANDBY;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_SURROUND;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumeDecibelToPercentage;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaCommandHelper.volumePercentageToDecibel;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.mute;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.mute_off;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.mute_on;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.standby;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.surround;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.surround_trim_set;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.volume;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.DIMENSIONLESS_DECIBEL;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaDataType.ON_OFF;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V2;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V3;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlRequest;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaDataType;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
|
||||
/**
|
||||
* Unit tests for the EmotivaCommandHelper.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaCommandHelperTest {
|
||||
|
||||
@Test
|
||||
void volumeToPercentage() {
|
||||
assertThat(volumeDecibelToPercentage("-100 dB"), is(PercentType.valueOf("0")));
|
||||
assertThat(volumeDecibelToPercentage(" -96"), is(PercentType.valueOf("0")));
|
||||
assertThat(volumeDecibelToPercentage("-41 dB "), is(PercentType.valueOf("50")));
|
||||
assertThat(volumeDecibelToPercentage("15"), is(PercentType.valueOf("100")));
|
||||
assertThat(volumeDecibelToPercentage("20"), is(PercentType.valueOf("100")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void volumeToDecibel() {
|
||||
assertThat(volumePercentageToDecibel("-10"), is(-96));
|
||||
assertThat(volumePercentageToDecibel("0%"), is(-96));
|
||||
assertThat(volumePercentageToDecibel("50 %"), is(-41));
|
||||
assertThat(volumePercentageToDecibel("100 % "), is(15));
|
||||
assertThat(volumePercentageToDecibel("110"), is(15));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> channelToControlRequest() {
|
||||
return Stream.of(
|
||||
Arguments.of(CHANNEL_SURROUND, "surround", DIMENSIONLESS_DECIBEL, surround, surround, surround,
|
||||
surround_trim_set, PROTOCOL_V2, -24.0, 24.0),
|
||||
Arguments.of(CHANNEL_SURROUND, "surround", DIMENSIONLESS_DECIBEL, surround, surround, surround,
|
||||
surround_trim_set, PROTOCOL_V3, -24.0, 24.0),
|
||||
Arguments.of(CHANNEL_MUTE, "mute", ON_OFF, mute, mute_on, mute_off, mute, PROTOCOL_V2, 0, 0),
|
||||
Arguments.of(CHANNEL_STANDBY, "standby", ON_OFF, standby, standby, standby, standby, PROTOCOL_V2, 0, 0),
|
||||
Arguments.of(CHANNEL_MAIN_VOLUME, "volume", DIMENSIONLESS_DECIBEL, volume, volume, volume, volume,
|
||||
PROTOCOL_V2, -96, 15));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("channelToControlRequest")
|
||||
void testChannelToControlRequest(String channel, String name, EmotivaDataType emotivaDataType,
|
||||
EmotivaControlCommands defaultCommand, EmotivaControlCommands onCommand, EmotivaControlCommands offCommand,
|
||||
EmotivaControlCommands setCommand, EmotivaProtocolVersion version, double min, double max) {
|
||||
final Map<String, Map<EmotivaControlCommands, String>> commandMaps = new ConcurrentHashMap<>();
|
||||
|
||||
EmotivaControlRequest surround = EmotivaCommandHelper.channelToControlRequest(channel, commandMaps, version);
|
||||
assertThat(surround.getName(), is(name));
|
||||
assertThat(surround.getChannel(), is(channel));
|
||||
assertThat(surround.getDataType(), is(emotivaDataType));
|
||||
assertThat(surround.getDefaultCommand(), is(defaultCommand));
|
||||
assertThat(surround.getOnCommand(), is(onCommand));
|
||||
assertThat(surround.getOffCommand(), is(offCommand));
|
||||
assertThat(surround.getSetCommand(), is(setCommand));
|
||||
assertThat(surround.getMinValue(), is(min));
|
||||
assertThat(surround.getMaxValue(), is(max));
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaAck message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaAckDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaAckDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallValidCommand() throws JAXBException {
|
||||
EmotivaAckDTO dto = (EmotivaAckDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaAckPowerOff);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getCommands().size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallOneValidCommand() throws JAXBException {
|
||||
EmotivaAckDTO dto = (EmotivaAckDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaAckPowerOffAndNotRealCommand);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
List<EmotivaCommandDTO> commands = xmlUtils.unmarshallXmlObjectsToControlCommands(dto.getCommands());
|
||||
assertThat(commands.size(), is(2));
|
||||
|
||||
assertThat(commands.get(0), is(notNullValue()));
|
||||
assertThat(commands.get(0).getName(), is(EmotivaControlCommands.power_off.name()));
|
||||
assertThat(commands.get(0).getStatus(), is("ack"));
|
||||
assertThat(commands.get(0).getVisible(), is(nullValue()));
|
||||
assertThat(commands.get(0).getValue(), is(nullValue()));
|
||||
|
||||
assertThat(commands.get(1), is(notNullValue()));
|
||||
assertThat(commands.get(1).getName(), is(EmotivaControlCommands.none.name()));
|
||||
assertThat(commands.get(1).getStatus(), is("ack"));
|
||||
assertThat(commands.get(1).getVisible(), is(nullValue()));
|
||||
assertThat(commands.get(1).getValue(), is(nullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaBarNotify message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaBarNotifyDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaBarNotifyDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshall() throws JAXBException {
|
||||
EmotivaBarNotifyWrapper dto = (EmotivaBarNotifyWrapper) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaBarNotifyBigText);
|
||||
assertThat(dto.getSequence(), is("98"));
|
||||
assertThat(dto.getTags().size(), is(1));
|
||||
|
||||
List<EmotivaBarNotifyDTO> commands = xmlUtils.unmarshallToBarNotify(dto.getTags());
|
||||
assertThat(commands.get(0).getType(), is("bigText"));
|
||||
assertThat(commands.get(0).getText(), is("XBox One"));
|
||||
assertThat(commands.get(0).getUnits(), is(nullValue()));
|
||||
assertThat(commands.get(0).getMin(), is(nullValue()));
|
||||
assertThat(commands.get(0).getMax(), is(nullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaCommandDTO command types.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaCommandDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaCommandDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallElements() {
|
||||
List<EmotivaCommandDTO> commandDTO = xmlUtils.unmarshallToCommands(emotivaCommandoPowerOn);
|
||||
assertThat(commandDTO, is(notNullValue()));
|
||||
assertThat(commandDTO.size(), is(1));
|
||||
assertThat(commandDTO.get(0).getName(), is(EmotivaControlCommands.power_on.name()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallFromEmotivaAckWithMissingEnumType() {
|
||||
List<EmotivaCommandDTO> commandDTO = xmlUtils.unmarshallToCommands(emotivaAckPowerOffAndNotRealCommand);
|
||||
assertThat(commandDTO, is(notNullValue()));
|
||||
assertThat(commandDTO.size(), is(2));
|
||||
assertThat(commandDTO.get(0).getName(), is(EmotivaControlCommands.power_off.name()));
|
||||
assertThat(commandDTO.get(0).getStatus(), is("ack"));
|
||||
assertThat(commandDTO.get(0).getValue(), is(nullValue()));
|
||||
assertThat(commandDTO.get(0).getVisible(), is(nullValue()));
|
||||
assertThat(commandDTO.get(1).getName(), is(EmotivaControlCommands.none.name()));
|
||||
assertThat(commandDTO.get(1).getStatus(), is("ack"));
|
||||
assertThat(commandDTO.get(1).getValue(), is(nullValue()));
|
||||
assertThat(commandDTO.get(1).getVisible(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallFromEmotivaAck() {
|
||||
List<EmotivaCommandDTO> commandDTO = xmlUtils.unmarshallToCommands(emotivaAckPowerOffAndVolume);
|
||||
assertThat(commandDTO, is(notNullValue()));
|
||||
assertThat(commandDTO.size(), is(2));
|
||||
assertThat(commandDTO.get(0).getName(), is(EmotivaControlCommands.power_off.name()));
|
||||
assertThat(commandDTO.get(0).getStatus(), is("ack"));
|
||||
assertThat(commandDTO.get(0).getValue(), is(nullValue()));
|
||||
assertThat(commandDTO.get(0).getVisible(), is(nullValue()));
|
||||
assertThat(commandDTO.get(1).getName(), is(EmotivaControlCommands.volume.name()));
|
||||
assertThat(commandDTO.get(1).getStatus(), is("ack"));
|
||||
assertThat(commandDTO.get(1).getValue(), is(nullValue()));
|
||||
assertThat(commandDTO.get(1).getVisible(), is(nullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaControl message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaControlDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaControlDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalWithNoCommand() {
|
||||
EmotivaControlDTO control = new EmotivaControlDTO(null);
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(control);
|
||||
assertThat(xmlString, containsString("<emotivaControl/>"));
|
||||
assertThat(xmlString, not(containsString("<property")));
|
||||
assertThat(xmlString, not(containsString("</emotivaControl>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalNoCommand() {
|
||||
EmotivaControlDTO control = new EmotivaControlDTO(Collections.emptyList());
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(control);
|
||||
assertThat(xmlString, containsString("<emotivaControl/>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalCommand() {
|
||||
EmotivaCommandDTO command = EmotivaCommandDTO.fromTypeWithAck(EmotivaControlCommands.set_volume, "10");
|
||||
EmotivaControlDTO control = new EmotivaControlDTO(List.of(command));
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(control);
|
||||
assertThat(xmlString, containsString("<emotivaControl>"));
|
||||
assertThat(xmlString, containsString("<set_volume value=\"10\" ack=\"yes\" />"));
|
||||
assertThat(xmlString, endsWith("</emotivaControl>\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalWithTwoCommands() {
|
||||
EmotivaControlDTO control = new EmotivaControlDTO(
|
||||
List.of(EmotivaCommandDTO.fromTypeWithAck(EmotivaControlCommands.power_on),
|
||||
EmotivaCommandDTO.fromTypeWithAck(EmotivaControlCommands.hdmi1)));
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(control);
|
||||
assertThat(xmlString, containsString("<emotivaControl>"));
|
||||
assertThat(xmlString, containsString("<power_on ack=\"yes\" />"));
|
||||
assertThat(xmlString, containsString("<hdmi1 ack=\"yes\" />"));
|
||||
assertThat(xmlString, endsWith("</emotivaControl>\n"));
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaMenuNotify message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaMenuNotifyDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaMenuNotifyDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallMenu() throws JAXBException {
|
||||
EmotivaMenuNotifyDTO dto = (EmotivaMenuNotifyDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaMenuNotify);
|
||||
assertThat(dto.getProgress(), is(nullValue()));
|
||||
assertThat(dto.getSequence(), is("2378"));
|
||||
assertThat(dto.getRow().size(), is(11));
|
||||
assertThat(dto.getRow().size(), is(11));
|
||||
assertThat(dto.getRow().get(0).getNumber(), is("0"));
|
||||
assertThat(dto.getRow().get(0).getCol().size(), is(3));
|
||||
assertThat(dto.getRow().get(0).getCol().get(0).getNumber(), is("0"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(0).getValue(), is(""));
|
||||
assertThat(dto.getRow().get(0).getCol().get(0).getHighlight(), is("no"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(0).getArrow(), is("no"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(1).getNumber(), is("1"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(1).getValue(), is("Left Display"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(1).getHighlight(), is("no"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(1).getArrow(), is("up"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(2).getNumber(), is("2"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(2).getValue(), is("Full Status"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(2).getHighlight(), is("no"));
|
||||
assertThat(dto.getRow().get(0).getCol().get(2).getArrow(), is("no"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallProgress() throws JAXBException {
|
||||
EmotivaMenuNotifyDTO dto = (EmotivaMenuNotifyDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaMenuNotifyProgress);
|
||||
assertThat(dto.getSequence(), is("2405"));
|
||||
assertThat(dto.getRow(), is(nullValue()));
|
||||
assertThat(dto.getProgress().getTime(), is("15"));
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaNotify wrapper.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaNotifyWrapperTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaNotifyWrapperTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithNoProperty() {
|
||||
EmotivaNotifyWrapper dto = new EmotivaNotifyWrapper(emotivaNotifyV2KeepAliveSequence, Collections.emptyList());
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString,
|
||||
containsString("<emotivaNotify sequence=\"" + emotivaNotifyV2KeepAliveSequence + "\"/>"));
|
||||
assertThat(xmlAsString, not(containsString("<property")));
|
||||
assertThat(xmlAsString, not(containsString("</emotivaNotify>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithOneProperty() {
|
||||
List<EmotivaPropertyDTO> keepAliveProperty = List.of(new EmotivaPropertyDTO("keepAlive", "7500", "true"));
|
||||
EmotivaNotifyWrapper dto = new EmotivaNotifyWrapper(emotivaNotifyV2KeepAliveSequence, keepAliveProperty);
|
||||
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString,
|
||||
containsString("<emotivaNotify sequence=\"" + emotivaNotifyV2KeepAliveSequence + "\">"));
|
||||
assertThat(xmlAsString, containsString("<property name=\"keepAlive\" value=\"7500\" visible=\"true\"/>"));
|
||||
assertThat(xmlAsString, containsString("</emotivaNotify>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallV2() throws JAXBException {
|
||||
EmotivaNotifyWrapper dto = (EmotivaNotifyWrapper) xmlUtils.unmarshallToEmotivaDTO(emotivaNotifyV2KeepAlive);
|
||||
assertThat(dto.getSequence(), is(emotivaNotifyV2KeepAliveSequence));
|
||||
assertThat(dto.getTags().size(), is(1));
|
||||
assertThat(dto.getTags().get(0), instanceOf(Element.class));
|
||||
Element keepAlive = (Element) dto.getTags().get(0);
|
||||
assertThat(keepAlive.getTagName(), is(EmotivaSubscriptionTags.keepAlive.name()));
|
||||
assertThat(keepAlive.hasAttribute("value"), is(true));
|
||||
assertThat(keepAlive.getAttribute("value"), is("7500"));
|
||||
assertThat(keepAlive.hasAttribute("visible"), is(true));
|
||||
assertThat(keepAlive.getAttribute("visible"), is("true"));
|
||||
assertThat(dto.getProperties(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallV2UnknownProperty() throws JAXBException {
|
||||
EmotivaNotifyWrapper dto = (EmotivaNotifyWrapper) xmlUtils.unmarshallToEmotivaDTO(emotivaNotifyV2UnknownTag);
|
||||
assertThat(dto.getSequence(), is(emotivaNotifyV2KeepAliveSequence));
|
||||
assertThat(dto.getTags().size(), is(1));
|
||||
assertThat(dto.getTags().get(0), instanceOf(Element.class));
|
||||
Element unknownCommand = (Element) dto.getTags().get(0);
|
||||
assertThat(unknownCommand.getTagName(), is("unknownTag"));
|
||||
assertThat(dto.getProperties(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallV3() throws JAXBException {
|
||||
EmotivaNotifyWrapper dto = (EmotivaNotifyWrapper) xmlUtils.unmarshallToEmotivaDTO(emotivaNotifyV3KeepAlive);
|
||||
assertThat(dto.getSequence(), is(emotivaNotifyV2KeepAliveSequence));
|
||||
assertThat(dto.getProperties().size(), is(1));
|
||||
assertThat(dto.getTags(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallV3EmptyValue() throws JAXBException {
|
||||
EmotivaNotifyWrapper dto = (EmotivaNotifyWrapper) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaNotifyV3EmptyMenuValue);
|
||||
assertThat(dto.getSequence(), is("23929"));
|
||||
assertThat(dto.getProperties().size(), is(1));
|
||||
assertThat(dto.getProperties().get(0).getName(), is("menu"));
|
||||
assertThat(dto.getProperties().get(0).getValue(), is(""));
|
||||
assertThat(dto.getProperties().get(0).getVisible(), is("true"));
|
||||
assertThat(dto.getProperties().get(0).getStatus(), is(notNullValue()));
|
||||
assertThat(dto.getTags(), is(nullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaPing message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaPingDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaPingDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallPlain() {
|
||||
EmotivaPingDTO dto = new EmotivaPingDTO();
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString, containsString("<emotivaPing/>"));
|
||||
assertThat(xmlAsString, not(containsString("<property")));
|
||||
assertThat(xmlAsString, not(containsString("</emotivaPing>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithProtocol() {
|
||||
EmotivaPingDTO dto = new EmotivaPingDTO("3.0");
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString, containsString("<emotivaPing protocol=\"3.0\"/>"));
|
||||
assertThat(xmlAsString, not(containsString("<property")));
|
||||
assertThat(xmlAsString, not(containsString("</emotivaPing>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV2() throws JAXBException {
|
||||
EmotivaPingDTO dto = (EmotivaPingDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaPingV2);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getProtocol(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV3() throws JAXBException {
|
||||
EmotivaPingDTO dto = (EmotivaPingDTO) xmlUtils.unmarshallToEmotivaDTO(emotivaPingV3);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getProtocol(), is(notNullValue()));
|
||||
assertThat(dto.getProtocol(), is("3.0"));
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus.VALID;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaCommandDTO command types.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaPropertyDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaPropertyDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallFromEmotivaNotify() throws JAXBException {
|
||||
EmotivaPropertyDTO commandDTO = (EmotivaPropertyDTO) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaNotifyEmotivaPropertyPower);
|
||||
assertThat(commandDTO, is(notNullValue()));
|
||||
assertThat(commandDTO.getName(), is(EmotivaSubscriptionTags.tuner_channel.name()));
|
||||
assertThat(commandDTO.getValue(), is("FM 106.50MHz"));
|
||||
assertThat(commandDTO.getVisible(), is("true"));
|
||||
assertThat(commandDTO.getStatus(), is(notNullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallFromEmotivaUpdate() throws JAXBException {
|
||||
EmotivaPropertyDTO commandDTO = (EmotivaPropertyDTO) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaUpdateEmotivaPropertyPower);
|
||||
assertThat(commandDTO, is(notNullValue()));
|
||||
assertThat(commandDTO.getName(), is(EmotivaControlCommands.power.name()));
|
||||
assertThat(commandDTO.getValue(), is("On"));
|
||||
assertThat(commandDTO.getVisible(), is("true"));
|
||||
assertThat(commandDTO.getStatus(), is(VALID.getValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_TUNER_RDS;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.PROTOCOL_V2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaSubscription requests.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaSubscriptionRequestTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaSubscriptionRequestTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalFromChannelUID() {
|
||||
EmotivaSubscriptionTags subscriptionChannel = EmotivaSubscriptionTags.fromChannelUID(CHANNEL_TUNER_RDS);
|
||||
EmotivaSubscriptionRequest emotivaSubscriptionRequest = new EmotivaSubscriptionRequest(subscriptionChannel);
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(emotivaSubscriptionRequest);
|
||||
assertThat(xmlString, containsString("<emotivaSubscription protocol=\"2.0\">"));
|
||||
assertThat(xmlString, containsString("<tuner_RDS ack=\"yes\" />"));
|
||||
assertThat(xmlString, containsString("</emotivaSubscription>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithTwoSubscriptionsNoAck() {
|
||||
EmotivaCommandDTO command1 = new EmotivaCommandDTO(EmotivaControlCommands.volume, "10", "yes");
|
||||
EmotivaCommandDTO command2 = new EmotivaCommandDTO(EmotivaControlCommands.power_off);
|
||||
|
||||
EmotivaSubscriptionRequest dto = new EmotivaSubscriptionRequest(List.of(command1, command2),
|
||||
PROTOCOL_V2.value());
|
||||
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(dto);
|
||||
assertThat(xmlString, containsString("<emotivaSubscription protocol=\"2.0\">"));
|
||||
assertThat(xmlString, containsString("<volume value=\"10\" ack=\"yes\" />"));
|
||||
assertThat(xmlString, containsString("<power_off />"));
|
||||
assertThat(xmlString, containsString("</emotivaSubscription>"));
|
||||
assertThat(xmlString, not(containsString("<volume>")));
|
||||
assertThat(xmlString, not(containsString("<command>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshall() throws JAXBException {
|
||||
var dto = (EmotivaSubscriptionResponse) xmlUtils.unmarshallToEmotivaDTO(emotivaSubscriptionRequest);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getTags().size(), is(3));
|
||||
assertThat(dto.getProperties(), is(nullValue()));
|
||||
|
||||
List<EmotivaNotifyDTO> commands = xmlUtils.unmarshallToNotification(dto.getTags());
|
||||
|
||||
assertThat(commands.get(0).getName(), is(EmotivaSubscriptionTags.selected_mode.name()));
|
||||
assertThat(commands.get(0).getStatus(), is(nullValue()));
|
||||
assertThat(commands.get(0).getValue(), is(nullValue()));
|
||||
assertThat(commands.get(0).getVisible(), is(nullValue()));
|
||||
|
||||
assertThat(commands.get(1).getName(), is(EmotivaSubscriptionTags.power.name()));
|
||||
assertThat(commands.get(1).getStatus(), is(nullValue()));
|
||||
assertThat(commands.get(1).getValue(), is(nullValue()));
|
||||
assertThat(commands.get(1).getVisible(), is(nullValue()));
|
||||
|
||||
assertThat(commands.get(2).getName(), is("unknown"));
|
||||
assertThat(commands.get(2).getStatus(), is(nullValue()));
|
||||
assertThat(commands.get(2).getValue(), is(nullValue()));
|
||||
assertThat(commands.get(2).getVisible(), is(nullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.power_on;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaSubscription responses.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaSubscriptionResponseTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaSubscriptionResponseTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallNoProperty() {
|
||||
var dto = new EmotivaSubscriptionResponse(Collections.emptyList());
|
||||
String xmlString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlString, containsString("<emotivaSubscription/>"));
|
||||
assertThat(xmlString, not(containsString("</emotivaSubscription>")));
|
||||
assertThat(xmlString, not(containsString("<property")));
|
||||
assertThat(xmlString, not(containsString("<property>")));
|
||||
assertThat(xmlString, not(containsString("</property>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithOneProperty() {
|
||||
EmotivaPropertyDTO emotivaPropertyDTO = new EmotivaPropertyDTO(power_on.name(), "On", "true");
|
||||
var dto = new EmotivaSubscriptionResponse(Collections.singletonList(emotivaPropertyDTO));
|
||||
String xmlString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlString, containsString("<emotivaSubscription>"));
|
||||
assertThat(xmlString, containsString("<property name=\"power_on\" value=\"On\" visible=\"true\"/>"));
|
||||
assertThat(xmlString, not(containsString("<property>")));
|
||||
assertThat(xmlString, not(containsString("</property>")));
|
||||
assertThat(xmlString, containsString("</emotivaSubscription>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshall() throws JAXBException {
|
||||
var dto = (EmotivaSubscriptionResponse) xmlUtils.unmarshallToEmotivaDTO(emotivaSubscriptionResponse);
|
||||
assertThat(dto.tags, is(notNullValue()));
|
||||
assertThat(dto.tags.size(), is(5));
|
||||
List<EmotivaNotifyDTO> commands = xmlUtils.unmarshallToNotification(dto.getTags());
|
||||
assertThat(commands, is(notNullValue()));
|
||||
assertThat(commands.size(), is(dto.tags.size()));
|
||||
assertThat(commands.get(0), instanceOf(EmotivaNotifyDTO.class));
|
||||
assertThat(commands.get(0).getName(), is(EmotivaSubscriptionTags.power.name()));
|
||||
assertThat(commands.get(0).getStatus(), is(EmotivaPropertyStatus.VALID.getValue()));
|
||||
assertThat(commands.get(0).getVisible(), is(nullValue()));
|
||||
assertThat(commands.get(0).getValue(), is(nullValue()));
|
||||
|
||||
assertThat(commands.get(1).getName(), is(EmotivaSubscriptionTags.source.name()));
|
||||
assertThat(commands.get(1).getValue(), is("SHIELD "));
|
||||
assertThat(commands.get(1).getVisible(), is("true"));
|
||||
assertThat(commands.get(1).getStatus(), is(EmotivaPropertyStatus.VALID.getValue()));
|
||||
|
||||
assertThat(commands.get(2).getName(), is(EmotivaSubscriptionTags.menu.name()));
|
||||
assertThat(commands.get(2).getValue(), is("Off"));
|
||||
assertThat(commands.get(2).getVisible(), is("true"));
|
||||
assertThat(commands.get(2).getStatus(), is(EmotivaPropertyStatus.VALID.getValue()));
|
||||
|
||||
assertThat(commands.get(3).getName(), is(EmotivaSubscriptionTags.treble.name()));
|
||||
assertThat(commands.get(3).getValue(), is("+ 1.5"));
|
||||
assertThat(commands.get(3).getVisible(), is("true"));
|
||||
assertThat(commands.get(3).getStatus(), is(EmotivaPropertyStatus.VALID.getValue()));
|
||||
assertThat(commands.get(3).getAck(), is("yes"));
|
||||
|
||||
assertThat(commands.get(4).getName(), is(EmotivaSubscriptionTags.UNKNOWN_TAG));
|
||||
assertThat(commands.get(4).getValue(), is(nullValue()));
|
||||
assertThat(commands.get(4).getVisible(), is(nullValue()));
|
||||
assertThat(commands.get(4).getStatus(), is(nullValue()));
|
||||
assertThat(commands.get(4).getAck(), is("no"));
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaTransponder message type.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaTransponderDTOTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaTransponderDTOTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV2() throws JAXBException {
|
||||
EmotivaTransponderDTO dto = (EmotivaTransponderDTO) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaTransponderResponseV2);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getModel(), is("XMC-1"));
|
||||
assertThat(dto.getRevision(), is("2.0"));
|
||||
assertThat(dto.getName(), is("Living Room"));
|
||||
assertThat(dto.getControl().getVersion(), is("2.0"));
|
||||
assertThat(dto.getControl().getControlPort(), is(7002));
|
||||
assertThat(dto.getControl().getNotifyPort(), is(7003));
|
||||
assertThat(dto.getControl().getInfoPort(), is(7004));
|
||||
assertThat(dto.getControl().getSetupPortTCP(), is(7100));
|
||||
assertThat(dto.getControl().getKeepAlive(), is(10000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV3() throws JAXBException {
|
||||
EmotivaTransponderDTO dto = (EmotivaTransponderDTO) xmlUtils
|
||||
.unmarshallToEmotivaDTO(emotivaTransponderResponseV3);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getModel(), is("XMC-2"));
|
||||
assertThat(dto.getRevision(), is("3.0"));
|
||||
assertThat(dto.getName(), is("Living Room"));
|
||||
assertThat(dto.getControl().getVersion(), is("3.0"));
|
||||
assertThat(dto.getControl().getControlPort(), is(7002));
|
||||
assertThat(dto.getControl().getNotifyPort(), is(7003));
|
||||
assertThat(dto.getControl().getInfoPort(), is(7004));
|
||||
assertThat(dto.getControl().getSetupPortTCP(), is(7100));
|
||||
assertThat(dto.getControl().getKeepAlive(), is(10000));
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.CHANNEL_TUNER_RDS;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaUnsubscribe requests.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaUnsubscriptionTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaUnsubscriptionTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalFromChannelUID() {
|
||||
EmotivaSubscriptionTags subscriptionChannel = EmotivaSubscriptionTags.fromChannelUID(CHANNEL_TUNER_RDS);
|
||||
EmotivaUnsubscribeDTO emotivaSubscriptionRequest = new EmotivaUnsubscribeDTO(subscriptionChannel);
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(emotivaSubscriptionRequest);
|
||||
assertThat(xmlString, containsString("<emotivaUnsubscribe>"));
|
||||
assertThat(xmlString, containsString("<tuner_RDS />"));
|
||||
assertThat(xmlString, containsString("</emotivaUnsubscribe>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithTwoUnsubscriptions() {
|
||||
EmotivaCommandDTO command1 = new EmotivaCommandDTO(EmotivaControlCommands.volume);
|
||||
EmotivaCommandDTO command2 = new EmotivaCommandDTO(EmotivaControlCommands.power_off);
|
||||
|
||||
EmotivaUnsubscribeDTO dto = new EmotivaUnsubscribeDTO(List.of(command1, command2));
|
||||
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(dto);
|
||||
assertThat(xmlString, containsString("<emotivaUnsubscribe>"));
|
||||
assertThat(xmlString, containsString("<volume />"));
|
||||
assertThat(xmlString, containsString("<power_off />"));
|
||||
assertThat(xmlString, containsString("</emotivaUnsubscribe>"));
|
||||
assertThat(xmlString, not(containsString("<volume>")));
|
||||
assertThat(xmlString, not(containsString("<command>")));
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.EmotivaBindingConstants;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaUpdate requests.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaUpdateRequestTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaUpdateRequestTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithNoProperty() {
|
||||
EmotivaUpdateRequest dto = new EmotivaUpdateRequest(Collections.emptyList());
|
||||
String xmlAsString = xmlUtils.marshallJAXBElementObjects(dto);
|
||||
assertThat(xmlAsString, containsString("<emotivaUpdate/>"));
|
||||
assertThat(xmlAsString, not(containsString("<property")));
|
||||
assertThat(xmlAsString, not(containsString("</emotivaUpdate>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshalFromChannelUID() {
|
||||
EmotivaSubscriptionTags subscriptionChannel = EmotivaSubscriptionTags
|
||||
.fromChannelUID(EmotivaBindingConstants.CHANNEL_TUNER_RDS);
|
||||
EmotivaUpdateRequest emotivaUpdateRequest = new EmotivaUpdateRequest(subscriptionChannel);
|
||||
String xmlString = xmlUtils.marshallJAXBElementObjects(emotivaUpdateRequest);
|
||||
assertThat(xmlString, containsString("<emotivaUpdate>"));
|
||||
assertThat(xmlString, containsString("<tuner_RDS />"));
|
||||
assertThat(xmlString, containsString("</emotivaUpdate>"));
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.dto;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus.NOT_VALID;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaPropertyStatus.VALID;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaUpdate responses.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaUpdateResponseTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaUpdateResponseTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void marshallWithNoProperty() {
|
||||
EmotivaUpdateResponse dto = new EmotivaUpdateResponse(Collections.emptyList());
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString, containsString("<emotivaUpdate/>"));
|
||||
assertThat(xmlAsString, not(containsString("<property")));
|
||||
assertThat(xmlAsString, not(containsString("</emotivaUpdate>")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV2() throws JAXBException {
|
||||
var dto = (EmotivaUpdateResponse) xmlUtils.unmarshallToEmotivaDTO(emotivaUpdateResponseV2);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getProperties(), is(nullValue()));
|
||||
List<EmotivaNotifyDTO> notifications = xmlUtils.unmarshallToNotification(dto.getTags());
|
||||
assertThat(notifications.size(), is(3));
|
||||
|
||||
assertThat(notifications.get(0).getName(), is(EmotivaSubscriptionTags.power.name()));
|
||||
assertThat(notifications.get(0).getValue(), is("On"));
|
||||
assertThat(notifications.get(0).getVisible(), is("true"));
|
||||
assertThat(notifications.get(0).getStatus(), is(VALID.getValue()));
|
||||
|
||||
assertThat(notifications.get(1).getName(), is(EmotivaSubscriptionTags.source.name()));
|
||||
assertThat(notifications.get(1).getValue(), is("HDMI 1"));
|
||||
assertThat(notifications.get(1).getVisible(), is("true"));
|
||||
assertThat(notifications.get(1).getStatus(), is(NOT_VALID.getValue()));
|
||||
|
||||
assertThat(notifications.get(2).getName(), is(EmotivaSubscriptionTags.unknown.name()));
|
||||
assertThat(notifications.get(2).getStatus(), is(nullValue()));
|
||||
assertThat(notifications.get(2).getValue(), is(nullValue()));
|
||||
assertThat(notifications.get(2).getVisible(), is(nullValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unmarshallV3() throws JAXBException {
|
||||
var dto = (EmotivaUpdateResponse) xmlUtils.unmarshallToEmotivaDTO(emotivaUpdateResponseV3);
|
||||
assertThat(dto, is(notNullValue()));
|
||||
assertThat(dto.getTags(), is(nullValue()));
|
||||
assertThat(dto.getProperties().size(), is(3));
|
||||
|
||||
assertThat(dto.getProperties().get(0), instanceOf(EmotivaPropertyDTO.class));
|
||||
assertThat(dto.getProperties().get(0).getName(), is(EmotivaSubscriptionTags.power.name()));
|
||||
assertThat(dto.getProperties().get(0).getValue(), is("On"));
|
||||
assertThat(dto.getProperties().get(0).getVisible(), is("true"));
|
||||
assertThat(dto.getProperties().get(0).getStatus(), is(VALID.getValue()));
|
||||
|
||||
assertThat(dto.getProperties().get(1).getName(), is(EmotivaSubscriptionTags.source.name()));
|
||||
assertThat(dto.getProperties().get(1).getValue(), is("HDMI 1"));
|
||||
assertThat(dto.getProperties().get(1).getVisible(), is("true"));
|
||||
assertThat(dto.getProperties().get(1).getStatus(), is(NOT_VALID.getValue()));
|
||||
|
||||
assertThat(dto.getProperties().get(2).getName(), is("noKnownTag"));
|
||||
assertThat(dto.getProperties().get(2).getStatus(), is(notNullValue()));
|
||||
assertThat(dto.getProperties().get(2).getValue(), is(notNullValue()));
|
||||
assertThat(dto.getProperties().get(2).getVisible(), is(notNullValue()));
|
||||
}
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.openhab.binding.emotiva.internal.EmotivaBindingConstants.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaControlCommands.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaProtocolVersion.*;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_band;
|
||||
import static org.openhab.binding.emotiva.internal.protocol.EmotivaSubscriptionTags.tuner_channel;
|
||||
import static org.openhab.core.types.RefreshType.REFRESH;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.openhab.binding.emotiva.internal.EmotivaBindingConstants;
|
||||
import org.openhab.binding.emotiva.internal.EmotivaCommandHelper;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaControlDTO;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.types.UpDownType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
* Unit tests for EmotivaControl requests.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaControlRequestTest {
|
||||
|
||||
private static Stream<Arguments> channelToDTOs() {
|
||||
return Stream.of(Arguments.of(CHANNEL_STANDBY, OnOffType.ON, standby, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_STANDBY, OnOffType.OFF, standby, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MAIN_ZONE_POWER, OnOffType.ON, power_on, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MAIN_ZONE_POWER, OnOffType.OFF, power_off, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SOURCE, new StringType("HDMI1"), hdmi1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SOURCE, new StringType("SHIELD"), source_2, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SOURCE, new StringType("hdmi1"), hdmi1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SOURCE, new StringType("coax1"), coax1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SOURCE, new StringType("NOT_REAL"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU, new StringType("0"), menu, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("0"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("MENU"), menu, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("ENTER"), enter, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("UP"), up, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("DOWN"), down, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("LEFT"), left, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_CONTROL, new StringType("RIGHT"), right, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_UP, new StringType("0"), up, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_DOWN, new StringType("0"), down, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_LEFT, new StringType("0"), left, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_RIGHT, new StringType("0"), right, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MENU_ENTER, new StringType("0"), enter, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MUTE, OnOffType.ON, mute_on, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MUTE, OnOffType.OFF, mute_off, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_DIM, OnOffType.ON, dim, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_DIM, OnOffType.OFF, dim, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new StringType("mode_ref_stereo"), reference_stereo, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new StringType("surround_mode"), surround_mode, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new StringType("mode_surround"), surround_mode, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new StringType("surround"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new StringType("1"), mode_up, PROTOCOL_V2, "1"),
|
||||
Arguments.of(CHANNEL_MODE, new DecimalType(-1), mode_down, PROTOCOL_V2, "-1"),
|
||||
Arguments.of(CHANNEL_MODE, OnOffType.ON, none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE, new DecimalType(1), mode_up, PROTOCOL_V2, "1"),
|
||||
Arguments.of(CHANNEL_MODE, new DecimalType(-10), mode_down, PROTOCOL_V2, "-1"),
|
||||
Arguments.of(CHANNEL_CENTER, new QuantityType<>(10, Units.DECIBEL), center_trim_set, PROTOCOL_V2,
|
||||
"20.0"),
|
||||
Arguments.of(CHANNEL_CENTER, new QuantityType<>(10, Units.DECIBEL), center_trim_set, PROTOCOL_V3,
|
||||
"20.0"),
|
||||
Arguments.of(CHANNEL_CENTER, new DecimalType(-30), center_trim_set, PROTOCOL_V2, "-24.0"),
|
||||
Arguments.of(CHANNEL_CENTER, new DecimalType(-30), center_trim_set, PROTOCOL_V3, "-24.0"),
|
||||
Arguments.of(CHANNEL_SUBWOOFER, new DecimalType(1), subwoofer_trim_set, PROTOCOL_V2, "2.0"),
|
||||
Arguments.of(CHANNEL_SUBWOOFER, new DecimalType(1), subwoofer_trim_set, PROTOCOL_V3, "2.0"),
|
||||
Arguments.of(CHANNEL_SUBWOOFER, new DecimalType(-25), subwoofer_trim_set, PROTOCOL_V2, "-24.0"),
|
||||
Arguments.of(CHANNEL_SUBWOOFER, new DecimalType(-25), subwoofer_trim_set, PROTOCOL_V3, "-24.0"),
|
||||
Arguments.of(CHANNEL_SURROUND, new DecimalType(30), surround_trim_set, PROTOCOL_V2, "24.0"),
|
||||
Arguments.of(CHANNEL_SURROUND, new DecimalType(30), surround_trim_set, PROTOCOL_V3, "24.0"),
|
||||
Arguments.of(CHANNEL_SURROUND, new DecimalType(-3.5), surround_trim_set, PROTOCOL_V2, "-7.0"),
|
||||
Arguments.of(CHANNEL_SURROUND, new DecimalType(-3), surround_trim_set, PROTOCOL_V3, "-6.0"),
|
||||
Arguments.of(CHANNEL_BACK, new DecimalType(-3), back_trim_set, PROTOCOL_V2, "-6.0"),
|
||||
Arguments.of(CHANNEL_BACK, new DecimalType(-3), back_trim_set, PROTOCOL_V3, "-6.0"),
|
||||
Arguments.of(CHANNEL_BACK, new DecimalType(30), back_trim_set, PROTOCOL_V2, "24.0"),
|
||||
Arguments.of(CHANNEL_BACK, new DecimalType(30), back_trim_set, PROTOCOL_V3, "24.0"),
|
||||
Arguments.of(CHANNEL_MODE_SURROUND, new StringType("0"), surround_mode, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SPEAKER_PRESET, OnOffType.ON, speaker_preset, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SPEAKER_PRESET, OnOffType.OFF, speaker_preset, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SPEAKER_PRESET, new StringType("preset2"), preset2, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SPEAKER_PRESET, new StringType("1"), speaker_preset, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SPEAKER_PRESET, new StringType("speaker_preset"), speaker_preset, PROTOCOL_V2,
|
||||
"0"),
|
||||
Arguments.of(CHANNEL_MAIN_VOLUME, new DecimalType(30), set_volume, PROTOCOL_V2, "15.0"),
|
||||
Arguments.of(CHANNEL_MAIN_VOLUME, new PercentType("50"), set_volume, PROTOCOL_V2, "-41"),
|
||||
Arguments.of(CHANNEL_MAIN_VOLUME_DB, new QuantityType<>(-96, Units.DECIBEL), set_volume, PROTOCOL_V2,
|
||||
"-96.0"),
|
||||
Arguments.of(CHANNEL_MAIN_VOLUME_DB, new QuantityType<>(-100, Units.DECIBEL), set_volume, PROTOCOL_V2,
|
||||
"-96.0"),
|
||||
Arguments.of(CHANNEL_LOUDNESS, OnOffType.ON, loudness_on, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_LOUDNESS, OnOffType.OFF, loudness_off, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_POWER, OnOffType.ON, zone2_power_on, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_POWER, OnOffType.OFF, zone2_power_off, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_VOLUME, new DecimalType(30), zone2_set_volume, PROTOCOL_V2, "15.0"),
|
||||
Arguments.of(CHANNEL_ZONE2_VOLUME, new PercentType("50"), zone2_set_volume, PROTOCOL_V2, "-41"),
|
||||
Arguments.of(CHANNEL_ZONE2_VOLUME_DB, new QuantityType<>(-96, Units.DECIBEL), zone2_set_volume,
|
||||
PROTOCOL_V2, "-96.0"),
|
||||
Arguments.of(CHANNEL_ZONE2_VOLUME_DB, new QuantityType<>(-100, Units.DECIBEL), zone2_set_volume,
|
||||
PROTOCOL_V2, "-96.0"),
|
||||
Arguments.of(CHANNEL_ZONE2_MUTE, OnOffType.ON, zone2_mute_on, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_MUTE, OnOffType.OFF, zone2_mute_off, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("HDMI1"), hdmi1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("SHIELD"), source_2, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("hdmi1"), hdmi1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("coax1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("zone2_coax1"), zone2_coax1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("zone2_ARC"), zone2_ARC, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("NOT_REAL"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_ZONE2_SOURCE, new StringType("zone2_follow_main"), zone2_follow_main, PROTOCOL_V2,
|
||||
"0"),
|
||||
Arguments.of(CHANNEL_FREQUENCY, UpDownType.UP, frequency, PROTOCOL_V2, "1"),
|
||||
Arguments.of(CHANNEL_FREQUENCY, UpDownType.DOWN, frequency, PROTOCOL_V2, "-1"),
|
||||
Arguments.of(CHANNEL_SEEK, UpDownType.UP, seek, PROTOCOL_V2, "1"),
|
||||
Arguments.of(CHANNEL_SEEK, UpDownType.DOWN, seek, PROTOCOL_V2, "-1"),
|
||||
Arguments.of(CHANNEL_CHANNEL, UpDownType.UP, channel, PROTOCOL_V2, "1"),
|
||||
Arguments.of(CHANNEL_CHANNEL, UpDownType.DOWN, channel, PROTOCOL_V2, "-1"),
|
||||
Arguments.of(CHANNEL_TUNER_BAND, new StringType("band_am"), band_am, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_BAND, new StringType("band_fm"), band_fm, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL, new StringType("FM 107.90MHz"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL, QuantityType.valueOf(103000000, Units.HERTZ), none, PROTOCOL_V2,
|
||||
"0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL, new StringType("channel_1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL_SELECT, new StringType("channel_1"), channel_1, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL_SELECT, new StringType("CHANNEL_2"), channel_2, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL_SELECT, new StringType("FM 107.90MHz"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_CHANNEL_SELECT, QuantityType.valueOf(103000000, Units.HERTZ), none,
|
||||
PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_SIGNAL, new StringType("Mono 0dBuV"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_PROGRAM, new StringType("Black Metal"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TUNER_RDS, new StringType("The Zombie Apocalypse is upon us!"), none, PROTOCOL_V2,
|
||||
"0"),
|
||||
Arguments.of(CHANNEL_AUDIO_INPUT, new StringType("HDMI 1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_AUDIO_BITSTREAM, new StringType("HDMI 1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_AUDIO_BITS, new StringType("PCM 5.1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_VIDEO_INPUT, new StringType("HDMI 1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_VIDEO_FORMAT, new StringType("1080P/60"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_VIDEO_SPACE, new StringType("RGB 8bits"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT1, new StringType("HDMI1"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT2, new StringType("HDMI2"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT3, new StringType("HDMI3"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT4, new StringType("HDMI4"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT5, new StringType("HDMI5"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT6, new StringType("HDMI6"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT7, new StringType("HDMI7"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_INPUT8, new StringType("HDMI8"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_REF_STEREO, new StringType("0"), reference_stereo, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_REF_STEREO, new StringType("0"), reference_stereo, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_REF_STEREO, REFRESH, none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_REF_STEREO, REFRESH, none, PROTOCOL_V3, "0"),
|
||||
Arguments.of(CHANNEL_MODE_STEREO, new StringType("0"), stereo, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_MUSIC, new StringType("0"), music, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_MOVIE, new StringType("0"), movie, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_DIRECT, new StringType("0"), direct, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_DOLBY, new StringType("0"), dolby, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_DTS, new StringType("0"), dts, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_ALL_STEREO, new StringType("0"), all_stereo, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_MODE_AUTO, new StringType("0"), auto, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SELECTED_MODE, new StringType("Auto"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_SELECTED_MOVIE_MUSIC, new StringType("Surround"), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TREBLE, new DecimalType(0.5), treble_up, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TREBLE, new DecimalType(-1), treble_up, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_TREBLE, new DecimalType(0.5), treble_up, PROTOCOL_V3, "0"),
|
||||
Arguments.of(CHANNEL_TREBLE, new DecimalType(-4), treble_down, PROTOCOL_V3, "0"),
|
||||
Arguments.of(CHANNEL_BASS, new QuantityType<>(0, Units.DECIBEL), none, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_BASS, new QuantityType<>(-1, Units.DECIBEL), bass_down, PROTOCOL_V2, "0"),
|
||||
Arguments.of(CHANNEL_BASS, new QuantityType<>(0, Units.DECIBEL), none, PROTOCOL_V3, "0"),
|
||||
Arguments.of(CHANNEL_BASS, new QuantityType<>(-1, Units.DECIBEL), bass_down, PROTOCOL_V3, "0"),
|
||||
Arguments.of(CHANNEL_WIDTH, new DecimalType(30), width_trim_set, PROTOCOL_V2, "24.0"),
|
||||
Arguments.of(CHANNEL_WIDTH, new DecimalType(30), width_trim_set, PROTOCOL_V3, "24.0"),
|
||||
Arguments.of(CHANNEL_WIDTH, new QuantityType<>(-1, Units.DECIBEL), width_trim_set, PROTOCOL_V2, "-2.0"),
|
||||
Arguments.of(CHANNEL_WIDTH, new QuantityType<>(-1, Units.DECIBEL), width_trim_set, PROTOCOL_V3, "-2.0"),
|
||||
Arguments.of(CHANNEL_HEIGHT, new DecimalType(0.499999), height_trim_set, PROTOCOL_V2, "1.0"),
|
||||
Arguments.of(CHANNEL_HEIGHT, new DecimalType(-1.00000000001), height_trim_set, PROTOCOL_V3, "-2.0"),
|
||||
Arguments.of(CHANNEL_HEIGHT, new QuantityType<>(-1, Units.DECIBEL), height_trim_set, PROTOCOL_V2,
|
||||
"-2.0"),
|
||||
Arguments.of(CHANNEL_HEIGHT, new QuantityType<>(-1, Units.DECIBEL), height_trim_set, PROTOCOL_V3,
|
||||
"-2.0"));
|
||||
}
|
||||
|
||||
private static final EnumMap<EmotivaControlCommands, String> MAP_SOURCES_MAIN_ZONE = new EnumMap<>(
|
||||
EmotivaControlCommands.class);
|
||||
private static final EnumMap<EmotivaControlCommands, String> MAP_SOURCES_ZONE_2 = new EnumMap<>(
|
||||
EmotivaControlCommands.class);
|
||||
private static final EnumMap<EmotivaControlCommands, String> CHANNEL_MAP = new EnumMap<>(
|
||||
EmotivaControlCommands.class);
|
||||
private static final EnumMap<EmotivaControlCommands, String> RADIO_BAND_MAP = new EnumMap<>(
|
||||
EmotivaControlCommands.class);
|
||||
private static final Map<String, State> STATE_MAP = Collections.synchronizedMap(new HashMap<>());
|
||||
private static final Map<String, Map<EmotivaControlCommands, String>> COMMAND_MAPS = new ConcurrentHashMap<>();
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
MAP_SOURCES_MAIN_ZONE.put(source_1, "HDMI 1");
|
||||
MAP_SOURCES_MAIN_ZONE.put(source_2, "SHIELD");
|
||||
MAP_SOURCES_MAIN_ZONE.put(hdmi1, "HDMI1");
|
||||
MAP_SOURCES_MAIN_ZONE.put(coax1, "Coax 1");
|
||||
COMMAND_MAPS.put(EmotivaBindingConstants.MAP_SOURCES_MAIN_ZONE, MAP_SOURCES_MAIN_ZONE);
|
||||
|
||||
MAP_SOURCES_ZONE_2.put(source_1, "HDMI 1");
|
||||
MAP_SOURCES_ZONE_2.put(source_2, "SHIELD");
|
||||
MAP_SOURCES_ZONE_2.put(hdmi1, "HDMI1");
|
||||
MAP_SOURCES_ZONE_2.put(zone2_coax1, "Coax 1");
|
||||
MAP_SOURCES_ZONE_2.put(zone2_ARC, "Audio Return Channel");
|
||||
MAP_SOURCES_ZONE_2.put(zone2_follow_main, "Follow Main");
|
||||
COMMAND_MAPS.put(EmotivaBindingConstants.MAP_SOURCES_ZONE_2, MAP_SOURCES_ZONE_2);
|
||||
|
||||
CHANNEL_MAP.put(channel_1, "Channel 1");
|
||||
CHANNEL_MAP.put(channel_2, "Channel 2");
|
||||
CHANNEL_MAP.put(channel_3, "My Radio Channel");
|
||||
COMMAND_MAPS.put(tuner_channel.getEmotivaName(), CHANNEL_MAP);
|
||||
|
||||
RADIO_BAND_MAP.put(band_am, "AM");
|
||||
RADIO_BAND_MAP.put(band_fm, "FM");
|
||||
COMMAND_MAPS.put(tuner_band.getEmotivaName(), RADIO_BAND_MAP);
|
||||
|
||||
STATE_MAP.put(CHANNEL_TREBLE, new DecimalType(-3));
|
||||
STATE_MAP.put(CHANNEL_TUNER_CHANNEL, new StringType("FM 87.50MHz"));
|
||||
STATE_MAP.put(CHANNEL_FREQUENCY, QuantityType.valueOf(107.90, Units.HERTZ));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("channelToDTOs")
|
||||
void createDTO(String channel, Command ohValue, EmotivaControlCommands controlCommand,
|
||||
EmotivaProtocolVersion protocolVersion, String requestValue) {
|
||||
EmotivaControlRequest controlRequest = EmotivaCommandHelper.channelToControlRequest(channel, COMMAND_MAPS,
|
||||
protocolVersion);
|
||||
|
||||
EmotivaControlDTO dto = controlRequest.createDTO(ohValue, STATE_MAP.get(channel));
|
||||
assertThat(dto.getCommands().size(), is(1));
|
||||
assertThat(dto.getCommands().get(0).getName(), is(controlCommand.name()));
|
||||
assertThat(dto.getCommands().get(0).getValue(), is(requestValue));
|
||||
assertThat(dto.getCommands().get(0).getVisible(), is(nullValue()));
|
||||
assertThat(dto.getCommands().get(0).getStatus(), is(nullValue()));
|
||||
assertThat(dto.getCommands().get(0).getAck(), is(DEFAULT_CONTROL_ACK_VALUE));
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 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.emotiva.internal.protocol;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.UnmarshalException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.emotiva.internal.AbstractDTOTestBase;
|
||||
import org.openhab.binding.emotiva.internal.dto.EmotivaNotifyWrapper;
|
||||
|
||||
/**
|
||||
* Unit tests for Emotiva message marshalling and unmarshalling.
|
||||
*
|
||||
* @author Espen Fossen - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class EmotivaXmlUtilsTest extends AbstractDTOTestBase {
|
||||
|
||||
public EmotivaXmlUtilsTest() throws JAXBException {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallEmptyString() {
|
||||
assertThrows(JAXBException.class, () -> xmlUtils.unmarshallToEmotivaDTO(""), "xml value is null or empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallNotValidXML() {
|
||||
assertThrows(UnmarshalException.class, () -> xmlUtils.unmarshallToEmotivaDTO("notXmlAtAll"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallInstanceObject() throws JAXBException {
|
||||
Object object = xmlUtils.unmarshallToEmotivaDTO(emotivaNotifyV2KeepAlive);
|
||||
|
||||
assertThat(object, instanceOf(EmotivaNotifyWrapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnmarshallXml() throws JAXBException {
|
||||
Object object = xmlUtils.unmarshallToEmotivaDTO(emotivaNotifyV2KeepAlive);
|
||||
|
||||
assertThat(object, instanceOf(EmotivaNotifyWrapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMarshallObjectWithoutXmlElements() {
|
||||
String commands = xmlUtils.marshallEmotivaDTO("");
|
||||
assertThat(commands, is(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMarshallNoValueDTO() {
|
||||
EmotivaNotifyWrapper dto = new EmotivaNotifyWrapper();
|
||||
String xmlAsString = xmlUtils.marshallEmotivaDTO(dto);
|
||||
assertThat(xmlAsString, not(containsString("<emotivaNotify>")));
|
||||
assertThat(xmlAsString, containsString("<emotivaNotify/>"));
|
||||
}
|
||||
}
|
@ -129,6 +129,7 @@
|
||||
<module>org.openhab.binding.electroluxair</module>
|
||||
<module>org.openhab.binding.elerotransmitterstick</module>
|
||||
<module>org.openhab.binding.elroconnects</module>
|
||||
<module>org.openhab.binding.emotiva</module>
|
||||
<module>org.openhab.binding.energenie</module>
|
||||
<module>org.openhab.binding.energidataservice</module>
|
||||
<module>org.openhab.binding.enigma2</module>
|
||||
|
Loading…
Reference in New Issue
Block a user