From a9cc0100f6f337d459546dd37ca8cb3bed0f9d91 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Wed, 21 Dec 2022 12:08:34 -0800
Subject: [PATCH 1/4] Squashed commit of the following from initial Omada
 widget:

commit ad3e664b56ad2a9024684f2141f8f21ead59177e
Author: Benoit <oupsman@oupsman.fr>
Date:   Tue Dec 13 19:54:54 2022 +0100

    Add .idea to .gitignore

commit 7e51a093845c6bcda4fac78a2c174adbcc7701b6
Merge: 93d8035 7dd0b0e
Author: Benoit SERRA <oupsman@oupsman.fr>
Date:   Tue Dec 13 18:38:51 2022 +0100

    Merge branch 'benphelps:main' into main

commit 93d80350b1d4519ac217b880568ccabbef43f03f
Author: Benoit <oupsman@oupsman.fr>
Date:   Tue Dec 13 18:15:20 2022 +0100

    Omada widget : One widget, shows only the number alerts, the number of connected AP, the number of connected devices to Wifi, the number of connected switches and gatewawys.

commit a1babd860ce8a2dd6981b7fef6e055e249cbccb2
Author: Benoit <oupsman@oupsman.fr>
Date:   Tue Dec 13 09:33:50 2022 +0100

    Omada widget : spliting widget between WLAN and LAN/WAN fields to have no more than 5 fields per widget.

commit e12cc65c7703f2f1930fc7646c1f4386b49e73fb
Merge: 331f31f 146326f
Author: Benoit SERRA <oupsman@oupsman.fr>
Date:   Sun Dec 11 14:39:27 2022 +0100

    Merge branch 'benphelps:main' into main

commit 331f31fc2be80e0738869fd050b3034638979350
Merge: 37154e3 ccc1229
Author: Benoit SERRA <oupsman@oupsman.fr>
Date:   Sat Dec 10 17:56:44 2022 +0100

    Merge branch 'benphelps:main' into main

commit 37154e327af7d3fe66e7638ba79851ef789d3649
Author: Benoit <oupsman@oupsman.fr>
Date:   Sat Dec 10 17:11:30 2022 +0100

    Omada widget : Improved error handling
    Omada widget: handling power as common.power in translation

commit 1f484914067e514f22c1f250b62f589fedefe1fd
Author: Benoit <oupsman@oupsman.fr>
Date:   Sat Dec 10 10:24:55 2022 +0100

    Omada widget : adding stats for isolated aps, connected gateways, connected switches, available ports, power consumption

commit f375f0b815bf6697f7044995203084427b14ea69
Merge: 467b678 775b511
Author: Benoit <oupsman@oupsman.fr>
Date:   Fri Dec 9 21:06:38 2022 +0100

    Merge branch 'main' of https://github.com/Oupsman/homepage into main

commit 467b67802a7b8dface01703b6035951dcaa1e069
Author: Benoit <oupsman@oupsman.fr>
Date:   Fri Dec 9 21:06:09 2022 +0100

    Omada widget : v3 v4 and v5 versions don't use the same fields for the same stats, I've corrected the code to make it more reliable

commit 775b5111e13072a18edb33f30a4d4f0589bc2af0
Merge: 8d66756 88c4375
Author: Benoit SERRA <oupsman@oupsman.fr>
Date:   Thu Dec 8 15:38:20 2022 +0100

    Merge branch 'benphelps:main' into main

commit 8d66756a7d8f9e0b43c8abde2f2e6f2fd86a1098
Author: Benoit <oupsman@oupsman.fr>
Date:   Thu Dec 8 12:45:44 2022 +0100

    Omada Widget : code cleanup

commit 282a6d0592c53a39184d63bba517f482a84f5b36
Author: Benoit <oupsman@oupsman.fr>
Date:   Thu Dec 8 12:42:41 2022 +0100

    Omada Widget : code cleanup

commit c3e9b8f87075e834ea1a520fd8c93708afb15ac0
Author: Benoit <oupsman@oupsman.fr>
Date:   Thu Dec 8 12:37:10 2022 +0100

    Omada Widget : No more legacy variable, the code detects the controller version and adapts the requests. Logic is not duplicated anymore

commit eafcc205975cc1bd04f063f1627d380f2a2b7f04
Author: Benoit <oupsman@oupsman.fr>
Date:   Wed Dec 7 15:46:00 2022 +0100

    V2 API is working

commit bcc2864ee2e1f0f1d2f4c009df1ba8a1a7244f80
Author: Benoit <oupsman@oupsman.fr>
Date:   Wed Dec 7 10:01:26 2022 +0100

    Code fore v2 API is not working but V1 code is.

commit ea8e297e849c2ef5659bfec94d76d2fff8677c4c
Author: Benoit <oupsman@oupsman.fr>
Date:   Tue Dec 6 14:28:05 2022 +0100

    Errors handling

commit ab6d51a88c8737654dd31bec46106d7c49ed39d2
Author: Benoit <oupsman@oupsman.fr>
Date:   Tue Dec 6 09:50:14 2022 +0100

    Adding alerts

commit 047db2cce867c0207be7fe0827b24107fdd84923
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 22:53:43 2022 +0100

    Fixed translation system

commit 42c5a3e6658f22662b1c58f54cba31dc90bfbc61
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 22:34:34 2022 +0100

    Translation system is still * up

commit c80eac9d5bd5491ec4a61da38cdaf82f0ea1cc2f
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 22:33:50 2022 +0100

    Translation system is still * up

commit f8ba6b02454d66eb96e7e6ebd8022492ff79e690
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 22:32:22 2022 +0100

    Translation system is still * up

commit dec7eec6de26298eb7c90fd01e1c0fd23b6469a4
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 22:16:13 2022 +0100

    Translation system is * up

commit cc840cf7ccb40509f1da3f521a4a1b3e26498ac0
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 21:33:00 2022 +0100

    First working version

commit 54b65e619e41c9963a614a177df3a4af68ebe77d
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 18:59:09 2022 +0100

    Using getGlobalStat method

