2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.spotify.internal.discovery;
15 import static org.openhab.binding.spotify.internal.SpotifyBindingConstants.PROPERTY_SPOTIFY_DEVICE_NAME;
16 import static org.openhab.binding.spotify.internal.SpotifyBindingConstants.THING_TYPE_DEVICE;
18 import java.time.Duration;
19 import java.util.Collections;
20 import java.util.HashMap;
23 import java.util.concurrent.ScheduledFuture;
24 import java.util.concurrent.TimeUnit;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.spotify.internal.SpotifyAccountHandler;
29 import org.openhab.binding.spotify.internal.SpotifyBindingConstants;
30 import org.openhab.binding.spotify.internal.api.model.Device;
31 import org.openhab.core.config.discovery.AbstractDiscoveryService;
32 import org.openhab.core.config.discovery.DiscoveryResult;
33 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
34 import org.openhab.core.config.discovery.DiscoveryService;
35 import org.openhab.core.thing.ThingTypeUID;
36 import org.openhab.core.thing.ThingUID;
37 import org.openhab.core.thing.binding.ThingHandler;
38 import org.openhab.core.thing.binding.ThingHandlerService;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * The {@link SpotifyDeviceDiscoveryService} queries the Spotify Web API for available devices.
45 * @author Andreas Stenlund - Initial contribution
46 * @author Hilbrand Bouwkamp - Simplfied code to make call to shared code
49 public class SpotifyDeviceDiscoveryService extends AbstractDiscoveryService
50 implements DiscoveryService, ThingHandlerService {
52 // id for device is derived by stripping id of device with this length
53 private static final int PLAYER_ID_LENGTH = 4;
54 // Only devices can be discovered. A bridge must be manually added.
55 private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_DEVICE);
56 // The call to listDevices is fast
57 private static final int DISCOVERY_TIME_SECONDS = 10;
58 // Check every minute for new devices
59 private static final long BACKGROUND_SCAN_REFRESH_MINUTES = 1;
60 // Time to life for discovered things.
61 private static final long TTL_SECONDS = Duration.ofHours(1).toSeconds();
63 private final Logger logger = LoggerFactory.getLogger(SpotifyDeviceDiscoveryService.class);
65 private @NonNullByDefault({}) SpotifyAccountHandler bridgeHandler;
66 private @NonNullByDefault({}) ThingUID bridgeUID;
68 private @Nullable ScheduledFuture<?> backgroundFuture;
70 public SpotifyDeviceDiscoveryService() {
71 super(SUPPORTED_THING_TYPES_UIDS, DISCOVERY_TIME_SECONDS);
75 public Set<ThingTypeUID> getSupportedThingTypes() {
76 return SUPPORTED_THING_TYPES_UIDS;
80 public void activate() {
81 final Map<String, Object> properties = new HashMap<>();
82 properties.put(DiscoveryService.CONFIG_PROPERTY_BACKGROUND_DISCOVERY, Boolean.TRUE);
83 super.activate(properties);
87 public void deactivate() {
92 public void setThingHandler(@Nullable ThingHandler handler) {
93 if (handler instanceof SpotifyAccountHandler) {
94 bridgeHandler = (SpotifyAccountHandler) handler;
95 bridgeUID = bridgeHandler.getUID();
100 public @Nullable ThingHandler getThingHandler() {
101 return bridgeHandler;
105 protected synchronized void startBackgroundDiscovery() {
106 stopBackgroundDiscovery();
107 backgroundFuture = scheduler.scheduleWithFixedDelay(this::startScan, BACKGROUND_SCAN_REFRESH_MINUTES,
108 BACKGROUND_SCAN_REFRESH_MINUTES, TimeUnit.MINUTES);
112 protected synchronized void stopBackgroundDiscovery() {
113 if (backgroundFuture != null) {
114 backgroundFuture.cancel(true);
115 backgroundFuture = null;
120 protected void startScan() {
121 // If the bridge is not online no other thing devices can be found, so no reason to scan at this moment.
122 removeOlderResults(getTimestampOfLastScan());
123 if (bridgeHandler.isOnline()) {
124 logger.debug("Starting Spotify Device discovery for bridge {}", bridgeUID);
126 bridgeHandler.listDevices().forEach(this::thingDiscovered);
127 } catch (final RuntimeException e) {
128 logger.debug("Finding devices failed with message: {}", e.getMessage(), e);
133 private void thingDiscovered(Device device) {
134 final Map<String, Object> properties = new HashMap<>();
136 properties.put(PROPERTY_SPOTIFY_DEVICE_NAME, device.getName());
137 final ThingUID thing = new ThingUID(SpotifyBindingConstants.THING_TYPE_DEVICE, bridgeUID,
138 device.getId().substring(0, PLAYER_ID_LENGTH));
140 final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thing).withBridge(bridgeUID)
141 .withProperties(properties).withRepresentationProperty(PROPERTY_SPOTIFY_DEVICE_NAME)
142 .withTTL(TTL_SECONDS).withLabel(device.getName()).build();
144 thingDiscovered(discoveryResult);