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