Skip to content

Commit

Permalink
[sonos] Add support for Sonos Arc/Arc SL + new controls for sub/surro…
Browse files Browse the repository at this point in the history
…und speakers (openhab#9916)

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

Related to openhab#9874

Signed-off-by: Laurent Garnier <[email protected]>

* Use OnOffType.from

Signed-off-by: Laurent Garnier <[email protected]>
  • Loading branch information
lolodomo authored and thinkingstone committed Nov 7, 2021
1 parent 7958dcd commit ef9f532
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 12 deletions.
6 changes: 6 additions & 0 deletions bundles/org.openhab.binding.sonos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public Set<ThingTypeUID> getSupportedThingTypeUIDs() {
case "One SL":
modelName = "OneSL";
break;
case "Arc SL":
modelName = "ArcSL";
break;
default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -259,6 +264,24 @@ public void handleCommand(ChannelUID channelUID, Command command) {
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;
Expand Down Expand Up @@ -485,6 +508,24 @@ public void onValueReceived(@Nullable String variable, @Nullable String value, @
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;
Expand Down Expand Up @@ -700,6 +741,42 @@ protected void updateChannel(String channelId) {
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) {
Expand Down Expand Up @@ -1238,6 +1315,30 @@ public long getRefreshedCurrenTrackNr() {
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");
}
Expand Down Expand Up @@ -1844,20 +1945,68 @@ public void setRepeat(Command command) {
}
}

public void setNightMode(Command command) {
if (command instanceof OnOffType || command instanceof OpenClosedType || command instanceof UpDownType) {
setEQ("NightMode", (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
|| command.equals(OpenClosedType.OPEN)) ? "1" : "0");
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("DialogLevel", (command.equals(OnOffType.ON) || command.equals(UpDownType.UP)
setEQ(eqType, (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);
}
}

private void setEQ(String eqType, String value) {
try {
Map<String, String> inputs = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Loading

0 comments on commit ef9f532

Please sign in to comment.