[sonos] Add support for Sonos Arc/Arc SL + new controls for sub/surround speakers (#9916)

* [sonos] Add support for Sonos Arc/Arc SL + new controls for sub/surround speakers

Related to #9874

Signed-off-by: Laurent Garnier <lg.hc@free.fr>

* Use OnOffType.from

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
This commit is contained in:
lolodomo 2021-01-24 16:10:33 +01:00 committed by GitHub
parent 5a2b6047e1
commit 3f13392180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 378 additions and 12 deletions

View File

@ -93,6 +93,12 @@ The devices support the following channels:
| standalone | Switch | W | Make the Zone Player leave its Group and become a standalone Zone Player | all |
| state | String | R | The State channel contains state of the Zone Player, e.g. PLAYING, STOPPED, ... | all |
| stop | Switch | W | Write `ON` to this channel: Stops the Zone Player player. | all |
| subwoofer | Switch | RW | Enable or disable the subwoofer | Arc, Arc SL |
| subwoofergain | Number | RW | Set or get the subwoofer gain adjustment (value in range -15 / 15) | Arc, Arc SL |
| surround | Switch | RW | Enable or disable the surround audio | Arc, Arc SL |
| surroundmusicmode | String | RW | Set or get the surround playback mode for music, either 0 for Ambient or 1 for full | Arc, Arc SL |
| surroundmusiclevel | Number | RW | Set or get the surround level adjustment for music (value in range -15 / 15) | Arc, Arc SL |
| surroundtvlevel | Number | RW | Set or get the surround level adjustment for TV (value in range -15 / 15) | Arc, Arc SL |
| tuneinstationid | String | RW | Provide the current TuneIn station id or play the TuneIn radio given by its station id | all |
| volume | Dimmer | RW | Set or get the master volume of the Zone Player | all |
| zonegroupid | String | R | Id of the Zone Group the Zone Player belongs to | all |

View File

@ -49,11 +49,13 @@ public class SonosBindingConstants {
public static final ThingTypeUID CONNECTAMP_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "CONNECTAMP");
public static final ThingTypeUID AMP_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "Amp");
public static final ThingTypeUID SYMFONISK_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "SYMFONISK");
public static final ThingTypeUID ARC_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "Arc");
public static final ThingTypeUID ARC_SL_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "ArcSL");
public static final ThingTypeUID ZONEPLAYER_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "zoneplayer");
public static final Set<ThingTypeUID> WITH_LINEIN_THING_TYPES_UIDS = Stream
.of(PLAY5_THING_TYPE_UID, PLAYBAR_THING_TYPE_UID, PLAYBASE_THING_TYPE_UID, BEAM_THING_TYPE_UID,
CONNECT_THING_TYPE_UID, CONNECTAMP_THING_TYPE_UID, PORT_THING_TYPE_UID)
public static final Set<ThingTypeUID> WITH_LINEIN_THING_TYPES_UIDS = Stream.of(PLAY5_THING_TYPE_UID,
PLAYBAR_THING_TYPE_UID, PLAYBASE_THING_TYPE_UID, BEAM_THING_TYPE_UID, CONNECT_THING_TYPE_UID,
CONNECTAMP_THING_TYPE_UID, PORT_THING_TYPE_UID, ARC_THING_TYPE_UID, ARC_SL_THING_TYPE_UID)
.collect(Collectors.toSet());
public static final Set<ThingTypeUID> WITH_ANALOG_LINEIN_THING_TYPES_UIDS = Stream.of(AMP_THING_TYPE_UID)
@ -62,10 +64,11 @@ public class SonosBindingConstants {
public static final Set<ThingTypeUID> WITH_DIGITAL_LINEIN_THING_TYPES_UIDS = Stream.of(AMP_THING_TYPE_UID)
.collect(Collectors.toSet());
public static final Set<ThingTypeUID> SUPPORTED_KNOWN_THING_TYPES_UIDS = Stream.of(ONE_THING_TYPE_UID,
ONE_SL_THING_TYPE_UID, PLAY1_THING_TYPE_UID, PLAY3_THING_TYPE_UID, PLAY5_THING_TYPE_UID,
PLAYBAR_THING_TYPE_UID, PLAYBASE_THING_TYPE_UID, BEAM_THING_TYPE_UID, CONNECT_THING_TYPE_UID,
CONNECTAMP_THING_TYPE_UID, PORT_THING_TYPE_UID, AMP_THING_TYPE_UID, SYMFONISK_THING_TYPE_UID)
public static final Set<ThingTypeUID> SUPPORTED_KNOWN_THING_TYPES_UIDS = Stream
.of(ONE_THING_TYPE_UID, ONE_SL_THING_TYPE_UID, PLAY1_THING_TYPE_UID, PLAY3_THING_TYPE_UID,
PLAY5_THING_TYPE_UID, PLAYBAR_THING_TYPE_UID, PLAYBASE_THING_TYPE_UID, BEAM_THING_TYPE_UID,
CONNECT_THING_TYPE_UID, CONNECTAMP_THING_TYPE_UID, PORT_THING_TYPE_UID, AMP_THING_TYPE_UID,
SYMFONISK_THING_TYPE_UID, ARC_THING_TYPE_UID, ARC_SL_THING_TYPE_UID)
.collect(Collectors.toSet());
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(SUPPORTED_KNOWN_THING_TYPES_UIDS);
@ -120,6 +123,12 @@ public class SonosBindingConstants {
public static final String STANDALONE = "standalone";
public static final String STATE = "state";
public static final String STOP = "stop";
public static final String SUBWOOFER = "subwoofer";
public static final String SUBWOOFERGAIN = "subwoofergain";
public static final String SURROUND = "surround";
public static final String SURROUNDMUSICMODE = "surroundmusicmode";
public static final String SURROUNDMUSICLEVEL = "surroundmusiclevel";
public static final String SURROUNDTVLEVEL = "surroundtvlevel";
public static final String TUNEINSTATIONID = "tuneinstationid";
public static final String VOLUME = "volume";
public static final String ZONEGROUPID = "zonegroupid";

View File

@ -91,6 +91,9 @@ public class ZonePlayerDiscoveryParticipant implements UpnpDiscoveryParticipant
case "One SL":
modelName = "OneSL";
break;
case "Arc SL":
modelName = "ArcSL";
break;
default:
break;
}

View File

@ -111,6 +111,11 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
private static final int TUNEIN_DEFAULT_SERVICE_TYPE = 65031;
private static final int MIN_SUBWOOFER_GAIN = -15;
private static final int MAX_SUBWOOFER_GAIN = 15;
private static final int MIN_SURROUND_LEVEL = -15;
private static final int MAX_SURROUND_LEVEL = 15;
private final Logger logger = LoggerFactory.getLogger(ZonePlayerHandler.class);
private final ThingRegistry localThingRegistry;
@ -259,6 +264,24 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
case VOLUME:
setVolumeForGroup(command);
break;
case SUBWOOFER:
setSubwoofer(command);
break;
case SUBWOOFERGAIN:
setSubwooferGain(command);
break;
case SURROUND:
setSurround(command);
break;
case SURROUNDMUSICMODE:
setSurroundMusicMode(command);
break;
case SURROUNDMUSICLEVEL:
setSurroundMusicLevel(command);
break;
case SURROUNDTVLEVEL:
setSurroundTvLevel(command);
break;
case ADD:
addMember(command);
break;
@ -485,6 +508,24 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
case "MuteMaster":
updateChannel(MUTE);
break;
case "SubEnabled":
updateChannel(SUBWOOFER);
break;
case "SubGain":
updateChannel(SUBWOOFERGAIN);
break;
case "SurroundEnabled":
updateChannel(SURROUND);
break;
case "SurroundMode":
updateChannel(SURROUNDMUSICMODE);
break;
case "SurroundLevel":
updateChannel(SURROUNDTVLEVEL);
break;
case "MusicSurroundLevel":
updateChannel(SURROUNDMUSICLEVEL);
break;
case "NightMode":
updateChannel(NIGHTMODE);
break;
@ -700,6 +741,42 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
newState = isMuted() ? OnOffType.ON : OnOffType.OFF;
}
break;
case SUBWOOFER:
value = getSubwooferEnabled();
if (value != null) {
newState = OnOffType.from(value);
}
break;
case SUBWOOFERGAIN:
value = getSubwooferGain();
if (value != null) {
newState = new DecimalType(value);
}
break;
case SURROUND:
value = getSurroundEnabled();
if (value != null) {
newState = OnOffType.from(value);
}
break;
case SURROUNDMUSICMODE:
value = getSurroundMusicMode();
if (value != null) {
newState = new StringType(value);
}
break;
case SURROUNDMUSICLEVEL:
value = getSurroundMusicLevel();
if (value != null) {
newState = new DecimalType(value);
}
break;
case SURROUNDTVLEVEL:
value = getSurroundTvLevel();
if (value != null) {
newState = new DecimalType(value);
}
break;
case NIGHTMODE:
value = getNightMode();
if (value != null) {
@ -1238,6 +1315,30 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
return stateMap.get("VolumeMaster");
}
public @Nullable String getSurroundEnabled() {
return stateMap.get("SurroundEnabled");
}
public @Nullable String getSurroundMusicMode() {
return stateMap.get("SurroundMode");
}
public @Nullable String getSurroundTvLevel() {
return stateMap.get("SurroundLevel");
}
public @Nullable String getSurroundMusicLevel() {
return stateMap.get("MusicSurroundLevel");
}
public @Nullable String getSubwooferEnabled() {
return stateMap.get("SubEnabled");
}
public @Nullable String getSubwooferGain() {
return stateMap.get("SubGain");
}
public @Nullable String getTransportState() {
return stateMap.get("TransportState");
}
@ -1844,17 +1945,65 @@ public class ZonePlayerHandler extends BaseThingHandler implements UpnpIOPartici
}
}
public void setSubwoofer(Command command) {
setEqualizerBooleanSetting(command, "SubEnabled");
}
public void setSubwooferGain(Command command) {
setEqualizerNumericSetting(command, "SubGain", getSubwooferGain(), MIN_SUBWOOFER_GAIN, MAX_SUBWOOFER_GAIN);
}
public void setSurround(Command command) {
setEqualizerBooleanSetting(command, "SurroundEnabled");
}
public void setSurroundMusicMode(Command command) {
if (command instanceof StringType) {
setEQ("SurroundMode", command.toString());
}
}
public void setSurroundMusicLevel(Command command) {
setEqualizerNumericSetting(command, "MusicSurroundLevel", getSurroundMusicLevel(), MIN_SURROUND_LEVEL,
MAX_SURROUND_LEVEL);
}
public void setSurroundTvLevel(Command command) {
setEqualizerNumericSetting(command, "SurroundLevel", getSurroundTvLevel(), MIN_SURROUND_LEVEL,
MAX_SURROUND_LEVEL);
}
public void setNightMode(Command command) {
setEqualizerBooleanSetting(command, "NightMode");
}
public void setSpeechEnhancement(Command command) {
setEqualizerBooleanSetting(command, "DialogLevel");
}
private void setEqualizerBooleanSetting(Command command, String eqType) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
setEQ("NightMode", (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
setEQ(eqType, (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
|| command.equals(OpenClosedType.OPEN)) ? "1" : "0");
}
}
public void setSpeechEnhancement(Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
setEQ("DialogLevel", (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
|| command.equals(OpenClosedType.OPEN)) ? "1" : "0");
private void setEqualizerNumericSetting(Command command, String eqType, @Nullable String currentValue, int minValue,
int maxValue) {
if (command instanceof IncreaseDecreaseType || command instanceof DecimalType) {
String newValue = null;
if (command == IncreaseDecreaseType.INCREASE && currentValue != null) {
int i = Integer.valueOf(currentValue);
newValue = String.valueOf(Math.min(maxValue, i + 1));
} else if (command == IncreaseDecreaseType.DECREASE && currentValue != null) {
int i = Integer.valueOf(currentValue);
newValue = String.valueOf(Math.max(minValue, i - 1));
} else if (command instanceof DecimalType) {
newValue = String.valueOf(((DecimalType) command).intValue());
} else {
return;
}
setEQ(eqType, newValue);
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonos"
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">
<!-- Soundbar Arc Thing Type -->
<thing-type id="Arc" listed="false">
<label>Arc</label>
<description>Represents SONOS Arc soundbar</description>
<channels>
<channel id="add" typeId="add"/>
<channel id="alarm" typeId="alarm"/>
<channel id="alarmproperties" typeId="alarmproperties"/>
<channel id="alarmrunning" typeId="alarmrunning"/>
<channel id="control" typeId="system.media-control"/>
<channel id="currentalbum" typeId="currentalbum"/>
<channel id="currentalbumart" typeId="currentalbumart"/>
<channel id="currentalbumarturl" typeId="currentalbumarturl"/>
<channel id="currentartist" typeId="system.media-artist"/>
<channel id="currenttitle" typeId="system.media-title"/>
<channel id="currenttrack" typeId="currenttrack"/>
<channel id="shuffle" typeId="shuffle"/>
<channel id="repeat" typeId="repeat"/>
<channel id="favorite" typeId="favorite"/>
<channel id="led" typeId="led"/>
<channel id="localcoordinator" typeId="localcoordinator"/>
<channel id="mute" typeId="system.mute"/>
<channel id="notificationsound" typeId="notificationsound"/>
<channel id="playlist" typeId="playlist"/>
<channel id="clearqueue" typeId="clearqueue"/>
<channel id="playlinein" typeId="playlinein"/>
<channel id="playqueue" typeId="playqueue"/>
<channel id="playtrack" typeId="playtrack"/>
<channel id="playuri" typeId="playuri"/>
<channel id="publicaddress" typeId="publicaddress"/>
<channel id="radio" typeId="radio"/>
<channel id="remove" typeId="remove"/>
<channel id="restore" typeId="restore"/>
<channel id="restoreall" typeId="restoreall"/>
<channel id="save" typeId="save"/>
<channel id="saveall" typeId="saveall"/>
<channel id="snooze" typeId="snooze"/>
<channel id="standalone" typeId="standalone"/>
<channel id="state" typeId="state"/>
<channel id="stop" typeId="stop"/>
<channel id="tuneinstationid" typeId="tuneinstationid"/>
<channel id="volume" typeId="system.volume"/>
<channel id="zonegroupid" typeId="zonegroupid"/>
<channel id="zonename" typeId="zonename"/>
<channel id="coordinator" typeId="coordinator"/>
<channel id="sleeptimer" typeId="sleeptimer"/>
<channel id="currenttransporturi" typeId="currenttransporturi"/>
<channel id="currenttrackuri" typeId="currenttrackuri"/>
<!-- Extended SONOS channels -->
<channel id="linein" typeId="linein"/>
<channel id="nightmode" typeId="nightmode"/>
<channel id="speechenhancement" typeId="speechenhancement"/>
<channel id="subwoofer" typeId="subwoofer"/>
<channel id="subwoofergain" typeId="subwoofergain"/>
<channel id="surround" typeId="surround"/>
<channel id="surroundmusicmode" typeId="surroundmusicmode"/>
<channel id="surroundmusiclevel" typeId="surroundmusiclevel"/>
<channel id="surroundtvlevel" typeId="surroundtvlevel"/>
</channels>
<properties>
<property name="vendor">SONOS</property>
<property name="modelId">Arc</property>
</properties>
<representation-property>udn</representation-property>
<config-description-ref uri="thing-type:sonos:zoneplayer"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="sonos"
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">
<!-- Soundbar Arc SL Thing Type -->
<thing-type id="ArcSL" listed="false">
<label>Arc</label>
<description>Represents SONOS Arc SL soundbar</description>
<channels>
<channel id="add" typeId="add"/>
<channel id="alarm" typeId="alarm"/>
<channel id="alarmproperties" typeId="alarmproperties"/>
<channel id="alarmrunning" typeId="alarmrunning"/>
<channel id="control" typeId="system.media-control"/>
<channel id="currentalbum" typeId="currentalbum"/>
<channel id="currentalbumart" typeId="currentalbumart"/>
<channel id="currentalbumarturl" typeId="currentalbumarturl"/>
<channel id="currentartist" typeId="system.media-artist"/>
<channel id="currenttitle" typeId="system.media-title"/>
<channel id="currenttrack" typeId="currenttrack"/>
<channel id="shuffle" typeId="shuffle"/>
<channel id="repeat" typeId="repeat"/>
<channel id="favorite" typeId="favorite"/>
<channel id="led" typeId="led"/>
<channel id="localcoordinator" typeId="localcoordinator"/>
<channel id="mute" typeId="system.mute"/>
<channel id="notificationsound" typeId="notificationsound"/>
<channel id="playlist" typeId="playlist"/>
<channel id="clearqueue" typeId="clearqueue"/>
<channel id="playlinein" typeId="playlinein"/>
<channel id="playqueue" typeId="playqueue"/>
<channel id="playtrack" typeId="playtrack"/>
<channel id="playuri" typeId="playuri"/>
<channel id="publicaddress" typeId="publicaddress"/>
<channel id="radio" typeId="radio"/>
<channel id="remove" typeId="remove"/>
<channel id="restore" typeId="restore"/>
<channel id="restoreall" typeId="restoreall"/>
<channel id="save" typeId="save"/>
<channel id="saveall" typeId="saveall"/>
<channel id="snooze" typeId="snooze"/>
<channel id="standalone" typeId="standalone"/>
<channel id="state" typeId="state"/>
<channel id="stop" typeId="stop"/>
<channel id="tuneinstationid" typeId="tuneinstationid"/>
<channel id="volume" typeId="system.volume"/>
<channel id="zonegroupid" typeId="zonegroupid"/>
<channel id="zonename" typeId="zonename"/>
<channel id="coordinator" typeId="coordinator"/>
<channel id="sleeptimer" typeId="sleeptimer"/>
<channel id="currenttransporturi" typeId="currenttransporturi"/>
<channel id="currenttrackuri" typeId="currenttrackuri"/>
<!-- Extended SONOS channels -->
<channel id="linein" typeId="linein"/>
<channel id="nightmode" typeId="nightmode"/>
<channel id="speechenhancement" typeId="speechenhancement"/>
<channel id="subwoofer" typeId="subwoofer"/>
<channel id="subwoofergain" typeId="subwoofergain"/>
<channel id="surround" typeId="surround"/>
<channel id="surroundmusicmode" typeId="surroundmusicmode"/>
<channel id="surroundmusiclevel" typeId="surroundmusiclevel"/>
<channel id="surroundtvlevel" typeId="surroundtvlevel"/>
</channels>
<properties>
<property name="vendor">SONOS</property>
<property name="modelId">Arc SL</property>
</properties>
<representation-property>udn</representation-property>
<config-description-ref uri="thing-type:sonos:zoneplayer"/>
</thing-type>
</thing:thing-descriptions>

View File

@ -251,6 +251,51 @@
<description>Stop the Zone Player. ON if the player is stopped.</description>
</channel-type>
<channel-type id="subwoofer" advanced="true">
<item-type>Switch</item-type>
<label>Subwoofer</label>
<description>Enable or disable the subwoofer</description>
</channel-type>
<channel-type id="subwoofergain" advanced="true">
<item-type>Number</item-type>
<label>Subwoofer Gain</label>
<description>Set or get the subwoofer gain adjustment</description>
<state min="-15" max="15" step="1" readOnly="false" pattern="%d"/>
</channel-type>
<channel-type id="surround" advanced="true">
<item-type>Switch</item-type>
<label>Surround Audio</label>
<description>Enable or disable the surround audio</description>
</channel-type>
<channel-type id="surroundmusicmode" advanced="true">
<item-type>String</item-type>
<label>Surround Music Mode</label>
<description>Set or get the surround playback mode for music</description>
<state readOnly="false">
<options>
<option value="0">Ambient</option>
<option value="1">Full</option>
</options>
</state>
</channel-type>
<channel-type id="surroundmusiclevel" advanced="true">
<item-type>Number</item-type>
<label>Surround Music Level</label>
<description>Set or get the surround level adjustment for music</description>
<state min="-15" max="15" step="1" readOnly="false" pattern="%d"/>
</channel-type>
<channel-type id="surroundtvlevel" advanced="true">
<item-type>Number</item-type>
<label>Surround TV Level</label>
<description>Set or get the surround level adjustment for TV</description>
<state min="-15" max="15" step="1" readOnly="false" pattern="%d"/>
</channel-type>
<channel-type id="tuneinstationid" advanced="true">
<item-type>String</item-type>
<label>TuneIn Station Id</label>