From 93230f542e2dda69b81293ba965a6cb3105361f5 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Sun, 1 Nov 2026 20:36:04 +0000 Subject: [PATCH 1/5] webui: show the duty cycle and carrier sense of all gateways on the start page --- src/webui/rega/www/pages/tabs/startpage.htm | 147 +++++++++++++++--- src/webui/www/api/methods.conf | 7 + .../api/methods/interface/getdutycycle.tcl | 24 +++ .../www/webui/css/extern/hmBootstrap.css | 16 ++ src/webui/www_source/ise/js/functions.js | 139 +++++++++++------ src/webui/www_source/webui/css/webui.css | 21 --- src/webui/www_source/webui/css/x_style.css | 6 +- src/webui/www_source/webui/js/configdata.js | 2 - src/webui/www_source/webui/js/webui.js | 2 - 9 files changed, 266 insertions(+), 98 deletions(-) create mode 100644 src/webui/www/api/methods/interface/getdutycycle.tcl diff --git a/src/webui/rega/www/pages/tabs/startpage.htm b/src/webui/rega/www/pages/tabs/startpage.htm index 97a745e9e..508c244d6 100644 --- a/src/webui/rega/www/pages/tabs/startpage.htm +++ b/src/webui/rega/www/pages/tabs/startpage.htm @@ -238,22 +238,134 @@ } %> -
- -
- - - ${lblDutyCycle} -
+ +
- - -
- - - ${lblCarrierSense} -
+ + + + + + + + + + +
${lblDutyCycle}${lblCarrierSense}
+ + + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
+ + + + + + +
@@ -294,14 +406,7 @@ } else { WEBUI_VERSION = "0.0.0"; } - product = getProduct(); - jQuery("#iFaceShowAll").text(translateKey("lblDutyCycleA") + product + ":"); - jQuery("#lblCarrierSense").text(translateKey("lblCarrierSenseA") + product + ":"); }); - } else { - product = getProduct(); - jQuery("#iFaceShowAll").text(translateKey("lblDutyCycleA") + product + ":"); - jQuery("#lblCarrierSense").text(translateKey("lblCarrierSenseA") + product + ":"); } translatePage(); diff --git a/src/webui/www/api/methods.conf b/src/webui/www/api/methods.conf index 847136546..4a6b01856 100644 --- a/src/webui/www/api/methods.conf +++ b/src/webui/www/api/methods.conf @@ -214,6 +214,13 @@ Interface.getValue { ARGUMENTS {_session_id_ interface address valueKey} } +Interface.getDutyCycle { + LEVEL USER + SCRIPT_FILE interface/getdutycycle.tcl + INFO {Gibt ein Array mit den DutyCycle Werten aller Gateways/Interfaces zurueck} + ARGUMENTS {_session_id_} +} + Interface.init { LEVEL ADMIN SCRIPT_FILE interface/dispatch.tcl diff --git a/src/webui/www/api/methods/interface/getdutycycle.tcl b/src/webui/www/api/methods/interface/getdutycycle.tcl new file mode 100644 index 000000000..2eb619e73 --- /dev/null +++ b/src/webui/www/api/methods/interface/getdutycycle.tcl @@ -0,0 +1,24 @@ +## +# Interface.getDutyCycle +# Gibt ein JSON array mit den DutyCycle Werten aller interfaces zurueck. +# +# Parameter: +# keine +# +# Rückgabewert: [array] +# Jedes Element ist ein Objekt mit den folgenden Elementen: +# sysvar: [string] Name der Systemvariable +# address: [string] Seriennummer +# dutyCycle: [integer] Duty Cycle (0 - 100) +# type: [string] Typ ("CCU2") des interfaces +## + +set result "" + +if { [file exists /tmp/dutycycle.json] } { + set file [open /tmp/dutycycle.json r] + set result [gets $file] + close $file +} + +jsonrpc_response "$result" diff --git a/src/webui/www/webui/css/extern/hmBootstrap.css b/src/webui/www/webui/css/extern/hmBootstrap.css index c29c078e9..d50a16aa2 100644 --- a/src/webui/www/webui/css/extern/hmBootstrap.css +++ b/src/webui/www/webui/css/extern/hmBootstrap.css @@ -53,3 +53,19 @@ h1, .h1 { border-style: inherit; border-width: inherit; } + +#hmBootstrap .progress { + position: relative; /* inserted */ + height: 14px; /* 20px */ + /* margin-bottom: 20px; /* removed */ +} +#hmBootstrap .progress span { + position: absolute; + display: block; + width: 100%; + color: black; +} +#hmBootstrap .progress-bar { + font-size: 11px; /* 12px */ + line-height: 14px; /* 20px */ +} diff --git a/src/webui/www_source/ise/js/functions.js b/src/webui/www_source/ise/js/functions.js index 98c905635..caeaf0aba 100644 --- a/src/webui/www_source/ise/js/functions.js +++ b/src/webui/www_source/ise/js/functions.js @@ -2574,74 +2574,111 @@ showDutyCycle = function() { var ifaceBidCosRF = "BidCos-RF", ifaceHmIPRF = "HmIP-RF", arInterfaceDutyCycle = {}, + arInterfaceCarrierSense = {}, showPartingLine = false, dcUnit = "%", dcNotAvailable = -1, - dcAlarm = 89; // Attention when dc >= 90% - - homematic("Interface.listBidcosInterfaces", {"interface": ifaceBidCosRF}, function (BidCosIFaces) { - if (BidCosIFaces) { - var linkElem = jQuery("#iFaceShowAll"); - if ((BidCosIFaces.length > 1) && (!linkElem.hasClass("UILink"))) { - linkElem - .addClass("UILink") - .on("click", function () { - showDutyCycle(); // actualize the dc value of the start page - showDCAllInterfaces(); - }); - } - - jQuery.each(BidCosIFaces, function (index, iFace) { - if (iFace.type == "CCU2") { - var dutyCycleProgressElem = jQuery("#dutyCycleProgress"), - dutyCycleProgressBarElm = jQuery("#dutyCycleProgressBar"), - dutyCycleValElm = jQuery("#dutyCycleVal"), - trDutyCycle = jQuery("[name='trDutyCycle']"), - trPartingLineElm = jQuery("#partingLine1"), - dcVal, - width, value; + csNotAvailable = -1, + dcWarn = 69, // Warning when dc >= 70% + dcAlarm = 89, // Attention when dc >= 90% + csWarn = 10, // Warning when cs >= 11% + csAlarm = 19; // Attention when cs >= 20% + + homematic("Interface.getDutyCycle", {}, function(dcArray) { + if(jQuery.isArray(dcArray)) { + jQuery.each(dcArray, function(index, iface) { + var dutyCycleValElm = jQuery("#dutyCycleVal"+index), + dutyCycleAddrElm = jQuery("#dutyCycleAddr"+index), + trDutyCycle = jQuery("[name='trDutyCycle"+index+"']"), + dutyCycleBar = jQuery("[name='dutyCycleBar"+index+"']"), + carrierSenseValElm = jQuery("#carrierSenseVal"+index), + carrierSenseBar = jQuery("[name='carrierSenseBar"+index+"']"), + carrierSenseCol = jQuery("[name='carrierSenseCol"+index+"']"), + trPartingLineElm = jQuery("#partingLine1"), + csVal, + dcVal; + + if (typeof iface.dutyCycle !== "undefined") { + dcVal = Math.floor(iface.dutyCycle); + csVal = Math.floor(iface.carrierSense); + conInfo("dutyCycle - " + ifaceBidCosRF + ": " + dcVal + " " + dcUnit); + conInfo("carrierSense - " + ifaceBidCosRF + ": " + csVal + " " + dcUnit); + arInterfaceDutyCycle[ifaceBidCosRF] = ((dcVal >= 0) && (dcVal <= 100)) ? dcVal : dcNotAvailable; + arInterfaceCarrierSense[ifaceBidCosRF] = ((csVal >= 0) && (csVal <= 100)) ? csVal : csNotAvailable; + } else { + conInfo("No gateway status for the interface " + ifaceBidCosRF + " available!"); + arInterfaceDutyCycle[ifaceBidCosRF] = dcNotAvailable; + } - if (typeof iFace.dutyCycle != "undefined") { - dcVal = parseInt(iFace.dutyCycle); - conInfo("dutyCycle - " + ifaceBidCosRF + ": " + dcVal + dcUnit); - arInterfaceDutyCycle[ifaceBidCosRF] = ((dcVal >= 0) && (dcVal <= 100)) ? dcVal : dcNotAvailable; + if (arInterfaceDutyCycle[ifaceBidCosRF] != dcNotAvailable) { + dutyCycleValElm.text(arInterfaceDutyCycle[ifaceBidCosRF] + " " + dcUnit); + if (iface.type === "CCU2") { + dutyCycleAddrElm.text("CCU:"); + } else if(iface.type === "HMIP-HAP") { + if(iface.name !== "") { + dutyCycleAddrElm.text("HAP ("+iface.name+"):"); + } else { + dutyCycleAddrElm.text("HAP ("+iface.address+"):"); + } + } else if(iface.name !== "") { + dutyCycleAddrElm.text("LGW ("+iface.name+"):"); } else { - conInfo("No gateway status for the interface " + ifaceBidCosRF + " available!"); - arInterfaceDutyCycle[ifaceBidCosRF] = dcNotAvailable; + dutyCycleAddrElm.text("LGW ("+iface.address+"):"); } - if (arInterfaceDutyCycle[ifaceBidCosRF] != dcNotAvailable) { - dutyCycleValElm.text(arInterfaceDutyCycle[ifaceBidCosRF] + dcUnit); - - width = parseInt(dutyCycleProgressElem.css("width")); - value = width - (width / 100 * arInterfaceDutyCycle[ifaceBidCosRF]); + dutyCycleBar.css("width", arInterfaceDutyCycle[ifaceBidCosRF]+"%").attr('aria-valuenow', arInterfaceDutyCycle[ifaceBidCosRF]); + dutyCycleBar.removeClass("bg-success"); + dutyCycleBar.removeClass("bg-warning"); + dutyCycleBar.removeClass("bg-danger"); - window.setTimeout(function () { - //dutyCycleProgressBarElm.css("width", value + "px"); - dutyCycleProgressBarElm.css("margin-left", (width - parseInt(value)) + "px"); - - }, 25); + if (arInterfaceDutyCycle[ifaceBidCosRF] > dcAlarm) { + dutyCycleBar.addClass("bg-danger"); + } else if (arInterfaceDutyCycle[ifaceBidCosRF] > dcWarn) { + dutyCycleBar.addClass("bg-warning"); + } else { + dutyCycleBar.addClass("bg-success"); + } - if (arInterfaceDutyCycle[ifaceBidCosRF] > dcAlarm) { - trDutyCycle.addClass("attention"); + if (arInterfaceCarrierSense[ifaceBidCosRF] != csNotAvailable) { + carrierSenseValElm.text(arInterfaceCarrierSense[ifaceBidCosRF] + " " + dcUnit); + carrierSenseBar.css("width", arInterfaceCarrierSense[ifaceBidCosRF]+"%").attr('aria-valuenow', arInterfaceCarrierSense[ifaceBidCosRF]); + carrierSenseBar.removeClass("bg-info"); + carrierSenseBar.removeClass("bg-warning"); + carrierSenseBar.removeClass("bg-danger"); + + if (arInterfaceCarrierSense[ifaceBidCosRF] > csAlarm) { + carrierSenseBar.addClass("bg-danger"); + } else if (arInterfaceCarrierSense[ifaceBidCosRF] > csWarn) { + carrierSenseBar.addClass("bg-warning"); } else { - trDutyCycle.removeClass("attention"); + carrierSenseBar.addClass("bg-info"); } - trPartingLineElm.show(); - showPartingLine = true; - trDutyCycle.css("visibility", "visible"); } else { - trDutyCycle.css("visibility", "hidden"); + carrierSenseValElm.text("n/a"); + carrierSenseBar.css("width", "0%"); } + trPartingLineElm.show(); + showPartingLine = true; + trDutyCycle.css("display", "table-row"); + } else { + trDutyCycle.css("display", "none"); + } - if (!showPartingLine) { - trPartingLineElm.hide(); - } - return false; // Leave each loop + if (!showPartingLine) { + trPartingLineElm.hide(); } }); + + // hide the rows of interfaces that are missing in this response + jQuery("[name^='trDutyCycle']").each(function() { + if (parseInt(jQuery(this).attr("name").substr(11), 10) >= dcArray.length) { + jQuery(this).css("display", "none"); + } + }); + if (!showPartingLine) { + jQuery("#partingLine1").hide(); + } } - showDutyCycleHmIP(); }); } }; diff --git a/src/webui/www_source/webui/css/webui.css b/src/webui/www_source/webui/css/webui.css index 2fae962f0..b1d61d143 100644 --- a/src/webui/www_source/webui/css/webui.css +++ b/src/webui/www_source/webui/css/webui.css @@ -154,24 +154,3 @@ progress[value] { progress::-moz-progress-bar { background: #3678c9; } progress::-webkit-progress-value { background: #3678c9; } -.dutyCycleProgress { - width: 75%; - - background: -moz-linear-gradient(0deg, rgba(13,255,0,1) 0%, rgba(255,255,0,1) 63%, rgba(255,0,0,1) 100%); /* ff3.6+ */ - background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(13,255,0,1)), color-stop(63%, rgba(255,255,0,1)), color-stop(100%, rgba(255,0,0,1))); /* safari4+,chrome */ - background: -webkit-linear-gradient(0deg, rgba(13,255,0,1) 0%, rgba(255,255,0,1) 63%, rgba(255,0,0,1) 100%); /* safari5.1+,chrome10+ */ - background: -o-linear-gradient(0deg, rgba(13,255,0,1) 0%, rgba(255,255,0,1) 63%, rgba(255,0,0,1) 100%); /* opera 11.10+ */ - background: -ms-linear-gradient(0deg, rgba(13,255,0,1) 0%, rgba(255,255,0,1) 63%, rgba(255,0,0,1) 100%); /* ie10+ */ - background: linear-gradient(90deg, rgba(13,255,0,1) 0%, rgba(255,255,0,1) 63%, rgba(255,0,0,1) 100%); /* w3c */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#0DFF00', endColorstr='#ff0000',GradientType=1 ); /* ie6-9 */ - - -} - -.dutyCycleProgressBar { - width: 1%; - height: 12px; - background-color: #d9d9d9; -} - - diff --git a/src/webui/www_source/webui/css/x_style.css b/src/webui/www_source/webui/css/x_style.css index f896a88ce..30b123ded 100644 --- a/src/webui/www_source/webui/css/x_style.css +++ b/src/webui/www_source/webui/css/x_style.css @@ -1185,9 +1185,13 @@ height:50%; } +#partingLine1 { + display: table-row; +} + .CLASS01811 { - padding-top: 5px; color: $_(textColorB); /* black */ + border-collapse: collapse; } .CLASS01812 { diff --git a/src/webui/www_source/webui/js/configdata.js b/src/webui/www_source/webui/js/configdata.js index e9e21933a..32d098c27 100644 --- a/src/webui/www_source/webui/js/configdata.js +++ b/src/webui/www_source/webui/js/configdata.js @@ -57,8 +57,6 @@ ConfigData = Singleton.create({ window.setTimeout(function() {jQuery("#PagePath").css('color',"white");},100); ConfigData.isPresent = true; if (ConfigData.showMessage) { this.hideMessage(); } - showDutyCycleHmIP(); // The DeviceList is now available for displaying the duty cycle and carrier sense of the HAP's - showCarrierSense(); if (ConfigData.callback) { ConfigData.callback(); } }); }, diff --git a/src/webui/www_source/webui/js/webui.js b/src/webui/www_source/webui/js/webui.js index 2d7f7d0e2..1d0f96521 100644 --- a/src/webui/www_source/webui/js/webui.js +++ b/src/webui/www_source/webui/js/webui.js @@ -52,7 +52,6 @@ WebUI = Singleton.create({ if(typeof dcTimeout == "undefined") { dcTimeout = window.setTimeout(function () { showDutyCycle(); - showCarrierSense(); delete dcTimeout; }, 10); } @@ -287,7 +286,6 @@ WebUI = Singleton.create({ // Check the dutyCycle and carrierSense periodically new PeriodicalExecuter(function () { showDutyCycle(); - showCarrierSense(); }, checkDutyCycleInterval); if (getProduct() >= 3) { From 5854dacc2a03225f257efbe7d7eb13a1477212f0 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Sun, 1 Nov 2026 21:05:02 +0000 Subject: [PATCH 2/5] webui: document all fields of the Interface.getDutyCycle response --- .../www/api/methods/interface/getdutycycle.tcl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/webui/www/api/methods/interface/getdutycycle.tcl b/src/webui/www/api/methods/interface/getdutycycle.tcl index 2eb619e73..1f376a39d 100644 --- a/src/webui/www/api/methods/interface/getdutycycle.tcl +++ b/src/webui/www/api/methods/interface/getdutycycle.tcl @@ -5,12 +5,15 @@ # Parameter: # keine # -# Rückgabewert: [array] +# Rückgabewert: [array], null ohne /tmp/dutycycle.json # Jedes Element ist ein Objekt mit den folgenden Elementen: -# sysvar: [string] Name der Systemvariable -# address: [string] Seriennummer -# dutyCycle: [integer] Duty Cycle (0 - 100) -# type: [string] Typ ("CCU2") des interfaces +# address: [string] Seriennummer +# name: [string] Name des Gateways ("CCU" für die CCU) +# sysVar: [string] Name der Systemvariable +# dutyCycle: [string] Duty Cycle (0 - 100, -1 wenn nicht verbunden) +# carrierSense: [string] Carrier Sense (0 - 100, -1 wenn nicht verfügbar) +# type: [string] Typ des interfaces ("CCU2", "HMIP-HAP" oder +# der Typ des LAN-Gateways) ## set result "" From a82636666605f0c4501d944d3a75562fdf84e647 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Sun, 1 Nov 2026 21:10:31 +0000 Subject: [PATCH 3/5] webui: clear the carrier sense value of an unavailable bar --- src/webui/www_source/ise/js/functions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webui/www_source/ise/js/functions.js b/src/webui/www_source/ise/js/functions.js index caeaf0aba..616b89a1f 100644 --- a/src/webui/www_source/ise/js/functions.js +++ b/src/webui/www_source/ise/js/functions.js @@ -2655,7 +2655,7 @@ showDutyCycle = function() { } } else { carrierSenseValElm.text("n/a"); - carrierSenseBar.css("width", "0%"); + carrierSenseBar.css("width", "0%").removeAttr("aria-valuenow"); } trPartingLineElm.show(); showPartingLine = true; From 64cb999369cf04c2b8b07ec9963f1ec756f2bd50 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Sun, 1 Nov 2026 21:38:09 +0000 Subject: [PATCH 4/5] webui: hide the duty cycle separator without duty cycle rows --- src/webui/rega/www/pages/tabs/startpage.htm | 2 +- src/webui/www_source/ise/js/functions.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webui/rega/www/pages/tabs/startpage.htm b/src/webui/rega/www/pages/tabs/startpage.htm index 508c244d6..477a099d8 100644 --- a/src/webui/rega/www/pages/tabs/startpage.htm +++ b/src/webui/rega/www/pages/tabs/startpage.htm @@ -239,7 +239,7 @@ %> - +
diff --git a/src/webui/www_source/ise/js/functions.js b/src/webui/www_source/ise/js/functions.js index 616b89a1f..214848335 100644 --- a/src/webui/www_source/ise/js/functions.js +++ b/src/webui/www_source/ise/js/functions.js @@ -2594,7 +2594,7 @@ showDutyCycle = function() { carrierSenseValElm = jQuery("#carrierSenseVal"+index), carrierSenseBar = jQuery("[name='carrierSenseBar"+index+"']"), carrierSenseCol = jQuery("[name='carrierSenseCol"+index+"']"), - trPartingLineElm = jQuery("#partingLine1"), + trPartingLineElm = jQuery("#partingLine0, #partingLine1"), csVal, dcVal; @@ -2676,7 +2676,7 @@ showDutyCycle = function() { } }); if (!showPartingLine) { - jQuery("#partingLine1").hide(); + jQuery("#partingLine0, #partingLine1").hide(); } } }); From 244b2d2c31dc47fbfbce20dae46efcaafaf6622f Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Mon, 2 Nov 2026 10:52:34 +0000 Subject: [PATCH 5/5] webui: ignore superseded duty cycle responses --- src/webui/www_source/ise/js/functions.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/webui/www_source/ise/js/functions.js b/src/webui/www_source/ise/js/functions.js index 214848335..55ea1509a 100644 --- a/src/webui/www_source/ise/js/functions.js +++ b/src/webui/www_source/ise/js/functions.js @@ -2569,6 +2569,8 @@ changeRoomOrSubsection = function(id) }; +var dutyCycleRequestId = 0; + showDutyCycle = function() { if (jQuery("#PagePathSpan").text().endsWith(translateKey("startPage"))) { var ifaceBidCosRF = "BidCos-RF", @@ -2584,7 +2586,10 @@ showDutyCycle = function() { csWarn = 10, // Warning when cs >= 11% csAlarm = 19; // Attention when cs >= 20% + var requestId = ++dutyCycleRequestId; homematic("Interface.getDutyCycle", {}, function(dcArray) { + // ignore the response of a request that a newer one has superseded + if (requestId !== dutyCycleRequestId) { return; } if(jQuery.isArray(dcArray)) { jQuery.each(dcArray, function(index, iface) { var dutyCycleValElm = jQuery("#dutyCycleVal"+index),