]> git.basschouten.com Git - openhab-addons.git/blob
35a65f7c5cfe6fd49960941ef29562ae4672f8c7
[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.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;
59
60 import inet.ipaddr.mac.MACAddress;
61
62 /**
63  * The {@link FreeboxOsDiscoveryService} is responsible for discovering all things
64  * except the Freebox API thing itself
65  *
66  * @author GaĆ«l L'hopital - Initial contribution
67  */
68 @Component(scope = ServiceScope.PROTOTYPE, service = FreeboxOsDiscoveryService.class)
69 @NonNullByDefault
70 public class FreeboxOsDiscoveryService extends AbstractThingHandlerDiscoveryService<FreeboxOsHandler> {
71     private static final int DISCOVERY_TIME_SECONDS = 10;
72
73     private final Logger logger = LoggerFactory.getLogger(FreeboxOsDiscoveryService.class);
74
75     private Optional<ScheduledFuture<?>> backgroundFuture = Optional.empty();
76
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);
81     }
82
83     @Override
84     protected void startBackgroundDiscovery() {
85         stopBackgroundDiscovery();
86         int interval = thingHandler.getConfiguration().discoveryInterval;
87         if (interval > 0) {
88             backgroundFuture = Optional
89                     .of(scheduler.scheduleWithFixedDelay(this::startScan, 1, interval, TimeUnit.MINUTES));
90         }
91     }
92
93     @Override
94     protected void stopBackgroundDiscovery() {
95         backgroundFuture.ifPresent(future -> future.cancel(true));
96         backgroundFuture = Optional.empty();
97     }
98
99     @Override
100     protected void startScan() {
101         logger.debug("Starting Freebox discovery scan");
102         if (thingHandler.getThing().getStatus() == ThingStatus.ONLINE) {
103             try {
104                 ThingUID bridgeUID = thingHandler.getThing().getUID();
105
106                 List<LanHost> lanHosts = new ArrayList<>(thingHandler.getManager(LanBrowserManager.class).getHosts()
107                         .stream().filter(LanHost::reachable).toList());
108
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);
118                 }
119             } catch (FreeboxException e) {
120                 logger.warn("Error while requesting data for things discovery: {}", e.getMessage());
121             }
122         }
123     }
124
125     private void discoverHome(ThingUID bridgeUID) {
126         NodeConfigurationBuilder builder = NodeConfigurationBuilder.getInstance();
127         try {
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());
132         }
133     }
134
135     private void discoverPlugs(ThingUID bridgeUID) {
136         FreeplugConfigurationBuilder builder = FreeplugConfigurationBuilder.getInstance();
137         try {
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());
142         }
143     }
144
145     private void discoverPhone(ThingUID bridgeUID) {
146         PhoneConfigurationBuilder builder = PhoneConfigurationBuilder.getInstance();
147         List<Status> statuses = List.of();
148         try {
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());
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(ThingUID bridgeUID, List<LanHost> lanHosts) {
164         try {
165             List<MACAddress> wifiMacs = new ArrayList<>();
166             wifiMacs.addAll(thingHandler.getManager(APManager.class).getStations().stream().map(Station::mac).toList());
167             wifiMacs.addAll(
168                     thingHandler.getManager(RepeaterManager.class).getHosts().stream().map(LanHost::getMac).toList());
169
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());
181             });
182         } catch (FreeboxException e) {
183             logger.warn("Error discovering Hosts: {}", e.getMessage());
184         }
185     }
186
187     private void discoverVM(ThingUID bridgeUID, List<LanHost> lanHosts) {
188         try {
189             thingHandler.getManager(VmManager.class).getDevices().forEach(vm -> {
190                 MACAddress mac = vm.mac();
191                 lanHosts.removeIf(host -> host.getMac().equals(mac));
192
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);
200             });
201         } catch (FreeboxException e) {
202             logger.warn("Error discovering VM: {}", e.getMessage());
203         }
204     }
205
206     private void discoverRepeater(ThingUID bridgeUID, List<LanHost> lanHosts) {
207         try {
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));
212
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);
220             });
221         } catch (FreeboxException e) {
222             logger.warn("Error discovering Repeater: {}", e.getMessage());
223         }
224     }
225
226     private void discoverServer(ThingUID bridgeUID) {
227         try {
228             Config config = thingHandler.getManager(SystemManager.class).getConfig();
229
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);
234
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());
241         }
242     }
243
244     private void discoverPlayer(ThingUID bridgeUID, List<LanHost> lanHosts) {
245         try {
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);
255             }
256         } catch (FreeboxException e) {
257             logger.warn("Error discovering Player: {}", e.getMessage());
258         }
259     }
260 }