2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.freeboxos.internal.api.rest;
15 import java.time.ZonedDateTime;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
20 import javax.ws.rs.core.UriBuilder;
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;
29 import inet.ipaddr.IPAddress;
30 import inet.ipaddr.IPAddressString;
31 import inet.ipaddr.mac.MACAddress;
34 * The {@link LanBrowserManager} is the Java class used to handle api requests related to lan
36 * https://dev.freebox.fr/sdk/os/system/#
38 * @author Gaƫl L'hopital - Initial contribution
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";
47 protected static class HostsResponse extends Response<LanHost> {
50 protected static class InterfacesResponse extends Response<Interface> {
53 public static enum Source {
63 public record HostName(@Nullable String name, Source source) {
66 protected static record Interface(String name, int hostCount) {
69 private static record WakeOnLineData(String mac, String password) {
72 private static enum Type {
77 private static record L2Ident(MACAddress id, Type type) {
80 private static record L3Connectivity(String addr, Af af, boolean active, boolean reachable,
81 ZonedDateTime lastActivity, ZonedDateTime lastTimeReachable, String model) {
83 private static enum Af {
89 public IPAddress getIPAddress() {
90 if (af != Af.UNKNOWN) {
91 return new IPAddressString(addr).getAddress();
97 public static record HostIntf(LanHost host, Interface intf) {
100 private static enum HostType {
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) {
132 public @Nullable LanAccessPoint accessPoint() {
136 public String vendorName() {
137 String localVendor = vendorName;
138 return localVendor != null ? localVendor : "Unknown";
141 public Optional<String> getPrimaryName() {
142 return Optional.ofNullable(primaryName);
145 public Optional<String> getUPnPName() {
146 return names.stream().filter(name -> name.source == Source.UPNP).findFirst().map(name -> name.name);
149 public MACAddress getMac() {
150 if (Type.MAC_ADDRESS.equals(l2ident.type)) {
153 throw new IllegalArgumentException("This host does not seem to have a Mac Address. Weird.");
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);
161 public @Nullable ZonedDateTime getLastSeen() {
162 ZonedDateTime localLastActivity = lastActivity;
163 if (lastTimeReachable == null && localLastActivity == null) {
166 if (lastTimeReachable == null) {
169 if (localLastActivity == null) {
170 return lastTimeReachable;
172 return localLastActivity.isAfter(lastTimeReachable) ? lastActivity : lastTimeReachable;
177 private final List<Interface> interfaces = new ArrayList<>();
179 public LanBrowserManager(FreeboxOsSession session, UriBuilder uriBuilder) throws FreeboxException {
180 super(session, LoginManager.Permission.NONE, InterfacesResponse.class, uriBuilder.path(PATH));
181 listSubPath = INTERFACES;
184 private List<LanHost> getInterfaceHosts(String lanInterface) throws FreeboxException {
185 return get(HostsResponse.class, lanInterface);
188 private @Nullable LanHost getHost(String lanInterface, String hostId) throws FreeboxException {
189 return getSingle(HostsResponse.class, lanInterface, hostId);
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());
200 public synchronized List<LanHost> getHosts() throws FreeboxException {
201 List<LanHost> hosts = new ArrayList<>();
203 for (Interface intf : getInterfaces()) {
204 hosts.addAll(getInterfaceHosts(intf.name()));
209 public Optional<HostIntf> getHost(MACAddress searched) throws FreeboxException {
210 for (Interface intf : getInterfaces()) {
211 LanHost host = getHost(intf.name(), "ether-" + searched.toColonDelimitedString());
213 return Optional.of(new HostIntf(host, intf));
216 return Optional.empty();
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);