[freeboxos] Support randomized MAC addresses by using mDNS name for Wi-Fi hosts (#15299)
* Take care of randomized mac addresses by using mDNS name for wifi hosts. Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
d499f2c777
commit
e792a92a38
@ -97,15 +97,28 @@ The *landline* thing requires the following configuration parameters:
|
||||
|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 2 |
|
||||
|
||||
### Network devices: Host and WifiHost
|
||||
### Network devices: Host
|
||||
|
||||
The *host* and *wifihost* things requires the following configuration parameters:
|
||||
The *host* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
|
||||
| MAC Address | macAddress | The MAC address of the network host . | Yes | |
|
||||
| MAC Address | macAddress | The MAC address of the network host. | Yes | |
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 30 |
|
||||
|
||||
### Network devices: WifiHost
|
||||
|
||||
The *wifihost* thing requires the following configuration parameters:
|
||||
|
||||
| Parameter Label | Parameter ID | Description | Required | Default |
|
||||
|------------------|-----------------|------------------------------------------------------------------------|----------|---------|
|
||||
| MAC Address | macAddress | The MAC address of the network host. | Yes | |
|
||||
| Refresh Interval | refreshInterval | The refresh interval in seconds which is used to poll for phone state. | No | 30 |
|
||||
| mDNS Name | mDNS | The mDNS name of the host. Useful in case of virtual MAC. | No | |
|
||||
|
||||
When used, mDNS will search the host based on its mDNS name and eventually update the MAC address accordingly.
|
||||
This is useful with devices, especially Apple equipment, that uses randomly generated MAC addresses.
|
||||
|
||||
### Repeater and Vm thing
|
||||
|
||||
The *repeater* thing is a specialized case of a *wifihost*. The *vm* derives from *host*. They share the same configuration definition:
|
||||
|
||||
@ -126,8 +126,8 @@ public class LanBrowserManager extends ListableRest<LanBrowserManager.Interface,
|
||||
public static record LanHost(String id, @Nullable String primaryName, HostType hostType, boolean primaryNameManual,
|
||||
L2Ident l2ident, @Nullable String vendorName, boolean persistent, boolean reachable,
|
||||
@Nullable ZonedDateTime lastTimeReachable, boolean active, @Nullable ZonedDateTime lastActivity,
|
||||
@Nullable ZonedDateTime firstActivity, List<HostName> names, List<L3Connectivity> l3connectivities,
|
||||
@Nullable LanAccessPoint accessPoint) {
|
||||
@Nullable ZonedDateTime firstActivity, @Nullable List<HostName> names,
|
||||
List<L3Connectivity> l3connectivities, @Nullable LanAccessPoint accessPoint) {
|
||||
|
||||
public @Nullable LanAccessPoint accessPoint() {
|
||||
return accessPoint;
|
||||
@ -135,15 +135,20 @@ public class LanBrowserManager extends ListableRest<LanBrowserManager.Interface,
|
||||
|
||||
public String vendorName() {
|
||||
String localVendor = vendorName;
|
||||
return localVendor != null ? localVendor : "Unknown";
|
||||
return localVendor == null || localVendor.isEmpty() ? "Unknown" : localVendor;
|
||||
}
|
||||
|
||||
public Optional<String> getPrimaryName() {
|
||||
return Optional.ofNullable(primaryName);
|
||||
}
|
||||
|
||||
public Optional<String> getUPnPName() {
|
||||
return names.stream().filter(name -> name.source == Source.UPNP).findFirst().map(name -> name.name);
|
||||
public List<HostName> getNames() {
|
||||
List<HostName> localNames = names;
|
||||
return localNames != null ? localNames : List.of();
|
||||
}
|
||||
|
||||
public Optional<String> getName(Source searchedSource) {
|
||||
return getNames().stream().filter(name -> name.source == searchedSource).findFirst().map(HostName::name);
|
||||
}
|
||||
|
||||
public MACAddress getMac() {
|
||||
@ -216,6 +221,29 @@ public class LanBrowserManager extends ListableRest<LanBrowserManager.Interface,
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Optional<LanHost> getHost(HostName identifier) throws FreeboxException {
|
||||
List<LanHost> hosts = getHosts();
|
||||
LanHost result = null;
|
||||
boolean multiple = false;
|
||||
for (LanHost host : hosts) {
|
||||
Optional<String> sourcedName = host.getName(identifier.source);
|
||||
if (sourcedName.isPresent() && sourcedName.get().equals(identifier.name)) {
|
||||
// We will not return something if multiple hosts are found. This can happen in case of IP change that
|
||||
// a previous name remains attached to a different host.
|
||||
if (result == null) {
|
||||
result = host;
|
||||
} else if (!result.getMac().equals(host.getMac())) {
|
||||
// Multiple hosts with different macs
|
||||
multiple = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (multiple) {
|
||||
result = null;
|
||||
}
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
public boolean wakeOnLan(MACAddress mac, String password) throws FreeboxException {
|
||||
Optional<HostIntf> target = getHost(mac);
|
||||
if (target.isPresent()) {
|
||||
|
||||
@ -20,7 +20,6 @@ import inet.ipaddr.mac.MACAddress;
|
||||
/**
|
||||
* The {@link HostConfiguration} is responsible for holding
|
||||
* configuration informations associated to a Freebox Network Device
|
||||
* thing type
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2023 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.freeboxos.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.HostName;
|
||||
|
||||
/**
|
||||
* The {@link WifiHostConfiguration} holds configuration information needed to
|
||||
* access/poll a wifi network device
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class WifiHostConfiguration extends HostConfiguration {
|
||||
private String mDNS = "";
|
||||
|
||||
public @Nullable HostName getIdentifier() {
|
||||
if (!mDNS.isEmpty()) {
|
||||
return new HostName(mDNS, LanBrowserManager.Source.MDNS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -126,8 +126,8 @@ public class FreeboxOsDiscoveryService extends AbstractDiscoveryService implemen
|
||||
try {
|
||||
ThingUID bridgeUID = handler.getThing().getUID();
|
||||
|
||||
List<LanHost> lanHosts = handler.getManager(LanBrowserManager.class).getHosts().stream()
|
||||
.filter(LanHost::reachable).toList();
|
||||
List<LanHost> lanHosts = new ArrayList<>(handler.getManager(LanBrowserManager.class).getHosts().stream()
|
||||
.filter(LanHost::reachable).toList());
|
||||
|
||||
discoverServer(handler.getManager(SystemManager.class), bridgeUID);
|
||||
discoverPhone(handler.getManager(PhoneManager.class), bridgeUID);
|
||||
|
||||
@ -56,8 +56,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import inet.ipaddr.IPAddress;
|
||||
import inet.ipaddr.MACAddressString;
|
||||
import inet.ipaddr.mac.MACAddress;
|
||||
|
||||
/**
|
||||
* The {@link ServerHandler} is a base abstract class for all devices made available by the FreeboxOs bridge
|
||||
@ -70,6 +68,7 @@ abstract class ApiConsumerHandler extends BaseThingHandler implements ApiConsume
|
||||
private final Map<String, ScheduledFuture<?>> jobs = new HashMap<>();
|
||||
|
||||
private @Nullable ServiceRegistration<?> reg;
|
||||
protected boolean statusDrivenByBridge = true;
|
||||
|
||||
ApiConsumerHandler(Thing thing) {
|
||||
super(thing);
|
||||
@ -167,10 +166,12 @@ abstract class ApiConsumerHandler extends BaseThingHandler implements ApiConsume
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge != null) {
|
||||
BridgeHandler handler = bridge.getHandler();
|
||||
if (handler instanceof FreeboxOsHandler) {
|
||||
if (handler instanceof FreeboxOsHandler fbOsHandler) {
|
||||
if (bridge.getStatus() == ThingStatus.ONLINE) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return (FreeboxOsHandler) handler;
|
||||
if (statusDrivenByBridge) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
return fbOsHandler;
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
} else {
|
||||
@ -343,10 +344,4 @@ abstract class ApiConsumerHandler extends BaseThingHandler implements ApiConsume
|
||||
public int getClientId() {
|
||||
return ((BigDecimal) getConfig().get(ClientConfiguration.ID)).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MACAddress getMac() {
|
||||
String mac = (String) getConfig().get(Thing.PROPERTY_MAC_ADDRESS);
|
||||
return new MACAddressString(mac).getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ public class FreeplugHandler extends ApiConsumerHandler {
|
||||
properties.put(Thing.PROPERTY_MODEL_ID, plug.model());
|
||||
properties.put(ROLE, plug.netRole().name());
|
||||
properties.put(NET_ID, plug.netId());
|
||||
properties.put(ETHERNET_SPEED, String.format("%d Mb/s", plug.ethSpeed()));
|
||||
properties.put(ETHERNET_SPEED, "%d Mb/s".formatted(plug.ethSpeed()));
|
||||
properties.put(LOCAL, Boolean.valueOf(plug.local()).toString());
|
||||
properties.put(FULL_DUPLEX, Boolean.valueOf(plug.ethFullDuplex()).toString());
|
||||
|
||||
@ -88,7 +88,7 @@ public class FreeplugHandler extends ApiConsumerHandler {
|
||||
getManager(FreeplugManager.class).reboot(getMac());
|
||||
logger.debug("Freeplug {} succesfully restarted", getMac());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error restarting freeplug: {}", e.getMessage());
|
||||
logger.warn("Error restarting freeplug {}: {}", getMac(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,14 +15,13 @@ package org.openhab.binding.freeboxos.internal.handler;
|
||||
import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.freeboxos.internal.action.HostActions;
|
||||
import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.HostIntf;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.Source;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.WebSocketManager;
|
||||
@ -42,46 +41,56 @@ import org.slf4j.LoggerFactory;
|
||||
public class HostHandler extends ApiConsumerHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(HostHandler.class);
|
||||
|
||||
// We start in pull mode and switch to push after a first update
|
||||
// We start in pull mode and switch to push after a first update...
|
||||
private boolean pushSubscribed = false;
|
||||
|
||||
public HostHandler(Thing thing) {
|
||||
super(thing);
|
||||
statusDrivenByBridge = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void initializeProperties(Map<String, String> properties) throws FreeboxException {
|
||||
getManager(LanBrowserManager.class).getHost(getMac()).ifPresent(result -> {
|
||||
LanHost host = result.host();
|
||||
properties.put(Thing.PROPERTY_VENDOR, host.vendorName());
|
||||
host.getUPnPName().ifPresent(upnpName -> properties.put(Source.UPNP.name(), upnpName));
|
||||
});
|
||||
LanHost host = getLanHost();
|
||||
properties.put(Thing.PROPERTY_VENDOR, host.vendorName());
|
||||
host.getName(Source.UPNP).ifPresent(upnpName -> properties.put(Source.UPNP.name(), upnpName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
try {
|
||||
getManager(WebSocketManager.class).unregisterListener(getMac());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error unregistering host from the websocket: {}", e.getMessage());
|
||||
}
|
||||
cancelPushSubscription();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected void cancelPushSubscription() {
|
||||
if (pushSubscribed) {
|
||||
try {
|
||||
getManager(WebSocketManager.class).unregisterListener(getMac());
|
||||
} catch (FreeboxException e) {
|
||||
logger.warn("Error unregistering host from the websocket: {}", e.getMessage());
|
||||
}
|
||||
pushSubscribed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void internalPoll() throws FreeboxException {
|
||||
if (pushSubscribed) {
|
||||
return;
|
||||
}
|
||||
HostIntf data = getManager(LanBrowserManager.class).getHost(getMac())
|
||||
.orElseThrow(() -> new FreeboxException("Host data not found"));
|
||||
|
||||
updateConnectivityChannels(data.host());
|
||||
LanHost host = getLanHost();
|
||||
updateConnectivityChannels(host);
|
||||
logger.debug("Switching to push mode - refreshInterval will now be ignored for Connectivity data");
|
||||
getManager(WebSocketManager.class).registerListener(data.host().getMac(), this);
|
||||
getManager(WebSocketManager.class).registerListener(host.getMac(), this);
|
||||
pushSubscribed = true;
|
||||
}
|
||||
|
||||
protected LanHost getLanHost() throws FreeboxException {
|
||||
return getManager(LanBrowserManager.class).getHost(getMac()).map(hostIntf -> hostIntf.host())
|
||||
.orElseThrow(() -> new FreeboxException("Host data not found"));
|
||||
}
|
||||
|
||||
public void updateConnectivityChannels(LanHost host) {
|
||||
updateChannelOnOff(CONNECTIVITY, REACHABLE, host.reachable());
|
||||
updateChannelDateTimeState(CONNECTIVITY, LAST_SEEN, host.getLastSeen());
|
||||
@ -100,6 +109,6 @@ public class HostHandler extends ApiConsumerHandler {
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> getServices() {
|
||||
return Collections.singletonList(HostActions.class);
|
||||
return Set.of(HostActions.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,13 +22,19 @@ import org.openhab.binding.freeboxos.internal.api.FreeboxException;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.LanAccessPoint;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.APManager.Station;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.HostName;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
|
||||
import org.openhab.binding.freeboxos.internal.api.rest.RepeaterManager;
|
||||
import org.openhab.binding.freeboxos.internal.config.WifiHostConfiguration;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link WifiStationHandler} is responsible for handling everything associated to
|
||||
@ -40,6 +46,8 @@ import org.openhab.core.types.UnDefType;
|
||||
public class WifiStationHandler extends HostHandler {
|
||||
private static final String SERVER_HOST = "Server";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(WifiStationHandler.class);
|
||||
|
||||
public WifiStationHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
@ -90,4 +98,26 @@ public class WifiStationHandler extends HostHandler {
|
||||
private int toQoS(int rssi) {
|
||||
return rssi > -50 ? 4 : rssi > -60 ? 3 : rssi > -70 ? 2 : rssi > -85 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LanHost getLanHost() throws FreeboxException {
|
||||
try {
|
||||
return super.getLanHost();
|
||||
} catch (FreeboxException e) {
|
||||
HostName identifier = getConfigAs(WifiHostConfiguration.class).getIdentifier();
|
||||
if (identifier != null) {
|
||||
cancelPushSubscription();
|
||||
Optional<LanHost> lanHost = getManager(LanBrowserManager.class).getHost(identifier);
|
||||
return lanHost.map(host -> {
|
||||
Configuration thingConfig = editConfiguration();
|
||||
thingConfig.put(Thing.PROPERTY_MAC_ADDRESS, host.getMac().toColonDelimitedString());
|
||||
updateConfiguration(thingConfig);
|
||||
logger.info("MAC address of the wifihost {} changed, configuration updated to {}", thing.getUID(),
|
||||
host.getMac());
|
||||
return host;
|
||||
}).orElseThrow(() -> new FreeboxException("Host data not found - mDNS failed also"));
|
||||
}
|
||||
throw new FreeboxException("Host not found - no mDNS alternative");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0
|
||||
https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:freeboxos:wifi-host">
|
||||
<parameter name="refreshInterval" type="integer" min="1" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>The refresh interval in seconds which is used to poll given device</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the network device</description>
|
||||
</parameter>
|
||||
<parameter name="mDNS" type="text">
|
||||
<label>mDNS Name</label>
|
||||
<description>The mDNS name of the network device</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
||||
@ -67,6 +67,8 @@ bridge-type.config.freeboxos.api.appToken.label = Application Token
|
||||
bridge-type.config.freeboxos.api.appToken.description = Token generated by the Freebox server
|
||||
bridge-type.config.freeboxos.api.discoverNetDevice.label = Network Device Discovery
|
||||
bridge-type.config.freeboxos.api.discoverNetDevice.description = Enable the discovery of network device things
|
||||
bridge-type.config.freeboxos.api.discoveryInterval.label = Background Discovery Interval
|
||||
bridge-type.config.freeboxos.api.discoveryInterval.description = Background discovery interval in minutes (default 10 - 0 disables background discovery)
|
||||
bridge-type.config.freeboxos.api.httpsAvailable.label = HTTPS Available
|
||||
bridge-type.config.freeboxos.api.httpsAvailable.description = Tells if https has been configured on the Freebox
|
||||
bridge-type.config.freeboxos.api.httpsPort.label = HTTPS port
|
||||
@ -77,6 +79,8 @@ thing-type.config.freeboxos.home-node.id.label = ID
|
||||
thing-type.config.freeboxos.home-node.id.description = Id of the Home Node
|
||||
thing-type.config.freeboxos.home-node.refreshInterval.label = Refresh Interval
|
||||
thing-type.config.freeboxos.home-node.refreshInterval.description = The refresh interval in seconds which is used to poll the Node
|
||||
thing-type.config.freeboxos.host.mDNS.label = mDNS Name
|
||||
thing-type.config.freeboxos.host.mDNS.description = The mDNS name of the network device
|
||||
thing-type.config.freeboxos.host.macAddress.label = MAC Address
|
||||
thing-type.config.freeboxos.host.macAddress.description = The MAC address of the network device
|
||||
thing-type.config.freeboxos.host.refreshInterval.label = Refresh Interval
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:freeboxos:host"/>
|
||||
<config-description-ref uri="thing-type:freeboxos:wifi-host"/>
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user