]> git.basschouten.com Git - openhab-addons.git/blob
9fc5b15fb64011fac492ce2afe38da530ec32806
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.freeboxos.internal.discovery;
14
15 import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
16
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
28 import org.openhab.binding.freeboxos.internal.api.PermissionException;
29 import org.openhab.binding.freeboxos.internal.api.rest.APManager;
30 import org.openhab.binding.freeboxos.internal.api.rest.APManager.Station;
31 import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager;
32 import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
33 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
34 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
35 import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager;
36 import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
37 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager;
38 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Player;
39 import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
40 import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager.Repeater;
41 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager;
42 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.Config;
43 import org.openhab.binding.freeboxos.internal.api.rest.VmManager;
44 import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
45 import org.openhab.binding.freeboxos.internal.config.FreeplugConfigurationBuilder;
46 import org.openhab.binding.freeboxos.internal.config.NodeConfigurationBuilder;
47 import org.openhab.binding.freeboxos.internal.config.PhoneConfigurationBuilder;
48 import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
49 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
50 import org.openhab.core.config.discovery.DiscoveryResult;
51 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
52 import org.openhab.core.thing.Thing;
53 import org.openhab.core.thing.ThingStatus;
54 import org.openhab.core.thing.ThingTypeUID;
55 import org.openhab.core.thing.ThingUID;
56 import org.osgi.service.component.annotations.Component;
57 import org.osgi.service.component.annotations.ServiceScope;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import inet.ipaddr.mac.MACAddress;
62
63 /**
64  * The {@link FreeboxOsDiscoveryService} is responsible for discovering all things
65  * except the Freebox API thing itself
66  *
67  * @author GaĆ«l L'hopital - Initial contribution
68  */
69 @Component(scope = ServiceScope.PROTOTYPE, service = FreeboxOsDiscoveryService.class)
70 @NonNullByDefault
71 public class FreeboxOsDiscoveryService extends AbstractThingHandlerDiscoveryService<FreeboxOsHandler> {
72     private static final int DISCOVERY_TIME_SECONDS = 10;
73
74     private final Logger logger = LoggerFactory.getLogger(FreeboxOsDiscoveryService.class);
75
76     private Optional<ScheduledFuture<?>> backgroundFuture = Optional.empty();
77
78     public FreeboxOsDiscoveryService() {
79         super(FreeboxOsHandler.class,
80                 Stream.of(THINGS_TYPES_UIDS, HOME_TYPES_UIDS).flatMap(Set::stream).collect(Collectors.toSet()),
81                 DISCOVERY_TIME_SECONDS);
82     }
83
84     @Override
85     protected void startBackgroundDiscovery() {
86         stopBackgroundDiscovery();
87         int interval = thingHandler.getConfiguration().discoveryInterval;
88         if (interval > 0) {
89             backgroundFuture = Optional
90                     .of(scheduler.scheduleWithFixedDelay(this::startScan, 1, interval, TimeUnit.MINUTES));
91         }
92     }
93
94     @Override
95     protected void stopBackgroundDiscovery() {
96         backgroundFuture.ifPresent(future -> future.cancel(true));
97         backgroundFuture = Optional.empty();
98     }
99
100     @Override
101     protected void startScan() {
102         logger.debug("Starting Freebox discovery scan");
103         if (thingHandler.getThing().getStatus() == ThingStatus.ONLINE) {
104             try {
105                 ThingUID bridgeUID = thingHandler.getThing().getUID();
106
107                 List<LanHost> lanHosts = new ArrayList<>(thingHandler.getManager(LanBrowserManager.class).getHosts()
108                         .stream().filter(LanHost::reachable).toList());
109
110                 discoverServer(thingHandler.getManager(SystemManager.class), bridgeUID);
111                 discoverPhone(thingHandler.getManager(PhoneManager.class), bridgeUID);
112                 discoverPlugs(thingHandler.getManager(FreeplugManager.class), bridgeUID);
113                 discoverRepeater(thingHandler.getManager(RepeaterManager.class), bridgeUID, lanHosts);
114                 discoverPlayer(thingHandler.getManager(PlayerManager.class), bridgeUID, lanHosts);
115                 discoverVM(thingHandler.getManager(VmManager.class), bridgeUID, lanHosts);
116                 discoverHome(thingHandler.getManager(HomeManager.class), bridgeUID);
117                 if (thingHandler.getConfiguration().discoverNetDevice) {
118                     discoverHosts(thingHandler, bridgeUID, lanHosts);
119                 }
120             } catch (FreeboxException e) {
121                 logger.warn("Error while requesting data for things discovery: {}", e.getMessage());
122             }
123         }
124     }
125
126     private void discoverHome(HomeManager homeManager, ThingUID bridgeUID) throws FreeboxException {
127         NodeConfigurationBuilder builder = NodeConfigurationBuilder.getInstance();
128         try {
129             homeManager.getHomeNodes().forEach(
130                     node -> builder.configure(bridgeUID, node).ifPresent(result -> thingDiscovered(result.build())));
131         } catch (PermissionException e) {
132             logger.warn("Missing permission to discover Home {}", e.getPermission());
133         }
134     }
135
136     private void discoverPlugs(FreeplugManager freeplugManager, ThingUID bridgeUID) {
137         FreeplugConfigurationBuilder builder = FreeplugConfigurationBuilder.getInstance();
138         try {
139             freeplugManager.getPlugs().forEach(plug -> thingDiscovered(builder.configure(bridgeUID, plug).build()));
140         } catch (FreeboxException e) {
141             logger.warn("Error discovering freeplugs {}", e.getMessage());
142         }
143     }
144
145     private void discoverPhone(PhoneManager phoneManager, ThingUID bridgeUID) throws FreeboxException {
146         PhoneConfigurationBuilder builder = PhoneConfigurationBuilder.getInstance();
147         List<Status> statuses = List.of();
148         try {
149             statuses = phoneManager.getPhoneStatuses();
150             statuses.forEach(phone -> thingDiscovered(builder.configure(bridgeUID, phone).build()));
151         } catch (FreeboxException e) {
152             logger.warn("Error discovering phones {}", e.getMessage());
153         }
154         if (!statuses.isEmpty()) {
155             ThingUID thingUID = new ThingUID(THING_TYPE_CALL, bridgeUID, "landline");
156             logger.debug("Adding new Call thing {} to inbox", thingUID);
157             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
158                     .withLabel("Freebox Calls").build();
159             thingDiscovered(discoveryResult);
160         }
161     }
162
163     private void discoverHosts(FreeboxOsHandler localHandler, ThingUID bridgeUID, List<LanHost> lanHosts)
164             throws FreeboxException {
165         try {
166             List<MACAddress> wifiMacs = new ArrayList<>();
167             wifiMacs.addAll(localHandler.getManager(APManager.class).getStations().stream().map(Station::mac).toList());
168             wifiMacs.addAll(
169                     localHandler.getManager(RepeaterManager.class).getHosts().stream().map(LanHost::getMac).toList());
170
171             lanHosts.forEach(lanHost -> {
172                 MACAddress mac = lanHost.getMac();
173                 String macString = mac.toColonDelimitedString();
174                 ThingUID thingUID = new ThingUID(wifiMacs.contains(mac) ? THING_TYPE_WIFI_HOST : THING_TYPE_HOST,
175                         bridgeUID, mac.toHexString(false));
176                 logger.debug("Adding new Freebox Network Host {} to inbox", thingUID);
177                 DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
178                         .withLabel(lanHost.getPrimaryName().orElse("Network Device %s".formatted(macString)))
179                         .withTTL(300).withProperty(Thing.PROPERTY_MAC_ADDRESS, macString)
180                         .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS);
181                 thingDiscovered(builder.build());
182             });
183         } catch (PermissionException e) {
184             logger.warn("Missing permission to discover Hosts {}", e.getPermission());
185         }
186     }
187
188     private void discoverVM(VmManager vmManager, ThingUID bridgeUID, List<LanHost> lanHosts) throws FreeboxException {
189         try {
190             vmManager.getDevices().forEach(vm -> {
191                 MACAddress mac = vm.mac();
192                 lanHosts.removeIf(host -> host.getMac().equals(mac));
193
194                 ThingUID thingUID = new ThingUID(THING_TYPE_VM, bridgeUID, mac.toHexString(false));
195                 logger.debug("Adding new VM Device {} to inbox", thingUID);
196                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
197                         .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
198                         .withLabel("%s (VM)".formatted(vm.name())).withProperty(ClientConfiguration.ID, vm.id())
199                         .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString()).build();
200                 thingDiscovered(discoveryResult);
201             });
202         } catch (PermissionException e) {
203             logger.warn("Missing permission to discover VM {}", e.getPermission());
204         }
205     }
206
207     private void discoverRepeater(RepeaterManager repeaterManager, ThingUID bridgeUID, List<LanHost> lanHosts)
208             throws FreeboxException {
209         try {
210             List<Repeater> repeaters = repeaterManager.getDevices();
211             repeaters.forEach(repeater -> {
212                 MACAddress mac = repeater.mainMac();
213                 lanHosts.removeIf(host -> host.getMac().equals(mac));
214
215                 ThingUID thingUID = new ThingUID(THING_TYPE_REPEATER, bridgeUID, Integer.toString(repeater.id()));
216                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
217                         .withLabel("Repeater %s".formatted(repeater.name()))
218                         .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString())
219                         .withProperty(ClientConfiguration.ID, repeater.id())
220                         .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
221                 thingDiscovered(discoveryResult);
222             });
223         } catch (PermissionException e) {
224             logger.warn("Missing permission to discover Repeater {}", e.getPermission());
225         }
226     }
227
228     private void discoverServer(SystemManager systemManager, ThingUID bridgeUID) throws FreeboxException {
229         try {
230             Config config = systemManager.getConfig();
231
232             ThingTypeUID targetType = config.boardName().startsWith("fbxgw7") ? THING_TYPE_DELTA
233                     : THING_TYPE_REVOLUTION;
234             ThingUID thingUID = new ThingUID(targetType, bridgeUID, config.serial());
235             logger.debug("Adding new Freebox Server {} to inbox", thingUID);
236
237             DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
238                     .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).withLabel(config.modelInfo().prettyName())
239                     .withProperty(Thing.PROPERTY_MAC_ADDRESS, config.mac()).build();
240             thingDiscovered(discoveryResult);
241         } catch (PermissionException e) {
242             logger.warn("Missing permission to discover Server {}", e.getPermission());
243         }
244     }
245
246     private void discoverPlayer(PlayerManager playerManager, ThingUID bridgeUID, List<LanHost> lanHosts)
247             throws FreeboxException {
248         try {
249             for (Player player : playerManager.getDevices()) {
250                 lanHosts.removeIf(host -> host.getMac().equals(player.mac()));
251                 ThingUID thingUID = new ThingUID(player.apiAvailable() ? THING_TYPE_ACTIVE_PLAYER : THING_TYPE_PLAYER,
252                         bridgeUID, Integer.toString(player.id()));
253                 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
254                         .withProperty(Thing.PROPERTY_MAC_ADDRESS, player.mac().toColonDelimitedString())
255                         .withProperty(ClientConfiguration.ID, player.id()).withLabel(player.deviceName())
256                         .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
257                 thingDiscovered(discoveryResult);
258             }
259         } catch (PermissionException e) {
260             logger.warn("Missing permission to discover Player {}", e.getPermission());
261         }
262     }
263 }