]> git.basschouten.com Git - openhab-addons.git/blob
694d964f22d5a86ff94675a4dfe6fa71e480000a
[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.api.rest;
14
15 import java.time.ZonedDateTime;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19
20 import javax.ws.rs.core.UriBuilder;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
25 import org.openhab.binding.freeboxos.internal.api.Response;
26 import org.openhab.binding.freeboxos.internal.api.rest.APManager.LanAccessPoint;
27 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.InterfacesResponse;
28
29 import inet.ipaddr.IPAddress;
30 import inet.ipaddr.IPAddressString;
31 import inet.ipaddr.mac.MACAddress;
32
33 /**
34  * The {@link LanBrowserManager} is the Java class used to handle api requests related to lan
35  *
36  * https://dev.freebox.fr/sdk/os/system/#
37  *
38  * @author GaĆ«l L'hopital - Initial contribution
39  */
40 @NonNullByDefault
41 public class LanBrowserManager extends ListableRest<LanBrowserManager.Interface, InterfacesResponse> {
42     private static final IPAddress NULL_IP = new IPAddressString("0.0.0.0").getAddress();
43     private static final String PATH = "browser";
44     private static final String INTERFACES = "interfaces";
45     private static final String WOL_ACTION = "wol";
46
47     protected static class HostsResponse extends Response<LanHost> {
48     }
49
50     protected static class InterfacesResponse extends Response<Interface> {
51     }
52
53     public static enum Source {
54         DHCP,
55         NETBIOS,
56         MDNS,
57         MDNS_SRV,
58         UPNP,
59         WSD,
60         UNKNOWN;
61     }
62
63     public record HostName(@Nullable String name, Source source) {
64     }
65
66     protected static record Interface(String name, int hostCount) {
67     }
68
69     private static record WakeOnLineData(String mac, String password) {
70     }
71
72     private static enum Type {
73         MAC_ADDRESS,
74         UNKNOWN;
75     }
76
77     private static record L2Ident(MACAddress id, Type type) {
78     }
79
80     private static record L3Connectivity(String addr, Af af, boolean active, boolean reachable,
81             ZonedDateTime lastActivity, ZonedDateTime lastTimeReachable, String model) {
82
83         private static enum Af {
84             IPV4,
85             IPV6,
86             UNKNOWN;
87         }
88
89         public IPAddress getIPAddress() {
90             if (af != Af.UNKNOWN) {
91                 return new IPAddressString(addr).getAddress();
92             }
93             return NULL_IP;
94         }
95     }
96
97     public static record HostIntf(LanHost host, Interface intf) {
98     }
99
100     private static enum HostType {
101         WORKSTATION,
102         LAPTOP,
103         SMARTPHONE,
104         TABLET,
105         PRINTER,
106         VG_CONSOLE,
107         TELEVISION,
108         NAS,
109         IP_CAMERA,
110         IP_PHONE,
111         FREEBOX_PLAYER,
112         FREEBOX_HD,
113         FREEBOX_CRYSTAL,
114         FREEBOX_MINI,
115         FREEBOX_DELTA,
116         FREEBOX_ONE,
117         FREEBOX_WIFI,
118         FREEBOX_POP,
119         NETWORKING_DEVICE,
120         MULTIMEDIA_DEVICE,
121         CAR,
122         OTHER,
123         UNKNOWN;
124     }
125
126     public static record LanHost(String id, @Nullable String primaryName, HostType hostType, boolean primaryNameManual,
127             L2Ident l2ident, @Nullable String vendorName, boolean persistent, boolean reachable,
128             @Nullable ZonedDateTime lastTimeReachable, boolean active, @Nullable ZonedDateTime lastActivity,
129             @Nullable ZonedDateTime firstActivity, List<HostName> names, List<L3Connectivity> l3connectivities,
130             @Nullable LanAccessPoint accessPoint) {
131
132         public @Nullable LanAccessPoint accessPoint() {
133             return accessPoint;
134         }
135
136         public String vendorName() {
137             String localVendor = vendorName;
138             return localVendor != null ? localVendor : "Unknown";
139         }
140
141         public Optional<String> getPrimaryName() {
142             return Optional.ofNullable(primaryName);
143         }
144
145         public Optional<String> getUPnPName() {
146             return names.stream().filter(name -> name.source == Source.UPNP).findFirst().map(name -> name.name);
147         }
148
149         public MACAddress getMac() {
150             if (Type.MAC_ADDRESS.equals(l2ident.type)) {
151                 return l2ident.id;
152             }
153             throw new IllegalArgumentException("This host does not seem to have a Mac Address. Weird.");
154         }
155
156         public @Nullable IPAddress getIpv4() {
157             return l3connectivities.stream().filter(L3Connectivity::reachable).map(L3Connectivity::getIPAddress)
158                     .filter(ip -> !ip.equals(NULL_IP) && ip.isIPv4()).findFirst().orElse(null);
159         }
160
161         public @Nullable ZonedDateTime getLastSeen() {
162             ZonedDateTime localLastActivity = lastActivity;
163             if (lastTimeReachable == null && localLastActivity == null) {
164                 return null;
165             }
166             if (lastTimeReachable == null) {
167                 return lastActivity;
168             }
169             if (localLastActivity == null) {
170                 return lastTimeReachable;
171             } else {
172                 return localLastActivity.isAfter(lastTimeReachable) ? lastActivity : lastTimeReachable;
173             }
174         }
175     }
176
177     private final List<Interface> interfaces = new ArrayList<>();
178
179     public LanBrowserManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
180         super(session, LoginManager.Permission.NONE, InterfacesResponse.class, uriBuilder.path(PATH));
181         listSubPath = INTERFACES;
182     }
183
184     private List<LanHost> getInterfaceHosts(String lanInterface) throws FreeboxException {
185         return get(HostsResponse.class, lanInterface);
186     }
187
188     private @Nullable LanHost getHost(String lanInterface, String hostId) throws FreeboxException {
189         return getSingle(HostsResponse.class, lanInterface, hostId);
190     }
191
192     // As the list of interfaces on the box may not change, we cache the result
193     private List<Interface> getInterfaces() throws FreeboxException {
194         if (interfaces.isEmpty()) {
195             interfaces.addAll(getDevices());
196         }
197         return interfaces;
198     }
199
200     public synchronized List<LanHost> getHosts() throws FreeboxException {
201         List<LanHost> hosts = new ArrayList<>();
202
203         for (Interface intf : getInterfaces()) {
204             hosts.addAll(getInterfaceHosts(intf.name()));
205         }
206         return hosts;
207     }
208
209     public Optional<HostIntf> getHost(MACAddress searched) throws FreeboxException {
210         for (Interface intf : getInterfaces()) {
211             LanHost host = getHost(intf.name(), "ether-" + searched.toColonDelimitedString());
212             if (host != null) {
213                 return Optional.of(new HostIntf(host, intf));
214             }
215         }
216         return Optional.empty();
217     }
218
219     public boolean wakeOnLan(MACAddress mac, String password) throws FreeboxException {
220         Optional<HostIntf> target = getHost(mac);
221         if (target.isPresent()) {
222             post(new WakeOnLineData(mac.toColonDelimitedString(), password), GenericResponse.class, WOL_ACTION,
223                     target.get().intf.name);
224             return true;
225         }
226         return false;
227     }
228 }