diff --git a/sysutils/apcupsd/Makefile b/sysutils/apcupsd/Makefile
new file mode 100644
index 000000000..1b08a9907
--- /dev/null
+++ b/sysutils/apcupsd/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= apcupsd
+PLUGIN_DEVEL= yes
+PLUGIN_VERSION= 0.1
+PLUGIN_DEPENDS= apcupsd
+PLUGIN_COMMENT= APCUPSD - APC UPS daemon
+PLUGIN_MAINTAINER= xbb@xbblabs.com
+
+.include "../../Mk/plugins.mk"
diff --git a/sysutils/apcupsd/pkg-descr b/sysutils/apcupsd/pkg-descr
new file mode 100644
index 000000000..28f097d83
--- /dev/null
+++ b/sysutils/apcupsd/pkg-descr
@@ -0,0 +1,21 @@
+Apcupsd, short for APC UPS daemon, can be used for controlling all APC UPS models.
+It can monitor and log the current power and battery status, perform automatic
+shutdown, and can run in network mode in order to power down other hosts on a LAN.
+
+This plugin allows you to configure an APC UPS for use with OPNsense using the
+Apcupsd project. The setup page allows you to set the most common options for
+connecting your UPS to your OPNsense router and a status page for the UPS
+status. It also includes support to act as an APC Netserver.
+
+WWW: http://www.apcupsd.org/
+
+Plugin Changelog
+================
+
+1.0
+
+Initial release
+
+* Apcupsd service control and configuration
+* UPS status page
+* Dashboard widget
diff --git a/sysutils/apcupsd/src/etc/inc/plugins.inc.d/apcupsd.inc b/sysutils/apcupsd/src/etc/inc/plugins.inc.d/apcupsd.inc
new file mode 100644
index 000000000..94a264016
--- /dev/null
+++ b/sysutils/apcupsd/src/etc/inc/plugins.inc.d/apcupsd.inc
@@ -0,0 +1,77 @@
+ gettext('APC UPS Daemon'),
+ 'configd' => array(
+ 'restart' => array('apcupsd restart'),
+ 'start' => array('apcupsd start'),
+ 'stop' => array('apcupsd stop'),
+ ),
+ 'name' => 'apcupsd',
+ 'pidfile' => '/var/run/apcupsd.pid'
+ );
+ }
+ return $services;
+}
+
+/**
+ * sync configuration via xmlrpc
+ * @return array
+ */
+function apcupsd_xmlrpc_sync()
+{
+ $result = array();
+
+ $result[] = array(
+ 'description' => gettext('APC UPS Daemon'),
+ 'section' => 'OPNsense.apcupsd',
+ 'id' => 'apcupsd',
+ 'services' => ["apcupsd"],
+ );
+
+ return $result;
+}
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/ServiceController.php b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/ServiceController.php
new file mode 100644
index 000000000..ea6380e96
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/ServiceController.php
@@ -0,0 +1,151 @@
+getUpsStatusOutput();
+ $result['status'] = null;
+ if (!$result['error']) {
+ $result['status'] = $this->parseUpsStatus($result['output']);
+ }
+ return $result;
+ }
+
+ private function parseUpsStatus($statusOutput)
+ {
+ $status = array();
+ foreach (explode("\n", $statusOutput) as $line) {
+ $kv = array_map('trim', explode(':', $line, 2));
+ $key = $kv[0];
+ $value = isset($kv[1]) ? $kv[1] : null;
+ $norm = $value;
+ if (empty($key)) {
+ continue;
+ }
+ if ($value === 'N/A') {
+ $norm = null;
+ } elseif (in_array($key, self::$dateTimeFields, true)) {
+ $norm = $this->tryParseDateTime($value);
+ } elseif (in_array($key, self::$dateFields, true)) {
+ $norm = $this->tryParseDate($value);
+ } elseif (preg_match('/^((?:[0-9]*[.])?[0-9]+)(?:\s+\w+)?$/i', $value, $matches)) {
+ $norm = floatval($matches[1]);
+ }
+ $status[$key] = array(
+ 'value' => $value,
+ 'norm' => $norm
+ );
+ }
+ return $status;
+ }
+
+ private function tryParseDateTime($dateTimeString) {
+ $formats = [
+ 'Y-m-d H:i:s P', // 2021-12-27 17:51:42 +0100
+ 'D M d H:i:s T Y' // Sat Sep 16 17:13:00 CEST 2000
+ ];
+ foreach ($formats as $format) {
+ $dt = DateTime::createFromFormat($format, $dateTimeString);
+ if ($dt) {
+ return $dt->format(DateTimeInterface::RFC3339);
+ }
+ }
+ return $dateTimeString;
+ }
+
+ private function tryParseDate($dateString) {
+ $formats = [
+ 'Y-m-d', // 2021-12-27
+ 'm/d/y', // 12/27/21
+ ];
+ foreach ($formats as $format) {
+ $dt = DateTime::createFromFormat($format, $dateString);
+ if ($dt) {
+ return $dt->format('Y-m-d');
+ }
+ }
+ return $dateString;
+ }
+
+ private function getUpsStatusOutput()
+ {
+ $output = $error = null;
+
+ if ($this->isEnabled()) {
+ $backend = new Backend();
+ $output = trim($backend->configdRun('apcupsd upsstatus'));
+ if (empty($output)) {
+ $error = 'Error: empty output from apcaccess';
+ }
+ } else {
+ $error = 'Error: apcupsd is disabled';
+ }
+
+ return array(
+ 'error' => $error,
+ 'output' => $output
+ );
+ }
+
+ private function isEnabled()
+ {
+ return $this->getModel()->general->Enabled == '1';
+ }
+}
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/SettingsController.php b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/SettingsController.php
new file mode 100644
index 000000000..a0132319e
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/Api/SettingsController.php
@@ -0,0 +1,41 @@
+view->pick('OPNsense/Apcupsd/index');
+ $this->view->generalForm = $this->getForm("general");
+ }
+}
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/StatusController.php b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/StatusController.php
new file mode 100644
index 000000000..7930cca27
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/StatusController.php
@@ -0,0 +1,47 @@
+view->pick('OPNsense/Apcupsd/status');
+ }
+}
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/forms/general.xml b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/forms/general.xml
new file mode 100644
index 000000000..0aa561920
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/controllers/OPNsense/Apcupsd/forms/general.xml
@@ -0,0 +1,183 @@
+
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/ACL/ACL.xml b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/ACL/ACL.xml
new file mode 100644
index 000000000..a911bfa87
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ Services: Apcupsd System Monitoring page
+
+ ui/apcupsd/*
+ api/apcupsd/*
+
+
+
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Apcupsd.php b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Apcupsd.php
new file mode 100644
index 000000000..98e12f8a3
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Apcupsd.php
@@ -0,0 +1,39 @@
+
+ //OPNsense/apcupsd
+ 0.0.2
+ APC UPS configuration
+
+
+
+ 0
+ Y
+
+
+ N
+ /^([0-9a-zA-Z._\- ]){1,99}$/
+
+ The name should be 1 to 99 characters and contain only alphanumeric characters,
+ dashes, underscores, dot or space.
+
+
+
+ smart
+ Y
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ apcsmart
+ Y
+
+ apcsmart
+ usb
+ net
+ snmp
+ netsnmp
+ dumb
+ pcnet
+ modbus
+
+
+
+ N
+
+
+ Y
+ 60
+ 1
+ 86400
+ Polltime must be between 1 and 86400.
+
+
+ 1
+ Y
+
+
+ 127.0.0.1
+ Y
+
+
+ 3551
+ Y
+
+
+ 6
+ Y
+ 0
+ 60
+ On battery delay must be between 1 and 60.
+
+
+ 5
+ Y
+ 1
+ 99
+ Battery level must be between 1 and 99 percent.
+
+
+ 3
+ Y
+ 1
+ 60
+ Remaining battery minutes must be between 1 and 60 minutes.
+
+
+ 0
+ Y
+ 0
+ 360
+ Timeout must be between 1 and 360 seconds.
+
+
+ 300
+ Y
+ 10
+ 360
+ Annoy time must be between 10 and 360 seconds.
+
+
+ 60
+ Y
+ 10
+ 360
+ Annoy delay time must be between 10 and 360 seconds.
+
+
+ 0
+ Y
+ 0
+ 360
+ Kill delay time must be between 0 and 360 seconds.
+
+
+ standalone
+ Y
+
+ standalone
+ shareslave
+ sharemaster
+
+
+
+ disable
+ Y
+
+ disable
+ share
+
+
+
+ disable
+ Y
+
+ disable
+ timeout
+ percent
+ minutes
+ always
+
+
+
+
+
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Menu/Menu.xml b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Menu/Menu.xml
new file mode 100644
index 000000000..557d3f8ab
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/models/OPNsense/Apcupsd/Menu/Menu.xml
@@ -0,0 +1,8 @@
+
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/index.volt b/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/index.volt
new file mode 100644
index 000000000..ddda5a92a
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/index.volt
@@ -0,0 +1,74 @@
+{#
+ # Copyright (C) 2021 Dan Lundqvist
+ # Copyright (C) 2021 David Berry
+ # Copyright (C) 2021 Nicola Pellegrini
+ #
+ # All rights reserved.
+ #
+ # 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.
+ #}
+
+
diff --git a/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/status.volt b/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/status.volt
new file mode 100644
index 000000000..951f112a7
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/mvc/app/views/OPNsense/Apcupsd/status.volt
@@ -0,0 +1,42 @@
+{#
+ # Copyright (C) 2021 Dan Lundqvist
+ # Copyright (C) 2021 David Berry
+ # Copyright (C) 2021 Nicola Pellegrini
+ #
+ # All rights reserved.
+ #
+ # 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.
+ #}
+
No status
+
diff --git a/sysutils/apcupsd/src/opnsense/service/conf/actions.d/actions_apcupsd.conf b/sysutils/apcupsd/src/opnsense/service/conf/actions.d/actions_apcupsd.conf
new file mode 100644
index 000000000..03896d5e3
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/service/conf/actions.d/actions_apcupsd.conf
@@ -0,0 +1,29 @@
+[start]
+command:/usr/local/etc/rc.d/apcupsd start
+parameters:
+type:script
+message:starting apcupsd
+
+[stop]
+command:/usr/local/etc/rc.d/apcupsd stop
+parameters:
+type:script
+message:stopping apcupsd
+
+[restart]
+command:/usr/local/etc/rc.d/apcupsd restart
+parameters:
+type:script
+message:restarting apcupsd
+
+[status]
+command:/usr/local/etc/rc.d/apcupsd status;exit 0
+parameters:
+type:script_output
+message:requesting apcupsd status
+
+[upsstatus]
+command:/usr/local/sbin/apcaccess
+parameters:
+type:script_output
+message:requesting UPS Status
diff --git a/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/+TARGETS b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/+TARGETS
new file mode 100644
index 000000000..3ad405f59
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/+TARGETS
@@ -0,0 +1,2 @@
+apcupsd:/etc/rc.conf.d/apcupsd
+apcupsd.conf:/usr/local/etc/apcupsd/apcupsd.conf
diff --git a/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd
new file mode 100644
index 000000000..cc639badf
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd
@@ -0,0 +1,23 @@
+{% if helpers.exists('OPNsense.apcupsd.general.Enabled') and OPNsense.apcupsd.general.Enabled == "1" %}
+apcupsd_enable="YES"
+{% else %}
+apcupsd_enable="NO"
+{% endif %}
+
+# Hook to post start/restart commands waiting for the pid file for max 3 seconds
+# this prevents the status command to erroneously report the service as stopped
+# since apcupsd detaches before the pid file creation
+
+start_postcmd="${name}_post_start_restart"
+restart_postcmd="${name}_post_start_restart"
+
+apcupsd_post_start_restart()
+{
+ if [ -n "$pidfile" ]; then
+ i=0
+ while [ ! -f "$pidfile" ] && [ "$i" -le 5 ]; do
+ i=$(( i + 1 ))
+ sleep 0.5
+ done
+ fi
+}
diff --git a/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd.conf b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd.conf
new file mode 100644
index 000000000..730587a3a
--- /dev/null
+++ b/sysutils/apcupsd/src/opnsense/service/templates/OPNsense/Apcupsd/apcupsd.conf
@@ -0,0 +1,356 @@
+## apcupsd.conf v1.1 ##
+#
+# for apcupsd release 3.14.14 (31 May 2016) - freebsd
+
+##############################################################
+#
+# This file is auto-generated - any changes made will be lost
+#
+#############################################################
+
+# "apcupsd" POSIX config file
+
+#
+# Note that the apcupsd daemon must be restarted in order for changes to
+# this configuration file to become active.
+#
+
+#
+# ========= General configuration parameters ============
+#
+
+# UPSNAME xxx
+# Use this to give your UPS a name in log files and such. This
+# is particulary useful if you have multiple UPSes. This does not
+# set the EEPROM. It should be 8 characters or less.
+{% if not helpers.empty('OPNsense.apcupsd.general.UPSName') %}
+UPSNAME {{ OPNsense.apcupsd.general.UPSName }}
+{% endif %}
+
+# UPSCABLE
+# Defines the type of cable connecting the UPS to your computer.
+#
+# Possible generic choices for are:
+# simple, smart, ether, usb
+#
+# Or a specific cable model number may be used:
+# 940-0119A, 940-0127A, 940-0128A, 940-0020B,
+# 940-0020C, 940-0023A, 940-0024B, 940-0024C,
+# 940-1524C, 940-0024G, 940-0095A, 940-0095B,
+# 940-0095C, 940-0625A, M-04-02-2000
+#
+UPSCABLE {{ OPNsense.apcupsd.general.UPSCable }}
+
+# To get apcupsd to work, in addition to defining the cable
+# above, you must also define a UPSTYPE, which corresponds to
+# the type of UPS you have (see the Description for more details).
+# You must also specify a DEVICE, sometimes referred to as a port.
+# For USB UPSes, please leave the DEVICE directive blank. For
+# other UPS types, you must specify an appropriate port or address.
+#
+# UPSTYPE DEVICE Description
+# apcsmart /dev/tty** Newer serial character device, appropriate for
+# SmartUPS models using a serial cable (not USB).
+#
+# usb Most new UPSes are USB. A blank DEVICE
+# setting enables autodetection, which is
+# the best choice for most installations.
+#
+# net hostname:port Network link to a master apcupsd through apcupsd's
+# Network Information Server. This is used if the
+# UPS powering your computer is connected to a
+# different computer for monitoring.
+#
+# snmp hostname:port:vendor:community
+# SNMP network link to an SNMP-enabled UPS device.
+# Hostname is the ip address or hostname of the UPS
+# on the network. Vendor can be can be "APC" or
+# "APC_NOTRAP". "APC_NOTRAP" will disable SNMP trap
+# catching; you usually want "APC". Port is usually
+# 161. Community is usually "private".
+#
+# netsnmp hostname:port:vendor:community
+# OBSOLETE
+# Same as SNMP above but requires use of the
+# net-snmp library. Unless you have a specific need
+# for this old driver, you should use 'snmp' instead.
+#
+# dumb /dev/tty** Old serial character device for use with
+# simple-signaling UPSes.
+#
+# pcnet ipaddr:username:passphrase:port
+# PowerChute Network Shutdown protocol which can be
+# used as an alternative to SNMP with the AP9617
+# family of smart slot cards. ipaddr is the IP
+# address of the UPS management card. username and
+# passphrase are the credentials for which the card
+# has been configured. port is the port number on
+# which to listen for messages from the UPS, normally
+# 3052. If this parameter is empty or missing, the
+# default of 3052 will be used.
+#
+# modbus /dev/tty** Serial device for use with newest SmartUPS models
+# supporting the MODBUS protocol.
+# modbus Leave the DEVICE setting blank for MODBUS over USB
+# or set to the serial number of the UPS to ensure
+# that apcupsd binds to that particular unit
+# (helpful if you have more than one USB UPS).
+#
+UPSTYPE {{ OPNsense.apcupsd.general.UPSType|default("apcsmart") }}
+DEVICE {{ OPNsense.apcupsd.general.Device }}
+
+# POLLTIME
+# Interval (in seconds) at which apcupsd polls the UPS for status. This
+# setting applies both to directly-attached UPSes (UPSTYPE apcsmart, usb,
+# dumb) and networked UPSes (UPSTYPE net, snmp). Lowering this setting
+# will improve apcupsd's responsiveness to certain events at the cost of
+# higher CPU utilization. The default of 60 is appropriate for most
+# situations.
+POLLTIME {{ OPNsense.apcupsd.general.Polltime|default("60") }}
+
+# LOCKFILE
+# Path for device lock file. This is the directory into which the lock file
+# will be written. The directory must already exist; apcupsd will not create
+# it. The actual name of the lock file is computed from DEVICE.
+# Not used on Win32.
+LOCKFILE /var/spool/lock
+
+# SCRIPTDIR
+# Directory in which apccontrol and event scripts are located.
+SCRIPTDIR /usr/local/etc/apcupsd
+
+# PWRFAILDIR
+# Directory in which to write the powerfail flag file. This file
+# is created when apcupsd initiates a system shutdown and is
+# checked in the OS halt scripts to determine if a killpower
+# (turning off UPS output power) is required.
+PWRFAILDIR /var/run
+
+# NOLOGINDIR
+# Directory in which to write the nologin file. The existence
+# of this flag file tells the OS to disallow new logins.
+NOLOGINDIR /var/run
+
+
+#
+# ======== Configuration parameters used during power failures ==========
+#
+
+# The ONBATTERYDELAY is the time in seconds from when a power failure
+# is detected until we react to it with an onbattery event.
+#
+# This means that, apccontrol will be called with the powerout argument
+# immediately when a power failure is detected. However, the
+# onbattery argument is passed to apccontrol only after the
+# ONBATTERYDELAY time. If you don't want to be annoyed by short
+# powerfailures, make sure that apccontrol powerout does nothing
+# i.e. comment out the wall.
+ONBATTERYDELAY {{ OPNsense.apcupsd.general.OnBatteryDelay|default("6") }}
+
+#
+# Note: BATTERYLEVEL, MINUTES, and TIMEOUT work in conjunction, so
+# the first that occurs will cause the initation of a shutdown.
+#
+
+# If during a power failure, the remaining battery percentage
+# (as reported by the UPS) is below or equal to BATTERYLEVEL,
+# apcupsd will initiate a system shutdown.
+BATTERYLEVEL {{ OPNsense.apcupsd.general.BatteryLevel|default("5") }}
+
+# If during a power failure, the remaining runtime in minutes
+# (as calculated internally by the UPS) is below or equal to MINUTES,
+# apcupsd, will initiate a system shutdown.
+MINUTES {{ OPNsense.apcupsd.general.Minutes|default("3") }}
+
+# If during a power failure, the UPS has run on batteries for TIMEOUT
+# many seconds or longer, apcupsd will initiate a system shutdown.
+# A value of 0 disables this timer.
+#
+# Note, if you have a Smart UPS, you will most likely want to disable
+# this timer by setting it to zero. That way, you UPS will continue
+# on batteries until either the % charge remaing drops to or below BATTERYLEVEL,
+# or the remaining battery runtime drops to or below MINUTES. Of course,
+# if you are testing, setting this to 60 causes a quick system shutdown
+# if you pull the power plug.
+# If you have an older dumb UPS, you will want to set this to less than
+# the time you know you can run on batteries.
+TIMEOUT {{ OPNsense.apcupsd.general.Timeout|default("0") }}
+
+# Time in seconds between annoying users to signoff prior to
+# system shutdown. 0 disables.
+ANNOY {{ OPNsense.apcupsd.general.Annoy|default("300") }}
+
+# Initial delay after power failure before warning users to get
+# off the system.
+ANNOYDELAY {{ OPNsense.apcupsd.general.AnnoyDelay|default("60") }}
+
+# The condition which determines when users are prevented from
+# logging in during a power failure.
+# NOLOGON [ disable | timeout | percent | minutes | always ]
+NOLOGON {{ OPNsense.apcupsd.general.NoLogon|default("disable") }}
+
+# If KILLDELAY is non-zero, apcupsd will continue running after a
+# shutdown has been requested, and after the specified time in
+# seconds attempt to kill the power. This is for use on systems
+# where apcupsd cannot regain control after a shutdown.
+# KILLDELAY 0 disables
+KILLDELAY {{ OPNsense.apcupsd.general.KillDelay|default("0") }}
+
+#
+# ==== Configuration statements for Network Information Server ====
+#
+
+# NETSERVER [ on | off ] on enables, off disables the network
+# information server. If netstatus is on, a network information
+# server process will be started for serving the STATUS and
+# EVENT data over the network (used by CGI programs).
+{% if helpers.exists('OPNsense.apcupsd.general.Netserver') and OPNsense.apcupsd.general.Netserver == "1" %}
+NETSERVER on
+{% else %}
+NETSERVER off
+{% endif %}
+
+# NISIP
+# IP address on which NIS server will listen for incoming connections.
+# This is useful if your server is multi-homed (has more than one
+# network interface and IP address). Default value is 0.0.0.0 which
+# means any incoming request will be serviced. Alternatively, you can
+# configure this setting to any specific IP address of your server and
+# NIS will listen for connections only on that interface. Use the
+# loopback address (127.0.0.1) to accept connections only from the
+# local machine.
+NISIP {{ OPNsense.apcupsd.general.NetserverAddress|default("127.0.0.1") }}
+
+# NISPORT default is 3551 as registered with the IANA
+# port to use for sending STATUS and EVENTS data over the network.
+# It is not used unless NETSERVER is on. If you change this port,
+# you will need to change the corresponding value in the cgi directory
+# and rebuild the cgi programs.
+NISPORT {{ OPNsense.apcupsd.general.NetserverPort|default("3551") }}
+
+# If you want the last few EVENTS to be available over the network
+# by the network information server, you must define an EVENTSFILE.
+EVENTSFILE /var/log/apcupsd.events
+
+# EVENTSFILEMAX
+# By default, the size of the EVENTSFILE will be not be allowed to exceed
+# 10 kilobytes. When the file grows beyond this limit, older EVENTS will
+# be removed from the beginning of the file (first in first out). The
+# parameter EVENTSFILEMAX can be set to a different kilobyte value, or set
+# to zero to allow the EVENTSFILE to grow without limit.
+EVENTSFILEMAX 10
+
+#
+# ========== Configuration statements used if sharing =============
+# a UPS with more than one machine
+
+#
+# Remaining items are for ShareUPS (APC expansion card) ONLY
+#
+
+# UPSCLASS [ standalone | shareslave | sharemaster ]
+# Normally standalone unless you share an UPS using an APC ShareUPS
+# card.
+UPSCLASS {{ OPNsense.apcupsd.general.UPSClass|default("standalone") }}
+
+# UPSMODE [ disable | share ]
+# Normally disable unless you share an UPS using an APC ShareUPS card.
+UPSMODE {{ OPNsense.apcupsd.general.UPSMode|default("disable") }}
+
+#
+# ===== Configuration statements to control apcupsd system logging ========
+#
+
+# Time interval in seconds between writing the STATUS file; 0 disables
+STATTIME 0
+
+# Location of STATUS file (written to only if STATTIME is non-zero)
+STATFILE /var/log/apcupsd.status
+
+# LOGSTATS [ on | off ] on enables, off disables
+# Note! This generates a lot of output, so if
+# you turn this on, be sure that the
+# file defined in syslog.conf for LOG_NOTICE is a named pipe.
+# You probably do not want this on.
+LOGSTATS off
+
+# Time interval in seconds between writing the DATA records to
+# the log file. 0 disables.
+DATATIME 0
+
+# FACILITY defines the logging facility (class) for logging to syslog.
+# If not specified, it defaults to "daemon". This is useful
+# if you want to separate the data logged by apcupsd from other
+# programs.
+#FACILITY DAEMON
+
+#
+# ========== Configuration statements used in updating the UPS EPROM =========
+#
+
+#
+# These statements are used only by apctest when choosing "Set EEPROM with conf
+# file values" from the EEPROM menu. THESE STATEMENTS HAVE NO EFFECT ON APCUPSD.
+#
+
+# UPS name, max 8 characters
+#UPSNAME UPS_IDEN
+
+# Battery date - 8 characters
+#BATTDATE mm/dd/yy
+
+# Sensitivity to line voltage quality (H cause faster transfer to batteries)
+# SENSITIVITY H M L (default = H)
+#SENSITIVITY H
+
+# UPS delay after power return (seconds)
+# WAKEUP 000 060 180 300 (default = 0)
+#WAKEUP 60
+
+# UPS Grace period after request to power off (seconds)
+# SLEEP 020 180 300 600 (default = 20)
+#SLEEP 180
+
+# Low line voltage causing transfer to batteries
+# The permitted values depend on your model as defined by last letter
+# of FIRMWARE or APCMODEL. Some representative values are:
+# D 106 103 100 097
+# M 177 172 168 182
+# A 092 090 088 086
+# I 208 204 200 196 (default = 0 => not valid)
+#LOTRANSFER 208
+
+# High line voltage causing transfer to batteries
+# The permitted values depend on your model as defined by last letter
+# of FIRMWARE or APCMODEL. Some representative values are:
+# D 127 130 133 136
+# M 229 234 239 224
+# A 108 110 112 114
+# I 253 257 261 265 (default = 0 => not valid)
+#HITRANSFER 253
+
+# Battery charge needed to restore power
+# RETURNCHARGE 00 15 50 90 (default = 15)
+#RETURNCHARGE 15
+
+# Alarm delay
+# 0 = zero delay after pwr fail, T = power fail + 30 sec, L = low battery, N = never
+# BEEPSTATE 0 T L N (default = 0)
+#BEEPSTATE T
+
+# Low battery warning delay in minutes
+# LOWBATT 02 05 07 10 (default = 02)
+#LOWBATT 2
+
+# UPS Output voltage when running on batteries
+# The permitted values depend on your model as defined by last letter
+# of FIRMWARE or APCMODEL. Some representative values are:
+# D 115
+# M 208
+# A 100
+# I 230 240 220 225 (default = 0 => not valid)
+#OUTPUTVOLTS 230
+
+# Self test interval in hours 336=2 weeks, 168=1 week, ON=at power on
+# SELFTEST 336 168 ON OFF (default = 336)
+#SELFTEST 336
diff --git a/sysutils/apcupsd/src/www/widgets/include/apcupsd.inc b/sysutils/apcupsd/src/www/widgets/include/apcupsd.inc
new file mode 100644
index 000000000..26fb46664
--- /dev/null
+++ b/sysutils/apcupsd/src/www/widgets/include/apcupsd.inc
@@ -0,0 +1,4 @@
+
+