/**
* Copyright (c) 2010-2014, openHAB.org and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.sonos.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
import org.openhab.binding.sonos.SonosCommandType;
import org.openhab.binding.sonos.internal.SonosBinding.SonosZonePlayerState;
import org.openhab.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.teleal.cling.UpnpService;
import org.teleal.cling.controlpoint.ActionCallback;
import org.teleal.cling.controlpoint.SubscriptionCallback;
import org.teleal.cling.model.action.ActionArgumentValue;
import org.teleal.cling.model.action.ActionException;
import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.gena.CancelReason;
import org.teleal.cling.model.gena.GENASubscription;
import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Action;
import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.meta.StateVariable;
import org.teleal.cling.model.meta.StateVariableTypeDetails;
import org.teleal.cling.model.state.StateVariableValue;
import org.teleal.cling.model.types.Datatype;
import org.teleal.cling.model.types.InvalidValueException;
import org.teleal.cling.model.types.UDAServiceId;
import org.teleal.cling.model.types.UDN;
import org.teleal.cling.model.types.UnsignedIntegerFourBytes;
import org.xml.sax.SAXException;
/**
* Internal data structure which carries the connection details of one Sonos player
* (there could be several)
*
* @author Karel Goderis
* @since 1.1.0
*
*/
class SonosZonePlayer {
private static Logger logger = LoggerFactory.getLogger(SonosZonePlayer.class);
protected final int interval = 600;
private boolean isConfigured = false;
/** the default socket timeout when requesting an url */
private static final int SO_TIMEOUT = 5000;
private RemoteDevice device = null;
private UDN udn;
private String id;
private DateTime lastOPMLQuery;
private SonosZonePlayerState savedState = null;
static protected UpnpService upnpService;
protected SonosBinding sonosBinding;
private Map<String, StateVariableValue> stateMap = Collections.synchronizedMap(new HashMap<String,StateVariableValue>());
/**
* @return the stateMap
*/
public Map<String, StateVariableValue> getStateMap() {
return stateMap;
}
public boolean isConfigured() {
return isConfigured;
}
SonosZonePlayer(String id, SonosBinding binding) {
if( binding != null) {
this.id = id;
sonosBinding = binding;
}
}
private void enableGENASubscriptions(){
if(device!=null && isConfigured()) {
// Create a GENA subscription of each service for this device, if supported by the device
List<SonosCommandType> subscriptionCommands = SonosCommandType.getSubscriptions();
List<String> addedSubscriptions = new ArrayList<String>();
for(SonosCommandType c : subscriptionCommands){
Service service = device.findService(new UDAServiceId(c.getService()));
if(service != null && !addedSubscriptions.contains(c.getService())) {
SonosPlayerSubscriptionCallback callback = new SonosPlayerSubscriptionCallback(service,interval);
addedSubscriptions.add(c.getService());
upnpService.getControlPoint().execute(callback);
}
}
}
}
protected boolean isUpdatedValue(String valueName,StateVariableValue newValue) {
if(newValue != null && valueName != null) {
StateVariableValue oldValue = stateMap.get(valueName);
if(newValue.getValue()== null) {
// we will *not* store an empty value, thank you.
return false;
} else {
if(oldValue == null) {
// there was nothing stored before
return true;
} else {
if (oldValue.getValue() == null) {
// something was defined, but no value present
return true;
} else {
if(newValue.getValue().equals(oldValue.getValue())) {
return false;
} else {
return true;
}
}
}
}
}
return false;
}
protected void processStateVariableValue(String valueName,StateVariableValue newValue) {
if(newValue!=null && isUpdatedValue(valueName,newValue)) {
Map<String, StateVariableValue> mapToProcess = new HashMap<String, StateVariableValue>();
mapToProcess.put(valueName,newValue);
stateMap.putAll(mapToProcess);
sonosBinding.processVariableMap(device,mapToProcess);
}
}
/**
* @return the device
*/
public RemoteDevice getDevice() {
return device;
}
/**
* @param device the device to set
*/
public void setDevice(RemoteDevice device) {
this.device = device;
if(upnpService !=null && device!=null) {
isConfigured = true;
enableGENASubscriptions();
}
}
public class SonosPlayerSubscriptionCallback extends SubscriptionCallback {
public SonosPlayerSubscriptionCallback(Service service) {
super(service);
}
public SonosPlayerSubscriptionCallback(Service service,
int requestedDurationSeconds) {
super(service, requestedDurationSeconds);
}
@Override
public void established(GENASubscription sub) {
}
@Override
protected void failed(GENASubscription subscription,
UpnpResponse responseStatus,
Exception exception,
String defaultMsg) {
}
public void eventReceived(GENASubscription sub) {
// get the device linked to this service linked to this subscription
Map<String, StateVariableValue> values = sub.getCurrentValues();
Map<String, StateVariableValue> mapToProcess = new HashMap<String, StateVariableValue>();
Map<String, StateVariableValue> parsedValues = null;
// now, lets deal with the specials - some UPNP responses require some XML parsing
// or we need to update our internal data structure
// or are things we want to store for further reference
for(String stateVariable : values.keySet()){
if(stateVariable.equals("LastChange") && service.getServiceType().getType().equals("AVTransport")){
try {
parsedValues = SonosXMLParser.getAVTransportFromXML(values.get(stateVariable).toString());
for(String someValue : parsedValues.keySet()) {
// logger.debug("Lastchange parsed into {}:{}",someValue,parsedValues.get(someValue));
if(isUpdatedValue(someValue,parsedValues.get(someValue))){
mapToProcess.put(someValue,parsedValues.get(someValue));
}
}
} catch (SAXException e) {
logger.error("Could not parse AVTransport from String {}",values.get(stateVariable).toString());
}
} else if(stateVariable.equals("LastChange") && service.getServiceType().getType().equals("RenderingControl")){
try {
parsedValues = SonosXMLParser.getRenderingControlFromXML(values.get(stateVariable).toString());
for(String someValue : parsedValues.keySet()) {
if(isUpdatedValue(someValue,parsedValues.get(someValue))){
mapToProcess.put(someValue,parsedValues.get(someValue));
}
}
} catch (SAXException e) {
logger.error("Could not parse RenderingControl from String {}",values.get(stateVariable).toString());
}
} else if(isUpdatedValue(stateVariable,values.get(stateVariable))){
mapToProcess.put(stateVariable, values.get(stateVariable));
}
}
if(isConfigured) {
stateMap.putAll(mapToProcess);
sonosBinding.processVariableMap(device,mapToProcess);
}
}
public void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
logger.warn("Missed events: " + numberOfMissedEvents);
}
@Override
protected void ended(GENASubscription subscription,
CancelReason reason, UpnpResponse responseStatus) {
if(device!=null && isConfigured()) {
//rebooting the GENA subscription
Service service = subscription.getService();
SonosPlayerSubscriptionCallback callback = new SonosPlayerSubscriptionCallback(service,interval);
upnpService.getControlPoint().execute(callback);
}
}
}
public void setService(UpnpService service) {
if(upnpService == null) {
upnpService = service;
}
if(upnpService !=null && device!=null) {
isConfigured = true;
enableGENASubscriptions();
}
}
public String getModel() {
if(device!=null) {
return device.getDetails().getModelDetails().getModelNumber();
} else {
return "Unknown";
}
}
/**
* @return the udn
*/
public UDN getUdn() {
return udn;
}
/**
* @param udn the udn to set
*/
public void setUdn(UDN udn) {
this.udn = udn;
}
public String getId() {
return id;
}
@Override
public String toString() {
return "Sonos [udn=" + udn + ", device=" + device +"]";
}
public boolean play() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Play");
ActionInvocation invocation = new ActionInvocation(action);
invocation.setInput("Speed", "1");
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean playRadio(String station){
if(isConfigured()) {
List<SonosEntry> stations = getFavoriteRadios();
SonosEntry theEntry = null;
// search for the appropriate radio based on its name (title)
for(SonosEntry someStation : stations){
if(someStation.getTitle().equals(station)){
theEntry = someStation;
break;
}
}
// set the URI of the group coordinator
if(theEntry != null) {
SonosZonePlayer coordinator = sonosBinding.getCoordinatorForZonePlayer(this);
coordinator.setCurrentURI(theEntry);
coordinator.play();
return true;
}
else {
return false;
}
} else {
return false;
}
}
public boolean playPlayList(String playlist){
if(isConfigured()) {
List<SonosEntry> playlists = getPlayLists();
SonosEntry theEntry = null;
// search for the appropriate play list based on its name (title)
for(SonosEntry somePlaylist : playlists){
if(somePlaylist.getTitle().equals(playlist)){
theEntry = somePlaylist;
break;
}
}
// set the URI of the group coordinator
if(theEntry != null) {
SonosZonePlayer coordinator = sonosBinding.getCoordinatorForZonePlayer(this);
//coordinator.setCurrentURI(theEntry);
coordinator.addURIToQueue(theEntry);
if(stateMap != null && isConfigured()) {
StateVariableValue firstTrackNumberEnqueued = stateMap.get("FirstTrackNumberEnqueued");
if(firstTrackNumberEnqueued!=null) {
coordinator.seek("TRACK_NR", firstTrackNumberEnqueued.getValue().toString());
}
}
coordinator.play();
return true;
}
else {
return false;
}
} else {
return false;
}
}
public boolean stop() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Stop");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean pause() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Pause");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean next() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Next");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean previous() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Previous");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public String getZoneName() {
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("ZoneName");
if(value != null) {
return value.getValue().toString();
}
}
return null;
}
public String getZoneGroupID() {
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("LocalGroupUUID");
if(value != null) {
return value.getValue().toString();
}
}
return null;
}
public boolean isGroupCoordinator() {
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("GroupCoordinatorIsLocal");
if(value != null) {
return (Boolean) value.getValue();
}
}
return false;
}
public SonosZonePlayer getCoordinator(){
return sonosBinding.getCoordinatorForZonePlayer(this);
}
public boolean isCoordinator() {
return this.equals(getCoordinator());
}
public boolean addMember(SonosZonePlayer newMember) {
if(newMember != null && isConfigured()) {
SonosEntry entry = new SonosEntry("", "", "", "", "", "", "", "x-rincon:"+udn.getIdentifierString());
return newMember.setCurrentURI(entry);
} else {
return false;
}
}
public boolean removeMember(SonosZonePlayer oldMember){
if(oldMember != null && isConfigured()) {
oldMember.becomeStandAlonePlayer();
SonosEntry entry = new SonosEntry("", "", "", "", "", "", "", "x-rincon-queue:"+oldMember.getUdn().getIdentifierString()+"#0");
return oldMember.setCurrentURI(entry);
} else {
return false;
}
}
public boolean becomeStandAlonePlayer() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("BecomeCoordinatorOfStandaloneGroup");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean setMute(String string) {
if(string != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("RenderingControl"));
Action action = service.getAction("SetMute");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("Channel", "Master");
if(string.equals("ON") || string.equals("OPEN") || string.equals("UP") ) {
invocation.setInput("DesiredMute", "True");
} else
if(string.equals("OFF") || string.equals("CLOSED") || string.equals("DOWN") ) {
invocation.setInput("DesiredMute", "False");
} else {
return false;
}
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public String getMute(){
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("MuteMaster");
if(value != null) {
return value.getValue().toString();
}
}
return null;
}
public boolean setVolume(String value) {
if(value != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("RenderingControl"));
Action action = service.getAction("SetVolume");
ActionInvocation invocation = new ActionInvocation(action);
try {
String newValue = value;
if(value.equals("INCREASE")) {
int i = Integer.valueOf(this.getVolume());
newValue = String.valueOf(Math.min(100, i+1));
} else if (value.equals("DECREASE")) {
int i = Integer.valueOf(this.getVolume());
newValue = String.valueOf(Math.max(0, i-1));
} else if (value.equals("ON")) {
newValue = "100";
} else if (value.equals("OFF")) {
newValue = "0";
} else {
newValue = value;
}
invocation.setInput("Channel", "Master");
invocation.setInput("DesiredVolume",newValue);
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public String getVolume() {
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("VolumeMaster");
if(value != null) {
return value.getValue().toString();
}
}
return null;
}
public boolean updateTime() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AlarmClock"));
Action action = service.getAction("GetTimeNow");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public String getTime() {
if(isConfigured()) {
updateTime();
if(stateMap != null) {
StateVariableValue value = stateMap.get("CurrentLocalTime");
if(value != null) {
return value.getValue().toString();
}
}
}
return null;
}
protected void executeActionInvocation(ActionInvocation invocation) {
if(invocation != null) {
new ActionCallback.Default(invocation, upnpService.getControlPoint()).run();
ActionException anException = invocation.getFailure();
if(anException!= null && anException.getMessage()!=null) {
logger.warn(anException.getMessage());
}
Map<String, ActionArgumentValue> result = invocation.getOutputMap();
Map<String, StateVariableValue> mapToProcess = new HashMap<String, StateVariableValue>();
if(result != null) {
// only process the variables that have changed value
for(String variable : result.keySet()) {
ActionArgumentValue newArgument = result.get(variable);
StateVariable newVariable = new StateVariable(variable,new StateVariableTypeDetails(newArgument.getDatatype()));
StateVariableValue newValue = new StateVariableValue(newVariable, newArgument.getValue());
if(isUpdatedValue(variable,newValue)) {
mapToProcess.put(variable, newValue);
}
}
stateMap.putAll(mapToProcess);
sonosBinding.processVariableMap(device,mapToProcess);
}
}
}
public boolean updateRunningAlarmProperties() {
if(stateMap != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("GetRunningAlarmProperties");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
// for this property we would like to "compile" a more friendly variable.
// this newly created "variable" is also store in the stateMap
StateVariableValue alarmID = stateMap.get("AlarmID");
StateVariableValue groupID = stateMap.get("GroupID");
StateVariableValue loggedStartTime = stateMap.get("LoggedStartTime");
String newStringValue = null;
if(alarmID != null && loggedStartTime != null) {
newStringValue = alarmID.getValue() + " - " + loggedStartTime.getValue();
} else {
newStringValue = "No running alarm";
}
StateVariable newVariable = new StateVariable("RunningAlarmProperties",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
StateVariableValue newValue = new StateVariableValue(newVariable, newStringValue);
processStateVariableValue(newVariable.getName(),newValue);
return true;
} else {
return false;
}
}
public String getRunningAlarmProperties() {
if(isConfigured()) {
updateRunningAlarmProperties();
if(stateMap != null) {
StateVariableValue value = stateMap.get("RunningAlarmProperties");
if(value != null) {
return value.getValue().toString();
}
}
}
return null;
}
public boolean updateZoneInfo() {
if(stateMap != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("DeviceProperties"));
Action action = service.getAction("GetZoneInfo");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
Service anotherservice = device.findService(new UDAServiceId("DeviceProperties"));
Action anotheraction = service.getAction("GetZoneAttributes");
ActionInvocation anotherinvocation = new ActionInvocation(anotheraction);
executeActionInvocation(anotherinvocation);
// anotherservice = device.findService(new UDAServiceId("ZoneGroupTopology"));
// anotheraction = service.getAction("GetZoneGroupState");
// anotherinvocation = new ActionInvocation(anotheraction);
// executeActionInvocation(anotherinvocation);
return true;
} else {
return false;
}
}
public String getMACAddress() {
if(isConfigured()) {
updateZoneInfo();
if(stateMap != null) {
StateVariableValue value = stateMap.get("MACAddress");
if(value != null) {
return value.getValue().toString();
}
}
}
return null;
}
public boolean setLed(String string) {
if(string != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("DeviceProperties"));
Action action = service.getAction("SetLEDState");
ActionInvocation invocation = new ActionInvocation(action);
try {
if(string.equals("ON") || string.equals("OPEN") || string.equals("UP") ) {
invocation.setInput("DesiredLEDState", "On");
} else
if(string.equals("OFF") || string.equals("CLOSED") || string.equals("DOWN") ) {
invocation.setInput("DesiredLEDState", "Off");
} else {
return false;
}
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean updateLed() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("DeviceProperties"));
Action action = service.getAction("GetLEDState");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
}
else {
return false;
}
}
public boolean getLed() {
if(isConfigured()) {
updateLed();
if(stateMap != null) {
StateVariableValue variable = stateMap.get("CurrentLEDState");
if(variable != null) {
return variable.getValue().equals("On") ? true : false;
}
}
}
return false;
}
public String getCurrentZoneName() {
if(isConfigured()) {
updateCurrentZoneName();
if(stateMap != null) {
StateVariableValue variable = stateMap.get("CurrentZoneName");
if(variable != null) {
return variable.getValue().toString();
}
}
}
return null;
}
public boolean updateCurrentZoneName() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("DeviceProperties"));
Action action = service.getAction("GetZoneAttributes");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
}
else {
return false;
}
}
public boolean updatePosition() {
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("GetPositionInfo");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
return true;
}
else {
return false;
}
}
public String getPosition() {
if(stateMap != null && isConfigured()) {
updatePosition();
if(stateMap != null) {
StateVariableValue variable = stateMap.get("RelTime");
if(variable != null) {
return variable.getValue().toString();
}
}
}
return null;
}
public boolean setPosition(String relTime) {
return seek("REL_TIME",relTime);
}
public boolean setPositionTrack(long tracknr) {
return seek("TRACK_NR",Long.toString(tracknr));
}
protected boolean seek(String unit, String target) {
if(isConfigured() && unit != null && target != null) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("Seek");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("InstanceID","0");
invocation.setInput("Unit", unit);
invocation.setInput("Target", target);
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
}
return false;
}
public Boolean isLineInConnected() {
if(stateMap != null && isConfigured()) {
StateVariableValue statusLineIn = stateMap.get("LineInConnected");
if(statusLineIn != null) {
return (Boolean) statusLineIn.getValue();
}
}
return null;
}
public Boolean isAlarmRunning() {
if(stateMap != null && isConfigured()) {
StateVariableValue status = stateMap.get("AlarmRunning");
if(status!=null) {
return status.getValue().equals("1") ? true : false;
}
}
return null;
}
public String getTransportState() {
if(stateMap != null && isConfigured()) {
StateVariableValue status = stateMap.get("TransportState");
if(status != null) {
return status.getValue().toString();
}
}
return null;
}
public boolean addURIToQueue(String URI, String meta,int desiredFirstTrack, boolean enqueueAsNext) {
if(isConfigured() && URI != null && meta != null) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("AddURIToQueue");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("InstanceID","0");
invocation.setInput("EnqueuedURI",URI);
invocation.setInput("EnqueuedURIMetaData",meta);
invocation.setInput("DesiredFirstTrackNumberEnqueued",new UnsignedIntegerFourBytes(desiredFirstTrack));
invocation.setInput("EnqueueAsNext",enqueueAsNext);
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
}
return false;
}
public String getCurrentURI(){
updateMediaInfo();
if(stateMap != null && isConfigured()) {
StateVariableValue status = stateMap.get("CurrentURI");
if(status != null) {
return status.getValue().toString();
}
}
return null;
}
public long getCurrenTrackNr() {
if(stateMap != null && isConfigured()) {
updatePosition();
if(stateMap != null) {
StateVariableValue variable = stateMap.get("Track");
if(variable != null) {
return ((UnsignedIntegerFourBytes)variable.getValue()).getValue();
}
}
}
return (long) -1;
}
public boolean updateMediaInfo(){
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("GetMediaInfo");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("InstanceID","0");
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
}
return false;
}
public boolean addURIToQueue(SonosEntry newEntry) {
return addURIToQueue(newEntry.getRes(),SonosXMLParser.compileMetadataString(newEntry),1,true);
}
public boolean setCurrentURI(String URI, String URIMetaData ) {
if(URI != null && URIMetaData != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("SetAVTransportURI");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("InstanceID","0");
invocation.setInput("CurrentURI",URI);
invocation.setInput("CurrentURIMetaData", URIMetaData);
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
public boolean setCurrentURI(SonosEntry newEntry) {
return setCurrentURI(newEntry.getRes(),SonosXMLParser.compileMetadataString(newEntry));
}
public boolean updateCurrentURIFormatted() {
if(stateMap != null && isConfigured()) {
String currentURI = null;
SonosMetaData currentURIMetaData = null;
SonosMetaData currentTrack = null;
if(!isGroupCoordinator()) {
currentURI = getCoordinator().getCurrentURI();
currentURIMetaData = getCoordinator().getCurrentURIMetadata();
currentTrack = getCoordinator().getTrackMetadata();
} else {
currentURI = getCurrentURI();
currentURIMetaData = getCurrentURIMetadata();
currentTrack = getTrackMetadata();
}
if(currentURI != null) {
String resultString = null;
String artist = null;
String album = null;
String title = null;
if(currentURI.contains("x-sonosapi-stream")) {
//TODO: Get partner ID for openhab.org
String stationID = StringUtils.substringBetween(currentURI, ":s", "?sid");
StateVariable newVariable = new StateVariable("StationID",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
StateVariableValue newValue = new StateVariableValue(newVariable, stationID);
if(this.isUpdatedValue("StationID", newValue) || lastOPMLQuery ==null || lastOPMLQuery.plusMinutes(1).isBeforeNow()) {
processStateVariableValue(newVariable.getName(),newValue);
String url = "http://opml.radiotime.com/Describe.ashx?c=nowplaying&id=" + stationID + "&partnerId=IAeIhU42&serial=" + getMACAddress();
String response = HttpUtil.executeUrl("GET", url, SO_TIMEOUT);
//logger.debug("RadioTime Response: {}",response);
lastOPMLQuery = DateTime.now();
List<String> fields = null;
try {
fields = SonosXMLParser.getRadioTimeFromXML(response);
} catch (SAXException e) {
logger.error("Could not parse RadioTime from String {}",response);
}
resultString = new String();
if(fields != null && fields.size()>1) {
artist = fields.get(0);
title = fields.get(1);
Iterator<String> listIterator = fields.listIterator();
while(listIterator.hasNext()){
String field = listIterator.next();
resultString = resultString + field;
if(listIterator.hasNext()) {
resultString = resultString + " - ";
}
}
}
} else {
resultString = stateMap.get("CurrentURIFormatted").getValue().toString();
title = stateMap.get("CurrentTitle").getValue().toString();
artist = stateMap.get("CurrentArtist").getValue().toString();
}
} else {
if(currentTrack != null) {
if(currentTrack.getResource().contains("x-rincon-stream")) {
title = currentTrack.getTitle();
album = " ";
artist = " ";
resultString = title;
} else if(!currentTrack.getResource().contains("x-sonosapi-stream")) {
if (currentTrack.getAlbumArtist().equals("")) {
resultString = currentTrack.getCreator() + " - " + currentTrack.getAlbum() + " - " + currentTrack.getTitle();
artist = currentTrack.getCreator();
} else {
resultString = currentTrack.getAlbumArtist() + " - " + currentTrack.getAlbum() + " - " + currentTrack.getTitle();
artist = currentTrack.getAlbumArtist();
}
album = currentTrack.getAlbum();
title = currentTrack.getTitle();
if(album.equals("")) {
album= " ";
}
if(artist.equals("")) {
artist= " ";
}
}
} else {
title=" ";
album = " ";
artist = " ";
resultString = " ";
}
}
StateVariable newVariable = new StateVariable("CurrentURIFormatted",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
StateVariableValue newValue = new StateVariableValue(newVariable, resultString);
processStateVariableValue(newVariable.getName(),newValue);
// update individual variables
newVariable = new StateVariable("CurrentArtist",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
if (artist != null) {
newValue = new StateVariableValue(newVariable, artist);
} else {
newValue = new StateVariableValue(newVariable, " ");
}
processStateVariableValue(newVariable.getName(), newValue);
newVariable = new StateVariable("CurrentTitle",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
if (title != null) {
newValue = new StateVariableValue(newVariable, title);
} else {
newValue = new StateVariableValue(newVariable, " ");
}
processStateVariableValue(newVariable.getName(), newValue);
newVariable = new StateVariable("CurrentAlbum",new StateVariableTypeDetails(Datatype.Builtin.STRING.getDatatype()));
if (album != null) {
newValue = new StateVariableValue(newVariable, album);
} else {
newValue = new StateVariableValue(newVariable, " ");
}
processStateVariableValue(newVariable.getName(), newValue);
return true;
}
}
return false;
}
public String getCurrentURIFormatted(){
updateCurrentURIFormatted();
if(stateMap != null && isConfigured()) {
StateVariableValue status = stateMap.get("CurrentURIFormatted");
if(status != null) {
return status.getValue().toString();
}
}
return null;
}
public String getCurrentURIMetadataAsString() {
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("CurrentTrackMetaData");
if(value != null) {
return value.getValue().toString();
}
}
return null;
}
public SonosMetaData getCurrentURIMetadata(){
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("CurrentURIMetaData");
SonosMetaData currentTrack = null;
if(value != null) {
try {
if(((String)value.getValue()).length()!=0) {
currentTrack = SonosXMLParser.getMetaDataFromXML((String)value.getValue());
}
} catch (SAXException e) {
logger.error("Could not parse MetaData from String {}",value.getValue().toString());
}
return currentTrack;
} else {
return null;
}
} else {
return null;
}
}
public SonosMetaData getTrackMetadata(){
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("CurrentTrackMetaData");
SonosMetaData currentTrack = null;
if(value != null) {
try {
if(((String)value.getValue()).length()!=0) {
currentTrack = SonosXMLParser.getMetaDataFromXML((String)value.getValue());
}
} catch (SAXException e) {
logger.error("Could not parse MetaData from String {}",value.getValue().toString());
}
return currentTrack;
} else {
return null;
}
} else {
return null;
}
}
public SonosMetaData getEnqueuedTransportURIMetaData(){
if(stateMap != null && isConfigured()) {
StateVariableValue value = stateMap.get("EnqueuedTransportURIMetaData");
SonosMetaData currentTrack = null;
if(value != null) {
try {
if(((String)value.getValue()).length()!=0) {
currentTrack = SonosXMLParser.getMetaDataFromXML((String)value.getValue());
}
} catch (SAXException e) {
logger.error("Could not parse MetaData from String {}",value.getValue().toString());
}
return currentTrack;
} else {
return null;
}
} else {
return null;
}
}
public String getCurrentVolume(){
if(stateMap != null && isConfigured()) {
StateVariableValue status = stateMap.get("VolumeMaster");
return status.getValue().toString();
} else {
return null;
}
}
protected List<SonosEntry> getEntries(String type, String filter){
List<SonosEntry> resultList = null;
if(isConfigured()) {
long startAt = 0;
Service service = device.findService(new UDAServiceId("ContentDirectory"));
Action action = service.getAction("Browse");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("ObjectID",type);
invocation.setInput("BrowseFlag","BrowseDirectChildren");
invocation.setInput("Filter", filter);
invocation.setInput("StartingIndex",new UnsignedIntegerFourBytes(startAt));
invocation.setInput("RequestedCount",new UnsignedIntegerFourBytes( 200));
invocation.setInput("SortCriteria","");
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
// Execute this action synchronously
new ActionCallback.Default(invocation, upnpService.getControlPoint()).run();
Long totalMatches = ((UnsignedIntegerFourBytes) invocation.getOutput("TotalMatches").getValue()).getValue();
Long initialNumberReturned = ((UnsignedIntegerFourBytes) invocation.getOutput("NumberReturned").getValue()).getValue();
String initialResult = (String) invocation.getOutput("Result").getValue();
try {
resultList = SonosXMLParser.getEntriesFromString(initialResult);
} catch (SAXException e) {
logger.error("Could not parse Entries from String {}",initialResult);
}
startAt = startAt + initialNumberReturned;
while(startAt<totalMatches){
invocation = new ActionInvocation(action);
try {
invocation.setInput("ObjectID",type);
invocation.setInput("BrowseFlag","BrowseDirectChildren");
invocation.setInput("Filter", filter);
invocation.setInput("StartingIndex",new UnsignedIntegerFourBytes(startAt));
invocation.setInput("RequestedCount",new UnsignedIntegerFourBytes( 200));
invocation.setInput("SortCriteria","");
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
// Execute this action synchronously
new ActionCallback.Default(invocation, upnpService.getControlPoint()).run();
String result = (String) invocation.getOutput("Result").getValue();
int numberReturned = (Integer) invocation.getOutput("NumberReturned").getValue();
try {
resultList.addAll(SonosXMLParser.getEntriesFromString(result));
} catch (SAXException e) {
logger.error("Could not parse Entries from String {}",result);
}
startAt = startAt + numberReturned;
}
}
return resultList;
}
public List<SonosEntry> getArtists( String filter){
return getEntries("A:",filter);
}
public List<SonosEntry> getArtists(){
return getEntries("A:","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosEntry> getAlbums(String filter){
return getEntries("A:ALBUM",filter);
}
public List<SonosEntry> getAlbums(){
return getEntries("A:ALBUM","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosEntry> getTracks( String filter){
return getEntries("A:TRACKS",filter);
}
public List<SonosEntry> getTracks(){
return getEntries("A:TRACKS","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosEntry> getQueue(String filter){
return getEntries("Q:0",filter);
}
public List<SonosEntry> getQueue(){
return getEntries("Q:0","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosEntry> getPlayLists(String filter){
return getEntries("SQ:",filter);
}
public List<SonosEntry> getPlayLists(){
return getEntries("SQ:","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosEntry> getFavoriteRadios(String filter){
return getEntries("R:0/0",filter);
}
public List<SonosEntry> getFavoriteRadios(){
return getEntries("R:0/0","dc:title,res,dc:creator,upnp:artist,upnp:album");
}
public List<SonosAlarm> getCurrentAlarmList(){
List<SonosAlarm> sonosAlarms = null;
if(isConfigured()) {
Service service = device.findService(new UDAServiceId("AlarmClock"));
Action action = service.getAction("ListAlarms");
ActionInvocation invocation = new ActionInvocation(action);
executeActionInvocation(invocation);
try {
sonosAlarms = SonosXMLParser.getAlarmsFromStringResult(invocation.getOutput("CurrentAlarmList").toString());
} catch (SAXException e) {
logger.error("Could not parse Alarms from String {}",invocation.getOutput("CurrentAlarmList").toString());
}
}
return sonosAlarms;
}
public boolean updateAlarm(SonosAlarm alarm) {
if(alarm != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("AlarmClock"));
Action action = service.getAction("ListAlarms");
ActionInvocation invocation = new ActionInvocation(action);
DateTimeFormatter formatter = DateTimeFormat.forPattern("HH:mm:ss");
PeriodFormatter pFormatter= new PeriodFormatterBuilder()
.printZeroAlways()
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
try {
invocation.setInput("ID",Integer.toString(alarm.getID()));
invocation.setInput("StartLocalTime",formatter.print(alarm.getStartTime()));
invocation.setInput("Duration",pFormatter.print(alarm.getDuration()));
invocation.setInput("Recurrence",alarm.getRecurrence());
invocation.setInput("RoomUUID",alarm.getRoomUUID());
invocation.setInput("ProgramURI",alarm.getProgramURI());
invocation.setInput("ProgramMetaData",alarm.getProgramMetaData());
invocation.setInput("PlayMode",alarm.getPlayMode());
invocation.setInput("Volume",Integer.toString(alarm.getVolume()));
if(alarm.getIncludeLinkedZones()) {
invocation.setInput("IncludeLinkedZones","1");
} else {
invocation.setInput("IncludeLinkedZones","0");
}
if(alarm.getEnabled()) {
invocation.setInput("Enabled", "1");
} else {
invocation.setInput("Enabled", "0");
}
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
}
else {
return false;
}
}
public boolean setAlarm(String alarmSwitch) {
if(alarmSwitch.equals("ON") || alarmSwitch.equals("OPEN") || alarmSwitch.equals("UP") ) {
return setAlarm(true);
} else
if(alarmSwitch.equals("OFF") || alarmSwitch.equals("CLOSED") || alarmSwitch.equals("DOWN") ) {
return setAlarm(false);
} else {
return false;
}
}
public boolean setAlarm(boolean alarmSwitch) {
List<SonosAlarm> sonosAlarms = getCurrentAlarmList();
if(isConfigured()) {
// find the nearest alarm - take the current time from the Sonos System, not the system where openhab is running
String currentLocalTime = getTime();
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime currentDateTime = fmt.parseDateTime(currentLocalTime);
Duration shortestDuration = Period.days(10).toStandardDuration();
SonosAlarm firstAlarm = null;
for(SonosAlarm anAlarm : sonosAlarms) {
Duration duration = new Duration(currentDateTime,anAlarm.getStartTime());
if(anAlarm.getStartTime().isBefore(currentDateTime.plus(shortestDuration)) && anAlarm.getRoomUUID().equals(udn.getIdentifierString())) {
shortestDuration = duration;
firstAlarm = anAlarm;
}
}
// Set the Alarm
if(firstAlarm != null) {
if(alarmSwitch) {
firstAlarm.setEnabled(true);
} else {
firstAlarm.setEnabled(false);
}
return updateAlarm(firstAlarm);
} else {
return false;
}
} else {
return false;
}
}
public boolean snoozeAlarm(int minutes){
if(isAlarmRunning() && isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("SnoozeAlarm");
ActionInvocation invocation = new ActionInvocation(action);
Period snoozePeriod = Period.minutes(minutes);
PeriodFormatter pFormatter= new PeriodFormatterBuilder()
.printZeroAlways()
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
try {
invocation.setInput("Duration",pFormatter.print(snoozePeriod));
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
logger.warn("There is no alarm running on {} ",this);
return false;
}
}
public boolean publicAddress(){
//check if sourcePlayer has a line-in connected
if(isLineInConnected() && isConfigured()) {
//first remove this player from its own group if any
becomeStandAlonePlayer();
List<SonosZoneGroup> currentSonosZoneGroups = new ArrayList<SonosZoneGroup>(sonosBinding.getSonosZoneGroups().size());
for(SonosZoneGroup grp : sonosBinding.getSonosZoneGroups()){
currentSonosZoneGroups.add((SonosZoneGroup) grp.clone());
}
//add all other players to this new group
for(SonosZoneGroup group : currentSonosZoneGroups){
for(String player : group.getMembers()){
SonosZonePlayer somePlayer = sonosBinding.getPlayerForID(player);
if(somePlayer != this){
somePlayer.becomeStandAlonePlayer();
somePlayer.stop();
addMember(somePlayer);
}
}
}
//set the URI of the group to the line-in
//TODO : check if this needs to be set on the group coordinator or can be done on any member
SonosZonePlayer coordinator = getCoordinator();
SonosEntry entry = new SonosEntry("", "", "", "", "", "", "", "x-rincon-stream:"+udn.getIdentifierString());
coordinator.setCurrentURI(entry);
coordinator.play();
return true;
} else {
logger.warn("Line-in of {} is not connected",this);
return false;
}
}
public boolean saveQueue(String name, String queueID) {
if(name != null && queueID != null && isConfigured()) {
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("SaveQueue");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("Title", name);
invocation.setInput("ObjectID", queueID);
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}",ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",ex.getMessage());
}
executeActionInvocation(invocation);
return true;
} else {
return false;
}
}
/**
* Play a given url to music in one of the music libraries.
*
* @param url in the format of //host/folder/filename.mp3
* @return true if the url started to play
*/
public boolean playURI(String url) {
if (!isConfigured) {
return false;
}
SonosZonePlayer coordinator = sonosBinding
.getCoordinatorForZonePlayer(this);
// stop whatever is currently playing
coordinator.stop();
// clear any tracks which are pending in the queue
coordinator.removeAllTracksFromQueue();
// add the new track we want to play to the queue
if (!url.startsWith("x-")) {
// default to file based url
url = "x-file-cifs:" + url;
}
coordinator.addURIToQueue(url, "", 0, true);
// set the current playlist to our new queue
coordinator.setCurrentURI("x-rincon-queue:" + udn.getIdentifierString()
+ "#0", "");
// take the system off mute
coordinator.setMute("OFF");
// start jammin'
return coordinator.play();
}
/**
* Play music from the line-in of the Player given its name or UDN
*
* @param udn
* @return true if the sonos device started to play
*/
public boolean playLineIn(String remotePlayerName) {
if (!isConfigured) {
return false;
}
SonosZonePlayer coordinator = sonosBinding.getCoordinatorForZonePlayer(this);
SonosZonePlayer remotePlayer = sonosBinding.getPlayerForID(remotePlayerName);
// stop whatever is currently playing
coordinator.stop();
// set the
coordinator.setCurrentURI("x-rincon-stream:" + remotePlayer.getUdn().getIdentifierString(), "");
// take the system off mute
coordinator.setMute("OFF");
// start jammin'
return coordinator.play();
}
/**
* Clear all scheduled music from the current queue.
*
* @return true if no error occurred.
*/
public boolean removeAllTracksFromQueue() {
if (!isConfigured) {
return false;
}
Service service = device.findService(new UDAServiceId("AVTransport"));
Action action = service.getAction("RemoveAllTracksFromQueue");
ActionInvocation invocation = new ActionInvocation(action);
try {
invocation.setInput("InstanceID", "0");
} catch (InvalidValueException ex) {
logger.error("Action Invalid Value Exception {}", ex.getMessage());
} catch (NumberFormatException ex) {
logger.error("Action Invalid Value Format Exception {}",
ex.getMessage());
}
executeActionInvocation(invocation);
return true;
}
/**
* Save the state (track, position etc) of the Sonos Zone player.
*
* @return true if no error occurred.
*/
protected boolean saveState() {
synchronized (this) {
savedState = sonosBinding.new SonosZonePlayerState();
String currentURI = getCurrentURI();
if (currentURI != null) {
if (currentURI.contains("x-sonosapi-stream:")) {
// we are streaming music
SonosMetaData track = getTrackMetadata();
SonosMetaData current = getCurrentURIMetadata();
if (track != null) {
savedState.entry = new SonosEntry("",
current.getTitle(), "", "",
track.getAlbumArtUri(), "",
current.getUpnpClass(), currentURI);
}
} else if (currentURI.contains("x-rincon:")) {
// we are a slave to some coordinator
savedState.entry = new SonosEntry("", "", "", "",
"", "", "", currentURI);
} else if (currentURI.contains("x-rincon-stream:")) {
// we are streaming from the Line In connection
savedState.entry = new SonosEntry("", "", "", "",
"", "", "", currentURI);
} else if (currentURI.contains("x-rincon-queue:")) {
// we are playing something that sits in the queue
SonosMetaData queued = getEnqueuedTransportURIMetaData();
if (queued != null) {
savedState.track = getCurrenTrackNr();
if (queued.getUpnpClass().contains(
"object.container.playlistContainer")) {
// we are playing a real 'saved' playlist
List<SonosEntry> playLists = getPlayLists();
for (SonosEntry someList : playLists) {
if (someList.getTitle().equals(
queued.getTitle())) {
savedState.entry = new SonosEntry(
someList.getId(),
someList.getTitle(),
someList.getParentId(), "",
"", "",
someList.getUpnpClass(),
someList.getRes());
break;
}
}
} else if (queued.getUpnpClass().contains(
"object.container")) {
// we are playing some other sort of
// 'container' - we will save that to a
// playlist for our convenience
logger.debug(
"Save State for a container of type {}",
queued.getUpnpClass());
// save the playlist
String existingList = "";
List<SonosEntry> playLists = getPlayLists();
for (SonosEntry someList : playLists) {
if (someList.getTitle().equals(
"openHAB-" + getUdn())) {
existingList = someList.getId();
break;
}
}
saveQueue(
"openHAB-" + getUdn(),
existingList);
// get all the playlists and a ref to our
// saved list
playLists = getPlayLists();
for (SonosEntry someList : playLists) {
if (someList.getTitle().equals(
"openHAB-" + getUdn())) {
savedState.entry = new SonosEntry(
someList.getId(),
someList.getTitle(),
someList.getParentId(), "",
"", "",
someList.getUpnpClass(),
someList.getRes());
break;
}
}
}
} else {
savedState.entry = new SonosEntry("", "", "",
"", "", "", "", "x-rincon-queue:"
+ getUdn()
.getIdentifierString()
+ "#0");
}
}
savedState.transportState = getTransportState();
savedState.volume = getCurrentVolume();
savedState.relTime = getPosition();
} else {
savedState.entry = null;
}
return true;
}
}
/**
* Restore the state (track, position etc) of the Sonos Zone player.
*
* @return true if no error occurred.
*/
protected boolean restoreState() {
synchronized (this) {
if (savedState != null) {
// put settings back
setVolume(savedState.volume);
if (isCoordinator()) {
if (savedState.entry != null) {
// check if we have a playlist to deal with
if (savedState.entry
.getUpnpClass()
.contains(
"object.container.playlistContainer")) {
addURIToQueue(
savedState.entry.getRes(),
SonosXMLParser
.compileMetadataString(savedState.entry),
0, true);
SonosEntry entry = new SonosEntry(
"",
"",
"",
"",
"",
"",
"",
"x-rincon-queue:"
+ getUdn()
.getIdentifierString()
+ "#0");
setCurrentURI(entry);
setPositionTrack(savedState.track);
} else {
setCurrentURI(savedState.entry);
setPosition(savedState.relTime);
}
if (savedState.transportState
.equals("PLAYING")) {
play();
} else if (savedState.transportState
.equals("STOPPED")) {
stop();
} else if (savedState.transportState
.equals("PAUSED_PLAYBACK")) {
pause();
}
}
}
}
return true;
}
}
}