]> git.basschouten.com Git - openhab-addons.git/blob
ccd64c1851eb939e322c8f3405488fcf138f2f7f
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.minecraft.internal.discovery;
14
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.Map;
18
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;
34
35 import rx.Observable;
36 import rx.Subscription;
37 import rx.functions.Func1;
38 import rx.schedulers.Schedulers;
39 import rx.subscriptions.CompositeSubscription;
40
41 /**
42  * Handles discovery of Minecraft server, players and signs.
43  *
44  * @author Mattias Markehed - Initial contribution
45  */
46 @Component(service = DiscoveryService.class, configurationPid = "discovery.minecraft")
47 public class MinecraftDiscoveryService extends AbstractDiscoveryService {
48
49     private final Logger logger = LoggerFactory.getLogger(MinecraftDiscoveryService.class);
50
51     private static final int DISCOVER_TIMEOUT_SECONDS = 60;
52
53     private CompositeSubscription subscription;
54
55     public MinecraftDiscoveryService() {
56         super(Collections.singleton(MinecraftBindingConstants.THING_TYPE_SERVER), DISCOVER_TIMEOUT_SECONDS, false);
57     }
58
59     @Override
60     protected void startScan() {
61         logger.debug("Starting Minecraft discovery scan");
62         discoverServers();
63     }
64
65     @Override
66     protected synchronized void stopScan() {
67         logger.debug("Stopping Minecraft discovery scan");
68         stopDiscovery();
69         super.stopScan();
70     }
71
72     /**
73      * Start scanning for players and signs on servers.
74      */
75     private void discoverServers() {
76         subscription = new CompositeSubscription();
77
78         Observable<ServerConnection> serverRx = serversConnectRx().cache();
79
80         Subscription playerSubscription = subscribePlayersRx(serverRx);
81         Subscription signsSubscription = subscribeSignsRx(serverRx);
82
83         subscription.add(playerSubscription);
84         subscription.add(signsSubscription);
85     }
86
87     /**
88      * Subscribe for sign updates
89      *
90      * @param serverRx server stream
91      * @return subscription for listening to sign events.
92      */
93     private Subscription subscribeSignsRx(Observable<ServerConnection> serverRx) {
94         return 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);
100                     }
101                 }, e -> logger.error("Error while scanning for signs", e));
102     }
103
104     /**
105      * Subscribe to player updates
106      *
107      * @param serverRx server stream
108      * @return subscription for listening to player events.
109      */
110     private Subscription subscribePlayersRx(Observable<ServerConnection> serverRx) {
111         return 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());
117                     }
118                 }, e -> logger.error("Error while scanning for players", e));
119     }
120
121     /**
122      * Teardown subscribers and stop searching for players and signs.
123      */
124     private void stopDiscovery() {
125         if (subscription != null && !subscription.isUnsubscribed()) {
126             subscription.unsubscribe();
127         }
128     }
129
130     /**
131      * Get all servers that have been added as observable.
132      *
133      * @return observable emitting server objects.
134      */
135     private Observable<ServerConnection> serversConnectRx() {
136         return Observable.from(MinecraftHandlerFactory.getMinecraftServers())
137                 .flatMap(new Func1<MinecraftServerHandler, Observable<ServerConnection>>() {
138                     @Override
139                     public Observable<ServerConnection> call(MinecraftServerHandler server) {
140                         ServerConfig config = server.getServerConfig();
141                         return ServerConnection.create(server.getThing().getUID(), config.getHostname(),
142                                 config.getPort());
143                     }
144                 });
145     }
146
147     /**
148      * Submit the discovered Devices to the inbox.
149      *
150      * @param bridgeUID
151      * @param name name of the player
152      */
153     private void submitPlayerDiscoveryResults(ThingUID bridgeUID, String name) {
154         String id = deviceNameToId(name);
155         ThingUID uid = new ThingUID(MinecraftBindingConstants.THING_TYPE_PLAYER, bridgeUID, id);
156
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());
161     }
162
163     /**
164      * Submit the discovered Signs to the inbox.
165      *
166      * @param bridgeUID
167      * @param sign data describing sign
168      */
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);
172
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());
177     }
178
179     /**
180      * Cleanup device name so it can be used as id.
181      *
182      * @param name the name of device.
183      * @return id of device.
184      */
185     private String deviceNameToId(String name) {
186         if (name == null) {
187             return "";
188         }
189         return name.replaceAll("[^a-zA-Z0-9]+", "");
190     }
191 }