Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    NUT Dashboard Widget (2.1.4)

    Scheduled Pinned Locked Moved webGUI
    40 Posts 12 Posters 10.5k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      Druqn
      last edited by

      Begin and author here - https://forum.pfsense.org/index.php?topic=43438.msg224857#msg224857
      and here - https://forum.pfsense.org/index.php?topic=43769.msg226724#msg226724

      I made a small change original code. Data is static, no AJAX magic.

      Update: Cosmetic, BatVolt show if exist (Eaton), otherwise Bat Temp (APC)

      /*
              $Id$
              Copyright 2008 Seth Mos
              Part of pfSense widgets (www.pfsense.com)
              originally based on m0n0wall (http://m0n0.ch/wall)
      
              Redistribution and use in source and binary forms, with or without
              modification, are permitted provided that the following conditions are met:
      
              1\. Redistributions of source code must retain the above copyright notice,
                 this list of conditions and the following disclaimer.
      
              2\. Redistributions in binary form must reproduce the above copyright
                 notice, this list of conditions and the following disclaimer in the
                 documentation and/or other materials provided with the distribution.
      
              THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
              INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
              AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
              AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
              OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
              SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
              INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
              CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
              ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
              POSSIBILITY OF SUCH DAMAGE.
      
      	v. 2.01
      */
      
      $nocsrf = true;
      
      require_once("guiconfig.inc");
      require_once("pfsense-utils.inc");
      require_once("functions.inc");
      
      $nut_config = $config['installedpackages']['nut']['config'][0];
      
      /* functions */
      
      function secs2hms($secs) {
           if ($secs<0) return false;
           $m = (int)($secs / 60); $s = $secs % 60;
           $h = (int)($m / 60); $m = $m % 60;
           return array($h, "h ", $m, "m ", $s, "s ");
      }
      
      ?>
      
      				 **Monitoring** |				 **Model** |				 **Status** |
      
           $running = ((int)exec('pgrep upsmon | wc -l') > 0) ? true : false;
      
           if($nut_config['monitor'] == 'local') {
               echo "Local UPS";
               $cmd = "upsc {$nut_config['name']}@localhost";
           } elseif($nut_config['monitor'] == 'remote') {
               echo "Remote UPS";
               $cmd = "upsc {$nut_config['remotename']}@{$nut_config['remoteaddr']}";
           } elseif($nut_config['monitor'] == 'snmp') {
               echo "SNMP UPS";
               $cmd = "upsc {$nut_config['snmpname']}@localhost";
           }
      
      ?>
      
                           | 
      
      if($running) $handle = popen($cmd, 'r');
      	elseif(isset($nut_config)) {
      		if($nut_config['monitor'] == 'snmp') $condition = "NUT enabled but service not running!\nSNMP UPS may be unreachable.";
      		else $condition = "NUT enabled but service not running!";
      		}
      	else $condition = "No NUT installed!";
      
          if($handle) {
              $read = fread($handle, 4096);
              pclose($handle);
      
              $lines = explode("\n", $read);
              $ups = array();
              foreach($lines as $line) {
                     $line = explode(':', $line);
                     $ups[$line[0]] = trim($line[1]);
              }
      
              if(count($lines) == 1) $condition = "ERROR:Data stale!";
      	}
      	if(isset($condition)) {
      	echo $condition;
          print(<< | ERROR |
      
      EOD
          ."\n");
      	return;
      	}
      	else echo $ups['ups.model'];
          print(<< <eod<br>EOD
          ."\n");
          $status = explode(' ', $ups['ups.status']);
          foreach($status as $condition) {
                  if($disp_status) $disp_status .= ', ';
                  switch ($condition) {
      					case 'WAIT':
      						$disp_status .= 'Waiting';
                              break;
                          case 'OFF':
                              $disp_status .= 'Off Line';
                              break;
                          case 'OL':
                              $disp_status .= 'On Line';
                              break;
                          case 'OB':
                              $disp_status .= 'On Battery';
                              break;
                          case 'TRIM':
                              $disp_status .= 'SmartTrim';
                              break;
                          case 'BOOST':
                              $disp_status .= 'SmartBoost';
                              break;
                          case 'OVER':
                              $disp_status .= 'Overload';
                              break;
                          case 'LB':
                              $disp_status .= 'Battery Low';
                              break;
                          case 'RB':
                              $disp_status .= 'Replace Battery';
                              break;
                          case 'CAL':
                              $disp_status .= 'Calibration';
                              break;
                          case 'CHRG':
                              $disp_status .= 'Charging';
                              break;
                          default:
                              $disp_status .= $condition;
                              break;
                  }
          }
      
          echo $disp_status;
      ##  Battery Temp or Battery Voltage
      	if ($ups['battery.voltage'] > 0) $col3 = array("Battery Volt" , sprintf("%d",$ups['battery.voltage'])." V");
      	elseif ($ups['ups.temperature'] > 0) $col3 = array("Battery Temp" , sprintf("%d",$ups['ups.temperature'])."°C");
      	else $col3 = array("" , "");
      ?>
      
      Battery Charge
      Time Remain
      
      ![left bar](./themes/<?= $g['theme']; ?>/images/misc/bar_left.gif)![red bar](./themes/<?= $g['theme']; ?>/images/misc/bar_blue.gif)![gray bar](./themes/<?= $g['theme']; ?>/images/misc/bar_gray.gif)![right bar](./themes/<?= $g['theme']; ?>/images/misc/bar_right.gif)
      
      /*          echo $ups['battery.charge'] . "%"; */
          print(<< <eod<br>EOD
          ."\n");
      
          echo implode(secs2hms($ups['battery.runtime']));
          print(<< <eod<br>EOD
           ."\n");
      
      ##	Print Column №3 - Volt or Temp
      	echo $col3[1];
      ##	$tm = time();
      ##	echo date('h:i:s',$tm);
      	print(<< <eod<br>Load
      Input Voltage
      Output Voltage
      
      EOD
          ."\n");
      
      ?>
      				![left bar](./themes/<?= $g['theme']; ?>/images/misc/bar_left.gif)![red bar](./themes/<?= $g['theme']; ?>/images/misc/bar_blue.gif)![gray bar](./themes/<?= $g['theme']; ?>/images/misc/bar_gray.gif)![right bar](./themes/<?= $g['theme']; ?>/images/misc/bar_right.gif)
      
      	print(<< <eod<br>EOD
          ."\n");
      
      ##          echo $ups['input.voltage'] . "V";
      	printf("%d", $ups['input.voltage']);
      	echo " V";
      
          print(<< <eod<br>EOD
           ."\n");
      
      ##	echo $ups['output.voltage'] . "V";
      	printf("%d", $ups['output.voltage']);
      	echo " V";
      ?></eod<br></eod<br></eod<br></eod<br></eod<br></eod<br> 
      
      1 Reply Last reply Reply Quote 0
      • T
        tweek
        last edited by

        Thanks for the improvements to my code!  I now hope someone with some AJAX experience can help make this widget so it automatically updates…

        -- tweek

        1 Reply Last reply Reply Quote 0
        • H
          haddock
          last edited by

          Thanks.

          How does one install this?

          1 Reply Last reply Reply Quote 0
          • D
            Druqn
            last edited by

            Common info - https://doc.pfsense.org/index.php/Creating_widgets

            Briefly:
            1. Copy code to file ups.widget.php
            2. Create file ups.inc with  next code:

            //set variable for custom title
            $ups_title = "UPS Status";
            ?>
            

            3. Copy ups.widget.php to /usr/local/www/widgets/widgets
            4. Copy ups.inc to /usr/local/www/widgets/include
            5. Refresh (F5) Dashboard and press "Add" button

            1 Reply Last reply Reply Quote 0
            • S
              SunStroke
              last edited by

              Hi, all.

              I`m added ajax autoupdates to widget.

              Step 1:
              in widget`s code replace strings (to get unique id):

              
              replace to
              
              

              Step 2:
              in file /usr/local/www/includes/functions.inc.php add:

              
              in function get_stats() add one string:
              ...
              	$stats['statepercent'] = get_pfstate(true);
              	$stats['nut'] = get_nutstats(true);   // Add this string
              	$stats = join("|", $stats);
              ...
              
              after function get_stats() add two functions:
              
              function secs2hms_($secs) {
              	if ($secs<0) return false;
              	$m = (int)($secs / 60); $s = $secs % 60;
              	$h = (int)($m / 60); $m = $m % 60;
              	return array($h, "h ", $m, "m ", $s, "s");
              }
              
              function get_nutstats() {
              	global $config;
              	$data = "";
              	$cmd = "";
              	$nut_config = $config['installedpackages']['nut']['config'][0];
              	// "Monitoring" field and get command to fetch ups data - [0] return array element
              	if($nut_config['monitor'] == 'local') {
              		$data = "Local UPS";
              		$cmd  = "upsc {$nut_config['name']}@localhost";
              	} elseif($nut_config['monitor'] == 'remote') {
              		$data = "Remote UPS";
              		$cmd  = "upsc {$nut_config['remotename']}@{$nut_config['remoteaddr']}";
              	} elseif($nut_config['monitor'] == 'snmp') {
              		$data = "SNMP UPS";
              		$cmd  = "upsc {$nut_config['snmpname']}@localhost";
              	}
              	// Find upsmon process
              	$running = ((int)exec('pgrep upsmon | wc -l') > 0) ? true : false;
              	if($running) {
              		// Fetch ups data
              		$handle = popen($cmd, 'r');
              	} elseif(isset($nut_config)) {
              		// No service running
              		if($nut_config['monitor'] == 'snmp') {
              			$condition = "NUT enabled but service not running!\nSNMP UPS may be unreachable.";
              		} else {
              			$condition = "NUT enabled but service not running!";
              		}
              	} else {
              		// No NUT package installed
              		$condition = "No NUT installed!";	
              	}
              	// Parse ups data
              	if($handle) {
              		$read = fread($handle, 4096);
              		pclose($handle);
              		$lines = explode("\n", $read);
              		$ups = array();
              		foreach($lines as $line) {
              			$line = explode(':', $line);
              			$ups[$line[0]] = trim($line[1]);
              		}
              		if(count($lines) == 1) 
              			$condition = "ERROR:Data stale!";
              	}
              	if(isset($condition)) {
              		// Drop all data and return "" if error (todo: return human readable result)
              		return "";
              	}
              	// "Model" field - [1] return array element
              	$data .= ":" . $ups['ups.model'];
              	// "Status" field - [2] return array element
              	$status = explode(' ', $ups['ups.status']);
              	foreach($status as $condition) {
              		if($disp_status) $disp_status .= ', ';
              		switch ($condition) {
              			case 'WAIT':
              				$disp_status .= 'Waiting';
              				break;
              			case 'OFF':
              				$disp_status .= 'Off Line';
              				break;
              			case 'OL':
              				$disp_status .= 'On Line';
              				break;
              			case 'OB':
              				$disp_status .= 'On Battery';
              				break;
              			case 'TRIM':
              				$disp_status .= 'SmartTrim';
              				break;
              			case 'BOOST':
              				$disp_status .= 'SmartBoost';
              				break;
              			case 'OVER':
              				$disp_status .= 'Overload';
              				break;
              			case 'LB':
              				$disp_status .= 'Battery Low';
              				break;
              			case 'RB':
              				$disp_status .= 'Replace Battery';
              				break;
              			case 'CAL':
              				$disp_status .= 'Calibration';
              				break;
              			case 'CHRG':
              				$disp_status .= 'Charging';
              				break;
              			default:
              				$disp_status .= $condition;
              				break;
              		}
              	}
              	$data .= ":" . $disp_status;
              	// "Battery Charge" bars and field - [3],[4],[5] return array elements
              	$data .= ":" . round($ups['battery.charge']);
              	$data .= ":" . (100 - round($ups['battery.charge']));
              	$data .= ":" . $ups['battery.charge'] . '%';
              	// "Time Remaning" field - [6] return array element
              	$data .= ":" . implode(secs2hms_($ups['battery.runtime']));
              	// "Battery Voltage or Battery Temp" field - [7] return array element
              	if($ups['battery.voltage'] > 0) {
              		$data .= ":" . $ups['battery.voltage'] . " V";
              	} elseif ($ups['ups.temperature'] > 0) {
              		$data .= ":" . $ups['ups.temperature'] . "&#176;C";
              	} else {
              		$data .= ":" . "";
              	}
              	// "Load" bars and field - [8],[9],[10] return array elements
              	$data .= ":" . round($ups['ups.load']);
              	$data .= ":" . (100 - round($ups['ups.load']));
              	$data .= ":" . $ups['ups.load'] . '%';
              	// "Input Voltage" field - [11] return array element
              	$data .= ":" . $ups['input.voltage'] . " V";
              	// "Output Voltage" field - [12] return array element
              	$data .= ":" . $ups['output.voltage'] . " V";
              
              	return $data;
              }
              
              

              Step 3:
              in file /usr/local/www/javascript/index/ajax.js add:

              
              in function stats(x) add one string:
              ...
                      updateStateMeter(values[13]);
                      updateNutStats(values[14]);   // Add this string
              }
              ...
              
              after function stats(x) add one function:
              
              function updateNutStats(x) {
              //console.log(x);
              	if (widgetActive("ups")){
              		upsdata_split = x.split(":");
              		if(upsdata_split.length) {
              			if(jQuery('#monitoring')) jQuery("#monitoring").html(upsdata_split[0]);
              			if(jQuery('#model')) jQuery("#model").html(upsdata_split[1]);
              			if(jQuery('#status')) jQuery("#status").html(upsdata_split[2]);
              			if(jQuery('#batwidtha')) jQuery("#batwidtha").css('width', upsdata_split[3] + 'px');
              			if(jQuery('#batwidthb')) jQuery("#batwidthb").css('width', upsdata_split[4] + 'px');
              			if(jQuery('#batmeter')) jQuery("#batmeter").html(upsdata_split[5]);
              			if(jQuery('#runtime')) jQuery("#runtime").html(upsdata_split[6]);
              			if(jQuery('#bvoltage')) jQuery("#bvoltage").html(upsdata_split[7]);
              			if(jQuery('#loadwidtha')) jQuery("#loadwidtha").css('width', upsdata_split[8] + 'px');
              			if(jQuery('#loadwidthb')) jQuery("#loadwidthb").css('width', upsdata_split[9] + 'px');
              			if(jQuery('#loadmeter')) jQuery("#loadmeter").html(upsdata_split[10]);
              			if(jQuery('#InputV')) jQuery("#InputV").html(upsdata_split[11]);
              			if(jQuery('#OutputV')) jQuery("#OutputV").html(upsdata_split[12]);
              		}
              	}
              }
              
              

              In my case works good. Do not know is this chages remain after reboot.
              And my code maybe need some rework to reflect all ups and NUT package status. Do it in the future.

              PS. Sorry for my bad english.

              Reviews and patches are welcomes.

              1 Reply Last reply Reply Quote 0
              • P
                phil.davis
                last edited by

                Since this seems useful for some people, one of you should go to https://github.com/pfsense/pfsense/tree/master/usr/local/www/widgets/widgets hit the plus sign, add these files and submit a pull request for review. If it is put in the official system then everyone can have the benefit without having to go through these manual install steps…

                As the Greek philosopher Isosceles used to say, "There are 3 sides to every triangle."
                If I helped you, then help someone else - buy someone a gift from the INF catalog http://secure.inf.org/gifts/usd/

                1 Reply Last reply Reply Quote 0
                • S
                  SunStroke
                  last edited by

                  @phil.davis:

                  … hit the plus sign, add these files and submit a pull request for review...

                  Can i use various git tools (clone and then pull commits), or should i use on github "+" & "pull" if i nearly newbie to git. And should i put all modified files to "widget" tree, although not all these files from that path?

                  1 Reply Last reply Reply Quote 0
                  • P
                    phil.davis
                    last edited by

                    Put the new files in the places they need to go in the final install - /usr/local/www/widgets/widgets /usr/local/www/widgets/include
                    Edit other files in the place they are already.
                    For this you can just use GitHub online, click the pencil to edit, the plus to add a new file. It will make a repository for you. It will do a commit every time you do something - so you end up with a few commits then make a pull request. It looks a bit messier having individual commits, but for small changes it is easy to do all online - no need to learn about git clone and so on.
                    If you mess it all up you cam always delete your repository and start again apply the changes until you have a clean set of commits, then do the pull request.

                    As the Greek philosopher Isosceles used to say, "There are 3 sides to every triangle."
                    If I helped you, then help someone else - buy someone a gift from the INF catalog http://secure.inf.org/gifts/usd/

                    1 Reply Last reply Reply Quote 0
                    • K
                      kejianshi
                      last edited by

                      I recently installed 2000W Enterprise UPS with 648 Watt/Hours of battery at each site where I have pfsense running for my personal use to keep the modems, switches and pfsense up during outages. (Very very long outages).  I hope these GUI changes become available as part of a normal package in pfsense.  It would be very convenient.

                      1 Reply Last reply Reply Quote 0
                      • S
                        SunStroke
                        last edited by

                        Sent request to merge changes to main project repository.

                        1 Reply Last reply Reply Quote 0
                        • T
                          tweek
                          last edited by

                          Is there any progress on SunStroke's merge request?  I'm currently unaware of where I can track it & check on the status.  I'm eager to see this widget in a release.

                          1 Reply Last reply Reply Quote 0
                          • S
                            SunStroke
                            last edited by

                            @tweek:

                            Is there any progress on SunStroke's merge request?  I'm currently unaware of where I can track it & check on the status.  I'm eager to see this widget in a release.

                            No, my commits was not merged till now. You can install "System Patches" package and try to apply my patches individually. I done it for myself.

                            NUT widget (add main file) - http://github.com/pfsense/pfsense/commit/d9fcbc2048041285690cc28159753acdb0bbb7a3.patch
                            NUT widget (add 'inc' file) - http://github.com/pfsense/pfsense/commit/4dda40b25ee72d899add9fb18e41310dfcc53a17.patch
                            NUT widget ajax callback - http://github.com/pfsense/pfsense/commit/5c5e2a846b4c98cb67fe7828ddb131054d71a644.patch
                            NUT widget update func. - http://github.com/pfsense/pfsense/commit/225ea5746d83b6e79b96904e3f3dfb7c7b7defd4.patch

                            1 Reply Last reply Reply Quote 0
                            • D
                              doktornotor Banned
                              last edited by

                              @SunStroke:

                              No, my commits was not merged till now. You can install "System Patches" package and try to apply my patches individually. I done it for myself.

                              NUT widget (add main file) - http://github.com/pfsense/pfsense/commit/d9fcbc2048041285690cc28159753acdb0bbb7a3.patch
                              NUT widget (add 'inc' file) - http://github.com/pfsense/pfsense/commit/4dda40b25ee72d899add9fb18e41310dfcc53a17.patch
                              NUT widget ajax callback - http://github.com/pfsense/pfsense/commit/5c5e2a846b4c98cb67fe7828ddb131054d71a644.patch
                              NUT widget update func. - http://github.com/pfsense/pfsense/commit/225ea5746d83b6e79b96904e3f3dfb7c7b7defd4.patch

                              Thank's god, finally. Will test for sure as time allows.

                              General note: People, when you are doing some patches, please post diffs. Do not describe what to edit where. Total waste of time and error prone like hell.

                              Edit: Seems to work pretty well…

                              1 Reply Last reply Reply Quote 0
                              • GertjanG
                                Gertjan
                                last edited by

                                Same thing here.

                                Well done !!

                                No "help me" PM's please. Use the forum, the community will thank you.
                                Edit : and where are the logs ??

                                1 Reply Last reply Reply Quote 0
                                • R
                                  r00f
                                  last edited by

                                  @SunStroke:

                                  No, my commits was not merged till now. You can install "System Patches" package and try to apply my patches individually. I done it for myself.

                                  NUT widget (add main file) - http://github.com/pfsense/pfsense/commit/d9fcbc2048041285690cc28159753acdb0bbb7a3.patch
                                  NUT widget (add 'inc' file) - http://github.com/pfsense/pfsense/commit/4dda40b25ee72d899add9fb18e41310dfcc53a17.patch
                                  NUT widget ajax callback - http://github.com/pfsense/pfsense/commit/5c5e2a846b4c98cb67fe7828ddb131054d71a644.patch
                                  NUT widget update func. - http://github.com/pfsense/pfsense/commit/225ea5746d83b6e79b96904e3f3dfb7c7b7defd4.patch

                                  When patching will be done, you need to remove "/usr/local/www/javascript/index/ajax.js.orig". Cos "/usr/local/www/index.php" load all scripts from "/usr/local/www/javascript/index/" directory and updates are not working.

                                  1 Reply Last reply Reply Quote 0
                                  • arrmoA
                                    arrmo
                                    last edited by

                                    Sorry, perhaps a dumb question, but … is the only way to install (add 4 patches)?

                                    Just trying to understand - this looks very good!

                                    Thanks.

                                    1 Reply Last reply Reply Quote 0
                                    • S
                                      SunStroke
                                      last edited by

                                      @arrmo:

                                      Sorry, perhaps a dumb question, but … is the only way to install (add 4 patches)?

                                      Just trying to understand - this looks very good!

                                      Thanks.

                                      Yes. At this moment add 4 pathes is the only way to install widget. If anyone (maybe i) restructures code to make standalone package, it will be included in main package repository.

                                      1 Reply Last reply Reply Quote 0
                                      • arrmoA
                                        arrmo
                                        last edited by

                                        Works well, thanks! Just a couple minor comments,

                                        • it would be good to have the title "UPS Status" to link to the appropriate page (like other widgets do)
                                        • my UPS shows up fine, all works … but Status shows as "ERROR"?

                                        Thanks again!

                                        1 Reply Last reply Reply Quote 0
                                        • S
                                          SunStroke
                                          last edited by

                                          @arrmo:

                                          Works well, thanks! Just a couple minor comments,

                                          • it would be good to have the title "UPS Status" to link to the appropriate page (like other widgets do)
                                          • my UPS shows up fine, all works … but Status shows as "ERROR"?

                                          Thanks again!

                                          • about link - it is already done in my repository, patch links slightly outdated
                                          • about "ERROR" - what error description is in "Model" column (screenshot, maybe?)?
                                          1 Reply Last reply Reply Quote 0
                                          • arrmoA
                                            arrmo
                                            last edited by

                                            Sounds good on the link, thanks!

                                            As for model / details, here you go (copy / paste, sorry) …

                                            Monitoring Model Status
                                            Local UPS UPS CP600 On Line
                                            Battery Charge Time Remain Battery Voltage
                                            left barred bargray barright bar
                                            100% 0h 52m 0s 13.3 V
                                            Load Input Voltage Output Voltage
                                            left barred bargray barright bar
                                            20% 121.0 V 121.0 V
                                            Monitoring Model Status
                                            Local UPS OL ERROR

                                            Thanks again - this is great!

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.