]> git.basschouten.com Git - openhab-addons.git/blob
b265781a4064c962573bddb89be9035b2a79b7dd
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.jellyfin.internal.discovery;
14
15 import static org.openhab.binding.jellyfin.internal.JellyfinBindingConstants.*;
16
17 import java.net.URI;
18 import java.net.URISyntaxException;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Objects;
22 import java.util.Set;
23
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;
47
48 /**
49  * The {@link JellyfinServerDiscoveryService} discover Jellyfin servers in the network.
50  *
51  * @author Miguel Alvarez - Initial contribution
52  */
53 @NonNullByDefault
54 @Component(service = DiscoveryService.class, configurationPid = "discovery.jellyfin")
55 public class JellyfinServerDiscoveryService extends AbstractDiscoveryService {
56     private final Logger logger = LoggerFactory.getLogger(JellyfinServerDiscoveryService.class);
57     @Nullable
58     private FlowJob cancelDiscovery;
59
60     public JellyfinServerDiscoveryService() throws IllegalArgumentException {
61         super(Set.of(THING_TYPE_CLIENT), 60);
62     }
63
64     @Override
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) -> {
73             if (info == null) {
74                 return;
75             }
76             logger.debug("Server found: [{}] {}", info.getId(), info.getName());
77             processDiscoveryResult(jellyfin, info);
78         }, (throwable) -> {
79             if (throwable != null) {
80                 logger.warn("Discovery Error: {}", throwable.getMessage());
81             } else {
82                 logger.debug("Discovery ends");
83             }
84         });
85     }
86
87     @Override
88     protected synchronized void stopScan() {
89         super.stopScan();
90         var cancelDiscovery = this.cancelDiscovery;
91         if (cancelDiscovery != null) {
92             cancelDiscovery.close();
93             this.cancelDiscovery = null;
94         }
95     }
96
97     private void processDiscoveryResult(Jellyfin jellyfin, ServerDiscoveryInfo info) {
98         URI uri;
99         try {
100             uri = new URI(Objects.requireNonNull(info.getAddress()));
101         } catch (URISyntaxException e) {
102             logger.warn("Error parsing server url: {}", e.getMessage());
103             return;
104         }
105         var jellyClient = jellyfin.createApi(info.getAddress());
106         var asyncResponse = new SyncResponse<PublicSystemInfo>();
107         new SystemApi(jellyClient).getPublicSystemInfo(asyncResponse);
108         try {
109             var publicSystemInfo = asyncResponse.awaitContent();
110             discoverServer(uri.getHost(), uri.getPort(), uri.getScheme().equalsIgnoreCase("https"), uri.getPath(),
111                     publicSystemInfo);
112         } catch (SyncCallback.SyncCallbackError | ApiClientException e) {
113             logger.warn("Discovery error: {}", e.getMessage());
114         }
115     }
116
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);
130         }
131         var version = publicSystemInfo.getVersion();
132         if (version != null) {
133             properties.put(Thing.PROPERTY_FIRMWARE_VERSION, version);
134         }
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());
138     }
139 }