]> git.basschouten.com Git - openhab-addons.git/commitdiff
[Mikrotik] Add support for RouterOS 7 devices and support internal radios with capsma...
authorMatthew Skinner <matt@pcmus.com>
Fri, 18 Oct 2024 13:02:50 +0000 (00:02 +1100)
committerGitHub <noreply@github.com>
Fri, 18 Oct 2024 13:02:50 +0000 (15:02 +0200)
* Add support for API version 7 and AX wifi6 range of devices.

Signed-off-by: Matthew Skinner <matt@pcmus.com>
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/handler/MikrotikBaseThingHandler.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/handler/MikrotikInterfaceThingHandler.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/handler/MikrotikRouterosBridgeHandler.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/handler/MikrotikWirelessClientThingHandler.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosDevice.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosInterfaceType.java
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosWifiInterface.java [new file with mode: 0644]
bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosWirelessRegistration.java

index d83fb8fb1649de0da28da2b24a26f65b4ae4ae33..9967bf45ef2379a448e35144176d19d05106dbea 100644 (file)
@@ -12,8 +12,7 @@
  */
 package org.openhab.binding.mikrotik.internal.handler;
 
-import static org.openhab.core.thing.ThingStatus.OFFLINE;
-import static org.openhab.core.thing.ThingStatus.ONLINE;
+import static org.openhab.core.thing.ThingStatus.*;
 import static org.openhab.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
 import static org.openhab.core.types.RefreshType.REFRESH;
 
@@ -63,8 +62,6 @@ public abstract class MikrotikBaseThingHandler<C extends ConfigValidation> exten
     protected ExpiringCache<Boolean> refreshCache = new ExpiringCache<>(Duration.ofDays(1), () -> false);
     protected Map<String, State> currentState = new HashMap<>();
 
-    // public static boolean supportsThingType(ThingTypeUID thingTypeUID) <- in subclasses
-
     public MikrotikBaseThingHandler(Thing thing) {
         super(thing);
     }
index e951d679c0c7d00dcc6621734053921a1f725c6e..f787d02959c3a79af36255e2c1ae2a8b29094c16 100644 (file)
@@ -12,8 +12,7 @@
  */
 package org.openhab.binding.mikrotik.internal.handler;
 
-import static org.openhab.core.thing.ThingStatus.OFFLINE;
-import static org.openhab.core.thing.ThingStatus.ONLINE;
+import static org.openhab.core.thing.ThingStatus.*;
 import static org.openhab.core.thing.ThingStatusDetail.GONE;
 
 import java.math.BigDecimal;
@@ -31,6 +30,7 @@ import org.openhab.binding.mikrotik.internal.model.RouterosL2TPSrvInterface;
 import org.openhab.binding.mikrotik.internal.model.RouterosLTEInterface;
 import org.openhab.binding.mikrotik.internal.model.RouterosPPPCliInterface;
 import org.openhab.binding.mikrotik.internal.model.RouterosPPPoECliInterface;
+import org.openhab.binding.mikrotik.internal.model.RouterosWifiInterface;
 import org.openhab.binding.mikrotik.internal.model.RouterosWlanInterface;
 import org.openhab.binding.mikrotik.internal.util.RateCalculator;
 import org.openhab.binding.mikrotik.internal.util.StateUtil;
@@ -184,6 +184,8 @@ public class MikrotikInterfaceThingHandler extends MikrotikBaseThingHandler<Inte
                         newState = getEtherIterfaceChannelState(channelID);
                     } else if (iface instanceof RouterosCapInterface) {
                         newState = getCapIterfaceChannelState(channelID);
+                    } else if (iface instanceof RouterosWifiInterface) {
+                        newState = getWifiIterfaceChannelState(channelID);
                     } else if (iface instanceof RouterosWlanInterface) {
                         newState = getWlanIterfaceChannelState(channelID);
                     } else if (iface instanceof RouterosPPPCliInterface) {
@@ -264,6 +266,26 @@ public class MikrotikInterfaceThingHandler extends MikrotikBaseThingHandler<Inte
         }
     }
 