commit 7ebc8500da9d52bd2911a620179fb6585f044c47
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 14:33:37 2022 +0100

    Working on Omada Widget : NOT WORKING FOR NOW

commit 04eaf28cae1be0935cb190e50ae5b75c19254403
Merge: 61065ac 826fe15
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 10:32:30 2022 +0100

    Merge branch 'main' of https://github.com/Oupsman/homepage into main

commit 61065ace2887c3c1d6486001d34ce6f58d58958d
Author: Benoit <oupsman@oupsman.fr>
Date:   Mon Dec 5 10:24:57 2022 +0100

    Working on Omada Widget

remove idea

Co-Authored-By: Benoit SERRA <11260343+oupsman@users.noreply.github.com>
---
 public/locales/en/common.json   |  10 ++
 src/widgets/components.js       |   1 +
 src/widgets/omada/component.jsx |  41 +++++
 src/widgets/omada/proxy.js      | 272 ++++++++++++++++++++++++++++++++
 src/widgets/omada/widget.js     |  15 ++
 src/widgets/widgets.js          |   2 +
 6 files changed, 341 insertions(+)
 create mode 100644 src/widgets/omada/component.jsx
 create mode 100644 src/widgets/omada/proxy.js
 create mode 100644 src/widgets/omada/widget.js

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index d8bfb039..78c5dce8 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -88,6 +88,16 @@
         "bitrate": "Bitrate",
         "no_active": "No Active Streams"
     },
