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