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.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.compatibility.JavaFlow.FlowJob;
32 import org.jellyfin.sdk.model.ClientInfo;
33 import org.jellyfin.sdk.model.DeviceInfo;
34 import org.jellyfin.sdk.model.api.PublicSystemInfo;
35 import org.jellyfin.sdk.model.api.ServerDiscoveryInfo;
36 import org.openhab.binding.jellyfin.internal.util.SyncCallback;
37 import org.openhab.binding.jellyfin.internal.util.SyncResponse;
38 import org.openhab.core.OpenHAB;
39 import org.openhab.core.config.discovery.AbstractDiscoveryService;
40 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
41 import org.openhab.core.config.discovery.DiscoveryService;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingUID;
44 import org.osgi.service.component.annotations.Component;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The {@link JellyfinServerDiscoveryService} discover Jellyfin servers in the network.
51 * @author Miguel Alvarez - Initial contribution
54 @Component(service = DiscoveryService.class, configurationPid = "discovery.jellyfin")
55 public class JellyfinServerDiscoveryService extends AbstractDiscoveryService {
56 private final Logger logger = LoggerFactory.getLogger(JellyfinServerDiscoveryService.class);
58 private FlowJob cancelDiscovery;
60 public JellyfinServerDiscoveryService() throws IllegalArgumentException {
61 super(Set.of(THING_TYPE_CLIENT), 60);
65 protected void startScan() {
66 var opts = new JellyfinOptions.Builder();
67 opts.setClientInfo(new ClientInfo("openHAB", OpenHAB.getVersion()));
68 opts.setDeviceInfo(new DeviceInfo("discovery", "openHAB"));
69 var jellyfin = new Jellyfin(opts.build());
70 var discoverySvc = new org.jellyfin.sdk.discovery.DiscoveryService(jellyfin);
71 logger.debug("Starting search");
72 cancelDiscovery = JavaFlow.collect(discoverySvc.discoverLocalServers(100, 10), null, (info) -> {
76 logger.debug("Server found: [{}] {}", info.getId(), info.getName());
77 processDiscoveryResult(jellyfin, info);
79 if (throwable != null) {
80 logger.warn("Discovery Error: {}", throwable.getMessage());
82 logger.debug("Discovery ends");
88 protected synchronized void stopScan() {
90 var cancelDiscovery = this.cancelDiscovery;
91 if (cancelDiscovery != null) {
92 cancelDiscovery.close();
93 this.cancelDiscovery = null;
97 private void processDiscoveryResult(Jellyfin jellyfin, ServerDiscoveryInfo info) {
100 uri = new URI(Objects.requireNonNull(info.getAddress()));
101 } catch (URISyntaxException e) {
102 logger.warn("Error parsing server url: {}", e.getMessage());
105 var jellyClient = jellyfin.createApi(info.getAddress());
106 var asyncResponse = new SyncResponse<PublicSystemInfo>();
107 new SystemApi(jellyClient).getPublicSystemInfo(asyncResponse);
109 var publicSystemInfo = asyncResponse.awaitContent();
110 discoverServer(uri.getHost(), uri.getPort(), "https".equalsIgnoreCase(uri.getScheme()), uri.getPath(),
112 } catch (SyncCallback.SyncCallbackError | ApiClientException e) {
113 logger.warn("Discovery error: {}", e.getMessage());
117 private void discoverServer(String hostname, int port, boolean ssl, String path,
118 PublicSystemInfo publicSystemInfo) {
119 logger.debug("Server discovered: [{}:{}] {}", hostname, port, publicSystemInfo.getServerName());
120 var id = Objects.requireNonNull(publicSystemInfo.getId());
121 Map<String, Object> properties = new HashMap<>();
122 properties.put("hostname", hostname);
123 properties.put("port", port);
124 properties.put("ssl", ssl);
125 properties.put("path", path);
126 properties.put(Thing.PROPERTY_SERIAL_NUMBER, id);
127 var productName = publicSystemInfo.getProductName();
128 if (productName != null) {
129 properties.put(Thing.PROPERTY_VENDOR, productName);
131 var version = publicSystemInfo.getVersion();
132 if (version != null) {
133 properties.put(Thing.PROPERTY_FIRMWARE_VERSION, version);
135 thingDiscovered(DiscoveryResultBuilder.create(new ThingUID(THING_TYPE_SERVER, publicSystemInfo.getId()))
136 .withTTL(DISCOVERY_RESULT_TTL_SEC).withRepresentationProperty(Thing.PROPERTY_SERIAL_NUMBER)
137 .withProperties(properties).withLabel(publicSystemInfo.getServerName()).build());