2 * Copyright (c) 2010-2022 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.jellyfin.internal.discovery;
15 import static org.openhab.binding.jellyfin.internal.JellyfinBindingConstants.*;
18 import java.net.URISyntaxException;
19 import java.util.HashMap;
21 import java.util.Objects;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.jellyfin.sdk.Jellyfin;
27 import org.jellyfin.sdk.JellyfinOptions;
28 import org.jellyfin.sdk.api.client.exception.ApiClientException;
29 import org.jellyfin.sdk.api.operations.SystemApi;
30 import org.jellyfin.sdk.compatibility.JavaFlow;
31 import org.jellyfin.sdk.model.ClientInfo;
32 import org.jellyfin.sdk.model.DeviceInfo;
33 import org.jellyfin.sdk.model.api.PublicSystemInfo;
34 import org.jellyfin.sdk.model.api.ServerDiscoveryInfo;
35 import org.openhab.binding.jellyfin.internal.util.SyncCallback;
36 import org.openhab.binding.jellyfin.internal.util.SyncResponse;
37 import org.openhab.core.OpenHAB;
38 import org.openhab.core.config.discovery.AbstractDiscoveryService;
39 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
40 import org.openhab.core.config.discovery.DiscoveryService;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingUID;
43 import org.osgi.service.component.annotations.Component;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * The {@link JellyfinServerDiscoveryService} discover Jellyfin servers in the network.
50 * @author Miguel Alvarez - Initial contribution
53 @Component(service = DiscoveryService.class, configurationPid = "discovery.jellyfin")
54 public class JellyfinServerDiscoveryService extends AbstractDiscoveryService {
55 private final Logger logger = LoggerFactory.getLogger(JellyfinServerDiscoveryService.class);
56 private JavaFlow.@Nullable FlowJob cancelDiscovery;
58 public JellyfinServerDiscoveryService() throws IllegalArgumentException {
59 super(Set.of(THING_TYPE_CLIENT), 60);
63 protected void startScan() {
64 var opts = new JellyfinOptions.Builder();
65 opts.setClientInfo(new ClientInfo("openHAB", OpenHAB.getVersion()));
66 opts.setDeviceInfo(new DeviceInfo("discovery", "openHAB"));
67 var jellyfin = new Jellyfin(opts.build());
68 var discoverySvc = new org.jellyfin.sdk.discovery.DiscoveryService(jellyfin);
69 logger.debug("Starting search");
70 cancelDiscovery = JavaFlow.collect(discoverySvc.discoverLocalServers(100, 10), null, (info) -> {
74 logger.debug("Server found: [{}] {}", info.getId(), info.getName());
75 processDiscoveryResult(jellyfin, info);
77 if (throwable != null) {
78 logger.warn("Discovery Error: {}", throwable.getMessage());
80 logger.debug("Discovery ends");
86 protected synchronized void stopScan() {
88 var cancelDiscovery = this.cancelDiscovery;
89 if (cancelDiscovery != null) {
90 cancelDiscovery.close();
91 this.cancelDiscovery = null;
95 private void processDiscoveryResult(Jellyfin jellyfin, ServerDiscoveryInfo info) {
98 uri = new URI(Objects.requireNonNull(info.getAddress()));
99 } catch (URISyntaxException e) {
100 logger.warn("Error parsing server url: {}", e.getMessage());
103 var jellyClient = jellyfin.createApi(info.getAddress());
104 var asyncResponse = new SyncResponse<PublicSystemInfo>();
105 new SystemApi(jellyClient).getPublicSystemInfo(asyncResponse);
107 var publicSystemInfo = asyncResponse.awaitContent();
108 discoverServer(uri.getHost(), uri.getPort(), uri.getScheme().equalsIgnoreCase("https"), publicSystemInfo);
109 } catch (SyncCallback.SyncCallbackError | ApiClientException e) {
110 logger.warn("Discovery error: {}", e.getMessage());
114 private void discoverServer(String hostname, int port, boolean ssl, PublicSystemInfo publicSystemInfo) {
115 logger.debug("Server discovered: [{}:{}] {}", hostname, port, publicSystemInfo.getServerName());
116 var id = Objects.requireNonNull(publicSystemInfo.getId());
117 Map<String, Object> properties = new HashMap<>();
118 properties.put("hostname", hostname);
119 properties.put("port", port);
120 properties.put("ssl", ssl);
121 properties.put(Thing.PROPERTY_SERIAL_NUMBER, id);
122 var productName = publicSystemInfo.getProductName();
123 if (productName != null) {
124 properties.put(Thing.PROPERTY_VENDOR, productName);
126 var version = publicSystemInfo.getVersion();
127 if (version != null) {
128 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, version);
130 thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_SERVER, publicSystemInfo.getId()))
131 .withTTL(DISCOVERY_RESULT_TTL_SEC).withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
132 .withProperties(properties).withLabel(publicSystemInfo.getServerName()).build());