/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* 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.eclipse.smarthome.binding.sonos.internal;
import static org.eclipse.smarthome.binding.sonos.config.ZonePlayerConfiguration.UDN;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.smarthome.binding.sonos.SonosBindingConstants;
import org.eclipse.smarthome.binding.sonos.handler.ZonePlayerHandler;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.config.discovery.DiscoveryServiceRegistry;
import org.eclipse.smarthome.core.audio.AudioHTTPServer;
import org.eclipse.smarthome.core.audio.AudioSink;
import org.eclipse.smarthome.core.net.HttpServiceUtil;
import org.eclipse.smarthome.core.net.NetUtil;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandlerFactory;
import org.eclipse.smarthome.core.thing.binding.ThingHandler;
import org.eclipse.smarthome.io.transport.upnp.UpnpIOService;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SonosHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Karel Goderis - Initial contribution
*/
public class SonosHandlerFactory extends BaseThingHandlerFactory {
private Logger logger = LoggerFactory.getLogger(SonosHandlerFactory.class);
private UpnpIOService upnpIOService;
private DiscoveryServiceRegistry discoveryServiceRegistry;
private AudioHTTPServer audioHTTPServer;
private Map<String, ServiceRegistration<AudioSink>> audioSinkRegistrations = new ConcurrentHashMap<>();
// optional OPML URL that can be configured through configuration admin
private String opmlUrl = null;
// url (scheme+server+port) to use for playing notification sounds
private String callbackUrl = null;
@Override
protected void activate(ComponentContext componentContext) {
super.activate(componentContext);
Dictionary<String, Object> properties = componentContext.getProperties();
opmlUrl = (String) properties.get("opmlUrl");
callbackUrl = (String) properties.get("callbackUrl");
};
@Override
public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID,
ThingUID bridgeUID) {
if (SonosBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
ThingUID sonosDeviceUID = getPlayerUID(thingTypeUID, thingUID, configuration);
logger.debug("Creating a sonos thing with ID '{}'", sonosDeviceUID);
return super.createThing(thingTypeUID, configuration, sonosDeviceUID, null);
}
throw new IllegalArgumentException(
"The thing type " + thingTypeUID + " is not supported by the sonos binding.");
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SonosBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SonosBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
logger.debug("Creating a ZonePlayerHandler for thing '{}' with UDN '{}'", thing.getUID(),
thing.getConfiguration().get(UDN));
ZonePlayerHandler handler = new ZonePlayerHandler(thing, upnpIOService, discoveryServiceRegistry, opmlUrl);
// register the speaker as an audio sink
String callbackUrl = createCallbackUrl();
SonosAudioSink audioSink = new SonosAudioSink(handler, audioHTTPServer, callbackUrl);
@SuppressWarnings("unchecked")
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
.registerService(AudioSink.class.getName(), audioSink, new Hashtable<String, Object>());
audioSinkRegistrations.put(thing.getUID().toString(), reg);
return handler;
}
return null;
}
private String createCallbackUrl() {
if (callbackUrl != null) {
return callbackUrl;
} else {
final String ipAddress = NetUtil.getLocalIpv4HostAddress();
if (ipAddress == null) {
logger.warn("No network interface could be found.");
return null;
}
// we do not use SSL as it can cause certificate validation issues.
final int port = HttpServiceUtil.getHttpServicePort(bundleContext);
if (port == -1) {
logger.warn("Cannot find port of the http service.");
return null;
}
return "http://" + ipAddress + ":" + port;
}
}
@Override
public void unregisterHandler(Thing thing) {
super.unregisterHandler(thing);
ServiceRegistration<AudioSink> reg = audioSinkRegistrations.get(thing.getUID().toString());
if (reg != null) {
reg.unregister();
}
}
private ThingUID getPlayerUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration) {
String udn = (String) configuration.get(UDN);
if (thingUID == null) {
thingUID = new ThingUID(thingTypeUID, udn);
}
return thingUID;
}
protected void setUpnpIOService(UpnpIOService upnpIOService) {
this.upnpIOService = upnpIOService;
}
protected void unsetUpnpIOService(UpnpIOService upnpIOService) {
this.upnpIOService = null;
}
protected void setDiscoveryServiceRegistry(DiscoveryServiceRegistry discoveryServiceRegistry) {
this.discoveryServiceRegistry = discoveryServiceRegistry;
}
protected void unsetDiscoveryServiceRegistry(DiscoveryServiceRegistry discoveryServiceRegistry) {
this.discoveryServiceRegistry = null;
}
protected void setAudioHTTPServer(AudioHTTPServer audioHTTPServer) {
this.audioHTTPServer = audioHTTPServer;
}
protected void unsetAudioHTTPServer(AudioHTTPServer audioHTTPServer) {
this.audioHTTPServer = null;
}
}