+    "omada": {
+      "activeUser": "Active devices",
+      "alerts": "Alerts",
+      "connectedAp": "Connected APs",
+      "isolatedAp": "Isolated APs",
+      "powerConsumption": "Power consumption",
+      "availablePorts"  : "Available ports",
+      "connectedGateway": "Connected gateways",
+      "connectedSwitches": "Connected switches"
+    },
     "nzbget": {
         "rate": "Rate",
         "remaining": "Remaining",
diff --git a/src/widgets/components.js b/src/widgets/components.js
index eb7c686f..9f3011b3 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -26,6 +26,7 @@ const components = {
   nextdns: dynamic(() => import("./nextdns/component")),
   npm: dynamic(() => import("./npm/component")),
   nzbget: dynamic(() => import("./nzbget/component")),
+  omada: dynamic(() => import("./omada/component")),
   ombi: dynamic(() => import("./ombi/component")),
   overseerr: dynamic(() => import("./overseerr/component")),
   paperlessngx: dynamic(() => import("./paperlessngx/component")),
diff --git a/src/widgets/omada/component.jsx b/src/widgets/omada/component.jsx
new file mode 100644
index 00000000..d499da36
--- /dev/null
+++ b/src/widgets/omada/component.jsx
@@ -0,0 +1,41 @@
+import { useTranslation } from "next-i18next";
+
+import useWidgetAPI from "../../utils/proxy/use-widget-api";
+import Container from "../../components/services/widget/container";
+import Block from "../../components/services/widget/block";
+
+export default function Component({ service }) {
+  const { t } = useTranslation();
+
+  const { widget } = service;
+
+  const { data: omadaData, error: omadaAPIError } = useWidgetAPI(widget, "stats", {
+    refreshInterval: 5000,
+  });
+
+  if (omadaAPIError) {
+    return <Container error={omadaAPIError} />;
+  }
+
+  if (!omadaData) {
+    return (
+      <Container service={service}>
+        <Block label="omada.connectedAp" />
+        <Block label="omada.activeUser" />
+        <Block label="omada.alerts" />
+        <Block label="omada.connectedGateway" />
+        <Block label="omada.connectedSwitches" />
+      </Container>
+    );
+  }
+
+  return (
+    <Container service={service}>
+      <Block label="omada.connectedAp" value={t( "common.number", { value: omadaData.connectedAp})} />
+      <Block label="omada.activeUser" value={t( "common.number", { value: omadaData.activeUser })} />
+      <Block label="omada.alerts" value={t( "common.number", { value: omadaData.alerts })} />
+      { omadaData.connectedGateways > 0 && <Block label="omada.connectedGateway" value={t("common.number", { value: omadaData.connectedGateways})} /> }
+      { omadaData.connectedSwitches > 0 && <Block label="omada.connectedSwitches" value={t("common.number", { value: omadaData.connectedSwitches})} /> }
+    </Container>
+  );
+}
diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js
new file mode 100644
index 00000000..58263052
--- /dev/null
+++ b/src/widgets/omada/proxy.js
@@ -0,0 +1,272 @@
+
+import { httpProxy } from "utils/proxy/http";
+import getServiceWidget from "utils/config/service-helpers";
+import createLogger from "utils/logger";
+import widgets from "widgets/widgets";
+
+const proxyName = "omadaProxyHandler";
+
+const logger = createLogger(proxyName);
+
+
+async function login(loginUrl, username, password, cversion) {
+  let params;
+  if (cversion < "4.0.0") {
+    // change the parameters of the query string
+    params = JSON.stringify({
+      "method": "login",
+      "params": {
+        "name": username,
+        "password": password
+      }
+    });
+  } else {
+    params = JSON.stringify({
+        "username": username,
+        "password": password
+    });
+  }
+  const authResponse = await httpProxy(loginUrl, {
+      method: "POST",
+      body: params,
+        headers: {
+          "Content-Type": "application/json",
+        },
+      });
+
+  const data = JSON.parse(authResponse[2]);
+  const status = authResponse[0];
+  let token;
+  if (data.errorCode === 0) {
+    token = data.result.token;
+  } else {
+    token = null;
+  }
+  return [status, token ?? data];
+}
+
+
+export default async function omadaProxyHandler(req, res) {
+  const { group, service } = req.query;
+
+  if (group && service) {
+    const widget = await getServiceWidget(group, service);
+
+    if (!widgets?.[widget.type]?.api) {
+      return res.status(403).json({ error: "Service does not support API calls" });
+    }
+
+    if (widget) {
+      let cid;
+      let cversion;
+      let connectedAp;
+      let activeuser;
+      let connectedSwitches;
+      let connectedGateways;
+
+      let alerts;
+      let loginUrl;
+      let siteName;
+      let requestresponse;
+
+      const {url} = widget;
+
+      const controllerInfoUrl = `${widget.url}/api/info`;
+
+      const cInfoResponse = await httpProxy(controllerInfoUrl, {
+          method: "GET",
+          headers: {
+            "Content-Type": "application/json",
+          },
+      });
+
+
+      if (cInfoResponse[0] === 500) {
+        logger.debug("Getting controller version ends with Error 500");
+        return res.status(cInfoResponse[0]).json({error: {message: "HTTP Error", controllerInfoUrl, data: cInfoResponse[2]}});
+
+      }
+      const cidresult = cInfoResponse[2];
+
+      try {
+        cid = JSON.parse(cidresult).result.omadacId;
+        cversion = JSON.parse(cidresult).result.controllerVer;
+      } catch (e) {
+        cversion = "3.2.17"
+      }
+      if (cversion < "4.0.0") {
+        loginUrl = `${widget.url}/api/user/login?ajax`;
+      } else if (cversion < "5.0.0") {
+        loginUrl = `${widget.url}/api/v2/login`;
+      } else {
+        loginUrl = `${widget.url}/${cid}/api/v2/login`;
+      }
+      requestresponse = await login(loginUrl, widget.username, widget.password, cversion);
+
+      if (requestresponse[1].errorCode) {
+        return res.status(requestresponse[0]).json({error: {message: "Error logging in", url, data: requestresponse[1]}});
+      }
+
+     const token = requestresponse[1];
+      // Switching to the site we want to gather stats from
+      // First, we get the list of sites
+      let sitesUrl;
+      let body;
+      let params;
+      let headers;
+      let method;
+      let sitetoswitch;
+      if (cversion < "4.0.0") {
+        sitesUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
+        body = JSON.stringify({
+          "method": "getUserSites",
+          "params": {
+            "userName": widget.username
+          }});
+        params = { "token": token };
+        headers = { };
+        method = "POST";
+      } else if (cversion < "5.0.0") {
+        sitesUrl = `${widget.url}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
+        body = {};
+        params = {"token": token};
+        headers = {"Csrf-Token": token };
+        method = "GET";
+
+      } else {
+        sitesUrl = `${widget.url}/${cid}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
+        body = {};
+        headers = { "Csrf-Token": token };
+        method = "GET";
+        params = { };
+      }
+      requestresponse = await httpProxy(sitesUrl, {
+        method,
+        params,
+        body: body.toString(),
+        headers,
+      });
+      const listresult = JSON.parse(requestresponse[2]);
+      if (listresult.errorCode !== 0) {
+        logger.debug(`HTTTP ${requestresponse[0]} getting sites list: ${requestresponse[2].msg}`);
+        return res.status(requestresponse[0]).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
+      }
+
+      // Switching site is really needed only for Omada 3.x.x controllers
+
+      let switchUrl;
+
+      if (cversion < "4.0.0") {
+        sitetoswitch = listresult.result.siteList.filter(site => site.name === widget.site);
+        siteName = sitetoswitch[0].siteName;
+        switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
+        method = "POST";
+        body = JSON.stringify({
+          "method": "switchSite",
+          "params": {
+            "siteName": siteName,
+            "userName": widget.username
+          }
+        });
+        headers = { "Content-Type": "application/json" };
+        params = { "token": token };
+        requestresponse = await httpProxy(switchUrl, {
+          method,
+          params,
+          body: body.toString(),
+          headers,
+        });
+        const switchresult = JSON.parse(requestresponse[2]);
+        if (switchresult.errorCode !== 0) {
+          logger.debug(`HTTTP ${requestresponse[0]} getting sites list: ${requestresponse[2]}`);
+          return res.status(requestresponse[0]).json({error: {message: "Error switching site", url, data: requestresponse[2]}});
+        }
+      }
+
+      // OK now we are on the correct site. Let's get the stats
+      // on modern controller, we need to call two different endpoints
+      // on older controller, we can call one endpoint
+      if (cversion < "4.0.0") {
+        const statsUrl = `${widget.url}/web/v1/controller?getGlobalStat=&token=${token}`;
+        const statResponse = await httpProxy(statsUrl, {
+          method: "POST",
+          params: { "token": token },
+          body: JSON.stringify({
+            "method": "getGlobalStat",
+          }),
+          headers: {
+            "Content-Type": "application/json",
+          },
+        });
+
+        const data = JSON.parse(statResponse[2]);
+
+        if (data.errorCode !== 0) {
+          return res.status(statResponse[0]).json({error: {message: "Error getting stats", url, data: statResponse[2]}});
+        }
+        connectedAp = data.result.connectedAp;
+        activeuser = data.result.activeUser;
+        alerts = data.result.alerts;
+
+      } else {
+        let siteStatsUrl;
+        let response;
+        sitetoswitch = listresult.result.data.filter(site => site.name === widget.site);
+
+        if (sitetoswitch.length === 0) {
+          return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data: requestresponse[2]}});
+        }
+
+        // On 5.0.0, the field we need is id, on 4.x.x, it's key ...
+        siteName = sitetoswitch[0].id ?? sitetoswitch[0].key;
+        if (cversion < "5.0.0") {
+          siteStatsUrl = `${url}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000`;
+        } else {
+          siteStatsUrl = `${url}/${cid}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000`;
+        }
+        response = await httpProxy(siteStatsUrl, {
+          method: "GET",
+          headers: {
+            "Csrf-Token": token,
+          },
+        });
+
+        const clientresult = JSON.parse(response[2]);
+        if (clientresult.errorCode !== 0) {
+          logger.debug(`HTTTP ${listresult.errorCode} getting clients stats for site ${widget.site} with message ${listresult.msg}`);
+          return res.status(500).send(response[2]);
+        }
+
+        activeuser = clientresult.result.totalClientNum;
+        connectedAp = clientresult.result.connectedApNum;
+        connectedGateways = clientresult.result.connectedGatewayNum;
+        connectedSwitches = clientresult.result.connectedSwitchNum;
+
+
+        let alertUrl;
+        if (cversion >= "5.0.0") {
+          alertUrl = `${url}/${cid}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
+        } else {
+          alertUrl = `${url}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
+        }
+        response = await httpProxy(alertUrl, {
+          method: "GET",
+          headers: {
+            "Csrf-Token": token,
+          },
+        });
+        const alertresult = JSON.parse(response[2]);
+        alerts = alertresult.result.alertNum;
+      }
+
+      return res.send(JSON.stringify({
+        "connectedAp": connectedAp,
+        "activeUser": activeuser,
+        "alerts": alerts,
+        "connectedGateways": connectedGateways,
+        "connectedSwitches": connectedSwitches,
+      }));
+    }
+  }
+  return res.status(400).json({ error: "Invalid proxy service type" });
+}
diff --git a/src/widgets/omada/widget.js b/src/widgets/omada/widget.js
new file mode 100644
index 00000000..0ef4177e
--- /dev/null
+++ b/src/widgets/omada/widget.js
@@ -0,0 +1,15 @@
+import omadaProxyHandler from "./proxy";
+// import genericProxyHandler from "../../utils/proxy/handlers/generic";
+
+const widget = {
+  api: "{url}/web/v1/{endpoint}",
+  proxyHandler: omadaProxyHandler,
+
+  mappings: {
+    stats: {
+      endpoint: "controller",
+    }
+  }
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index 2b45e55a..c68dfe3e 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -21,6 +21,7 @@ import navidrome from "./navidrome/widget";
 import nextdns from "./nextdns/widget";
 import npm from "./npm/widget";
 import nzbget from "./nzbget/widget";
+import omada from "./omada/widget";
 import ombi from "./ombi/widget";
 import overseerr from "./overseerr/widget";
 import paperlessngx from "./paperlessngx/widget";
@@ -73,6 +74,7 @@ const widgets = {
   nextdns,
   npm,
   nzbget,
+  omada,
   ombi,
   overseerr,
   paperlessngx,

From 4a3f836020b8461c4baaa39d5e0fc24d3e08144f Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Wed, 21 Dec 2022 12:20:17 -0800
Subject: [PATCH 2/4] Refactor Omada proxy for v4/v5

---
 src/widgets/omada/proxy.js | 291 ++++++++++++++++++-------------------
 1 file changed, 139 insertions(+), 152 deletions(-)

diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js
index 58263052..d5f4d9ba 100644
--- a/src/widgets/omada/proxy.js
+++ b/src/widgets/omada/proxy.js
@@ -9,40 +9,26 @@ const proxyName = "omadaProxyHandler";
 const logger = createLogger(proxyName);
 
 
-async function login(loginUrl, username, password, cversion) {
-  let params;
-  if (cversion < "4.0.0") {
-    // change the parameters of the query string
-    params = JSON.stringify({
-      "method": "login",
-      "params": {
-        "name": username,
-        "password": password
-      }
-    });
-  } else {
-    params = JSON.stringify({
-        "username": username,
-        "password": password
-    });
+async function login(loginUrl, username, password, controllerVersionMajor) {
+  const params = {
+    username: username,
+    password: password
   }
-  const authResponse = await httpProxy(loginUrl, {
-      method: "POST",
-      body: params,
-        headers: {
-          "Content-Type": "application/json",
-        },
-      });
 
-  const data = JSON.parse(authResponse[2]);
-  const status = authResponse[0];
-  let token;
-  if (data.errorCode === 0) {
-    token = data.result.token;
-  } else {
-    token = null;
+  if (controllerVersionMajor < 4) {
+    params.method = "login";
+    params.name = username;
   }
-  return [status, token ?? data];
+  
+  const [status, contentType, data] = await httpProxy(loginUrl, {
+    method: "POST",
+    body: JSON.stringify(params),
+    headers: {
+      "Content-Type": "application/json",
+    },
+  });
+
+  return [status, JSON.parse(data.toString())];
 }
 
 
@@ -57,108 +43,121 @@ export default async function omadaProxyHandler(req, res) {
     }
 
     if (widget) {
-      let cid;
-      let cversion;
-      let connectedAp;
-      let activeuser;
-      let connectedSwitches;
-      let connectedGateways;
-
-      let alerts;
-      let loginUrl;
-      let siteName;
-      let requestresponse;
 
       const {url} = widget;
 
-      const controllerInfoUrl = `${widget.url}/api/info`;
+      const controllerInfoURL = `${widget.url}/api/info`;
 
-      const cInfoResponse = await httpProxy(controllerInfoUrl, {
-          method: "GET",
+      let [status, contentType, data] = await httpProxy(controllerInfoURL, {
           headers: {
             "Content-Type": "application/json",
           },
       });
 
-
-      if (cInfoResponse[0] === 500) {
-        logger.debug("Getting controller version ends with Error 500");
-        return res.status(cInfoResponse[0]).json({error: {message: "HTTP Error", controllerInfoUrl, data: cInfoResponse[2]}});
-
+      if (status !== 200) {
+        logger.error("Unable to retrieve Omada controller info");
+        return res.status(status).json({error: {message: `HTTP Error ${status}`, url: controllerInfoURL, data: data}});
       }
-      const cidresult = cInfoResponse[2];
+
+      const cId = JSON.parse(data).result.omadacId;
+      let controllerVersion;
 
       try {
-        cid = JSON.parse(cidresult).result.omadacId;
-        cversion = JSON.parse(cidresult).result.controllerVer;
+        controllerVersion = JSON.parse(data).result.controllerVer;
       } catch (e) {
-        cversion = "3.2.17"
-      }
-      if (cversion < "4.0.0") {
-        loginUrl = `${widget.url}/api/user/login?ajax`;
-      } else if (cversion < "5.0.0") {
-        loginUrl = `${widget.url}/api/v2/login`;
-      } else {
-        loginUrl = `${widget.url}/${cid}/api/v2/login`;
-      }
-      requestresponse = await login(loginUrl, widget.username, widget.password, cversion);
-
-      if (requestresponse[1].errorCode) {
-        return res.status(requestresponse[0]).json({error: {message: "Error logging in", url, data: requestresponse[1]}});
+        // fallback to this random version?
+        controllerVersion = "3.2.17"
       }
 
-     const token = requestresponse[1];
-      // Switching to the site we want to gather stats from
-      // First, we get the list of sites
+      const controllerVersionMajor = parseInt(controllerVersion.split('.')[0], 10)
+
+      if (![3,4,5].includes(controllerVersionMajor)) {
+        return res.status(500).json({error: {message: "Error determining controller version", data}});
+      }
+
+      let loginUrl;
+
+      switch (controllerVersionMajor) {
+        case 3:
+          loginUrl = `${widget.url}/api/user/login?ajax`;
+          break;
+        case 4:
+          loginUrl = `${widget.url}/api/v2/login`;
+          break;
+        case 5:
+          loginUrl = `${widget.url}/${cId}/api/v2/login`;
+          break;
+        default:
+          break;
+      }
+      
+      const [loginStatus, loginResponseData] = await login(loginUrl, widget.username, widget.password, controllerVersionMajor);
+
+      if (loginStatus !== 200 || loginResponseData.errorCode > 0) {
+        return res.status(requestresponse[0]).json({error: {message: "Error logging in to Oamda controller", url: loginUrl, data: loginResponseData}});
+      }
+
+      const token = loginResponseData.result.token;
+      
+      // List sites
       let sitesUrl;
-      let body;
-      let params;
-      let headers;
-      let method;
-      let sitetoswitch;
-      if (cversion < "4.0.0") {
-        sitesUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
-        body = JSON.stringify({
-          "method": "getUserSites",
-          "params": {
-            "userName": widget.username
-          }});
-        params = { "token": token };
-        headers = { };
-        method = "POST";
-      } else if (cversion < "5.0.0") {
-        sitesUrl = `${widget.url}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
-        body = {};
-        params = {"token": token};
-        headers = {"Csrf-Token": token };
-        method = "GET";
+      let body = {};
+      let params = { token };
+      let headers = { "Csrf-Token": token };
+      let method = "GET";
 
-      } else {
-        sitesUrl = `${widget.url}/${cid}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
-        body = {};
-        headers = { "Csrf-Token": token };
-        method = "GET";
-        params = { };
+      switch (controllerVersionMajor) {
+        case 3:
+          sitesUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
+          body = {
+            "method": "getUserSites",
+            "params": {
+              "userName": widget.username
+            }
+          };
+          method = "POST";
+          break;
+        case 4:
+          sitesUrl = `${widget.url}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;          
+          break;
+        case 5:
+          sitesUrl = `${widget.url}/${cId}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
+          break;
       }
-      requestresponse = await httpProxy(sitesUrl, {
+      
+      [status, contentType, data] = await httpProxy(sitesUrl, {
         method,
         params,
-        body: body.toString(),
+        body: JSON.stringify(body),
         headers,
       });
-      const listresult = JSON.parse(requestresponse[2]);
-      if (listresult.errorCode !== 0) {
-        logger.debug(`HTTTP ${requestresponse[0]} getting sites list: ${requestresponse[2].msg}`);
-        return res.status(requestresponse[0]).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
+
+      const sitesResponseData = JSON.parse(data);
+
+      let site;
+
+      let connectedAp;
+      let activeUser;
+      let connectedSwitches;
+      let connectedGateways;
+      let alerts;
+
+      if (sitesResponseData.errorCode > 0) {
+        logger.debug(`HTTTP ${status} getting sites list: ${requestresponse[2].msg}`);
+        return res.status(status).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
       }
 
-      // Switching site is really needed only for Omada 3.x.x controllers
+      // on modern controller, we need to call two different endpoints
+      // on older controller, we can call one endpoint
 
-      let switchUrl;
+      if (controllerVersionMajor === 3) {
 
-      if (cversion < "4.0.0") {
-        sitetoswitch = listresult.result.siteList.filter(site => site.name === widget.site);
-        siteName = sitetoswitch[0].siteName;
+        // Switching site is really needed only for Omada 3.x.x controllers
+  
+        let switchUrl;
+  
+        site = listresult.result.siteList.filter(site => site.name === widget.site);
+        siteName = site[0].siteName;
         switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
         method = "POST";
         body = JSON.stringify({
@@ -181,12 +180,7 @@ export default async function omadaProxyHandler(req, res) {
           logger.debug(`HTTTP ${requestresponse[0]} getting sites list: ${requestresponse[2]}`);
           return res.status(requestresponse[0]).json({error: {message: "Error switching site", url, data: requestresponse[2]}});
         }
-      }
-
-      // OK now we are on the correct site. Let's get the stats
-      // on modern controller, we need to call two different endpoints
-      // on older controller, we can call one endpoint
-      if (cversion < "4.0.0") {
+        
         const statsUrl = `${widget.url}/web/v1/controller?getGlobalStat=&token=${token}`;
         const statResponse = await httpProxy(statsUrl, {
           method: "POST",
@@ -205,68 +199,61 @@ export default async function omadaProxyHandler(req, res) {
           return res.status(statResponse[0]).json({error: {message: "Error getting stats", url, data: statResponse[2]}});
         }
         connectedAp = data.result.connectedAp;
-        activeuser = data.result.activeUser;
+        activeUser = data.result.activeUser;
         alerts = data.result.alerts;
 
-      } else {
-        let siteStatsUrl;
-        let response;
-        sitetoswitch = listresult.result.data.filter(site => site.name === widget.site);
+      } else if (controllerVersionMajor === 4 || controllerVersionMajor === 5) {
+        site = sitesResponseData.result.data.find(site => site.name === widget.site);
 
-        if (sitetoswitch.length === 0) {
-          return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data: requestresponse[2]}});
+        if (site.length === 0) {
+          return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data}});
         }
 
-        // On 5.0.0, the field we need is id, on 4.x.x, it's key ...
-        siteName = sitetoswitch[0].id ?? sitetoswitch[0].key;
-        if (cversion < "5.0.0") {
-          siteStatsUrl = `${url}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000`;
-        } else {
-          siteStatsUrl = `${url}/${cid}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000`;
-        }
-        response = await httpProxy(siteStatsUrl, {
-          method: "GET",
+        const siteName = (controllerVersionMajor === 5) ? site.id : site.key;
+        const siteStatsUrl = (controllerVersionMajor === 4) ? 
+          `${url}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000` :
+          `${url}/${cId}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000`;
+
+        [status, contentType, data] = await httpProxy(siteStatsUrl, {
           headers: {
             "Csrf-Token": token,
           },
         });
 
-        const clientresult = JSON.parse(response[2]);
-        if (clientresult.errorCode !== 0) {
-          logger.debug(`HTTTP ${listresult.errorCode} getting clients stats for site ${widget.site} with message ${listresult.msg}`);
-          return res.status(500).send(response[2]);
+        const siteResponseData = JSON.parse(data);
+        
+        if (status !== 200 || siteResponseData.errorCode > 0) {
+          logger.debug(`HTTP ${status} getting stats for site ${widget.site} with message ${listresult.msg}`);
+          return res.status(500).send(data);
         }
 
-        activeuser = clientresult.result.totalClientNum;
-        connectedAp = clientresult.result.connectedApNum;
-        connectedGateways = clientresult.result.connectedGatewayNum;
-        connectedSwitches = clientresult.result.connectedSwitchNum;
+        activeUser = siteResponseData.result.totalClientNum;
+        connectedAp = siteResponseData.result.connectedApNum;
+        connectedGateways = siteResponseData.result.connectedGatewayNum;
+        connectedSwitches = siteResponseData.result.connectedSwitchNum;
 
+        const alertUrl = (controllerVersionMajor === 4) ? 
+          `${url}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000` :
+          `${url}/${cId}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
 
-        let alertUrl;
-        if (cversion >= "5.0.0") {
-          alertUrl = `${url}/${cid}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
-        } else {
-          alertUrl = `${url}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
-        }
-        response = await httpProxy(alertUrl, {
-          method: "GET",
+        [status, contentType, data] = await httpProxy(alertUrl, {
           headers: {
             "Csrf-Token": token,
           },
         });
-        const alertresult = JSON.parse(response[2]);
-        alerts = alertresult.result.alertNum;
+        const alertResponseData = JSON.parse(data);
+        alerts = alertResponseData.result.alertNum;
       }
 
       return res.send(JSON.stringify({
-        "connectedAp": connectedAp,
-        "activeUser": activeuser,
-        "alerts": alerts,
-        "connectedGateways": connectedGateways,
-        "connectedSwitches": connectedSwitches,
+        connectedAp,
+        activeUser,
+        alerts,
+        connectedGateways,
+        connectedSwitches,
       }));
     }
   }
+  
   return res.status(400).json({ error: "Invalid proxy service type" });
 }

From b01e6eaf5661e94112fed7f012f387a6839167a2 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Wed, 21 Dec 2022 12:51:20 -0800
Subject: [PATCH 3/4] Refactor Omada proxy for api v3

---
 src/widgets/omada/proxy.js | 100 ++++++++++++++++++-------------------
 1 file changed, 48 insertions(+), 52 deletions(-)

diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js
index d5f4d9ba..d867e112 100644
--- a/src/widgets/omada/proxy.js
+++ b/src/widgets/omada/proxy.js
@@ -15,9 +15,12 @@ async function login(loginUrl, username, password, controllerVersionMajor) {
     password: password
   }
 
-  if (controllerVersionMajor < 4) {
-    params.method = "login";
-    params.name = username;
+  if (controllerVersionMajor === 3) {
+    params["method"] = "login";
+    params["params"] = {
+      name: username,
+      password
+    };
   }
   
   const [status, contentType, data] = await httpProxy(loginUrl, {
@@ -59,14 +62,14 @@ export default async function omadaProxyHandler(req, res) {
         return res.status(status).json({error: {message: `HTTP Error ${status}`, url: controllerInfoURL, data: data}});
       }
 
-      const cId = JSON.parse(data).result.omadacId;
+      let cId;
       let controllerVersion;
 
       try {
+        cId = JSON.parse(data).result.omadacId;
         controllerVersion = JSON.parse(data).result.controllerVer;
       } catch (e) {
-        // fallback to this random version?
-        controllerVersion = "3.2.17"
+        controllerVersion = "3.2.x"
       }
 
       const controllerVersionMajor = parseInt(controllerVersion.split('.')[0], 10)
@@ -134,7 +137,20 @@ export default async function omadaProxyHandler(req, res) {
 
       const sitesResponseData = JSON.parse(data);
 
-      let site;
+      if (sitesResponseData.errorCode > 0) {
+        logger.debug(`HTTTP ${status} getting sites list: ${requestresponse[2].msg}`);
+        return res.status(status).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
+      }
+
+      const site = (controllerVersionMajor === 3) ? 
+      sitesResponseData.result.siteList.find(site => site.name === widget.site):
+        sitesResponseData.result.data.find(site => site.name === widget.site);
+
+      if (!site) {
+        return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data}});
+      }
+
+      let siteResponseData;
 
       let connectedAp;
       let activeUser;
@@ -142,73 +158,53 @@ export default async function omadaProxyHandler(req, res) {
       let connectedGateways;
       let alerts;
 
-      if (sitesResponseData.errorCode > 0) {
-        logger.debug(`HTTTP ${status} getting sites list: ${requestresponse[2].msg}`);
-        return res.status(status).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
-      }
-
-      // on modern controller, we need to call two different endpoints
-      // on older controller, we can call one endpoint
-
       if (controllerVersionMajor === 3) {
-
-        // Switching site is really needed only for Omada 3.x.x controllers
-  
-        let switchUrl;
-  
-        site = listresult.result.siteList.filter(site => site.name === widget.site);
-        siteName = site[0].siteName;
-        switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
+        // Omada 3.x.x controller requires switching site
+        const switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
         method = "POST";
-        body = JSON.stringify({
+        body = {
           "method": "switchSite",
           "params": {
-            "siteName": siteName,
+            "siteName": site.siteName,
             "userName": widget.username
           }
-        });
+        };
         headers = { "Content-Type": "application/json" };
         params = { "token": token };
-        requestresponse = await httpProxy(switchUrl, {
+
+        [status, contentType, data] = await httpProxy(switchUrl, {
           method,
           params,
-          body: body.toString(),
+          body: JSON.stringify(body),
           headers,
         });
-        const switchresult = JSON.parse(requestresponse[2]);
-        if (switchresult.errorCode !== 0) {
-          logger.debug(`HTTTP ${requestresponse[0]} getting sites list: ${requestresponse[2]}`);
-          return res.status(requestresponse[0]).json({error: {message: "Error switching site", url, data: requestresponse[2]}});
+
+        const switchResponseData = JSON.parse(data);
+        if (status !== 200 || switchResponseData.errorCode > 0) {
+          logger.error(`HTTP ${status} getting sites list: ${data}`);
+          return res.status(status).json({error: {message: "Error switching site", url: switchUrl, data}});
         }
         
         const statsUrl = `${widget.url}/web/v1/controller?getGlobalStat=&token=${token}`;
-        const statResponse = await httpProxy(statsUrl, {
-          method: "POST",
-          params: { "token": token },
+        [status, contentType, data] = await httpProxy(statsUrl, {
+          method,
+          params,
           body: JSON.stringify({
             "method": "getGlobalStat",
           }),
-          headers: {
-            "Content-Type": "application/json",
-          },
+          headers
         });
 
-        const data = JSON.parse(statResponse[2]);
+        siteResponseData = JSON.parse(data);
 
-        if (data.errorCode !== 0) {
-          return res.status(statResponse[0]).json({error: {message: "Error getting stats", url, data: statResponse[2]}});
+        if (status !== 200 || siteResponseData.errorCode > 0) {
+          return res.status(status).json({error: {message: "Error getting stats", url: statsUrl, data}});
         }
-        connectedAp = data.result.connectedAp;
-        activeUser = data.result.activeUser;
-        alerts = data.result.alerts;
 
+        connectedAp = siteResponseData.result.connectedAp;
+        activeUser = siteResponseData.result.activeUser;
+        alerts = siteResponseData.result.alerts;
       } else if (controllerVersionMajor === 4 || controllerVersionMajor === 5) {
-        site = sitesResponseData.result.data.find(site => site.name === widget.site);
-
-        if (site.length === 0) {
-          return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data}});
-        }
-
         const siteName = (controllerVersionMajor === 5) ? site.id : site.key;
         const siteStatsUrl = (controllerVersionMajor === 4) ? 
           `${url}/api/v2/sites/${siteName}/dashboard/overviewDiagram?token=${token}&currentPage=1&currentPageSize=1000` :
@@ -220,7 +216,7 @@ export default async function omadaProxyHandler(req, res) {
           },
         });
 
-        const siteResponseData = JSON.parse(data);
+        siteResponseData = JSON.parse(data);
         
         if (status !== 200 || siteResponseData.errorCode > 0) {
           logger.debug(`HTTP ${status} getting stats for site ${widget.site} with message ${listresult.msg}`);
@@ -254,6 +250,6 @@ export default async function omadaProxyHandler(req, res) {
       }));
     }
   }
-  
+
   return res.status(400).json({ error: "Invalid proxy service type" });
 }

From 952305492cf28fe523a6afac121bbfdd2d320608 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Wed, 21 Dec 2022 13:04:14 -0800
Subject: [PATCH 4/4] More Omada widget cleanup

---
 public/locales/en/common.json   | 13 +++----
 src/widgets/omada/component.jsx |  4 +--
 src/widgets/omada/proxy.js      | 61 ++++++++++++++++-----------------
 src/widgets/omada/widget.js     |  8 -----
 4 files changed, 35 insertions(+), 51 deletions(-)

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 78c5dce8..9f5637bf 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -89,14 +89,11 @@
         "no_active": "No Active Streams"
     },
     "omada": {
-      "activeUser": "Active devices",
-      "alerts": "Alerts",
-      "connectedAp": "Connected APs",
-      "isolatedAp": "Isolated APs",
-      "powerConsumption": "Power consumption",
-      "availablePorts"  : "Available ports",
-      "connectedGateway": "Connected gateways",
-      "connectedSwitches": "Connected switches"
+        "connectedAp": "Connected APs",
+        "activeUser": "Active devices",
+        "alerts": "Alerts",
+        "connectedGateway": "Connected gateways",
+        "connectedSwitches": "Connected switches"
     },
     "nzbget": {
         "rate": "Rate",
diff --git a/src/widgets/omada/component.jsx b/src/widgets/omada/component.jsx
index d499da36..dee60842 100644
--- a/src/widgets/omada/component.jsx
+++ b/src/widgets/omada/component.jsx
@@ -9,7 +9,7 @@ export default function Component({ service }) {
 
   const { widget } = service;
 
-  const { data: omadaData, error: omadaAPIError } = useWidgetAPI(widget, "stats", {
+  const { data: omadaData, error: omadaAPIError } = useWidgetAPI(widget, {
     refreshInterval: 5000,
   });
 
@@ -23,8 +23,6 @@ export default function Component({ service }) {
         <Block label="omada.connectedAp" />
         <Block label="omada.activeUser" />
         <Block label="omada.alerts" />
-        <Block label="omada.connectedGateway" />
-        <Block label="omada.connectedSwitches" />
       </Container>
     );
   }
diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js
index d867e112..e89ad81d 100644
--- a/src/widgets/omada/proxy.js
+++ b/src/widgets/omada/proxy.js
@@ -2,27 +2,26 @@
 import { httpProxy } from "utils/proxy/http";
 import getServiceWidget from "utils/config/service-helpers";
 import createLogger from "utils/logger";
-import widgets from "widgets/widgets";
 
 const proxyName = "omadaProxyHandler";
 
 const logger = createLogger(proxyName);
 
-
 async function login(loginUrl, username, password, controllerVersionMajor) {
   const params = {
-    username: username,
-    password: password
+    username,
+    password
   }
 
   if (controllerVersionMajor === 3) {
-    params["method"] = "login";
-    params["params"] = {
+    params.method = "login";
+    params.params = {
       name: username,
       password
     };
   }
-  
+
+  // eslint-disable-next-line no-unused-vars
   const [status, contentType, data] = await httpProxy(loginUrl, {
     method: "POST",
     body: JSON.stringify(params),
@@ -41,10 +40,6 @@ export default async function omadaProxyHandler(req, res) {
   if (group && service) {
     const widget = await getServiceWidget(group, service);
 
-    if (!widgets?.[widget.type]?.api) {
-      return res.status(403).json({ error: "Service does not support API calls" });
-    }
-
     if (widget) {
 
       const {url} = widget;
@@ -59,7 +54,7 @@ export default async function omadaProxyHandler(req, res) {
 
       if (status !== 200) {
         logger.error("Unable to retrieve Omada controller info");
-        return res.status(status).json({error: {message: `HTTP Error ${status}`, url: controllerInfoURL, data: data}});
+        return res.status(status).json({error: {message: `HTTP Error ${status}`, url: controllerInfoURL, data}});
       }
 
       let cId;
@@ -97,12 +92,11 @@ export default async function omadaProxyHandler(req, res) {
       const [loginStatus, loginResponseData] = await login(loginUrl, widget.username, widget.password, controllerVersionMajor);
 
       if (loginStatus !== 200 || loginResponseData.errorCode > 0) {
-        return res.status(requestresponse[0]).json({error: {message: "Error logging in to Oamda controller", url: loginUrl, data: loginResponseData}});
+        return res.status(status).json({error: {message: "Error logging in to Oamda controller", url: loginUrl, data: loginResponseData}});
       }
 
-      const token = loginResponseData.result.token;
+      const { token } = loginResponseData.result;
       
-      // List sites
       let sitesUrl;
       let body = {};
       let params = { token };
@@ -126,6 +120,8 @@ export default async function omadaProxyHandler(req, res) {
         case 5:
           sitesUrl = `${widget.url}/${cId}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
           break;
+        default:
+          break;
       }
       
       [status, contentType, data] = await httpProxy(sitesUrl, {
@@ -138,16 +134,16 @@ export default async function omadaProxyHandler(req, res) {
       const sitesResponseData = JSON.parse(data);
 
       if (sitesResponseData.errorCode > 0) {
-        logger.debug(`HTTTP ${status} getting sites list: ${requestresponse[2].msg}`);
-        return res.status(status).json({error: {message: "Error getting sites list", url, data: requestresponse[2]}});
+        logger.debug(`HTTTP ${status} getting sites list: ${sitesResponseData.msg}`);
+        return res.status(status).json({error: {message: "Error getting sites list", url, data: sitesResponseData}});
       }
 
       const site = (controllerVersionMajor === 3) ? 
-      sitesResponseData.result.siteList.find(site => site.name === widget.site):
-        sitesResponseData.result.data.find(site => site.name === widget.site);
+        sitesResponseData.result.siteList.find(s => s.name === widget.site):
+        sitesResponseData.result.data.find(s => s.name === widget.site);
 
       if (!site) {
-        return res.status(requestresponse[0]).json({error: {message: `Site ${widget.site} is not found`, url, data}});
+        return res.status(status).json({error: {message: `Site ${widget.site} is not found`, url, data}});
       }
 
       let siteResponseData;
@@ -159,18 +155,18 @@ export default async function omadaProxyHandler(req, res) {
       let alerts;
 
       if (controllerVersionMajor === 3) {
-        // Omada 3.x.x controller requires switching site
+        // Omada v3 controller requires switching site
         const switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
         method = "POST";
         body = {
-          "method": "switchSite",
-          "params": {
-            "siteName": site.siteName,
-            "userName": widget.username
+          method: "switchSite",
+          params: {
+            siteName: site.siteName,
+            userName: widget.username
           }
         };
         headers = { "Content-Type": "application/json" };
-        params = { "token": token };
+        params = { token };
 
         [status, contentType, data] = await httpProxy(switchUrl, {
           method,
@@ -219,25 +215,26 @@ export default async function omadaProxyHandler(req, res) {
         siteResponseData = JSON.parse(data);
         
         if (status !== 200 || siteResponseData.errorCode > 0) {
-          logger.debug(`HTTP ${status} getting stats for site ${widget.site} with message ${listresult.msg}`);
+          logger.debug(`HTTP ${status} getting stats for site ${widget.site} with message ${siteResponseData.msg}`);
           return res.status(500).send(data);
         }
 
-        activeUser = siteResponseData.result.totalClientNum;
-        connectedAp = siteResponseData.result.connectedApNum;
-        connectedGateways = siteResponseData.result.connectedGatewayNum;
-        connectedSwitches = siteResponseData.result.connectedSwitchNum;
-
         const alertUrl = (controllerVersionMajor === 4) ? 
           `${url}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000` :
           `${url}/${cId}/api/v2/sites/${siteName}/alerts/num?token=${token}&currentPage=1&currentPageSize=1000`;
 
+        // eslint-disable-next-line no-unused-vars
         [status, contentType, data] = await httpProxy(alertUrl, {
           headers: {
             "Csrf-Token": token,
           },
         });
         const alertResponseData = JSON.parse(data);
+
+        activeUser = siteResponseData.result.totalClientNum;
+        connectedAp = siteResponseData.result.connectedApNum;
+        connectedGateways = siteResponseData.result.connectedGatewayNum;
+        connectedSwitches = siteResponseData.result.connectedSwitchNum;
         alerts = alertResponseData.result.alertNum;
       }
 
diff --git a/src/widgets/omada/widget.js b/src/widgets/omada/widget.js
index 0ef4177e..5e32edad 100644
--- a/src/widgets/omada/widget.js
+++ b/src/widgets/omada/widget.js
@@ -1,15 +1,7 @@
 import omadaProxyHandler from "./proxy";
-// import genericProxyHandler from "../../utils/proxy/handlers/generic";
 
 const widget = {
-  api: "{url}/web/v1/{endpoint}",
   proxyHandler: omadaProxyHandler,
-
-  mappings: {
-    stats: {
-      endpoint: "controller",
-    }
-  }
 };
 
 export default widget;