2 * Copyright (c) 2010-2024 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.freeboxos.internal.discovery;
15 import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.Optional;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
28 import org.openhab.binding.freeboxos.internal.api.rest.APManager;
29 import org.openhab.binding.freeboxos.internal.api.rest.APManager.Station;
30 import org.openhab.binding.freeboxos.internal.api.rest.FreeplugManager;
31 import org.openhab.binding.freeboxos.internal.api.rest.HomeManager;
32 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
33 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
34 import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager;
35 import org.openhab.binding.freeboxos.internal.api.rest.PhoneManager.Status;
36 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager;
37 import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.Player;
38 import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
39 import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager.Repeater;
40 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager;
41 import org.openhab.binding.freeboxos.internal.api.rest.SystemManager.Config;
42 import org.openhab.binding.freeboxos.internal.api.rest.VmManager;
43 import org.openhab.binding.freeboxos.internal.config.ClientConfiguration;
44 import org.openhab.binding.freeboxos.internal.config.FreeplugConfigurationBuilder;
45 import org.openhab.binding.freeboxos.internal.config.NodeConfigurationBuilder;
46 import org.openhab.binding.freeboxos.internal.config.PhoneConfigurationBuilder;
47 import org.openhab.binding.freeboxos.internal.handler.FreeboxOsHandler;
48 import org.openhab.core.config.discovery.AbstractThingHandlerDiscoveryService;
49 import org.openhab.core.config.discovery.DiscoveryResult;
50 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
51 import org.openhab.core.thing.Thing;
52 import org.openhab.core.thing.ThingStatus;
53 import org.openhab.core.thing.ThingTypeUID;
54 import org.openhab.core.thing.ThingUID;
55 import org.osgi.service.component.annotations.Component;
56 import org.osgi.service.component.annotations.ServiceScope;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import inet.ipaddr.mac.MACAddress;
63 * The {@link FreeboxOsDiscoveryService} is responsible for discovering all things
64 * except the Freebox API thing itself
66 * @author Gaƫl L'hopital - Initial contribution
68 @Component(scope = ServiceScope.PROTOTYPE, service = FreeboxOsDiscoveryService.class)
70 public class FreeboxOsDiscoveryService extends AbstractThingHandlerDiscoveryService<FreeboxOsHandler> {
71 private static final int DISCOVERY_TIME_SECONDS = 10;
73 private final Logger logger = LoggerFactory.getLogger(FreeboxOsDiscoveryService.class);
75 private Optional<ScheduledFuture<?>> backgroundFuture = Optional.empty();
77 public FreeboxOsDiscoveryService() {
78 super(FreeboxOsHandler.class,
79 Stream.of(THINGS_TYPES_UIDS, HOME_TYPES_UIDS).flatMap(Set::stream).collect(Collectors.toSet()),
80 DISCOVERY_TIME_SECONDS);
84 protected void startBackgroundDiscovery() {
85 stopBackgroundDiscovery();
86 int interval = thingHandler.getConfiguration().discoveryInterval;
88 backgroundFuture = Optional
89 .of(scheduler.scheduleWithFixedDelay(this::startScan, 1, interval, TimeUnit.MINUTES));
94 protected void stopBackgroundDiscovery() {
95 backgroundFuture.ifPresent(future -> future.cancel(true));
96 backgroundFuture = Optional.empty();
100 protected void startScan() {
101 logger.debug("Starting Freebox discovery scan");
102 if (thingHandler.getThing().getStatus() == ThingStatus.ONLINE) {
104 ThingUID bridgeUID = thingHandler.getThing().getUID();
106 List<LanHost> lanHosts = new ArrayList<>(thingHandler.getManager(LanBrowserManager.class).getHosts()
107 .stream().filter(LanHost::reachable).toList());
109 discoverServer(bridgeUID);
110 discoverPhone(bridgeUID);
111 discoverPlugs(bridgeUID);
112 discoverRepeater(bridgeUID, lanHosts);
113 discoverPlayer(bridgeUID, lanHosts);
114 discoverVM(bridgeUID, lanHosts);
115 discoverHome(bridgeUID);
116 if (thingHandler.getConfiguration().discoverNetDevice) {
117 discoverHosts(bridgeUID, lanHosts);
119 } catch (FreeboxException e) {
120 logger.warn("Error while requesting data for things discovery: {}", e.getMessage());
125 private void discoverHome(ThingUID bridgeUID) {
126 NodeConfigurationBuilder builder = NodeConfigurationBuilder.getInstance();
128 thingHandler.getManager(HomeManager.class).getHomeNodes().forEach(
129 node -> builder.configure(bridgeUID, node).ifPresent(result -> thingDiscovered(result.build())));
130 } catch (FreeboxException e) {
131 logger.warn("Error discovering Home: {}", e.getMessage());
135 private void discoverPlugs(ThingUID bridgeUID) {
136 FreeplugConfigurationBuilder builder = FreeplugConfigurationBuilder.getInstance();
138 thingHandler.getManager(FreeplugManager.class).getPlugs()
139 .forEach(plug -> thingDiscovered(builder.configure(bridgeUID, plug).build()));
140 } catch (FreeboxException e) {
141 logger.warn("Error discovering freeplugs: {}", e.getMessage());
145 private void discoverPhone(ThingUID bridgeUID) {
146 PhoneConfigurationBuilder builder = PhoneConfigurationBuilder.getInstance();
147 List<Status> statuses = List.of();
149 statuses = thingHandler.getManager(PhoneManager.class).getPhoneStatuses();
150 statuses.forEach(phone -> thingDiscovered(builder.configure(bridgeUID, phone).build()));
151 } catch (FreeboxException e) {
152 logger.warn("Error discovering phones: {}", e.getMessage());
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);
163 private void discoverHosts(ThingUID bridgeUID, List<LanHost> lanHosts) {
165 List<MACAddress> wifiMacs = new ArrayList<>();
166 wifiMacs.addAll(thingHandler.getManager(APManager.class).getStations().stream().map(Station::mac).toList());
168 thingHandler.getManager(RepeaterManager.class).getHosts().stream().map(LanHost::getMac).toList());
170 lanHosts.forEach(lanHost -> {
171 MACAddress mac = lanHost.getMac();
172 String macString = mac.toColonDelimitedString();
173 ThingUID thingUID = new ThingUID(wifiMacs.contains(mac) ? THING_TYPE_WIFI_HOST : THING_TYPE_HOST,
174 bridgeUID, mac.toHexString(false));
175 logger.debug("Adding new Freebox Network Host {} to inbox", thingUID);
176 DiscoveryResultBuilder builder = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
177 .withLabel(lanHost.getPrimaryName().orElse("Network Device %s".formatted(macString)))
178 .withTTL(300).withProperty(Thing.PROPERTY_MAC_ADDRESS, macString)
179 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS);
180 thingDiscovered(builder.build());
182 } catch (FreeboxException e) {
183 logger.warn("Error discovering Hosts: {}", e.getMessage());
187 private void discoverVM(ThingUID bridgeUID, List<LanHost> lanHosts) {
189 thingHandler.getManager(VmManager.class).getDevices().forEach(vm -> {
190 MACAddress mac = vm.mac();
191 lanHosts.removeIf(host -> host.getMac().equals(mac));
193 ThingUID thingUID = new ThingUID(THING_TYPE_VM, bridgeUID, mac.toHexString(false));
194 logger.debug("Adding new VM Device {} to inbox", thingUID);
195 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
196 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS)
197 .withLabel("%s (VM)".formatted(vm.name())).withProperty(ClientConfiguration.ID, vm.id())
198 .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString()).build();
199 thingDiscovered(discoveryResult);
201 } catch (FreeboxException e) {
202 logger.warn("Error discovering VM: {}", e.getMessage());
206 private void discoverRepeater(ThingUID bridgeUID, List<LanHost> lanHosts) {
208 List<Repeater> repeaters = thingHandler.getManager(RepeaterManager.class).getDevices();
209 repeaters.forEach(repeater -> {
210 MACAddress mac = repeater.mainMac();
211 lanHosts.removeIf(host -> host.getMac().equals(mac));
213 ThingUID thingUID = new ThingUID(THING_TYPE_REPEATER, bridgeUID, Integer.toString(repeater.id()));
214 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
215 .withLabel("Repeater %s".formatted(repeater.name()))
216 .withProperty(Thing.PROPERTY_MAC_ADDRESS, mac.toColonDelimitedString())
217 .withProperty(ClientConfiguration.ID, repeater.id())
218 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
219 thingDiscovered(discoveryResult);
221 } catch (FreeboxException e) {
222 logger.warn("Error discovering Repeater: {}", e.getMessage());
226 private void discoverServer(ThingUID bridgeUID) {
228 Config config = thingHandler.getManager(SystemManager.class).getConfig();
230 ThingTypeUID targetType = config.boardName().startsWith("fbxgw7") ? THING_TYPE_DELTA
231 : THING_TYPE_REVOLUTION;
232 ThingUID thingUID = new ThingUID(targetType, bridgeUID, config.serial());
233 logger.debug("Adding new Freebox Server {} to inbox", thingUID);
235 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
236 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).withLabel(config.modelInfo().prettyName())
237 .withProperty(Thing.PROPERTY_MAC_ADDRESS, config.mac().toColonDelimitedString()).build();
238 thingDiscovered(discoveryResult);
239 } catch (FreeboxException e) {
240 logger.warn("Error discovering Server: {}", e.getMessage());
244 private void discoverPlayer(ThingUID bridgeUID, List<LanHost> lanHosts) {
246 for (Player player : thingHandler.getManager(PlayerManager.class).getDevices()) {
247 lanHosts.removeIf(host -> host.getMac().equals(player.mac()));
248 ThingUID thingUID = new ThingUID(player.apiAvailable() ? THING_TYPE_ACTIVE_PLAYER : THING_TYPE_PLAYER,
249 bridgeUID, Integer.toString(player.id()));
250 DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
251 .withProperty(Thing.PROPERTY_MAC_ADDRESS, player.mac().toColonDelimitedString())
252 .withProperty(ClientConfiguration.ID, player.id()).withLabel(player.deviceName())
253 .withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).build();
254 thingDiscovered(discoveryResult);
256 } catch (FreeboxException e) {
257 logger.warn("Error discovering Player: {}", e.getMessage());