+    protected State getWifiIterfaceChannelState(String channelID) {
+        RouterosWifiInterface wlIface = (RouterosWifiInterface) this.iface;
+        if (wlIface == null) {
+            return UnDefType.UNDEF;
+        }
+
+        switch (channelID) {
+            case MikrotikBindingConstants.CHANNEL_STATE:
+                return StateUtil.stringOrNull(wlIface.getCurrentState());
+            case MikrotikBindingConstants.CHANNEL_RATE:
+                return StateUtil.stringOrNull(wlIface.getRate());
+            case MikrotikBindingConstants.CHANNEL_REGISTERED_CLIENTS:
+                return StateUtil.intOrNull(wlIface.getRegisteredClients());
+            case MikrotikBindingConstants.CHANNEL_AUTHORIZED_CLIENTS:
+                return StateUtil.intOrNull(wlIface.getAuthorizedClients());
+            default:
+                return UnDefType.UNDEF;
+        }
+    }
+
     protected State getPPPoECliChannelState(String channelID) {
         RouterosPPPoECliInterface pppCli = (RouterosPPPoECliInterface) this.iface;
         if (pppCli == null) {
index 83985d0f5ea71bec86a6215f0aadfbac2b7eac8b..448ebdde79adbacad96f3be6b62bc3461a619d1c 100644 (file)
@@ -281,6 +281,8 @@ public class MikrotikRouterosBridgeHandler extends BaseBridgeHandler {
                 case MikrotikBindingConstants.CHANNEL_CPU_LOAD:
                     newState = StateUtil.qtyPercentOrNull(rbRes.getCpuLoad());
                     break;
+                default:
+                    logger.warn("Unimplemented channel:{}", channelID);
             }
         }
 
index 2db1e195fa8e6ec6d43c221a59d711a72f4df863..9c35d7bbc67c0fd6effe046154e4681b983cf2e7 100644 (file)
@@ -27,6 +27,7 @@ import org.openhab.binding.mikrotik.internal.model.RouterosRegistrationBase;
 import org.openhab.binding.mikrotik.internal.model.RouterosWirelessRegistration;
 import org.openhab.binding.mikrotik.internal.util.RateCalculator;
 import org.openhab.binding.mikrotik.internal.util.StateUtil;
+import org.openhab.core.library.types.DecimalType;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingTypeUID;
@@ -214,11 +215,10 @@ public class MikrotikWirelessClientThingHandler extends MikrotikBaseThingHandler
         if (this.wifiReg == null) {
             return UnDefType.UNDEF;
         }
-
         RouterosWirelessRegistration wirelessReg = (RouterosWirelessRegistration) this.wifiReg;
         switch (channelID) {
             case CHANNEL_SIGNAL:
-                return StateUtil.intOrNull(wirelessReg.getRxSignal());
+                return new DecimalType(wirelessReg.getRxSignal());
             default:
                 return UnDefType.UNDEF;
         }
index dc7d881603ac643aa4cd9e42e15f610e239e478c..1d0e648e49fdc62c688b564b5905d1178553622d 100644 (file)
@@ -52,12 +52,13 @@ public class RouterosDevice {
     public static final String PROP_TYPE_KEY = "type";
     public static final String PROP_NAME_KEY = "name";
     public static final String PROP_SSID_KEY = "ssid";
-
+    public static final String PROP_CONFIG_SSID_KEY = "configuration.ssid";
     private static final String CMD_PRINT_IFACES = "/interface/print";
     private static final String CMD_PRINT_IFACE_TYPE_TPL = "/interface/%s/print";
     private static final String CMD_MONTOR_IFACE_MONITOR_TPL = "/interface/%s/monitor numbers=%s once";
     private static final String CMD_PRINT_CAPS_IFACES = "/caps-man/interface/print";
     private static final String CMD_PRINT_CAPSMAN_REGS = "/caps-man/registration-table/print";
+    private static final String CMD_PRINT_WIFI_REGS = "/interface/wifi/registration-table/print";
     private static final String CMD_PRINT_WIRELESS_REGS = "/interface/wireless/registration-table/print";
     private static final String CMD_PRINT_RESOURCE = "/system/resource/print";
     private static final String CMD_PRINT_RB_INFO = "/system/routerboard/print";
@@ -85,6 +86,8 @@ public class RouterosDevice {
                 return Optional.of(new RouterosCapInterface(interfaceProps));
             case WLAN:
                 return Optional.of(new RouterosWlanInterface(interfaceProps));
+            case WIFI:
+                return Optional.of(new RouterosWifiInterface(interfaceProps));
             case PPPOE_CLIENT:
                 return Optional.of(new RouterosPPPoECliInterface(interfaceProps));
             case PPP_CLIENT:
@@ -116,6 +119,9 @@ public class RouterosDevice {
     public void start() throws MikrotikApiException {
         login();
         updateRouterboardInfo();
+        if (rbInfo != null) {
+            logger.debug("RouterOS Version = {}", rbInfo.getFirmwareVersion());
+        }
     }
 
     public void stop() {
@@ -160,8 +166,25 @@ public class RouterosDevice {
         synchronized (this) {
             updateResources();
             updateInterfaceData();
-            updateCapsmanRegistrations();
-            updateWirelessRegistrations();
+            try {
+                updateCapsmanRegistrations();
+            } catch (MikrotikApiException e) {
+                logger.debug(
+                        "MikrotikApiException: Device may have the CAPsMAN feature for wireless management disabled.");
+            }
+            wirelessRegistrationCache.clear();
+            try {
+                updateWirelessRegistrations();
+            } catch (MikrotikApiException e) {
+                logger.debug(
+                        "MikrotikApiException: Device does not appear to have any built in CAPsMAN wireless devices.");
+            }
+            try {
+                updateWifiRegistrations();
+            } catch (MikrotikApiException e) {
+                logger.debug("MikrotikApiException: Device does not appear to have any built in wifi.");
+            }
+            logger.trace("There are {} wirelessRegistration's registered in cache.", wirelessRegistrationCache.size());
         }
     }
 
@@ -205,6 +228,7 @@ public class RouterosDevice {
         this.wlanSsid.clear();
         this.interfaceCache.clear();
         ifaceResponse.forEach(props -> {
+            logger.trace("Interface Details:{}", props.toString());
             Optional<RouterosInterfaceBase> ifaceOpt = createTypedInterface(props);
             if (ifaceOpt.isPresent()) {
                 RouterosInterfaceBase iface = ifaceOpt.get();
@@ -215,12 +239,14 @@ public class RouterosDevice {
             }
         });
 
+        // Checks if any new interfaces have been setup since last check
         Map<String, Map<String, String>> typedIfaceResponse = new HashMap<>();
         for (String ifaceApiType : interfaceTypesToPoll) {
             String cmd = String.format(CMD_PRINT_IFACE_TYPE_TPL, ifaceApiType);
             if (ifaceApiType.compareTo("cap") == 0) {
                 cmd = CMD_PRINT_CAPS_IFACES;
             }
+            logger.debug("Command used for updating the interfaces is:{}", cmd);
             connection.execute(cmd).forEach(propMap -> {
                 String ifaceName = propMap.get(PROP_NAME_KEY);
                 if (ifaceName != null) {
@@ -235,24 +261,36 @@ public class RouterosDevice {
 
         for (RouterosInterfaceBase ifaceModel : interfaceCache) {
             // Enrich with detailed data
-
             Map<String, String> additionalIfaceProps = typedIfaceResponse.get(ifaceModel.getName());
             if (additionalIfaceProps != null) {
                 ifaceModel.mergeProps(additionalIfaceProps);
             }
-            // Get monitor data
+            // Get monitor data, runs if you have added an interface thing.
             if (ifaceModel.hasMonitor() && monitoredInterfaces.contains(ifaceModel.getName())) {
                 String cmd = String.format(CMD_MONTOR_IFACE_MONITOR_TPL, ifaceModel.getApiType(), ifaceModel.getName());
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Getting detailed data for Interface:{}, with command:{}", ifaceModel.getName(), cmd);
+                }
                 List<Map<String, String>> monitorProps = connection.execute(cmd);
                 ifaceModel.mergeProps(monitorProps.get(0));
             }
             // Note SSIDs for non-CAPsMAN wireless clients
             String ifaceName = ifaceModel.getName();
-            String ifaceSsid = ifaceModel.getProperty(PROP_SSID_KEY);
+            String ifaceSsid;
+            switch (ifaceModel.getApiType()) {
+                case "wifi":
+                    ifaceSsid = ifaceModel.getProperty(PROP_CONFIG_SSID_KEY);
+                    break;
+                case "caps":
+                case "wireless":
+                default:
+                    ifaceSsid = ifaceModel.getProperty(PROP_SSID_KEY);
+            }
             if (ifaceName != null && ifaceSsid != null && !ifaceName.isBlank() && !ifaceSsid.isBlank()) {
                 this.wlanSsid.put(ifaceName, ifaceSsid);
             }
         }
+        logger.debug("Found the following SSID's:{}", wlanSsid.toString());
     }
 
     private void updateCapsmanRegistrations() throws MikrotikApiException {
@@ -273,7 +311,6 @@ public class RouterosDevice {
             return;
         }
         List<Map<String, String>> response = conn.execute(CMD_PRINT_WIRELESS_REGS);
-        wirelessRegistrationCache.clear();
         response.forEach(props -> {
             String wlanIfaceName = props.get("interface");
             String wlanSsidName = wlanSsid.get(wlanIfaceName);
@@ -285,6 +322,23 @@ public class RouterosDevice {
         });
     }
 
+    private void updateWifiRegistrations() throws MikrotikApiException {
+        ApiConnection conn = this.connection;
+        if (conn == null) {
+            return;
+        }
+        List<Map<String, String>> response = conn.execute(CMD_PRINT_WIFI_REGS);
+        response.forEach(props -> {
+            String wlanIfaceName = props.get("interface");
+            String wlanSsidName = wlanSsid.get(wlanIfaceName);
+
+            if (wlanSsidName != null && wlanIfaceName != null && !wlanIfaceName.isBlank() && !wlanSsidName.isBlank()) {
+                props.put("configuration.ssid", wlanSsidName);
+            }
+            wirelessRegistrationCache.add(new RouterosWirelessRegistration(props));
+        });
+    }
+
     private void updateResources() throws MikrotikApiException {
         ApiConnection conn = this.connection;
         if (conn == null) {
index 18391d54bb6d85473b2abd4281fc7b7fe30a8ef9..40fc26d23583a3936fb45e7d8f4d8bc257e02b66 100644 (file)
@@ -26,9 +26,12 @@ public enum RouterosInterfaceType {
     ETHERNET("ether"),
     BRIDGE("bridge"),
     WLAN("wlan"),
+    WIFI("wifi"),
     CAP("cap"),
     PPP_CLIENT("ppp-out"),
     PPPOE_CLIENT("pppoe-out"),
+    VLAN("vlan"),
+    VETH("veth"), // docker containers create virtual ether ports
     L2TP_SERVER("l2tp-in"),
     L2TP_CLIENT("l2tp-out"),
     LTE("lte");
diff --git a/bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosWifiInterface.java b/bundles/org.openhab.binding.mikrotik/src/main/java/org/openhab/binding/mikrotik/internal/model/RouterosWifiInterface.java
new file mode 100644 (file)
index 0000000..707b913
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2010-2024 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.mikrotik.internal.model;
+
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link RouterosWifiInterface} is a model class for `wifi` interface models having casting accessors for
+ * data that is specific to this network interface kind. Is a subclass of {@link RouterosInterfaceBase}.
+ *
+ * @author Matthew Skinner - Initial contribution
+ */
+@NonNullByDefault
+public class RouterosWifiInterface extends RouterosWlanInterface {
+
+    public RouterosWifiInterface(Map<String, String> props) {
+        super(props);
+    }
+
+    @Override
+    public RouterosInterfaceType getDesignedType() {
+        return RouterosInterfaceType.WIFI;
+    }
+
+    @Override
+    public String getApiType() {
+        return "wifi";
+    }
+
+    @Override
+    public int getRegisteredClients() {
+        Integer registeredClients = getIntProp("registered-peers");
+        return registeredClients == null ? 0 : registeredClients;
+    }
+
+    @Override
+    public int getAuthorizedClients() {
+        Integer authedClients = getIntProp("authorized-peers");
+        return authedClients == null ? 0 : authedClients;
+    }
+}
index 904c4c585123d52edc3ae887faf05fba88c5e7c3..4bb2dd66a4f635dc06312198b7531cb7ffbdb6b8 100644 (file)
@@ -27,12 +27,29 @@ import org.openhab.binding.mikrotik.internal.util.Converter;
  */
 @NonNullByDefault
 public class RouterosWirelessRegistration extends RouterosRegistrationBase {
+
     public RouterosWirelessRegistration(Map<String, String> props) {
         super(props);
     }
 
     public int getRxSignal() {
-        String signalValue = getProp("signal-strength", "0@hz").split("@")[0];
+        String signalValue = getProp("signal", "0");
+        if (signalValue.isEmpty()) {
+            signalValue = getProp("signal-strength", "0@hz").split("@")[0];
+        } else {
+            int value = Integer.parseInt(signalValue);
+            if (value < -80) {
+                return 0;
+            } else if (value < -75) {
+                return 1;
+            } else if (value < -68) {
+                return 2;
+            } else if (value < -55) {
+                return 3;
+            } else {
+                return 4;
+            }
+        }
         return Integer.parseInt(signalValue);
     }