[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:
Jacob Laursen
2025-03-09 18:51:12 +01:00
committed by GitHub
parent e40481c2e4
commit 703796df19
20 changed files with 267 additions and 194 deletions
@@ -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) {
@@ -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";
}
@@ -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.");
@@ -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;
}
@@ -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
@@ -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(),
@@ -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
@@ -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();
@@ -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);
}
@@ -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;
}
}
@@ -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,
@@ -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);
@@ -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);
}
@@ -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
@@ -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);
@@ -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;
@@ -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;
@@ -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;
@@ -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>