mirror of
https://github.com/danieldemus/openhab-addons
synced 2026-07-31 13:34:22 +02:00
[squeezebox] Fix properties for manually configured Things (#18347)
* Refactor discovery Add null annotations and fix warnings Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> * Fix property initialization for manually configured player Things Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk> --------- Signed-off-by: Jacob Laursen <jacob-github@vindvejr.dk>
This commit is contained in:
+8
-6
@@ -17,11 +17,11 @@ import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
|
||||
import org.openhab.core.audio.AudioFormat;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.audio.AudioSinkSync;
|
||||
import org.openhab.core.audio.AudioStream;
|
||||
import org.openhab.core.audio.FileAudioStream;
|
||||
@@ -36,11 +36,12 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This makes a SqueezeBox Player serve as an {@link AudioSink}-
|
||||
* This makes a SqueezeBox Player serve as an {@link org.openhab.core.audio.AudioSink}
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
* @author Mark Hilbush - Add callbackUrl
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxAudioSink extends AudioSinkSync {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxAudioSink.class);
|
||||
|
||||
@@ -50,13 +51,13 @@ public class SqueezeBoxAudioSink extends AudioSinkSync {
|
||||
// Needed because Squeezebox does multiple requests for the stream
|
||||
private static final int STREAM_TIMEOUT = 10;
|
||||
|
||||
private String callbackUrl;
|
||||
private @Nullable String callbackUrl;
|
||||
|
||||
private AudioHTTPServer audioHTTPServer;
|
||||
private SqueezeBoxPlayerHandler playerHandler;
|
||||
|
||||
public SqueezeBoxAudioSink(SqueezeBoxPlayerHandler playerHandler, AudioHTTPServer audioHTTPServer,
|
||||
String callbackUrl) {
|
||||
@Nullable String callbackUrl) {
|
||||
this.playerHandler = playerHandler;
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.callbackUrl = callbackUrl;
|
||||
@@ -71,12 +72,12 @@ public class SqueezeBoxAudioSink extends AudioSinkSync {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(Locale locale) {
|
||||
public @Nullable String getLabel(@Nullable Locale locale) {
|
||||
return playerHandler.getThing().getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processSynchronously(AudioStream audioStream)
|
||||
public void processSynchronously(@Nullable AudioStream audioStream)
|
||||
throws UnsupportedAudioFormatException, UnsupportedAudioStreamException {
|
||||
if (audioStream == null) {
|
||||
return;
|
||||
@@ -105,6 +106,7 @@ public class SqueezeBoxAudioSink extends AudioSinkSync {
|
||||
|
||||
// Form the URL for streaming the notification from the OH web server
|
||||
// Use the callback URL if it is set in the binding configuration
|
||||
String callbackUrl = this.callbackUrl;
|
||||
String host = callbackUrl == null || callbackUrl.isEmpty() ? playerHandler.getHostAndPort()
|
||||
: callbackUrl;
|
||||
if (host == null) {
|
||||
|
||||
+5
@@ -74,4 +74,9 @@ public class SqueezeBoxBindingConstants {
|
||||
public static final String CHANNEL_FAVORITES_PLAY = "playFavorite";
|
||||
public static final String CHANNEL_RATE = "rate";
|
||||
public static final String CHANNEL_SLEEP = "sleep";
|
||||
|
||||
// List of all properties
|
||||
public static final String PROPERTY_NAME = "name";
|
||||
public static final String PROPERTY_UID = "uid";
|
||||
public static final String PROPERTY_IP = "ip";
|
||||
}
|
||||
|
||||
+22
-74
@@ -15,7 +15,6 @@ package org.openhab.binding.squeezebox.internal;
|
||||
import static org.openhab.binding.squeezebox.internal.SqueezeBoxBindingConstants.*;
|
||||
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -23,19 +22,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.squeezebox.internal.discovery.SqueezeBoxPlayerDiscoveryParticipant;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerEventListener;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxServerHandler;
|
||||
import org.openhab.core.audio.AudioHTTPServer;
|
||||
import org.openhab.core.audio.AudioSink;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
@@ -56,6 +53,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Add callbackUrl
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.squeezebox")
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxHandlerFactory extends BaseThingHandlerFactory {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxHandlerFactory.class);
|
||||
|
||||
@@ -64,8 +62,6 @@ public class SqueezeBoxHandlerFactory extends BaseThingHandlerFactory {
|
||||
SqueezeBoxPlayerHandler.SUPPORTED_THING_TYPES_UIDS.stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
private final AudioHTTPServer audioHTTPServer;
|
||||
private final NetworkAddressService networkAddressService;
|
||||
private final SqueezeBoxStateDescriptionOptionsProvider stateDescriptionProvider;
|
||||
@@ -73,12 +69,12 @@ public class SqueezeBoxHandlerFactory extends BaseThingHandlerFactory {
|
||||
private Map<String, ServiceRegistration<AudioSink>> audioSinkRegistrations = new ConcurrentHashMap<>();
|
||||
|
||||
// Callback url (scheme+server+port) to use for playing notification sounds
|
||||
private String callbackUrl = null;
|
||||
private @Nullable String callbackUrl = null;
|
||||
|
||||
@Activate
|
||||
public SqueezeBoxHandlerFactory(@Reference AudioHTTPServer audioHTTPServer,
|
||||
@Reference NetworkAddressService networkAddressService,
|
||||
@Reference SqueezeBoxStateDescriptionOptionsProvider stateDescriptionProvider) {
|
||||
public SqueezeBoxHandlerFactory(final @Reference AudioHTTPServer audioHTTPServer,
|
||||
final @Reference NetworkAddressService networkAddressService,
|
||||
final @Reference SqueezeBoxStateDescriptionOptionsProvider stateDescriptionProvider) {
|
||||
this.audioHTTPServer = audioHTTPServer;
|
||||
this.networkAddressService = networkAddressService;
|
||||
this.stateDescriptionProvider = stateDescriptionProvider;
|
||||
@@ -97,28 +93,25 @@ public class SqueezeBoxHandlerFactory extends BaseThingHandlerFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(SQUEEZEBOXSERVER_THING_TYPE)) {
|
||||
logger.trace("creating handler for bridge thing {}", thing);
|
||||
SqueezeBoxServerHandler bridge = new SqueezeBoxServerHandler((Bridge) thing);
|
||||
registerSqueezeBoxPlayerDiscoveryService(bridge);
|
||||
return bridge;
|
||||
if (SQUEEZEBOXSERVER_THING_TYPE.equals(thingTypeUID)) {
|
||||
logger.trace("Creating handler for bridge thing {}", thing);
|
||||
return new SqueezeBoxServerHandler((Bridge) thing);
|
||||
}
|
||||
|
||||
if (thingTypeUID.equals(SQUEEZEBOXPLAYER_THING_TYPE)) {
|
||||
logger.trace("creating handler for player thing {}", thing);
|
||||
if (SQUEEZEBOXPLAYER_THING_TYPE.equals(thingTypeUID)) {
|
||||
logger.trace("Creating handler for player thing {}", thing);
|
||||
SqueezeBoxPlayerHandler playerHandler = new SqueezeBoxPlayerHandler(thing, createCallbackUrl(),
|
||||
stateDescriptionProvider);
|
||||
|
||||
// Register the player as an audio sink
|
||||
logger.trace("Registering an audio sink for player thing {}", thing.getUID());
|
||||
SqueezeBoxAudioSink audioSink = new SqueezeBoxAudioSink(playerHandler, audioHTTPServer, callbackUrl);
|
||||
@SuppressWarnings("unchecked")
|
||||
ServiceRegistration<AudioSink> reg = (ServiceRegistration<AudioSink>) bundleContext
|
||||
.registerService(AudioSink.class.getName(), audioSink, new Hashtable<>());
|
||||
audioSinkRegistrations.put(thing.getUID().toString(), reg);
|
||||
ServiceRegistration<AudioSink> audioSinkRegistration = bundleContext.registerService(AudioSink.class,
|
||||
audioSink, new Hashtable<>());
|
||||
audioSinkRegistrations.put(thing.getUID().toString(), audioSinkRegistration);
|
||||
|
||||
return playerHandler;
|
||||
}
|
||||
@@ -126,72 +119,27 @@ public class SqueezeBoxHandlerFactory extends BaseThingHandlerFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds SqueezeBoxServerHandlers to the discovery service to find SqueezeBox
|
||||
* Players
|
||||
*
|
||||
* @param squeezeBoxServerHandler
|
||||
*/
|
||||
private synchronized void registerSqueezeBoxPlayerDiscoveryService(
|
||||
SqueezeBoxServerHandler squeezeBoxServerHandler) {
|
||||
logger.trace("registering player discovery service");
|
||||
|
||||
SqueezeBoxPlayerDiscoveryParticipant discoveryService = new SqueezeBoxPlayerDiscoveryParticipant(
|
||||
squeezeBoxServerHandler);
|
||||
|
||||
// Register the PlayerListener with the SqueezeBoxServerHandler
|
||||
squeezeBoxServerHandler.registerSqueezeBoxPlayerListener(discoveryService);
|
||||
|
||||
// Register the service, then add the service to the ServiceRegistration map
|
||||
discoveryServiceRegs.put(squeezeBoxServerHandler.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof SqueezeBoxServerHandler serverHandler) {
|
||||
logger.trace("removing handler for bridge thing {}", thingHandler.getThing());
|
||||
|
||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
logger.trace("unregistering player discovery service");
|
||||
|
||||
// Get the discovery service object and use it to cancel the RequestPlayerJob
|
||||
SqueezeBoxPlayerDiscoveryParticipant discoveryService = (SqueezeBoxPlayerDiscoveryParticipant) bundleContext
|
||||
.getService(serviceReg.getReference());
|
||||
discoveryService.cancelRequestPlayerJob();
|
||||
|
||||
// Unregister the PlayerListener from the SqueezeBoxServerHandler
|
||||
serverHandler.unregisterSqueezeBoxPlayerListener(
|
||||
(SqueezeBoxPlayerEventListener) bundleContext.getService(serviceReg.getReference()));
|
||||
|
||||
// Unregister the PlayerListener service
|
||||
serviceReg.unregister();
|
||||
|
||||
// Remove the service from the ServiceRegistration map
|
||||
discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
}
|
||||
}
|
||||
|
||||
if (thingHandler instanceof SqueezeBoxPlayerHandler playerHandler) {
|
||||
SqueezeBoxServerHandler bridge = playerHandler.getSqueezeBoxServerHandler();
|
||||
if (bridge != null) {
|
||||
// Unregister the player's audio sink
|
||||
logger.trace("Unregistering the audio sync service for player thing {}",
|
||||
thingHandler.getThing().getUID());
|
||||
ServiceRegistration<AudioSink> reg = audioSinkRegistrations
|
||||
.get(thingHandler.getThing().getUID().toString());
|
||||
if (reg != null) {
|
||||
reg.unregister();
|
||||
ServiceRegistration<AudioSink> audioSinkRegistration = audioSinkRegistrations
|
||||
.remove(thingHandler.getThing().getUID().toString());
|
||||
if (audioSinkRegistration != null) {
|
||||
audioSinkRegistration.unregister();
|
||||
}
|
||||
|
||||
logger.trace("removing handler for player thing {}", thingHandler.getThing());
|
||||
logger.trace("Removing handler for player thing {}", thingHandler.getThing());
|
||||
bridge.removePlayerCache(playerHandler.getMac());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String createCallbackUrl() {
|
||||
private @Nullable String createCallbackUrl() {
|
||||
final String ipAddress = networkAddressService.getPrimaryIpv4HostAddress();
|
||||
if (ipAddress == null) {
|
||||
logger.warn("No network interface could be found.");
|
||||
|
||||
+6
-2
@@ -12,6 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration for a player
|
||||
*
|
||||
@@ -19,11 +22,12 @@ package org.openhab.binding.squeezebox.internal.config;
|
||||
* @author Mark Hilbush - Convert sound notification volume from channel to config parameter
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxPlayerConfig {
|
||||
/**
|
||||
* MAC address of player
|
||||
*/
|
||||
public String mac;
|
||||
public String mac = "";
|
||||
|
||||
/**
|
||||
* Number of seconds to wait to time out a notification
|
||||
@@ -33,5 +37,5 @@ public class SqueezeBoxPlayerConfig {
|
||||
/**
|
||||
* Volume used for playing notifications
|
||||
*/
|
||||
public Integer notificationVolume;
|
||||
public @Nullable Integer notificationVolume;
|
||||
}
|
||||
|
||||
+58
-24
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.discovery;
|
||||
|
||||
import static org.openhab.binding.squeezebox.internal.SqueezeBoxBindingConstants.SQUEEZEBOXPLAYER_THING_TYPE;
|
||||
import static org.openhab.binding.squeezebox.internal.SqueezeBoxBindingConstants.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -20,15 +20,20 @@ import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayer;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerEventListener;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxPlayerHandler;
|
||||
import org.openhab.binding.squeezebox.internal.handler.SqueezeBoxServerHandler;
|
||||
import org.openhab.binding.squeezebox.internal.model.Favorite;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.ServiceScope;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -42,63 +47,92 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Added event to update favorites list
|
||||
*
|
||||
*/
|
||||
public class SqueezeBoxPlayerDiscoveryParticipant extends AbstractDiscoveryService
|
||||
@Component(scope = ServiceScope.PROTOTYPE, service = SqueezeBoxPlayerDiscoveryService.class)
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxPlayerDiscoveryService extends AbstractThingHandlerDiscoveryService<SqueezeBoxServerHandler>
|
||||
implements SqueezeBoxPlayerEventListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxPlayerDiscoveryParticipant.class);
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxPlayerDiscoveryService.class);
|
||||
|
||||
private static final int TIMEOUT = 60;
|
||||
private static final int TTL = 60;
|
||||
|
||||
private SqueezeBoxServerHandler squeezeBoxServerHandler;
|
||||
private ScheduledFuture<?> requestPlayerJob;
|
||||
private @Nullable ScheduledFuture<?> requestPlayerJob;
|
||||
|
||||
/**
|
||||
* Discovers SqueezeBox Players attached to a SqueezeBox Server
|
||||
*
|
||||
* @param squeezeBoxServerHandler
|
||||
*/
|
||||
public SqueezeBoxPlayerDiscoveryParticipant(SqueezeBoxServerHandler squeezeBoxServerHandler) {
|
||||
super(SqueezeBoxPlayerHandler.SUPPORTED_THING_TYPES_UIDS, TIMEOUT, true);
|
||||
this.squeezeBoxServerHandler = squeezeBoxServerHandler;
|
||||
public SqueezeBoxPlayerDiscoveryService() {
|
||||
super(SqueezeBoxServerHandler.class, SqueezeBoxPlayerHandler.SUPPORTED_THING_TYPES_UIDS, TIMEOUT, true);
|
||||
setupRequestPlayerJob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
|
||||
// Register the PlayerListener with the SqueezeBoxServerHandler
|
||||
thingHandler.registerSqueezeBoxPlayerListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// Unregister the PlayerListener from the SqueezeBoxServerHandler
|
||||
thingHandler.unregisterSqueezeBoxPlayerListener(this);
|
||||
|
||||
cancelRequestPlayerJob();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("startScan invoked in SqueezeBoxPlayerDiscoveryParticipant");
|
||||
this.squeezeBoxServerHandler.requestPlayers();
|
||||
this.squeezeBoxServerHandler.requestFavorites();
|
||||
thingHandler.requestPlayers();
|
||||
thingHandler.requestFavorites();
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows request player job to be canceled when server handler is removed
|
||||
*/
|
||||
public void cancelRequestPlayerJob() {
|
||||
private void cancelRequestPlayerJob() {
|
||||
logger.debug("canceling RequestPlayerJob");
|
||||
ScheduledFuture<?> requestPlayerJob = this.requestPlayerJob;
|
||||
if (requestPlayerJob != null) {
|
||||
requestPlayerJob.cancel(true);
|
||||
requestPlayerJob = null;
|
||||
this.requestPlayerJob = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerAdded(SqueezeBoxPlayer player) {
|
||||
ThingUID bridgeUID = squeezeBoxServerHandler.getThing().getUID();
|
||||
ThingUID bridgeUID = thingHandler.getThing().getUID();
|
||||
|
||||
ThingUID thingUID = new ThingUID(SQUEEZEBOXPLAYER_THING_TYPE, bridgeUID, player.macAddress.replace(":", ""));
|
||||
|
||||
if (!playerThingExists(thingUID)) {
|
||||
logger.debug("player added {} : {} ", player.macAddress, player.name);
|
||||
logger.debug("Player added {}: {} ", player.macAddress, player.name);
|
||||
|
||||
Map<String, Object> properties = new HashMap<>(1);
|
||||
String representationPropertyName = "mac";
|
||||
properties.put(representationPropertyName, player.macAddress);
|
||||
|
||||
// Added other properties
|
||||
properties.put("modelId", player.model);
|
||||
properties.put("name", player.name);
|
||||
properties.put("uid", player.uuid);
|
||||
properties.put("ip", player.ipAddr);
|
||||
String model = player.model;
|
||||
if (model != null) {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, model);
|
||||
}
|
||||
String name = player.name;
|
||||
if (name != null) {
|
||||
properties.put(PROPERTY_NAME, name);
|
||||
}
|
||||
String uuid = player.uuid;
|
||||
if (uuid != null) {
|
||||
properties.put(PROPERTY_UID, uuid);
|
||||
}
|
||||
String ipAddr = player.ipAddr;
|
||||
if (ipAddr != null) {
|
||||
properties.put(PROPERTY_IP, ipAddr);
|
||||
}
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
|
||||
.withRepresentationProperty(representationPropertyName).withBridge(bridgeUID).withLabel(player.name)
|
||||
@@ -109,7 +143,7 @@ public class SqueezeBoxPlayerDiscoveryParticipant extends AbstractDiscoveryServi
|
||||
}
|
||||
|
||||
private boolean playerThingExists(ThingUID newThingUID) {
|
||||
return squeezeBoxServerHandler.getThing().getThing(newThingUID) != null;
|
||||
return thingHandler.getThing().getThing(newThingUID) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +152,7 @@ public class SqueezeBoxPlayerDiscoveryParticipant extends AbstractDiscoveryServi
|
||||
private void setupRequestPlayerJob() {
|
||||
logger.debug("Request player job scheduled to run every {} seconds", TTL);
|
||||
requestPlayerJob = scheduler.scheduleWithFixedDelay(() -> {
|
||||
squeezeBoxServerHandler.requestPlayers();
|
||||
thingHandler.requestPlayers();
|
||||
}, 10, TTL, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -228,7 +262,7 @@ public class SqueezeBoxPlayerDiscoveryParticipant extends AbstractDiscoveryServi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buttonsChangeEvent(String mac, String likeCommand, String unlikeCommand) {
|
||||
public void buttonsChangeEvent(String mac, @Nullable String likeCommand, @Nullable String unlikeCommand) {
|
||||
}
|
||||
|
||||
@Override
|
||||
+5
-2
@@ -19,6 +19,8 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.jupnp.model.meta.RemoteDevice;
|
||||
import org.openhab.binding.squeezebox.internal.utils.HttpUtils;
|
||||
import org.openhab.binding.squeezebox.internal.utils.SqueezeBoxCommunicationException;
|
||||
@@ -40,6 +42,7 @@ import org.slf4j.LoggerFactory;
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxServerDiscoveryParticipant implements UpnpDiscoveryParticipant {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxServerDiscoveryParticipant.class);
|
||||
|
||||
@@ -54,7 +57,7 @@ public class SqueezeBoxServerDiscoveryParticipant implements UpnpDiscoveryPartic
|
||||
}
|
||||
|
||||
@Override
|
||||
public DiscoveryResult createResult(RemoteDevice device) {
|
||||
public @Nullable DiscoveryResult createResult(RemoteDevice device) {
|
||||
ThingUID uid = getThingUID(device);
|
||||
if (uid != null) {
|
||||
Map<String, Object> properties = new HashMap<>(3);
|
||||
@@ -98,7 +101,7 @@ public class SqueezeBoxServerDiscoveryParticipant implements UpnpDiscoveryPartic
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThingUID getThingUID(RemoteDevice device) {
|
||||
public @Nullable ThingUID getThingUID(RemoteDevice device) {
|
||||
if (device.getDetails().getFriendlyName() != null) {
|
||||
if (device.getDetails().getModelDetails().getModelName().contains(MODEL_NAME)) {
|
||||
logger.debug("Discovered a {} thing with UDN '{}'", device.getDetails().getFriendlyName(),
|
||||
|
||||
+4
-1
@@ -16,6 +16,8 @@ import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.model.Favorite;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -27,6 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
* @author Mark Hilbush - Added event to update favorites list
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public final class SqueezeBoxNotificationListener implements SqueezeBoxPlayerEventListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxNotificationListener.class);
|
||||
|
||||
@@ -238,7 +241,7 @@ public final class SqueezeBoxNotificationListener implements SqueezeBoxPlayerEve
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buttonsChangeEvent(String mac, String likeCommand, String unlikeCommand) {
|
||||
public void buttonsChangeEvent(String mac, @Nullable String likeCommand, @Nullable String unlikeCommand) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-4
@@ -14,6 +14,7 @@ package org.openhab.binding.squeezebox.internal.handler;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.squeezebox.internal.utils.SqueezeBoxTimeoutException;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.slf4j.Logger;
|
||||
@@ -29,6 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Convert sound notification volume from channel to config parameter
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class SqueezeBoxNotificationPlayer implements Closeable {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxNotificationPlayer.class);
|
||||
|
||||
@@ -61,10 +63,6 @@ class SqueezeBoxNotificationPlayer implements Closeable {
|
||||
}
|
||||
|
||||
void play() throws InterruptedException, SqueezeBoxTimeoutException {
|
||||
if (squeezeBoxServerHandler == null) {
|
||||
logger.warn("Server handler is null");
|
||||
return;
|
||||
}
|
||||
setupPlayerForNotification();
|
||||
addNotificationMessageToPlaylist();
|
||||
playNotification();
|
||||
|
||||
+4
-1
@@ -14,6 +14,8 @@ package org.openhab.binding.squeezebox.internal.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.model.Favorite;
|
||||
|
||||
/**
|
||||
@@ -23,6 +25,7 @@ import org.openhab.binding.squeezebox.internal.model.Favorite;
|
||||
* @author Mark Hilbush - Added durationEvent
|
||||
* @author Mark Hilbush - Added event to update favorites list
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface SqueezeBoxPlayerEventListener {
|
||||
|
||||
void playerAdded(SqueezeBoxPlayer player);
|
||||
@@ -91,7 +94,7 @@ public interface SqueezeBoxPlayerEventListener {
|
||||
|
||||
void sourceChangeEvent(String mac, String source);
|
||||
|
||||
void buttonsChangeEvent(String mac, String likeCommand, String unlikeCommand);
|
||||
void buttonsChangeEvent(String mac, @Nullable String likeCommand, @Nullable String unlikeCommand);
|
||||
|
||||
void connectedStateChangeEvent(String mac, boolean connected);
|
||||
}
|
||||
|
||||
+72
-35
@@ -26,7 +26,8 @@ import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.SqueezeBoxStateDescriptionOptionsProvider;
|
||||
import org.openhab.binding.squeezebox.internal.config.SqueezeBoxPlayerConfig;
|
||||
import org.openhab.binding.squeezebox.internal.model.Favorite;
|
||||
@@ -72,6 +73,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Convert sound notification volume from channel to config parameter
|
||||
* @author Mark Hilbush - Add like/unlike functionality
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxPlayerHandler extends BaseThingHandler implements SqueezeBoxPlayerEventListener {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxPlayerHandler.class);
|
||||
|
||||
@@ -86,17 +88,17 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
/**
|
||||
* Keeps current track time
|
||||
*/
|
||||
private ScheduledFuture<?> timeCounterJob;
|
||||
private @Nullable ScheduledFuture<?> timeCounterJob;
|
||||
|
||||
/**
|
||||
* Local reference to our bridge
|
||||
*/
|
||||
private SqueezeBoxServerHandler squeezeBoxServerHandler;
|
||||
private @Nullable SqueezeBoxServerHandler squeezeBoxServerHandler;
|
||||
|
||||
/**
|
||||
* Our mac address, needed everywhere
|
||||
*/
|
||||
private String mac;
|
||||
private String mac = "";
|
||||
|
||||
/**
|
||||
* The server sends us the current time on play/pause/stop events, we
|
||||
@@ -113,26 +115,27 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
/**
|
||||
* Separate volume level for notifications
|
||||
*/
|
||||
private Integer notificationSoundVolume = null;
|
||||
private @Nullable Integer notificationSoundVolume = null;
|
||||
|
||||
private String callbackUrl;
|
||||
private @Nullable String callbackUrl;
|
||||
|
||||
private SqueezeBoxStateDescriptionOptionsProvider stateDescriptionProvider;
|
||||
|
||||
private static final ExpiringCacheMap<String, RawType> IMAGE_CACHE = new ExpiringCacheMap<>(
|
||||
private static final ExpiringCacheMap<String, @Nullable RawType> IMAGE_CACHE = new ExpiringCacheMap<>(
|
||||
TimeUnit.MINUTES.toMillis(15)); // 15min
|
||||
|
||||
private String likeCommand;
|
||||
private String unlikeCommand;
|
||||
private @Nullable String likeCommand;
|
||||
private @Nullable String unlikeCommand;
|
||||
private boolean connected = false;
|
||||
|
||||
/**
|
||||
* Creates SqueezeBox Player Handler
|
||||
*
|
||||
* @param thing
|
||||
* @param callbackUrl
|
||||
* @param stateDescriptionProvider
|
||||
*/
|
||||
public SqueezeBoxPlayerHandler(@NonNull Thing thing, String callbackUrl,
|
||||
public SqueezeBoxPlayerHandler(Thing thing, @Nullable String callbackUrl,
|
||||
SqueezeBoxStateDescriptionOptionsProvider stateDescriptionProvider) {
|
||||
super(thing);
|
||||
this.callbackUrl = callbackUrl;
|
||||
@@ -142,9 +145,15 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
@Override
|
||||
public void initialize() {
|
||||
mac = getConfig().as(SqueezeBoxPlayerConfig.class).mac;
|
||||
if (mac.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error.mac-not-set");
|
||||
return;
|
||||
}
|
||||
timeCounter();
|
||||
updateThingStatus();
|
||||
logger.debug("player thing {} initialized with mac {}", getThing().getUID(), mac);
|
||||
logger.debug("Player thing {} initialized with mac {}", getThing().getUID(), mac);
|
||||
SqueezeBoxServerHandler squeezeBoxServerHandler = this.squeezeBoxServerHandler;
|
||||
if (squeezeBoxServerHandler != null) {
|
||||
// ensure we get an up-to-date connection state
|
||||
squeezeBoxServerHandler.requestPlayers();
|
||||
@@ -178,20 +187,23 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
@Override
|
||||
public void dispose() {
|
||||
// stop our duration counter
|
||||
ScheduledFuture<?> timeCounterJob = this.timeCounterJob;
|
||||
if (timeCounterJob != null && !timeCounterJob.isCancelled()) {
|
||||
timeCounterJob.cancel(true);
|
||||
timeCounterJob = null;
|
||||
this.timeCounterJob = null;
|
||||
}
|
||||
|
||||
SqueezeBoxServerHandler squeezeBoxServerHandler = this.squeezeBoxServerHandler;
|
||||
if (squeezeBoxServerHandler != null) {
|
||||
squeezeBoxServerHandler.removePlayerCache(mac);
|
||||
}
|
||||
logger.debug("player thing {} disposed for mac {}", getThing().getUID(), mac);
|
||||
logger.debug("Player thing {} disposed for mac {}", getThing().getUID(), mac);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
SqueezeBoxServerHandler squeezeBoxServerHandler = this.squeezeBoxServerHandler;
|
||||
if (squeezeBoxServerHandler == null) {
|
||||
logger.debug("Player {} has no server configured, ignoring command: {}", getThing().getUID(), command);
|
||||
return;
|
||||
@@ -319,9 +331,11 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
squeezeBoxServerHandler.playFavorite(mac, command.toString());
|
||||
break;
|
||||
case CHANNEL_RATE:
|
||||
if (command.equals(OnOffType.ON)) {
|
||||
String likeCommand = this.likeCommand;
|
||||
String unlikeCommand = this.unlikeCommand;
|
||||
if (command.equals(OnOffType.ON) && likeCommand != null) {
|
||||
squeezeBoxServerHandler.rate(mac, likeCommand);
|
||||
} else if (command.equals(OnOffType.OFF)) {
|
||||
} else if (command.equals(OnOffType.OFF) && unlikeCommand != null) {
|
||||
squeezeBoxServerHandler.rate(mac, unlikeCommand);
|
||||
}
|
||||
break;
|
||||
@@ -342,7 +356,30 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
|
||||
@Override
|
||||
public void playerAdded(SqueezeBoxPlayer player) {
|
||||
// Player properties are saved in SqueezeBoxPlayerDiscoveryParticipant
|
||||
if (!isMe(player.macAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String> properties = editProperties();
|
||||
|
||||
String model = player.model;
|
||||
if (model != null) {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, model);
|
||||
}
|
||||
String name = player.name;
|
||||
if (name != null) {
|
||||
properties.put(PROPERTY_NAME, name);
|
||||
}
|
||||
String uuid = player.uuid;
|
||||
if (uuid != null) {
|
||||
properties.put(PROPERTY_UID, uuid);
|
||||
}
|
||||
String ipAddr = player.ipAddr;
|
||||
if (ipAddr != null) {
|
||||
properties.put(PROPERTY_IP, ipAddr);
|
||||
}
|
||||
|
||||
updateProperties(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -482,10 +519,10 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
* @return A RawType object containing the image, null if the content type could not be found or the content type is
|
||||
* not an image.
|
||||
*/
|
||||
private RawType downloadImage(String mac, String url) {
|
||||
private @Nullable RawType downloadImage(String mac, String url) {
|
||||
// Only get the image if this is my PlayerHandler instance
|
||||
if (isMe(mac)) {
|
||||
if (url != null && !url.isEmpty()) {
|
||||
if (!url.isEmpty()) {
|
||||
RawType image = IMAGE_CACHE.putIfAbsentAndGet(url, () -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Trying to download the content of URL {}", sanitizeUrl(url));
|
||||
@@ -500,14 +537,10 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (image == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to download the content of URL {}", sanitizeUrl(url));
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return image;
|
||||
if (image == null && logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to download the content of URL {}", sanitizeUrl(url));
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -536,7 +569,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
/**
|
||||
* Wrap the given RawType and return it as {@link State} or return {@link UnDefType#UNDEF} if the RawType is null.
|
||||
*/
|
||||
private State createImage(RawType image) {
|
||||
private State createImage(@Nullable RawType image) {
|
||||
if (image == null) {
|
||||
return UnDefType.UNDEF;
|
||||
} else {
|
||||
@@ -567,7 +600,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buttonsChangeEvent(String mac, String likeCommand, String unlikeCommand) {
|
||||
public void buttonsChangeEvent(String mac, @Nullable String likeCommand, @Nullable String unlikeCommand) {
|
||||
if (isMe(mac)) {
|
||||
this.likeCommand = likeCommand;
|
||||
this.unlikeCommand = unlikeCommand;
|
||||
@@ -656,7 +689,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
return cachedStateAsInt(CHANNEL_CURRENT_PLAYLIST_REPEAT);
|
||||
}
|
||||
|
||||
private boolean cachedStateAsBoolean(String key, @NonNull State activeState) {
|
||||
private boolean cachedStateAsBoolean(String key, State activeState) {
|
||||
return activeState.equals(stateMap.get(key));
|
||||
}
|
||||
|
||||
@@ -693,8 +726,8 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public SqueezeBoxServerHandler getSqueezeBoxServerHandler() {
|
||||
return this.squeezeBoxServerHandler;
|
||||
public @Nullable SqueezeBoxServerHandler getSqueezeBoxServerHandler() {
|
||||
return squeezeBoxServerHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,7 +736,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
* @return
|
||||
*/
|
||||
public String getMac() {
|
||||
return this.mac;
|
||||
return mac;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -723,6 +756,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
public PercentType getNotificationSoundVolume() {
|
||||
// Get the notification sound volume from this player thing's configuration
|
||||
Integer configNotificationSoundVolume = getConfigAs(SqueezeBoxPlayerConfig.class).notificationVolume;
|
||||
Integer notificationSoundVolume = this.notificationSoundVolume;
|
||||
|
||||
// Determine which volume to use
|
||||
Integer currentNotificationSoundVolume;
|
||||
@@ -740,9 +774,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
* Used by the AudioSink to set the volume level that should be used to play the notification
|
||||
*/
|
||||
public void setNotificationSoundVolume(PercentType newNotificationSoundVolume) {
|
||||
if (newNotificationSoundVolume != null) {
|
||||
notificationSoundVolume = Integer.valueOf(newNotificationSoundVolume.intValue());
|
||||
}
|
||||
notificationSoundVolume = Integer.valueOf(newNotificationSoundVolume.intValue());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -751,6 +783,11 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
public void playNotificationSoundURI(StringType uri) {
|
||||
logger.debug("Play notification sound on player {} at URI {}", mac, uri);
|
||||
|
||||
SqueezeBoxServerHandler squeezeBoxServerHandler = this.squeezeBoxServerHandler;
|
||||
if (squeezeBoxServerHandler == null) {
|
||||
logger.warn("Server handler is null");
|
||||
return;
|
||||
}
|
||||
try (SqueezeBoxNotificationPlayer notificationPlayer = new SqueezeBoxNotificationPlayer(this,
|
||||
squeezeBoxServerHandler, uri)) {
|
||||
notificationPlayer.play();
|
||||
@@ -766,7 +803,7 @@ public class SqueezeBoxPlayerHandler extends BaseThingHandler implements Squeeze
|
||||
/*
|
||||
* Return the IP and port of the OH2 web server
|
||||
*/
|
||||
public String getHostAndPort() {
|
||||
public @Nullable String getHostAndPort() {
|
||||
return callbackUrl;
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -12,12 +12,15 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/***
|
||||
* Enumeration of the play states of a player.
|
||||
*
|
||||
* @author Patrik Gfeller - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
enum SqueezeBoxPlayerPlayState {
|
||||
STOP,
|
||||
PLAY,
|
||||
|
||||
+2
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.handler;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -21,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
* @author Patrik Gfeller - Moved class to its own file.
|
||||
*/
|
||||
@NonNullByDefault
|
||||
class SqueezeBoxPlayerState {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxPlayerState.class);
|
||||
|
||||
|
||||
+60
-40
@@ -27,6 +27,7 @@ import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -39,8 +40,10 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.squeezebox.internal.SqueezeBoxBindingConstants;
|
||||
import org.openhab.binding.squeezebox.internal.config.SqueezeBoxServerConfig;
|
||||
import org.openhab.binding.squeezebox.internal.discovery.SqueezeBoxPlayerDiscoveryService;
|
||||
import org.openhab.binding.squeezebox.internal.dto.ButtonDTO;
|
||||
import org.openhab.binding.squeezebox.internal.dto.ButtonDTODeserializer;
|
||||
import org.openhab.binding.squeezebox.internal.dto.ButtonsDTO;
|
||||
@@ -57,6 +60,7 @@ import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerService;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
@@ -82,6 +86,7 @@ import com.google.gson.JsonSyntaxException;
|
||||
* @author Mark Hilbush - Get favorites from LMS; update channel and send to players
|
||||
* @author Mark Hilbush - Add like/unlike functionality
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(SqueezeBoxServerHandler.class);
|
||||
|
||||
@@ -93,7 +98,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
// the value by which the volume is changed by each INCREASE or
|
||||
// DECREASE-Event
|
||||
private static final int VOLUME_CHANGE_SIZE = 5;
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
private static final @Nullable String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
private static final String CHANNEL_CONFIG_QUOTE_LIST = "quoteList";
|
||||
|
||||
@@ -105,24 +110,24 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
private Map<String, SqueezeBoxPlayer> players = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
// client socket and listener thread
|
||||
private Socket clientSocket;
|
||||
private SqueezeServerListener listener;
|
||||
private Future<?> reconnectFuture;
|
||||
private @Nullable Socket clientSocket;
|
||||
private @Nullable SqueezeServerListener listener;
|
||||
private @Nullable Future<?> reconnectFuture;
|
||||
|
||||
private String host;
|
||||
private String host = "";
|
||||
|
||||
private int cliport;
|
||||
|
||||
private int webport;
|
||||
|
||||
private String userId;
|
||||
private String userId = "";
|
||||
|
||||
private String password;
|
||||
private String password = "";
|
||||
|
||||
private final Gson gson = new GsonBuilder().registerTypeAdapter(ButtonDTO.class, new ButtonDTODeserializer())
|
||||
.create();
|
||||
private String jsonRpcUrl;
|
||||
private String basicAuthorization;
|
||||
private @Nullable String jsonRpcUrl;
|
||||
private @Nullable String basicAuthorization;
|
||||
|
||||
public SqueezeBoxServerHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
@@ -145,12 +150,18 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Set.of(SqueezeBoxPlayerDiscoveryService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we have a connection to the Server
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public synchronized boolean isConnected() {
|
||||
Socket clientSocket = this.clientSocket;
|
||||
if (clientSocket == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -229,7 +240,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
addPlaylistItem(mac, url, null);
|
||||
}
|
||||
|
||||
public void addPlaylistItem(String mac, String url, String title) {
|
||||
public void addPlaylistItem(String mac, String url, @Nullable String title) {
|
||||
StringBuilder playlistCommand = new StringBuilder();
|
||||
playlistCommand.append(mac).append(" playlist add ").append(url);
|
||||
if (title != null) {
|
||||
@@ -294,9 +305,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
}
|
||||
|
||||
public void rate(String mac, String rateCommand) {
|
||||
if (rateCommand != null) {
|
||||
sendCommand(mac + " " + rateCommand);
|
||||
}
|
||||
sendCommand(mac + " " + rateCommand);
|
||||
}
|
||||
|
||||
public void sleep(String mac, Duration sleepDuration) {
|
||||
@@ -349,8 +358,9 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isConnected()) {
|
||||
logger.debug("no connection to squeeze server when trying to send command, returning...");
|
||||
Socket clientSocket = this.clientSocket;
|
||||
if (clientSocket == null || !isConnected()) {
|
||||
logger.debug("No connection to squeeze server when trying to send command, returning...");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -367,7 +377,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
/*
|
||||
* Remove password from login command to prevent it from being logged
|
||||
*/
|
||||
String sanitizeCommand(String command) {
|
||||
private String sanitizeCommand(String command) {
|
||||
String sanitizedCommand = command;
|
||||
if (command.startsWith("login")) {
|
||||
sanitizedCommand = command.replace(password, "**********");
|
||||
@@ -406,7 +416,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
listener = new SqueezeServerListener();
|
||||
SqueezeServerListener listener = this.listener = new SqueezeServerListener();
|
||||
listener.start();
|
||||
logger.debug("listener connection started to server {}:{}", host, cliport);
|
||||
} catch (IllegalThreadStateException e) {
|
||||
@@ -420,6 +430,8 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
* Disconnects from a SqueezeBox Server
|
||||
*/
|
||||
private void disconnect() {
|
||||
SqueezeServerListener listener = this.listener;
|
||||
Socket clientSocket = this.clientSocket;
|
||||
try {
|
||||
if (listener != null) {
|
||||
listener.terminate();
|
||||
@@ -431,8 +443,8 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
logger.trace("Error attempting to disconnect from Squeeze Server", e);
|
||||
return;
|
||||
} finally {
|
||||
clientSocket = null;
|
||||
listener = null;
|
||||
this.clientSocket = null;
|
||||
this.listener = null;
|
||||
}
|
||||
players.clear();
|
||||
logger.trace("Squeeze Server connection stopped.");
|
||||
@@ -457,6 +469,10 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
ScheduledFuture<?> requestFavoritesJob = null;
|
||||
|
||||
try {
|
||||
Socket clientSocket = SqueezeBoxServerHandler.this.clientSocket;
|
||||
if (clientSocket == null) {
|
||||
throw new IllegalStateException("Client socket is null");
|
||||
}
|
||||
reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||
login();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
@@ -522,7 +538,6 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
return URLEncoder.encode(raw, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@NonNullByDefault
|
||||
private class KeyValue {
|
||||
final String key;
|
||||
final String value;
|
||||
@@ -535,15 +550,13 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
|
||||
private List<KeyValue> decodeKeyValueResponse(String[] response) {
|
||||
final List<KeyValue> keysAndValues = new ArrayList<>();
|
||||
if (response != null) {
|
||||
for (String line : response) {
|
||||
final String decoded = decode(line);
|
||||
int colonPos = decoded.indexOf(":");
|
||||
if (colonPos < 0) {
|
||||
continue;
|
||||
}
|
||||
keysAndValues.add(new KeyValue(decoded.substring(0, colonPos), decoded.substring(colonPos + 1)));
|
||||
for (String line : response) {
|
||||
final String decoded = decode(line);
|
||||
int colonPos = decoded.indexOf(":");
|
||||
if (colonPos < 0) {
|
||||
continue;
|
||||
}
|
||||
keysAndValues.add(new KeyValue(decoded.substring(0, colonPos), decoded.substring(colonPos + 1)));
|
||||
}
|
||||
return keysAndValues;
|
||||
}
|
||||
@@ -555,7 +568,8 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
String[] playersList = message.split("playerindex\\S*\\s");
|
||||
for (String playerParams : playersList) {
|
||||
// For each player, split out parameters and decode parameter
|
||||
final Map<String, String> keysAndValues = decodeKeyValueResponse(playerParams.split("\\s")).stream()
|
||||
String[] playParamsArray = playerParams.split("\\s");
|
||||
final Map<String, String> keysAndValues = decodeKeyValueResponse(playParamsArray).stream()
|
||||
.collect(Collectors.toMap(kv -> kv.key, kv -> kv.value));
|
||||
final String macAddress = keysAndValues.get("playerid");
|
||||
|
||||
@@ -820,7 +834,8 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
});
|
||||
}
|
||||
|
||||
private String constructCoverArtUrl(String mac, boolean coverart, String coverid, String artwork_url) {
|
||||
private String constructCoverArtUrl(String mac, boolean coverart, @Nullable String coverid,
|
||||
@Nullable String artworkUrl) {
|
||||
String hostAndPort;
|
||||
if (!userId.isEmpty()) {
|
||||
hostAndPort = "http://" + encode(userId) + ":" + encode(password) + "@" + host + ":" + webport;
|
||||
@@ -837,16 +852,16 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
// Typically is used to access cover art of local music files
|
||||
url = hostAndPort + "/music/" + coverid + "/cover.jpg";
|
||||
}
|
||||
} else if (artwork_url != null) {
|
||||
if (artwork_url.startsWith("http")) {
|
||||
} else if (artworkUrl != null) {
|
||||
if (artworkUrl.startsWith("http")) {
|
||||
// Typically indicates that cover art is not local to LMS
|
||||
url = artwork_url;
|
||||
} else if (artwork_url.startsWith("/")) {
|
||||
url = artworkUrl;
|
||||
} else if (artworkUrl.startsWith("/")) {
|
||||
// Typically used for default coverart for plugins (e.g. Pandora, etc.)
|
||||
url = hostAndPort + artwork_url;
|
||||
url = hostAndPort + artworkUrl;
|
||||
} else {
|
||||
// Another variation of default coverart for plugins (e.g. Pandora, etc.)
|
||||
url = hostAndPort + "/" + artwork_url;
|
||||
url = hostAndPort + "/" + artworkUrl;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
@@ -933,7 +948,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
isTypePlaylist = false;
|
||||
}
|
||||
// Favorite name
|
||||
else if ("name".equals(entry.key)) {
|
||||
else if ("name".equals(entry.key) && f != null) {
|
||||
f.name = entry.value;
|
||||
} else if ("type".equals(entry.key) && "playlist".equals(entry.value)) {
|
||||
isTypePlaylist = true;
|
||||
@@ -992,6 +1007,10 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
}
|
||||
|
||||
private void updateCustomButtons(final String mac) {
|
||||
String jsonRpcUrl = SqueezeBoxServerHandler.this.jsonRpcUrl;
|
||||
if (jsonRpcUrl == null) {
|
||||
throw new IllegalStateException("JSON-RPC URL is not initialized");
|
||||
}
|
||||
String response = executePost(jsonRpcUrl, JSONRPC_STATUS_REQUEST.replace("@@MAC@@", mac));
|
||||
if (response != null) {
|
||||
logger.trace("Status response: {}", response);
|
||||
@@ -1018,7 +1037,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private String executePost(String url, String content) {
|
||||
private @Nullable String executePost(String url, String content) {
|
||||
// @formatter:off
|
||||
HttpRequestBuilder builder = HttpRequestBuilder.postTo(url)
|
||||
.withTimeout(Duration.ofSeconds(5))
|
||||
@@ -1068,7 +1087,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
for (Thing thing : things) {
|
||||
ThingHandler handler = thing.getHandler();
|
||||
if (handler instanceof SqueezeBoxPlayerEventListener playerEventListener
|
||||
&& !squeezeBoxPlayerListeners.contains(handler)) {
|
||||
&& !squeezeBoxPlayerListeners.contains(playerEventListener)) {
|
||||
event.updateListener(playerEventListener);
|
||||
}
|
||||
}
|
||||
@@ -1110,7 +1129,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
* Schedule the server to try and reconnect
|
||||
*/
|
||||
private void scheduleReconnect() {
|
||||
logger.debug("scheduling squeeze server reconnect in {} seconds", RECONNECT_TIME);
|
||||
logger.debug("Scheduling squeeze server reconnect in {} seconds", RECONNECT_TIME);
|
||||
cancelReconnect();
|
||||
reconnectFuture = scheduler.schedule(this::connect, RECONNECT_TIME, TimeUnit.SECONDS);
|
||||
}
|
||||
@@ -1119,6 +1138,7 @@ public class SqueezeBoxServerHandler extends BaseBridgeHandler {
|
||||
* Clears our reconnect job if exists
|
||||
*/
|
||||
private void cancelReconnect() {
|
||||
Future<?> reconnectFuture = this.reconnectFuture;
|
||||
if (reconnectFuture != null) {
|
||||
reconnectFuture.cancel(true);
|
||||
}
|
||||
|
||||
+4
-1
@@ -12,12 +12,15 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.model;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Attributes of a Squeezebox Server favorite
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Favorite {
|
||||
/**
|
||||
* Favorite id is of form xxxxxxxx.nn
|
||||
@@ -32,7 +35,7 @@ public class Favorite {
|
||||
/**
|
||||
* The name given to the favorite in the Squeezebox Server.
|
||||
*/
|
||||
public String name;
|
||||
public String name = "";
|
||||
|
||||
/**
|
||||
* Creates a preset from the given favorite id
|
||||
|
||||
+2
@@ -16,6 +16,7 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||
@@ -35,6 +36,7 @@ import com.google.gson.JsonParser;
|
||||
* @author Mark Hilbush - Add support for LMS authentication
|
||||
* @author Mark Hilbush - Rework exception handling
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class HttpUtils {
|
||||
private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
|
||||
+3
@@ -12,11 +12,14 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.utils;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown when unable to communicate with LMS server.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxCommunicationException extends Exception {
|
||||
private static final long serialVersionUID = 1540489268747099161L;
|
||||
|
||||
|
||||
+3
@@ -12,12 +12,15 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.utils;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Exception thrown when calling LMS command line interface, and
|
||||
* the LMS is set up to require authentication.
|
||||
*
|
||||
* @author Mark Hilbush - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxNotAuthorizedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -5190671725971757821L;
|
||||
|
||||
+3
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package org.openhab.binding.squeezebox.internal.utils;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/***
|
||||
*
|
||||
* Exception class to indicate a timeout during comminication with
|
||||
@@ -20,6 +22,7 @@ package org.openhab.binding.squeezebox.internal.utils;
|
||||
* @author Patrik Gfeller - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SqueezeBoxTimeoutException extends Exception {
|
||||
private static final long serialVersionUID = 4542388088266882905L;
|
||||
|
||||
|
||||
+1
@@ -116,4 +116,5 @@ channel-type.config.squeezebox.favoritesList.quoteList.description = Wrap the ri
|
||||
|
||||
offline.conf-error.bridge-not-found = Bridge not found
|
||||
offline.conf-error.host-not-set = host is not set
|
||||
offline.conf-error.mac-not-set = MAC address is not set
|
||||
offline.comm-error.end-of-stream = end of stream on socket read
|
||||
|
||||
@@ -94,10 +94,6 @@
|
||||
|
||||
<properties>
|
||||
<property name="vendor">Logitech</property>
|
||||
<property name="modelId"></property>
|
||||
<property name="name"></property>
|
||||
<property name="uid"></property>
|
||||
<property name="ip"></property>
|
||||
<property name="thingTypeVersion">1</property>
|
||||
</properties>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user