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