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.minecraft.internal.discovery;
15 import java.util.Collections;
16 import java.util.HashMap;
19 import org.openhab.binding.minecraft.internal.MinecraftBindingConstants;
20 import org.openhab.binding.minecraft.internal.MinecraftHandlerFactory;
21 import org.openhab.binding.minecraft.internal.config.ServerConfig;
22 import org.openhab.binding.minecraft.internal.handler.MinecraftServerHandler;
23 import org.openhab.binding.minecraft.internal.message.data.PlayerData;
24 import org.openhab.binding.minecraft.internal.message.data.SignData;
25 import org.openhab.binding.minecraft.internal.server.ServerConnection;
26 import org.openhab.binding.minecraft.internal.util.Pair;
27 import org.openhab.core.config.discovery.AbstractDiscoveryService;
28 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
29 import org.openhab.core.config.discovery.DiscoveryService;
30 import org.openhab.core.thing.ThingUID;
31 import org.osgi.service.component.annotations.Component;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
36 import rx.Subscription;
37 import rx.functions.Func1;
38 import rx.schedulers.Schedulers;
39 import rx.subscriptions.CompositeSubscription;
42 * Handles discovery of Minecraft server, players and signs.
44 * @author Mattias Markehed - Initial contribution
46 @Component(service = DiscoveryService.class, configurationPid = "discovery.minecraft")
47 public class MinecraftDiscoveryService extends AbstractDiscoveryService {
49 private final Logger logger = LoggerFactory.getLogger(MinecraftDiscoveryService.class);
51 private static final int DISCOVER_TIMEOUT_SECONDS = 60;
53 private CompositeSubscription subscription;
55 public MinecraftDiscoveryService() {
56 super(Collections.singleton(MinecraftBindingConstants.THING_TYPE_SERVER), DISCOVER_TIMEOUT_SECONDS, false);
60 protected void startScan() {
61 logger.debug("Starting Minecraft discovery scan");
66 protected synchronized void stopScan() {
67 logger.debug("Stopping Minecraft discovery scan");
73 * Start scanning for players and signs on servers.
75 private void discoverServers() {
76 subscription = new CompositeSubscription();
78 Observable<ServerConnection> serverRx = serversConnectRx().cache();
80 Subscription playerSubscription = subscribePlayersRx(serverRx);
81 Subscription signsSubscription = subscribeSignsRx(serverRx);
83 subscription.add(playerSubscription);
84 subscription.add(signsSubscription);
88 * Subscribe for sign updates
90 * @param serverRx server stream
91 * @return subscription for listening to sign events.
93 private Subscription subscribeSignsRx(Observable<ServerConnection> serverRx) {
95 .flatMap(connection -> connection.getSocketHandler().getSignsRx().distinct(), (connection, signs) -> {
96 return new Pair<>(connection, signs);
97 }).subscribe(conectionSignPair -> {
98 for (SignData sign : conectionSignPair.second) {
99 submitSignDiscoveryResults(conectionSignPair.first.getThingUID(), sign);
101 }, e -> logger.error("Error while scanning for signs", e));
105 * Subscribe to player updates
107 * @param serverRx server stream
108 * @return subscription for listening to player events.
110 private Subscription subscribePlayersRx(Observable<ServerConnection> serverRx) {
112 .flatMap(socketHandler -> socketHandler.getSocketHandler().getPlayersRx().distinct(),
113 (connection, players) -> new Pair<>(connection, players))
114 .subscribeOn(Schedulers.newThread()).subscribe(conectionPlayerPair -> {
115 for (PlayerData player : conectionPlayerPair.second) {
116 submitPlayerDiscoveryResults(conectionPlayerPair.first.getThingUID(), player.getName());
118 }, e -> logger.error("Error while scanning for players", e));
122 * Teardown subscribers and stop searching for players and signs.
124 private void stopDiscovery() {
125 if (subscription != null && !subscription.isUnsubscribed()) {
126 subscription.unsubscribe();
131 * Get all servers that have been added as observable.
133 * @return observable emitting server objects.
135 private Observable<ServerConnection> serversConnectRx() {
136 return Observable.from(MinecraftHandlerFactory.getMinecraftServers())
137 .flatMap(new Func1<MinecraftServerHandler, Observable<ServerConnection>>() {
139 public Observable<ServerConnection> call(MinecraftServerHandler server) {
140 ServerConfig config = server.getServerConfig();
141 return ServerConnection.create(server.getThing().getUID(), config.getHostname(),
148 * Submit the discovered Devices to the inbox.
151 * @param name name of the player
153 private void submitPlayerDiscoveryResults(ThingUID bridgeUID, String name) {
154 String id = deviceNameToId(name);
155 ThingUID uid = new ThingUID(MinecraftBindingConstants.THING_TYPE_PLAYER, bridgeUID, id);
157 Map<String, Object> properties = new HashMap<>();
158 properties.put(MinecraftBindingConstants.PARAMETER_PLAYER_NAME, name);
159 thingDiscovered(DiscoveryResultBuilder.create(uid).withProperties(properties).withBridge(bridgeUID)
160 .withLabel(name).build());
164 * Submit the discovered Signs to the inbox.
167 * @param sign data describing sign
169 private void submitSignDiscoveryResults(ThingUID bridgeUID, SignData sign) {
170 String id = deviceNameToId(sign.getName());
171 ThingUID uid = new ThingUID(MinecraftBindingConstants.THING_TYPE_SIGN, bridgeUID, id);
173 Map<String, Object> properties = new HashMap<>();
174 properties.put(MinecraftBindingConstants.PARAMETER_SIGN_NAME, sign.getName());
175 thingDiscovered(DiscoveryResultBuilder.create(uid).withProperties(properties).withBridge(bridgeUID)
176 .withLabel(sign.getName()).build());
180 * Cleanup device name so it can be used as id.
182 * @param name the name of device.
183 * @return id of device.
185 private String deviceNameToId(String name) {
189 return name.replaceAll("[^a-zA-Z0-9]+", "");