mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[nuvo] Add zone actions for rules (#13658)
* Add zone actions for rules * Don't scan for NuvoNet source messages if openHAB NuvoNet sources are not being used Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
parent
a1270c7822
commit
160e0c2548
@ -85,35 +85,36 @@ connection: &conNuvo
|
|||||||
|
|
||||||
The following channels are available:
|
The following channels are available:
|
||||||
|
|
||||||
| Channel ID | Item Type | Description |
|
| Channel ID | Item Type | Description |
|
||||||
|--------------------------------------|-------------|-----------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| system#alloff | Switch | Turn all zones off simultaneously |
|
| system#alloff | Switch | Turn all zones off simultaneously |
|
||||||
| system#allmute | Switch | Mute or unmute all zones simultaneously |
|
| system#allmute | Switch | Mute or unmute all zones simultaneously |
|
||||||
| system#page | Switch | Turn on or off the Page All Zones feature (while on the amplifier switches to source 6) |
|
| system#page | Switch | Turn on or off the Page All Zones feature (while on the amplifier switches to source 6) |
|
||||||
| system#sendcmd | String | Send a command to the amplifier |
|
| system#sendcmd | String | Send a command to the amplifier |
|
||||||
| zoneN#power (where N= 1-20) | Switch | Turn the power for a zone on or off |
|
| system#buttonpress | String | Indicates the zone number followed by a comma and the last button pressed or NuvoNet menu item selected on a keypad (ReadOnly) |
|
||||||
| zoneN#source (where N= 1-20) | Number | Select the source input for a zone (1-6) |
|
| zoneN#power (where N= 1-20) | Switch | Turn the power for a zone on or off |
|
||||||
| zoneN#volume (where N= 1-20) | Dimmer | Control the volume for a zone (0-100%) [translates to 0-79] |
|
| zoneN#source (where N= 1-20) | Number | Select the source input for a zone (1-6) |
|
||||||
| zoneN#mute (where N= 1-20) | Switch | Mute or unmute a zone |
|
| zoneN#volume (where N= 1-20) | Dimmer | Control the volume for a zone (0-100%) [translates to 0-79] |
|
||||||
| zoneN#favorite (where N= 1-20) | Number | Select a preset Favorite for a zone (1-12) |
|
| zoneN#mute (where N= 1-20) | Switch | Mute or unmute a zone |
|
||||||
| zoneN#control (where N= 1-20) | Player | Simulate pressing the transport control buttons on the keypad e.g. play/pause/next/previous |
|
| zoneN#favorite (where N= 1-20) | Number | Select a preset Favorite for a zone (1-12) |
|
||||||
| zoneN#treble (where N= 1-20) | Number | Adjust the treble control for a zone (-18 to 18 [in increments of 2]) -18=none, 0=flat, 18=full |
|
| zoneN#control (where N= 1-20) | Player | Simulate pressing the transport control buttons on the keypad e.g. play/pause/next/previous |
|
||||||
| zoneN#bass (where N= 1-20) | Number | Adjust the bass control for a zone (-18 to 18 [in increments of 2]) -18=none, 0=flat, 18=full |
|
| zoneN#treble (where N= 1-20) | Number | Adjust the treble control for a zone (-18 to 18 [in increments of 2]) -18=none, 0=flat, 18=full |
|
||||||
| zoneN#balance (where N= 1-20) | Number | Adjust the balance control for a zone (-18 to 18 [in increments of 2]) -18=left, 0=center, 18=right |
|
| zoneN#bass (where N= 1-20) | Number | Adjust the bass control for a zone (-18 to 18 [in increments of 2]) -18=none, 0=flat, 18=full |
|
||||||
| zoneN#loudness (where N= 1-20) | Switch | Turn on or off the loudness compensation setting for the zone |
|
| zoneN#balance (where N= 1-20) | Number | Adjust the balance control for a zone (-18 to 18 [in increments of 2]) -18=left, 0=center, 18=right |
|
||||||
| zoneN#dnd (where N= 1-20) | Switch | Turn on or off the Do Not Disturb for the zone (for when the amplifier's Page All Zones feature is activated) |
|
| zoneN#loudness (where N= 1-20) | Switch | Turn on or off the loudness compensation setting for the zone |
|
||||||
| zoneN#lock (where N= 1-20) | Contact | Indicates if this zone is currently locked |
|
| zoneN#dnd (where N= 1-20) | Switch | Turn on or off the Do Not Disturb for the zone (for when the amplifier's Page All Zones feature is activated) |
|
||||||
| zoneN#party (where N= 1-20) | Switch | Turn on or off the party mode feature with this zone as the host |
|
| zoneN#lock (where N= 1-20) | Contact | Indicates if this zone is currently locked |
|
||||||
| sourceN#display_line1 (where N= 1-6) | String | 1st line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
| zoneN#party (where N= 1-20) | Switch | Turn on or off the party mode feature with this zone as the host |
|
||||||
| sourceN#display_line2 (where N= 1-6) | String | 2nd line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
| sourceN#display_line1 (where N= 1-6) | String | 1st line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
||||||
| sourceN#display_line3 (where N= 1-6) | String | 3rd line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
| sourceN#display_line2 (where N= 1-6) | String | 2nd line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
||||||
| sourceN#display_line4 (where N= 1-6) | String | 4th line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
| sourceN#display_line3 (where N= 1-6) | String | 3rd line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
||||||
| sourceN#play_mode (where N= 1-6) | String | The current playback mode of the source, ie: Playing, Paused, etc. (ReadOnly) See rules example for updating |
|
| sourceN#display_line4 (where N= 1-6) | String | 4th line of text being displayed on the keypad. Can be updated for a non NuvoNet source |
|
||||||
| sourceN#track_length (where N= 1-6) | Number:Time | The total running time of the current playing track (ReadOnly) See rules example for updating |
|
| sourceN#play_mode (where N= 1-6) | String | The current playback mode of the source, ie: Playing, Paused, etc. (ReadOnly) See rules example for updating |
|
||||||
| sourceN#track_position (where N= 1-6)| Number:Time | The running time elapsed of the current playing track (ReadOnly) See rules example for updating |
|
| sourceN#track_length (where N= 1-6) | Number:Time | The total running time of the current playing track (ReadOnly) See rules example for updating |
|
||||||
| sourceN#button_press (where N= 1-6) | String | Indicates the last button pressed on the keypad for a non NuvoNet source or openHAB NuvoNet source (ReadOnly) |
|
| sourceN#track_position (where N= 1-6)| Number:Time | The running time elapsed of the current playing track (ReadOnly) See rules example for updating |
|
||||||
| sourceN#art_url (where N= 1-6) | String | MPS4 Only! The URL of the Album Art JPG for this source that is displayed on a CTP-36. See *very advanced* rules (SendOnly) |
|
| sourceN#button_press (where N= 1-6) | String | Indicates the last button pressed on the keypad for a non NuvoNet source or openHAB NuvoNet source (ReadOnly) |
|
||||||
| sourceN#album_art (where N= 1-6) | Image | The Album Art loaded from the art_url channel for display in a UI widget (ReadOnly) |
|
| sourceN#art_url (where N= 1-6) | String | MPS4 Only! The URL of the Album Art JPG for this source that is displayed on a CTP-36. See *very advanced* rules (SendOnly) |
|
||||||
|
| sourceN#album_art (where N= 1-6) | Image | The Album Art loaded from the art_url channel for display in a UI widget (ReadOnly) |
|
||||||
|
|
||||||
## Full Example
|
## Full Example
|
||||||
|
|
||||||
@ -139,6 +140,7 @@ Switch nuvo_system_alloff "All Zones Off" { channel="nuvo:amplifier:myamp:system
|
|||||||
Switch nuvo_system_allmute "All Zones Mute" { channel="nuvo:amplifier:myamp:system#allmute" }
|
Switch nuvo_system_allmute "All Zones Mute" { channel="nuvo:amplifier:myamp:system#allmute" }
|
||||||
Switch nuvo_system_page "Page All Zones" { channel="nuvo:amplifier:myamp:system#page" }
|
Switch nuvo_system_page "Page All Zones" { channel="nuvo:amplifier:myamp:system#page" }
|
||||||
String nuvo_system_sendcmd "Send Command" { channel="nuvo:amplifier:myamp:system#sendcmd" }
|
String nuvo_system_sendcmd "Send Command" { channel="nuvo:amplifier:myamp:system#sendcmd" }
|
||||||
|
String nuvo_system_buttonpress "Zone Button: [%s]" { channel="nuvo:amplifier:myamp:system#buttonpress" }
|
||||||
|
|
||||||
// zones
|
// zones
|
||||||
Switch nuvo_z1_power "Power" { channel="nuvo:amplifier:myamp:zone1#power" }
|
Switch nuvo_z1_power "Power" { channel="nuvo:amplifier:myamp:zone1#power" }
|
||||||
@ -473,12 +475,57 @@ A complete XML string for the desired menu is then stored in the `menuXmlSrcN` c
|
|||||||
<item>menu3 x</item>
|
<item>menu3 x</item>
|
||||||
<item>menu3 y</item>
|
<item>menu3 y</item>
|
||||||
</topmenu>
|
</topmenu>
|
||||||
|
<topmenu text="Turn off other zones"/>
|
||||||
```
|
```
|
||||||
|
|
||||||
When a menu item is selected, the text of the topmenu item and sub menu item (if applicable) will be sent to the button channel in a pipe delimited format.
|
When a menu item is selected, the text of the topmenu item and sub menu item (if applicable) will be sent to the button channel in a pipe delimited format.
|
||||||
For example, when item `menu1 b` is selected, the text `Top menu 1|menu1 b` will be sent to the button channel.
|
For example, when item `menu1 b` is selected, the text `Top menu 1|menu1 b` will be sent to the button channel.
|
||||||
When the item `Top menu 2` is selected the text sent to the button channel will simply be `Top menu 2` since this menu item does not have any sub menu items.
|
When the item `Top menu 2` is selected the text sent to the button channel will simply be `Top menu 2` since this menu item does not have any sub menu items.
|
||||||
|
|
||||||
|
### Rule to trigger an action based on which keypad zone where a button was pressed or menu item selected
|
||||||
|
|
||||||
|
By using the `system#buttonpress` channel it is possible to trigger an action based on which keypad zone was used to send the action.
|
||||||
|
This channel appends the zone number and a comma before the button action or menu item selection.
|
||||||
|
|
||||||
|
For example if the Play/Pause button is pressed on Zone 7, the channel will display: `7,PLAYPAUSE`
|
||||||
|
Also if a menu item from a custom menu was selected, ie: `Top menu 1` on Zone 5, the channel will display: `5,Top menu 1`
|
||||||
|
|
||||||
|
The functionality can be used to create very powerful rules as demontrated below. The following rule triggered from a menu item turns off all zones except for the zone that triggered the rule.
|
||||||
|
|
||||||
|
nuvo-turn-off-all-but-caller.rules:
|
||||||
|
|
||||||
|
```
|
||||||
|
rule "Turn off all zones except caller zone"
|
||||||
|
when
|
||||||
|
Item nuvo_system_buttonpress received update
|
||||||
|
then
|
||||||
|
var callerZone = newState.toString().split(",").get(0)
|
||||||
|
var button = newState.toString().split(",").get(1)
|
||||||
|
|
||||||
|
if (button == "Turn off other zones") {
|
||||||
|
if (callerZone != "1") {
|
||||||
|
nuvo_z1_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
if (callerZone != "2") {
|
||||||
|
nuvo_z2_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
if (callerZone != "3") {
|
||||||
|
nuvo_z3_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
if (callerZone != "4") {
|
||||||
|
nuvo_z4_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
if (callerZone != "5") {
|
||||||
|
nuvo_z5_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
if (callerZone != "6") {
|
||||||
|
nuvo_z6_power.sendCommand(OFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### MPS4 openHAB NuvoNet source custom integration rules *(very advanced)*
|
### MPS4 openHAB NuvoNet source custom integration rules *(very advanced)*
|
||||||
|
|
||||||
The following are a set of example rules necessary to integrate metadata and control of another openHAB connected source (ie: Chromecast) into an openHAB NuvoNet source.
|
The following are a set of example rules necessary to integrate metadata and control of another openHAB connected source (ie: Chromecast) into an openHAB NuvoNet source.
|
||||||
|
@ -35,6 +35,7 @@ public class NuvoBindingConstants {
|
|||||||
public static final String CHANNEL_TYPE_ALLMUTE = "allmute";
|
public static final String CHANNEL_TYPE_ALLMUTE = "allmute";
|
||||||
public static final String CHANNEL_TYPE_PAGE = "page";
|
public static final String CHANNEL_TYPE_PAGE = "page";
|
||||||
public static final String CHANNEL_TYPE_SENDCMD = "sendcmd";
|
public static final String CHANNEL_TYPE_SENDCMD = "sendcmd";
|
||||||
|
public static final String CHANNEL_TYPE_BUTTONPRESS = "buttonpress";
|
||||||
|
|
||||||
// zone
|
// zone
|
||||||
public static final String CHANNEL_TYPE_POWER = "power";
|
public static final String CHANNEL_TYPE_POWER = "power";
|
||||||
@ -71,14 +72,14 @@ public class NuvoBindingConstants {
|
|||||||
public static final String TYPE_PAGE = "page";
|
public static final String TYPE_PAGE = "page";
|
||||||
public static final String TYPE_SOURCE_UPDATE = "source_update";
|
public static final String TYPE_SOURCE_UPDATE = "source_update";
|
||||||
public static final String TYPE_ZONE_UPDATE = "zone_update";
|
public static final String TYPE_ZONE_UPDATE = "zone_update";
|
||||||
public static final String TYPE_ZONE_BUTTON = "zone_button";
|
|
||||||
public static final String TYPE_ZONE_BUTTON2 = "zone_button2";
|
|
||||||
public static final String TYPE_ZONE_MENUREQ = "zone_menureq";
|
|
||||||
public static final String TYPE_MENU_ITEM_SELECTED = "top_menu_button";
|
|
||||||
public static final String TYPE_ZONE_CONFIG = "zone_config";
|
public static final String TYPE_ZONE_CONFIG = "zone_config";
|
||||||
public static final String TYPE_ALBUM_ART_REQ = "album_art_req";
|
public static final String TYPE_ZONE_SOURCE_BUTTON = "zone_source_button";
|
||||||
public static final String TYPE_ALBUM_ART_FRAG_REQ = "album_art_frag_req";
|
public static final String TYPE_NN_BUTTON = "nn_button";
|
||||||
public static final String TYPE_FAVORITE_REQ = "favorite_req";
|
public static final String TYPE_NN_MENUREQ = "nn_menureq";
|
||||||
|
public static final String TYPE_NN_MENU_ITEM_SELECTED = "nn_menu_item_selected";
|
||||||
|
public static final String TYPE_NN_ALBUM_ART_REQ = "nn_album_art_req";
|
||||||
|
public static final String TYPE_NN_ALBUM_ART_FRAG_REQ = "nn_album_art_frag_req";
|
||||||
|
public static final String TYPE_NN_FAVORITE_REQ = "nn_favorite_req";
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
public static final String ON = "ON";
|
public static final String ON = "ON";
|
||||||
|
@ -54,21 +54,21 @@ public abstract class NuvoConnector {
|
|||||||
|
|
||||||
private static final Pattern SRC_PATTERN = Pattern.compile("^#S(\\d{1})(.*)$");
|
private static final Pattern SRC_PATTERN = Pattern.compile("^#S(\\d{1})(.*)$");
|
||||||
private static final Pattern ZONE_PATTERN = Pattern.compile("^#Z(\\d{1,2}),(.*)$");
|
private static final Pattern ZONE_PATTERN = Pattern.compile("^#Z(\\d{1,2}),(.*)$");
|
||||||
private static final Pattern ZONE_BUTTON_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})(.*)$");
|
private static final Pattern ZONE_SOURCE_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})(.*)$");
|
||||||
private static final Pattern ZONE_MENUREQ_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})MENUREQ(.*)$");
|
private static final Pattern NN_MENUREQ_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})MENUREQ(.*)$");
|
||||||
private static final Pattern ZONE_BUTTON2_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})BUTTON(.*)$");
|
private static final Pattern NN_BUTTON_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})BUTTON(.*)$");
|
||||||
private static final Pattern ZONE_BUTTONTWO_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})BUTTONTWO(.*)$");
|
private static final Pattern NN_BUTTONTWO_PATTERN = Pattern.compile("^#Z(\\d{1,2})S(\\d{1})BUTTONTWO(.*)$");
|
||||||
|
|
||||||
private static final Pattern ZONE_CFG_PATTERN = Pattern.compile("^#ZCFG(\\d{1,2}),(.*)$");
|
private static final Pattern ZONE_CFG_PATTERN = Pattern.compile("^#ZCFG(\\d{1,2}),(.*)$");
|
||||||
|
|
||||||
// S2ALBUMARTREQ0x620FD879,80,80,2,0x00C0C0C0,0,0,0,0,1
|
// S2ALBUMARTREQ0x620FD879,80,80,2,0x00C0C0C0,0,0,0,0,1
|
||||||
private static final Pattern ALBUM_ART_REQ = Pattern.compile("^#S(\\d{1})ALBUMARTREQ(.*)$");
|
private static final Pattern NN_ALBUM_ART_REQ = Pattern.compile("^#S(\\d{1})ALBUMARTREQ(.*)$");
|
||||||
|
|
||||||
// S2ALBUMARTFRAGREQ0x620FD879,0,750
|
// S2ALBUMARTFRAGREQ0x620FD879,0,750
|
||||||
private static final Pattern ALBUM_ART_FRAG_REQ = Pattern.compile("^#S(\\d{1})ALBUMARTFRAGREQ(.*)$");
|
private static final Pattern NN_ALBUM_ART_FRAG_REQ = Pattern.compile("^#S(\\d{1})ALBUMARTFRAGREQ(.*)$");
|
||||||
|
|
||||||
// S6FAVORITE0x000003E8
|
// S6FAVORITE0x000003E8
|
||||||
private static final Pattern FAVORITE_PATTERN = Pattern.compile("^#S(\\d{1})FAVORITE0x(.*)$");
|
private static final Pattern NN_FAVORITE_PATTERN = Pattern.compile("^#S(\\d{1})FAVORITE0x(.*)$");
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(NuvoConnector.class);
|
private final Logger logger = LoggerFactory.getLogger(NuvoConnector.class);
|
||||||
|
|
||||||
@ -88,6 +88,7 @@ public abstract class NuvoConnector {
|
|||||||
private List<NuvoMessageEventListener> listeners = new ArrayList<>();
|
private List<NuvoMessageEventListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
private boolean isEssentia = true;
|
private boolean isEssentia = true;
|
||||||
|
private boolean isAnyOhNuvoNet = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether the connection is established or not
|
* Get whether the connection is established or not
|
||||||
@ -116,6 +117,15 @@ public abstract class NuvoConnector {
|
|||||||
this.isEssentia = isEssentia;
|
this.isEssentia = isEssentia;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the connector to listen for NuvoNet source messages
|
||||||
|
*
|
||||||
|
* @param true if any sources are configured as openHAB NuvoNet sources
|
||||||
|
*/
|
||||||
|
public void setAnyOhNuvoNet(boolean isAnyOhNuvoNet) {
|
||||||
|
this.isAnyOhNuvoNet = isAnyOhNuvoNet;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the thread that handles the feedback messages
|
* Set the thread that handles the feedback messages
|
||||||
*
|
*
|
||||||
@ -307,7 +317,7 @@ public abstract class NuvoConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyze an incoming message and dispatch corresponding (type, key, value) to the event listeners
|
* Analyze an incoming message and dispatch corresponding (type, zone, src, value) to the event listeners
|
||||||
*
|
*
|
||||||
* @param incomingMessage the received message
|
* @param incomingMessage the received message
|
||||||
*/
|
*/
|
||||||
@ -327,119 +337,117 @@ public abstract class NuvoConnector {
|
|||||||
} catch (NuvoException e) {
|
} catch (NuvoException e) {
|
||||||
logger.debug("Error sending response to PING command");
|
logger.debug("Error sending response to PING command");
|
||||||
}
|
}
|
||||||
dispatchKeyValue(TYPE_PING, BLANK, BLANK);
|
dispatchKeyValue(TYPE_PING, BLANK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RESTART.equals(message)) {
|
if (RESTART.equals(message)) {
|
||||||
dispatchKeyValue(TYPE_RESTART, BLANK, BLANK);
|
dispatchKeyValue(TYPE_RESTART, BLANK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.contains(VER_STR_E6) || message.contains(VER_STR_GC)) {
|
if (message.contains(VER_STR_E6) || message.contains(VER_STR_GC)) {
|
||||||
// example: #VER"NV-E6G FWv2.66 HWv0"
|
// example: #VER"NV-E6G FWv2.66 HWv0"
|
||||||
// split on " and return the version number
|
// split on " and return the version number
|
||||||
dispatchKeyValue(TYPE_VERSION, "", message.split("\"")[1]);
|
dispatchKeyValue(TYPE_VERSION, message.split("\"")[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.equals(ALL_OFF)) {
|
if (message.equals(ALL_OFF)) {
|
||||||
dispatchKeyValue(TYPE_ALLOFF, BLANK, BLANK);
|
dispatchKeyValue(TYPE_ALLOFF, BLANK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.contains(MUTE)) {
|
if (message.contains(MUTE)) {
|
||||||
dispatchKeyValue(TYPE_ALLMUTE, BLANK, message.substring(message.length() - 1));
|
dispatchKeyValue(TYPE_ALLMUTE, message.substring(message.length() - 1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.contains(PAGE)) {
|
if (message.contains(PAGE)) {
|
||||||
dispatchKeyValue(TYPE_PAGE, BLANK, message.substring(message.length() - 1));
|
dispatchKeyValue(TYPE_PAGE, message.substring(message.length() - 1));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent an album art request
|
Matcher matcher;
|
||||||
Matcher matcher = ALBUM_ART_REQ.matcher(message);
|
|
||||||
if (matcher.find()) {
|
|
||||||
// pull out the source id and the remainder of the message
|
|
||||||
dispatchKeyValue(TYPE_ALBUM_ART_REQ, matcher.group(1), matcher.group(2));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Amp controller sent an album art fragment request
|
if (isAnyOhNuvoNet) {
|
||||||
matcher = ALBUM_ART_FRAG_REQ.matcher(message);
|
// Amp controller sent a NuvoNet album art request
|
||||||
if (matcher.find()) {
|
matcher = NN_ALBUM_ART_REQ.matcher(message);
|
||||||
// pull out the source id and the remainder of the message
|
if (matcher.find()) {
|
||||||
dispatchKeyValue(TYPE_ALBUM_ART_FRAG_REQ, matcher.group(1), matcher.group(2));
|
dispatchKeyValue(TYPE_NN_ALBUM_ART_REQ, BLANK, matcher.group(1), matcher.group(2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent a request to play a favorite
|
// Amp controller sent a NuvoNet album art fragment request
|
||||||
matcher = FAVORITE_PATTERN.matcher(message);
|
matcher = NN_ALBUM_ART_FRAG_REQ.matcher(message);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
// pull out the source id and the remainder of the message
|
dispatchKeyValue(TYPE_NN_ALBUM_ART_FRAG_REQ, BLANK, matcher.group(1), matcher.group(2));
|
||||||
dispatchKeyValue(TYPE_FAVORITE_REQ, matcher.group(1), matcher.group(2));
|
return;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
// Amp controller sent a request for a NuvoNet source to play a favorite
|
||||||
|
matcher = NN_FAVORITE_PATTERN.matcher(message);
|
||||||
|
if (matcher.find()) {
|
||||||
|
dispatchKeyValue(TYPE_NN_FAVORITE_REQ, BLANK, matcher.group(1), matcher.group(2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent a source update ie: #S2DISPINFO,DUR3380,POS3090,STATUS2
|
// Amp controller sent a source update ie: #S2DISPINFO,DUR3380,POS3090,STATUS2
|
||||||
// or #S2DISPLINE1,"1 of 17"
|
// or #S2DISPLINE1,"1 of 17"
|
||||||
matcher = SRC_PATTERN.matcher(message);
|
matcher = SRC_PATTERN.matcher(message);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
// pull out the source id and the remainder of the message
|
dispatchKeyValue(TYPE_SOURCE_UPDATE, BLANK, matcher.group(1), matcher.group(2));
|
||||||
dispatchKeyValue(TYPE_SOURCE_UPDATE, matcher.group(1), matcher.group(2));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent a zone update ie: #Z11,ON,SRC3,VOL63,DND0,LOCK0
|
// Amp controller sent a zone update ie: #Z11,ON,SRC3,VOL63,DND0,LOCK0
|
||||||
matcher = ZONE_PATTERN.matcher(message);
|
matcher = ZONE_PATTERN.matcher(message);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
// pull out the zone id and the remainder of the message
|
dispatchKeyValue(TYPE_ZONE_UPDATE, matcher.group(1), BLANK, matcher.group(2));
|
||||||
dispatchKeyValue(TYPE_ZONE_UPDATE, matcher.group(1), matcher.group(2));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent a zone BUTTONTWO press event ie: #Z11S3BUTTONTWO4,2,0,0,0
|
if (isAnyOhNuvoNet) {
|
||||||
matcher = ZONE_BUTTONTWO_PATTERN.matcher(message);
|
// Amp controller sent a NuvoNet zone/source BUTTONTWO press event ie: #Z11S3BUTTONTWO4,2,0,0,0
|
||||||
if (matcher.find()) {
|
matcher = NN_BUTTONTWO_PATTERN.matcher(message);
|
||||||
// redundant - ignore
|
if (matcher.find()) {
|
||||||
return;
|
// redundant - ignore
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
// Amp controller sent a zone BUTTON press event ie: #Z4S6BUTTON1,1,0xFFFFFFFF,1,3
|
|
||||||
matcher = ZONE_BUTTON2_PATTERN.matcher(message);
|
// Amp controller sent a NuvoNet zone/source BUTTON press event ie: #Z4S6BUTTON1,1,0xFFFFFFFF,1,3
|
||||||
if (matcher.find()) {
|
matcher = NN_BUTTON_PATTERN.matcher(message);
|
||||||
// pull out the remainder of the message: button #, action, menuid, itemid, itemidx
|
if (matcher.find()) {
|
||||||
String[] buttonSplit = matcher.group(3).split(COMMA);
|
// pull out the remainder of the message: button #, action, menuid, itemid, itemidx
|
||||||
|
String[] buttonSplit = matcher.group(3).split(COMMA);
|
||||||
// second field is button action, only send DOWNUP (0) or DOWN (1), ignore UP (2)
|
|
||||||
if (ZERO.equals(buttonSplit[1]) || ONE.equals(buttonSplit[1])) {
|
// second field is button action, only send DOWNUP (0) or DOWN (1), ignore UP (2)
|
||||||
// a button in a menu was pressed, send SxZy,menuid,itemidx
|
if (ZERO.equals(buttonSplit[1]) || ONE.equals(buttonSplit[1])) {
|
||||||
if (!ZERO.equals(buttonSplit[2])) {
|
// a button in a menu was pressed, send 'menuid,itemidx'
|
||||||
dispatchKeyValue(TYPE_MENU_ITEM_SELECTED, matcher.group(2), SRC_KEY + matcher.group(2) + ZONE_KEY
|
if (!ZERO.equals(buttonSplit[2])) {
|
||||||
+ matcher.group(1) + COMMA + buttonSplit[2] + COMMA + buttonSplit[3]);
|
dispatchKeyValue(TYPE_NN_MENU_ITEM_SELECTED, matcher.group(1), matcher.group(2),
|
||||||
} else {
|
buttonSplit[2] + COMMA + buttonSplit[3]);
|
||||||
// send the button # in the event, don't send extra fields menuid, itemid, etc..
|
} else {
|
||||||
dispatchKeyValue(TYPE_ZONE_BUTTON2, matcher.group(2), buttonSplit[0]);
|
// send the button # in the event, don't send extra fields menuid, itemid, etc..
|
||||||
}
|
dispatchKeyValue(TYPE_NN_BUTTON, matcher.group(1), matcher.group(2), buttonSplit[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Amp controller sent a NuvoNet zone/source menu request event ie: #Z2S6MENUREQ0x0000000B,1,0,0
|
||||||
|
matcher = NN_MENUREQ_PATTERN.matcher(message);
|
||||||
|
if (matcher.find()) {
|
||||||
|
dispatchKeyValue(TYPE_NN_MENUREQ, matcher.group(1), matcher.group(2), matcher.group(3));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amp controller sent a menu request event ie: #Z2S6MENUREQ0x0000000B,1,0,0
|
// Amp controller sent a zone/source button press event ie: #Z11S3PLAYPAUSE
|
||||||
matcher = ZONE_MENUREQ_PATTERN.matcher(message);
|
matcher = ZONE_SOURCE_PATTERN.matcher(message);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
// pull out the source id and send SxZy plus the remainder of the message
|
dispatchKeyValue(TYPE_ZONE_SOURCE_BUTTON, matcher.group(1), matcher.group(2), matcher.group(3));
|
||||||
dispatchKeyValue(TYPE_ZONE_MENUREQ, matcher.group(2),
|
|
||||||
SRC_KEY + matcher.group(2) + ZONE_KEY + matcher.group(1) + COMMA + matcher.group(3));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Amp controller sent a zone button press event ie: #Z11S3PLAYPAUSE
|
|
||||||
matcher = ZONE_BUTTON_PATTERN.matcher(message);
|
|
||||||
if (matcher.find()) {
|
|
||||||
// pull out the source id and the remainder of the message, ignore the zone id
|
|
||||||
dispatchKeyValue(TYPE_ZONE_BUTTON, matcher.group(2), matcher.group(3));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +455,7 @@ public abstract class NuvoConnector {
|
|||||||
matcher = ZONE_CFG_PATTERN.matcher(message);
|
matcher = ZONE_CFG_PATTERN.matcher(message);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
// pull out the zone id and the remainder of the message
|
// pull out the zone id and the remainder of the message
|
||||||
dispatchKeyValue(TYPE_ZONE_CONFIG, matcher.group(1), matcher.group(2));
|
dispatchKeyValue(TYPE_ZONE_CONFIG, matcher.group(1), BLANK, matcher.group(2));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,14 +463,25 @@ public abstract class NuvoConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch an event (type, key, value) to the event listeners
|
* Dispatch a system level event without zone or src to the event listeners
|
||||||
*
|
*
|
||||||
* @param type the type
|
* @param type the type
|
||||||
* @param key the key
|
|
||||||
* @param value the value
|
* @param value the value
|
||||||
*/
|
*/
|
||||||
private void dispatchKeyValue(String type, String key, String value) {
|
private void dispatchKeyValue(String type, String value) {
|
||||||
NuvoMessageEvent event = new NuvoMessageEvent(this, type, key, value);
|
dispatchKeyValue(type, BLANK, BLANK, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch an event (type, zone, src, value) to the event listeners
|
||||||
|
*
|
||||||
|
* @param type the type
|
||||||
|
* @param zone the zone id
|
||||||
|
* @param src the source id
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
private void dispatchKeyValue(String type, String zone, String src, String value) {
|
||||||
|
NuvoMessageEvent event = new NuvoMessageEvent(this, type, zone, src, value);
|
||||||
listeners.forEach(l -> l.onNewMessageEvent(event));
|
listeners.forEach(l -> l.onNewMessageEvent(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,15 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
public class NuvoMessageEvent extends EventObject {
|
public class NuvoMessageEvent extends EventObject {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final String type;
|
private final String type;
|
||||||
private final String key;
|
private final String zone;
|
||||||
|
private final String src;
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
public NuvoMessageEvent(Object source, String type, String key, String value) {
|
public NuvoMessageEvent(Object source, String type, String zone, String src, String value) {
|
||||||
super(source);
|
super(source);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.key = key;
|
this.zone = zone;
|
||||||
|
this.src = src;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +41,12 @@ public class NuvoMessageEvent extends EventObject {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getZone() {
|
||||||
return key;
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSrc() {
|
||||||
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
|
@ -215,7 +215,7 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
|
|
||||||
if (serialPort != null && !serialPort.isEmpty()) {
|
if (serialPort != null && !serialPort.isEmpty()) {
|
||||||
connector = new NuvoSerialConnector(serialPortManager, serialPort, uid);
|
connector = new NuvoSerialConnector(serialPortManager, serialPort, uid);
|
||||||
} else if (port != null) {
|
} else if (host != null && port != null) {
|
||||||
connector = new NuvoIpConnector(host, port, uid);
|
connector = new NuvoIpConnector(host, port, uid);
|
||||||
this.isMps4 = (port.intValue() == MPS4_PORT);
|
this.isMps4 = (port.intValue() == MPS4_PORT);
|
||||||
} else {
|
} else {
|
||||||
@ -239,6 +239,7 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
|
|
||||||
if (this.isAnyOhNuvoNet) {
|
if (this.isAnyOhNuvoNet) {
|
||||||
logger.debug("At least one source is configured as an openHAB NuvoNet source");
|
logger.debug("At least one source is configured as an openHAB NuvoNet source");
|
||||||
|
connector.setAnyOhNuvoNet(true);
|
||||||
loadMenuConfiguration(config);
|
loadMenuConfiguration(config);
|
||||||
|
|
||||||
favoriteMap.put("1",
|
favoriteMap.put("1",
|
||||||
@ -640,11 +641,12 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNewMessageEvent(NuvoMessageEvent evt) {
|
public void onNewMessageEvent(NuvoMessageEvent evt) {
|
||||||
logger.debug("onNewMessageEvent: key {} = {}", evt.getKey(), evt.getValue());
|
logger.debug("onNewMessageEvent: zone {}, source {}, value {}", evt.getZone(), evt.getSrc(), evt.getValue());
|
||||||
lastEventReceived = System.currentTimeMillis();
|
lastEventReceived = System.currentTimeMillis();
|
||||||
|
|
||||||
String type = evt.getType();
|
String type = evt.getType();
|
||||||
String key = evt.getKey();
|
String zoneId = evt.getZone();
|
||||||
|
String srcId = evt.getSrc();
|
||||||
String updateData = evt.getValue().trim();
|
String updateData = evt.getValue().trim();
|
||||||
if (this.getThing().getStatus() != ThingStatus.ONLINE) {
|
if (this.getThing().getStatus() != ThingStatus.ONLINE) {
|
||||||
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, this.versionString);
|
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, this.versionString);
|
||||||
@ -687,8 +689,8 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_PAGE, ONE.equals(updateData) ? ON : OFF);
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_PAGE, ONE.equals(updateData) ? ON : OFF);
|
||||||
break;
|
break;
|
||||||
case TYPE_SOURCE_UPDATE:
|
case TYPE_SOURCE_UPDATE:
|
||||||
logger.debug("Source update: Source: {} - Value: {}", key, updateData);
|
logger.debug("Source update: Source: {} - Value: {}", srcId, updateData);
|
||||||
NuvoEnum targetSource = NuvoEnum.valueOf(SOURCE + key);
|
NuvoEnum targetSource = NuvoEnum.valueOf(SOURCE + srcId);
|
||||||
|
|
||||||
if (updateData.contains(DISPLINE)) {
|
if (updateData.contains(DISPLINE)) {
|
||||||
// example: DISPLINE2,"Play My Song (Featuring Dee Ajayi)"
|
// example: DISPLINE2,"Play My Song (Featuring Dee Ajayi)"
|
||||||
@ -712,16 +714,16 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
} else if (updateData.contains(NAME_QUOTE)) {
|
} else if (updateData.contains(NAME_QUOTE)) {
|
||||||
// example: NAME"Ipod"
|
// example: NAME"Ipod"
|
||||||
String name = updateData.split("\"")[1];
|
String name = updateData.split("\"")[1];
|
||||||
sourceLabels.put(key, name);
|
sourceLabels.put(srcId, name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ZONE_UPDATE:
|
case TYPE_ZONE_UPDATE:
|
||||||
logger.debug("Zone update: Zone: {} - Value: {}", key, updateData);
|
logger.debug("Zone update: Zone: {} - Value: {}", zoneId, updateData);
|
||||||
// example : OFF
|
// example : OFF
|
||||||
// or: ON,SRC3,VOL63,DND0,LOCK0
|
// or: ON,SRC3,VOL63,DND0,LOCK0
|
||||||
// or: ON,SRC3,MUTE,DND0,LOCK0
|
// or: ON,SRC3,MUTE,DND0,LOCK0
|
||||||
|
|
||||||
NuvoEnum targetZone = NuvoEnum.valueOf(ZONE + key);
|
NuvoEnum targetZone = NuvoEnum.valueOf(ZONE + zoneId);
|
||||||
|
|
||||||
if (OFF.equals(updateData)) {
|
if (OFF.equals(updateData)) {
|
||||||
updateChannelState(targetZone, CHANNEL_TYPE_POWER, OFF);
|
updateChannelState(targetZone, CHANNEL_TYPE_POWER, OFF);
|
||||||
@ -746,35 +748,43 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ZONE_BUTTON:
|
case TYPE_ZONE_SOURCE_BUTTON:
|
||||||
logger.debug("Zone Button pressed: Source: {} - Button: {}", key, updateData);
|
logger.debug("Source Button pressed: Source: {} - Button: {}", srcId, updateData);
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS, updateData);
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS, updateData);
|
||||||
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_BUTTONPRESS, zoneId + COMMA + updateData);
|
||||||
break;
|
break;
|
||||||
case TYPE_ZONE_BUTTON2:
|
case TYPE_NN_BUTTON:
|
||||||
String buttonAction = NuvoStatusCodes.BUTTON_CODE.get(updateData);
|
String buttonAction = NuvoStatusCodes.BUTTON_CODE.get(updateData);
|
||||||
|
|
||||||
if (buttonAction != null) {
|
if (buttonAction != null) {
|
||||||
logger.debug("Zone NuvoNet Button pressed: Source: {} - Button: {}", key, buttonAction);
|
logger.debug("NuvoNet Source Button pressed: Source: {} - Button: {}", srcId, buttonAction);
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS, buttonAction);
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS, buttonAction);
|
||||||
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_BUTTONPRESS, zoneId + COMMA + buttonAction);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Zone NuvoNet Button pressed: Source: {} - Unknown button code: {}", key, updateData);
|
logger.debug("NuvoNet Source Button pressed: Source: {} - Unknown button code: {}", srcId,
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS, updateData);
|
updateData);
|
||||||
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS, updateData);
|
||||||
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_BUTTONPRESS, zoneId + COMMA + updateData);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_MENU_ITEM_SELECTED:
|
case TYPE_NN_MENU_ITEM_SELECTED:
|
||||||
// ignore this update unless openHAB is handling this source
|
// ignore this update unless openHAB is handling this source
|
||||||
if (nuvoNetSrcMap.get(key).equals(2)) {
|
if (nuvoNetSrcMap.get(srcId).equals(2)) {
|
||||||
|
String sourceZone = SRC_KEY + srcId + ZONE_KEY + zoneId;
|
||||||
String[] updateDataSplit = updateData.split(COMMA);
|
String[] updateDataSplit = updateData.split(COMMA);
|
||||||
String zoneSource = updateDataSplit[0];
|
String menuId = updateDataSplit[0];
|
||||||
String menuId = updateDataSplit[1];
|
int menuItemIdx = Integer.parseInt(updateDataSplit[1]) - 1;
|
||||||
int menuItemIdx = Integer.parseInt(updateDataSplit[2]) - 1;
|
|
||||||
|
|
||||||
boolean exitMenu = false;
|
boolean exitMenu = false;
|
||||||
if ("0xFFFFFFFF".equals(menuId)) {
|
if ("0xFFFFFFFF".equals(menuId)) {
|
||||||
TopMenu topMenuItem = nuvoMenus.getSource().get(Integer.parseInt(key) - 1).getTopMenu()
|
TopMenu topMenuItem = nuvoMenus.getSource().get(Integer.parseInt(srcId) - 1).getTopMenu()
|
||||||
.get(menuItemIdx);
|
.get(menuItemIdx);
|
||||||
logger.debug("Top Menu item selected: Source: {} - Menu Item: {}", key, topMenuItem.getText());
|
logger.debug("Top Menu item selected: Source: {} - Menu Item: {}", srcId,
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS, topMenuItem.getText());
|
topMenuItem.getText());
|
||||||
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS,
|
||||||
|
topMenuItem.getText());
|
||||||
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_BUTTONPRESS,
|
||||||
|
zoneId + COMMA + topMenuItem.getText());
|
||||||
|
|
||||||
List<String> subMenuItems = topMenuItem.getItems();
|
List<String> subMenuItems = topMenuItem.getItems();
|
||||||
|
|
||||||
@ -784,135 +794,139 @@ public class NuvoHandler extends BaseThingHandler implements NuvoMessageEventLis
|
|||||||
// send submenu (maximum of 20 items)
|
// send submenu (maximum of 20 items)
|
||||||
int subMenuSize = subMenuItems.size() < 20 ? subMenuItems.size() : 20;
|
int subMenuSize = subMenuItems.size() < 20 ? subMenuItems.size() : 20;
|
||||||
try {
|
try {
|
||||||
connector.sendCommand(zoneSource + "MENU" + (menuItemIdx + 11) + ",0,0," + subMenuSize
|
connector.sendCommand(sourceZone + "MENU" + (menuItemIdx + 11) + ",0,0," + subMenuSize
|
||||||
+ ",0,0," + subMenuSize + ",\"" + topMenuItem.getText() + "\"");
|
+ ",0,0," + subMenuSize + ",\"" + topMenuItem.getText() + "\"");
|
||||||
Thread.sleep(SLEEP_BETWEEN_CMD_MS);
|
Thread.sleep(SLEEP_BETWEEN_CMD_MS);
|
||||||
|
|
||||||
for (int i = 0; i < subMenuSize; i++) {
|
for (int i = 0; i < subMenuSize; i++) {
|
||||||
connector.sendCommand(
|
connector.sendCommand(
|
||||||
zoneSource + "MENUITEM" + (i + 1) + ",0,0,\"" + subMenuItems.get(i) + "\"");
|
sourceZone + "MENUITEM" + (i + 1) + ",0,0,\"" + subMenuItems.get(i) + "\"");
|
||||||
}
|
}
|
||||||
} catch (NuvoException | InterruptedException e) {
|
} catch (NuvoException | InterruptedException e) {
|
||||||
logger.debug("Error sending sub menu for {}", zoneSource);
|
logger.debug("Error sending sub menu to {}", sourceZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// a sub menu item was selected
|
// a sub menu item was selected
|
||||||
TopMenu topMenuItem = nuvoMenus.getSource().get(Integer.parseInt(key) - 1).getTopMenu()
|
TopMenu topMenuItem = nuvoMenus.getSource().get(Integer.parseInt(srcId) - 1).getTopMenu()
|
||||||
.get(Integer.decode(menuId) - 11);
|
.get(Integer.decode(menuId) - 11);
|
||||||
String subMenuItem = topMenuItem.getItems().get(menuItemIdx);
|
String subMenuItem = topMenuItem.getItems().get(menuItemIdx);
|
||||||
|
|
||||||
logger.debug("Sub Menu item selected: Source: {} - Menu Item: {}", key,
|
logger.debug("Sub Menu item selected: Source: {} - Menu Item: {}", srcId,
|
||||||
topMenuItem.getText() + "|" + subMenuItem);
|
topMenuItem.getText() + "|" + subMenuItem);
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS,
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS,
|
||||||
topMenuItem.getText() + "|" + subMenuItem);
|
topMenuItem.getText() + "|" + subMenuItem);
|
||||||
|
updateChannelState(NuvoEnum.SYSTEM, CHANNEL_TYPE_BUTTONPRESS,
|
||||||
|
zoneId + COMMA + topMenuItem.getText() + "|" + subMenuItem);
|
||||||
exitMenu = true;
|
exitMenu = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exitMenu) {
|
if (exitMenu) {
|
||||||
try {
|
try {
|
||||||
// tell the zone to exit the menu
|
// tell the zone to exit the menu
|
||||||
connector.sendCommand(zoneSource + "MENU0,0,0,0,0,0,0,\"\"");
|
connector.sendCommand(sourceZone + "MENU0,0,0,0,0,0,0,\"\"");
|
||||||
} catch (NuvoException e) {
|
} catch (NuvoException e) {
|
||||||
logger.debug("Error sending exit menu command for {}", zoneSource);
|
logger.debug("Error sending exit menu command to {}", sourceZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ZONE_MENUREQ:
|
case TYPE_NN_MENUREQ:
|
||||||
// ignore this update unless openHAB is handling this source
|
// ignore this update unless openHAB is handling this source
|
||||||
if (nuvoNetSrcMap.get(key).equals(2)) {
|
if (nuvoNetSrcMap.get(srcId).equals(2)) {
|
||||||
logger.debug("Menu Request: Source: {} - Value: {}", key, updateData);
|
logger.debug("Menu Request: Source: {} - Value: {}", srcId, updateData);
|
||||||
// For now we only support one level deep menus. If third field is '1', indicates go back to main
|
String sourceZone = SRC_KEY + srcId + ZONE_KEY + zoneId;
|
||||||
|
// For now we only support one level deep menus. If second field is '1', indicates go back to main
|
||||||
// menu.
|
// menu.
|
||||||
String[] menuDataSplit = updateData.split(",");
|
String[] menuDataSplit = updateData.split(COMMA);
|
||||||
if (menuDataSplit.length > 3 && ONE.equals(menuDataSplit[2])) {
|
if (menuDataSplit.length > 2 && ONE.equals(menuDataSplit[1])) {
|
||||||
try {
|
try {
|
||||||
connector.sendCommand(menuDataSplit[0] + "MENU0xFFFFFFFF,0,0,0,0,0,0,\"\"");
|
connector.sendCommand(sourceZone + "MENU0xFFFFFFFF,0,0,0,0,0,0,\"\"");
|
||||||
} catch (NuvoException e) {
|
} catch (NuvoException e) {
|
||||||
logger.debug("Error sending main menu command for {}", menuDataSplit[0]);
|
logger.debug("Error sending main menu command to {}", sourceZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ZONE_CONFIG:
|
case TYPE_ZONE_CONFIG:
|
||||||
logger.debug("Zone Configuration: Zone: {} - Value: {}", key, updateData);
|
logger.debug("Zone Configuration: Zone: {} - Value: {}", zoneId, updateData);
|
||||||
// example: BASS1,TREB-2,BALR2,LOUDCMP1
|
// example: BASS1,TREB-2,BALR2,LOUDCMP1
|
||||||
Matcher matcher = ZONE_CFG_PATTERN.matcher(updateData);
|
Matcher matcher = ZONE_CFG_PATTERN.matcher(updateData);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
updateChannelState(NuvoEnum.valueOf(ZONE + key), CHANNEL_TYPE_BASS, matcher.group(1));
|
updateChannelState(NuvoEnum.valueOf(ZONE + zoneId), CHANNEL_TYPE_BASS, matcher.group(1));
|
||||||
updateChannelState(NuvoEnum.valueOf(ZONE + key), CHANNEL_TYPE_TREBLE, matcher.group(2));
|
updateChannelState(NuvoEnum.valueOf(ZONE + zoneId), CHANNEL_TYPE_TREBLE, matcher.group(2));
|
||||||
updateChannelState(NuvoEnum.valueOf(ZONE + key), CHANNEL_TYPE_BALANCE,
|
updateChannelState(NuvoEnum.valueOf(ZONE + zoneId), CHANNEL_TYPE_BALANCE,
|
||||||
NuvoStatusCodes.getBalanceFromStr(matcher.group(3)));
|
NuvoStatusCodes.getBalanceFromStr(matcher.group(3)));
|
||||||
updateChannelState(NuvoEnum.valueOf(ZONE + key), CHANNEL_TYPE_LOUDNESS,
|
updateChannelState(NuvoEnum.valueOf(ZONE + zoneId), CHANNEL_TYPE_LOUDNESS,
|
||||||
ONE.equals(matcher.group(4)) ? ON : OFF);
|
ONE.equals(matcher.group(4)) ? ON : OFF);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("no match on message: {}", updateData);
|
logger.debug("no match on message: {}", updateData);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ALBUM_ART_REQ:
|
case TYPE_NN_ALBUM_ART_REQ:
|
||||||
// ignore this update unless openHAB is handling this source
|
// ignore this update unless openHAB is handling this source
|
||||||
if (nuvoNetSrcMap.get(key).equals(2)) {
|
if (nuvoNetSrcMap.get(srcId).equals(2)) {
|
||||||
logger.debug("Album Art Request for Source: {} - Data: {}", key, updateData);
|
logger.debug("Album Art Request for Source: {} - Data: {}", srcId, updateData);
|
||||||
// 0x620FD879,80,80,2,0x00C0C0C0,0,0,0,0,1
|
// 0x620FD879,80,80,2,0x00C0C0C0,0,0,0,0,1
|
||||||
String[] albumArtReq = updateData.split(COMMA);
|
String[] albumArtReq = updateData.split(COMMA);
|
||||||
albumArtIds.put(SRC_KEY + key, Integer.decode(albumArtReq[0]));
|
albumArtIds.put(SRC_KEY + srcId, Integer.decode(albumArtReq[0]));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (albumArtMap.get(SRC_KEY + key).length > 1) {
|
if (albumArtMap.get(SRC_KEY + srcId).length > 1) {
|
||||||
connector.sendCommand(SRC_KEY + key + ALBUM_ART_AVAILABLE + albumArtIds.get(SRC_KEY + key)
|
connector.sendCommand(
|
||||||
+ COMMA + albumArtMap.get(SRC_KEY + key).length);
|
SRC_KEY + srcId + ALBUM_ART_AVAILABLE + albumArtIds.get(SRC_KEY + srcId) + COMMA
|
||||||
|
+ albumArtMap.get(SRC_KEY + srcId).length);
|
||||||
} else {
|
} else {
|
||||||
connector.sendCommand(SRC_KEY + key + ALBUM_ART_AVAILABLE + ZERO_COMMA);
|
connector.sendCommand(SRC_KEY + srcId + ALBUM_ART_AVAILABLE + ZERO_COMMA);
|
||||||
}
|
}
|
||||||
} catch (NuvoException e) {
|
} catch (NuvoException e) {
|
||||||
logger.debug("Error sending ALBUMARTAVAILABLE command for source: {}", key);
|
logger.debug("Error sending ALBUMARTAVAILABLE command for source: {}", srcId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_ALBUM_ART_FRAG_REQ:
|
case TYPE_NN_ALBUM_ART_FRAG_REQ:
|
||||||
// ignore this update unless openHAB is handling this source
|
// ignore this update unless openHAB is handling this source
|
||||||
if (nuvoNetSrcMap.get(key).equals(2)) {
|
if (nuvoNetSrcMap.get(srcId).equals(2)) {
|
||||||
logger.debug("Album Art Fragment Request for Source: {} - Data: {}", key, updateData);
|
logger.debug("Album Art Fragment Request for Source: {} - Data: {}", srcId, updateData);
|
||||||
// 0x620FD879,0,750 (id, requested offset from start of image, byte length requested)
|
// 0x620FD879,0,750 (id, requested offset from start of image, byte length requested)
|
||||||
String[] albumArtFragReq = updateData.split(COMMA);
|
String[] albumArtFragReq = updateData.split(COMMA);
|
||||||
int requestedId = Integer.decode(albumArtFragReq[0]);
|
int requestedId = Integer.decode(albumArtFragReq[0]);
|
||||||
int offset = Integer.parseInt(albumArtFragReq[1]);
|
int offset = Integer.parseInt(albumArtFragReq[1]);
|
||||||
int length = Integer.parseInt(albumArtFragReq[2]);
|
int length = Integer.parseInt(albumArtFragReq[2]);
|
||||||
|
|
||||||
if (requestedId == albumArtIds.get(SRC_KEY + key)) {
|
if (requestedId == albumArtIds.get(SRC_KEY + srcId)) {
|
||||||
byte[] chunk = new byte[length];
|
byte[] chunk = new byte[length];
|
||||||
byte[] albumArtBytes = albumArtMap.get(SRC_KEY + key);
|
byte[] albumArtBytes = albumArtMap.get(SRC_KEY + srcId);
|
||||||
|
|
||||||
if (albumArtBytes != null) {
|
if (albumArtBytes != null) {
|
||||||
System.arraycopy(albumArtBytes, offset, chunk, 0, length);
|
System.arraycopy(albumArtBytes, offset, chunk, 0, length);
|
||||||
final String frag = Base64.getEncoder().encodeToString(chunk);
|
final String frag = Base64.getEncoder().encodeToString(chunk);
|
||||||
try {
|
try {
|
||||||
connector.sendCommand(SRC_KEY + key + ALBUM_ART_FRAG + requestedId + COMMA + offset
|
connector.sendCommand(SRC_KEY + srcId + ALBUM_ART_FRAG + requestedId + COMMA + offset
|
||||||
+ COMMA + frag.length() + COMMA + frag);
|
+ COMMA + frag.length() + COMMA + frag);
|
||||||
} catch (NuvoException e) {
|
} catch (NuvoException e) {
|
||||||
logger.debug("Error sending ALBUMARTFRAG command for source: {}, artId: {}", key,
|
logger.debug("Error sending ALBUMARTFRAG command for source: {}, artId: {}", srcId,
|
||||||
requestedId);
|
requestedId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TYPE_FAVORITE_REQ:
|
case TYPE_NN_FAVORITE_REQ:
|
||||||
// ignore this update unless openHAB is handling this source
|
// ignore this update unless openHAB is handling this source
|
||||||
if (nuvoNetSrcMap.get(key).equals(2)) {
|
if (nuvoNetSrcMap.get(srcId).equals(2)) {
|
||||||
logger.debug("Favorite request for source: {} - favoriteId: {}", key, updateData);
|
logger.debug("Favorite request for source: {} - favoriteId: {}", srcId, updateData);
|
||||||
try {
|
try {
|
||||||
int playlistIdx = Integer.parseInt(updateData, 16) - 1000;
|
int playlistIdx = Integer.parseInt(updateData, 16) - 1000;
|
||||||
updateChannelState(NuvoEnum.valueOf(SOURCE + key), CHANNEL_BUTTON_PRESS,
|
updateChannelState(NuvoEnum.valueOf(SOURCE + srcId), CHANNEL_BUTTON_PRESS,
|
||||||
"PLAY_MUSIC_PRESET:" + favoriteMap.get(key)[playlistIdx]);
|
"PLAY_MUSIC_PRESET:" + favoriteMap.get(srcId)[playlistIdx]);
|
||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
logger.debug("Unable to parse favoriteId: {}", updateData);
|
logger.debug("Unable to parse favoriteId: {}", updateData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.debug("onNewMessageEvent: unhandled key {}", key);
|
logger.debug("onNewMessageEvent: unhandled event type {}", type);
|
||||||
// Return here because receiving an unknown message does not indicate that one can poll
|
// Return here because receiving an unknown message does not indicate that one can poll
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ channel-type.nuvo.balance.description = Adjust the Balance Setting for the Zone
|
|||||||
channel-type.nuvo.bass.label = Bass Adjustment
|
channel-type.nuvo.bass.label = Bass Adjustment
|
||||||
channel-type.nuvo.bass.description = Adjust the Bass Setting for the Zone
|
channel-type.nuvo.bass.description = Adjust the Bass Setting for the Zone
|
||||||
channel-type.nuvo.button_press.label = Button Pressed
|
channel-type.nuvo.button_press.label = Button Pressed
|
||||||
channel-type.nuvo.button_press.description = Indicates the Last Button Pressed On the Keypad for a Non NuvoNet Source
|
channel-type.nuvo.button_press.description = Indicates the Last Button Pressed On the Keypad
|
||||||
channel-type.nuvo.control.label = Control
|
channel-type.nuvo.control.label = Control
|
||||||
channel-type.nuvo.control.description = Transport Controls e.g. Play/Pause/Next/Previous for the Current Source
|
channel-type.nuvo.control.description = Transport Controls e.g. Play/Pause/Next/Previous for the Current Source
|
||||||
channel-type.nuvo.display_line1.label = Display Line 1
|
channel-type.nuvo.display_line1.label = Display Line 1
|
||||||
|
@ -342,6 +342,7 @@
|
|||||||
<channel id="allmute" typeId="system.mute"/>
|
<channel id="allmute" typeId="system.mute"/>
|
||||||
<channel id="page" typeId="page"/>
|
<channel id="page" typeId="page"/>
|
||||||
<channel id="sendcmd" typeId="sendcmd"/>
|
<channel id="sendcmd" typeId="sendcmd"/>
|
||||||
|
<channel id="buttonpress" typeId="button_press"/>
|
||||||
</channels>
|
</channels>
|
||||||
</channel-group-type>
|
</channel-group-type>
|
||||||
|
|
||||||
@ -518,7 +519,7 @@
|
|||||||
<channel-type id="button_press">
|
<channel-type id="button_press">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Button Pressed</label>
|
<label>Button Pressed</label>
|
||||||
<description>Indicates the Last Button Pressed On the Keypad for a Non NuvoNet Source</description>
|
<description>Indicates the Last Button Pressed On the Keypad</description>
|
||||||
<state readOnly="true"/>
|
<state readOnly="true"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user